2013-12-05 15:44:18 +00:00
|
|
|
#include "TerrainRasterizer.h"
|
2012-12-09 17:49:28 +00:00
|
|
|
|
2015-12-08 23:32:29 +00:00
|
|
|
#include <cmath>
|
2013-12-05 15:44:18 +00:00
|
|
|
#include "SoftwareRenderer.h"
|
2013-11-14 17:47:03 +00:00
|
|
|
#include "BoundingBox.h"
|
2013-12-05 15:44:18 +00:00
|
|
|
#include "CameraDefinition.h"
|
2013-12-08 19:54:34 +00:00
|
|
|
#include "TerrainRenderer.h"
|
|
|
|
#include "WaterRenderer.h"
|
|
|
|
#include "TexturesRenderer.h"
|
|
|
|
#include "Scenery.h"
|
2014-06-18 20:10:46 +00:00
|
|
|
#include "CanvasPortion.h"
|
2014-08-18 10:17:16 +00:00
|
|
|
#include "CanvasFragment.h"
|
2015-08-23 18:22:37 +00:00
|
|
|
#include "RenderProgress.h"
|
2012-12-09 17:49:28 +00:00
|
|
|
|
2015-12-14 21:20:28 +00:00
|
|
|
TerrainRasterizer::TerrainRasterizer(SoftwareRenderer *renderer, RenderProgress *progress, unsigned short client_id)
|
|
|
|
: Rasterizer(renderer, progress, client_id, Color(1.0, 0.7, 0.7)) {
|
2015-10-18 15:26:19 +00:00
|
|
|
setBackFaceCulling(true);
|
|
|
|
yoffset = 0.0;
|
2013-12-05 15:44:18 +00:00
|
|
|
}
|
2012-12-09 17:49:28 +00:00
|
|
|
|
2015-11-09 21:30:46 +00:00
|
|
|
void TerrainRasterizer::setQuality(double base_chunk_size, double detail_factor, int max_chunk_detail) {
|
2015-09-10 17:33:52 +00:00
|
|
|
this->base_chunk_size = base_chunk_size;
|
|
|
|
this->detail_factor = detail_factor;
|
|
|
|
this->max_chunk_detail = max_chunk_detail;
|
|
|
|
}
|
|
|
|
|
2015-11-09 21:30:46 +00:00
|
|
|
void TerrainRasterizer::setQuality(double factor) {
|
|
|
|
setQuality(5.0 - 4.5 * factor * factor, 1.0 / (25.0 - 20.0 * factor), 1 + 49 * factor * factor);
|
2015-09-10 17:33:52 +00:00
|
|
|
}
|
|
|
|
|
2015-11-09 21:30:46 +00:00
|
|
|
static inline Vector3 _getPoint(SoftwareRenderer *renderer, double x, double z) {
|
2014-11-21 08:45:19 +00:00
|
|
|
return Vector3(x, renderer->getTerrainRenderer()->getHeight(x, z, true), z);
|
2012-12-09 17:49:28 +00:00
|
|
|
}
|
|
|
|
|
2015-11-09 21:30:46 +00:00
|
|
|
void TerrainRasterizer::tessellateChunk(CanvasPortion *canvas, TerrainChunkInfo *chunk, int detail) {
|
2014-06-18 20:10:46 +00:00
|
|
|
double water_height = renderer->getWaterRenderer()->getHeightInfo().min_height;
|
|
|
|
|
|
|
|
double startx = chunk->point_nw.x;
|
|
|
|
double startz = chunk->point_nw.z;
|
2015-12-17 00:13:20 +00:00
|
|
|
double size = (chunk->point_ne.x - chunk->point_nw.x) / to_double(detail);
|
2014-06-18 20:10:46 +00:00
|
|
|
int i, j;
|
|
|
|
|
2015-11-09 21:30:46 +00:00
|
|
|
for (i = 0; i < detail; i++) {
|
|
|
|
for (j = 0; j < detail; j++) {
|
2015-12-17 00:13:20 +00:00
|
|
|
renderQuad(canvas, startx + to_double(i) * size, startz + to_double(j) * size, size, water_height);
|
2014-06-18 20:10:46 +00:00
|
|
|
}
|
2015-08-23 18:22:37 +00:00
|
|
|
progress->add(detail);
|
2014-06-18 20:10:46 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-11-09 21:30:46 +00:00
|
|
|
void TerrainRasterizer::renderQuad(CanvasPortion *canvas, double x, double z, double size, double water_height) {
|
2013-04-06 13:29:12 +00:00
|
|
|
Vector3 ov1, ov2, ov3, ov4;
|
|
|
|
Vector3 dv1, dv2, dv3, dv4;
|
2012-12-09 17:49:28 +00:00
|
|
|
|
2013-06-26 15:28:21 +00:00
|
|
|
ov1.y = ov2.y = ov3.y = ov4.y = 0.0;
|
2013-04-04 20:44:49 +00:00
|
|
|
|
2013-04-06 13:29:12 +00:00
|
|
|
ov1.x = x;
|
|
|
|
ov1.z = z;
|
2016-01-10 13:27:32 +00:00
|
|
|
dv1 = renderer->getTerrainRenderer()->getDisplaced(x, z, true);
|
2012-12-09 17:49:28 +00:00
|
|
|
|
2013-04-06 13:29:12 +00:00
|
|
|
ov2.x = x;
|
|
|
|
ov2.z = z + size;
|
2016-01-10 13:27:32 +00:00
|
|
|
dv2 = renderer->getTerrainRenderer()->getDisplaced(x, z + size, true);
|
2013-04-06 13:29:12 +00:00
|
|
|
|
|
|
|
ov3.x = x + size;
|
|
|
|
ov3.z = z + size;
|
2016-01-10 13:27:32 +00:00
|
|
|
dv3 = renderer->getTerrainRenderer()->getDisplaced(x + size, z + size, true);
|
2013-04-06 13:29:12 +00:00
|
|
|
|
|
|
|
ov4.x = x + size;
|
|
|
|
ov4.z = z;
|
2016-01-10 13:27:32 +00:00
|
|
|
dv4 = renderer->getTerrainRenderer()->getDisplaced(x + size, z, true);
|
2013-04-06 13:29:12 +00:00
|
|
|
|
2015-11-09 21:38:00 +00:00
|
|
|
if (yoffset != 0.0) {
|
2015-10-18 15:26:19 +00:00
|
|
|
dv1.y += yoffset;
|
|
|
|
dv2.y += yoffset;
|
|
|
|
dv3.y += yoffset;
|
|
|
|
dv4.y += yoffset;
|
|
|
|
}
|
|
|
|
|
2015-11-09 21:30:46 +00:00
|
|
|
if (dv1.y > water_height || dv2.y > water_height || dv3.y > water_height || dv4.y > water_height) {
|
2014-06-18 20:10:46 +00:00
|
|
|
pushDisplacedQuad(canvas, dv1, dv2, dv3, dv4, ov1, ov2, ov3, ov4);
|
2013-06-27 09:47:16 +00:00
|
|
|
}
|
2013-06-26 15:28:21 +00:00
|
|
|
}
|
|
|
|
|
2015-11-09 21:38:00 +00:00
|
|
|
void TerrainRasterizer::setYOffset(double offset) {
|
2015-10-18 15:26:19 +00:00
|
|
|
this->yoffset = offset;
|
|
|
|
}
|
|
|
|
|
2015-11-09 21:38:00 +00:00
|
|
|
void TerrainRasterizer::getChunk(SoftwareRenderer *renderer, TerrainRasterizer::TerrainChunkInfo *chunk, double x,
|
2016-01-10 13:27:32 +00:00
|
|
|
double z, double size) {
|
|
|
|
chunk->point_nw = renderer->getTerrainRenderer()->getResult(x, z, true).location;
|
|
|
|
chunk->point_sw = renderer->getTerrainRenderer()->getResult(x, z + size, true).location;
|
|
|
|
chunk->point_se = renderer->getTerrainRenderer()->getResult(x + size, z + size, true).location;
|
|
|
|
chunk->point_ne = renderer->getTerrainRenderer()->getResult(x + size, z, true).location;
|
2013-06-26 15:28:21 +00:00
|
|
|
|
2015-11-09 21:38:00 +00:00
|
|
|
if (yoffset != 0.0) {
|
2015-10-18 15:26:19 +00:00
|
|
|
chunk->point_nw.y += yoffset;
|
|
|
|
chunk->point_sw.y += yoffset;
|
|
|
|
chunk->point_se.y += yoffset;
|
|
|
|
chunk->point_ne.y += yoffset;
|
|
|
|
}
|
|
|
|
|
2016-01-10 13:27:32 +00:00
|
|
|
double displacement_power =
|
|
|
|
renderer->getTexturesRenderer()->getMaximalDisplacement(renderer->getScenery()->getTextures());
|
2013-06-27 09:47:16 +00:00
|
|
|
|
|
|
|
BoundingBox box;
|
2015-11-09 21:30:46 +00:00
|
|
|
if (displacement_power > 0.0) {
|
2013-12-11 10:32:10 +00:00
|
|
|
box.pushPoint(chunk->point_nw.add(Vector3(-displacement_power, displacement_power, -displacement_power)));
|
|
|
|
box.pushPoint(chunk->point_nw.add(Vector3(-displacement_power, -displacement_power, -displacement_power)));
|
|
|
|
box.pushPoint(chunk->point_sw.add(Vector3(-displacement_power, displacement_power, displacement_power)));
|
|
|
|
box.pushPoint(chunk->point_sw.add(Vector3(-displacement_power, -displacement_power, displacement_power)));
|
|
|
|
box.pushPoint(chunk->point_se.add(Vector3(displacement_power, displacement_power, displacement_power)));
|
|
|
|
box.pushPoint(chunk->point_se.add(Vector3(displacement_power, -displacement_power, displacement_power)));
|
|
|
|
box.pushPoint(chunk->point_ne.add(Vector3(displacement_power, displacement_power, -displacement_power)));
|
|
|
|
box.pushPoint(chunk->point_ne.add(Vector3(displacement_power, -displacement_power, -displacement_power)));
|
2015-11-09 21:30:46 +00:00
|
|
|
} else {
|
2013-11-14 17:47:03 +00:00
|
|
|
box.pushPoint(chunk->point_nw);
|
|
|
|
box.pushPoint(chunk->point_sw);
|
|
|
|
box.pushPoint(chunk->point_se);
|
|
|
|
box.pushPoint(chunk->point_ne);
|
2013-06-27 09:47:16 +00:00
|
|
|
}
|
2013-06-26 15:28:21 +00:00
|
|
|
|
2013-11-14 17:47:03 +00:00
|
|
|
int coverage = renderer->render_camera->isUnprojectedBoxInView(box);
|
2015-11-09 21:30:46 +00:00
|
|
|
if (coverage > 0) {
|
2015-12-17 00:13:20 +00:00
|
|
|
chunk->detail_hint = ceil_to_int(sqrt(to_double(coverage)) * detail_factor);
|
2015-11-09 21:30:46 +00:00
|
|
|
if (chunk->detail_hint > max_chunk_detail) {
|
2015-09-10 17:33:52 +00:00
|
|
|
chunk->detail_hint = max_chunk_detail;
|
2013-06-27 09:47:16 +00:00
|
|
|
}
|
2015-11-09 21:30:46 +00:00
|
|
|
} else {
|
2013-06-27 09:47:16 +00:00
|
|
|
/* Not in view */
|
2013-06-26 15:28:21 +00:00
|
|
|
chunk->detail_hint = -1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-01-10 13:27:32 +00:00
|
|
|
int TerrainRasterizer::performTessellation(CanvasPortion *canvas) {
|
2013-06-26 15:28:21 +00:00
|
|
|
TerrainChunkInfo chunk;
|
2015-08-23 18:22:37 +00:00
|
|
|
int chunk_factor, chunk_count, i, result;
|
2016-01-14 23:07:02 +00:00
|
|
|
Vector3 cam = renderer->getCameraLocation();
|
2013-06-26 15:28:21 +00:00
|
|
|
double radius_int, radius_ext;
|
2015-09-10 17:33:52 +00:00
|
|
|
double chunk_size;
|
2012-12-09 17:49:28 +00:00
|
|
|
|
|
|
|
chunk_factor = 1;
|
|
|
|
chunk_count = 2;
|
|
|
|
radius_int = 0.0;
|
2013-06-26 15:28:21 +00:00
|
|
|
radius_ext = base_chunk_size;
|
|
|
|
chunk_size = base_chunk_size;
|
2015-08-23 18:22:37 +00:00
|
|
|
result = 0;
|
2012-12-09 17:49:28 +00:00
|
|
|
|
2013-06-27 09:47:16 +00:00
|
|
|
double cx = cam.x - fmod(cam.x, base_chunk_size);
|
|
|
|
double cz = cam.z - fmod(cam.x, base_chunk_size);
|
|
|
|
|
2016-01-14 23:39:33 +00:00
|
|
|
while (radius_int < Scenery::FAR_LIMIT_SCALED) {
|
2015-11-09 21:30:46 +00:00
|
|
|
for (i = 0; i < chunk_count - 1; i++) {
|
2016-01-10 13:27:32 +00:00
|
|
|
getChunk(renderer, &chunk, cx - radius_ext + chunk_size * i, cz - radius_ext, chunk_size);
|
2015-11-09 21:30:46 +00:00
|
|
|
if (chunk.detail_hint > 0) {
|
2015-08-23 18:22:37 +00:00
|
|
|
result += chunk.detail_hint * chunk.detail_hint;
|
2015-11-09 21:30:46 +00:00
|
|
|
if (canvas) {
|
2015-08-23 18:22:37 +00:00
|
|
|
processChunk(canvas, &chunk);
|
|
|
|
}
|
|
|
|
}
|
2015-11-09 21:30:46 +00:00
|
|
|
if (interrupted) {
|
2015-08-23 18:22:37 +00:00
|
|
|
return result;
|
2013-06-26 15:28:21 +00:00
|
|
|
}
|
|
|
|
|
2016-01-10 13:27:32 +00:00
|
|
|
getChunk(renderer, &chunk, cx + radius_int, cz - radius_ext + chunk_size * i, chunk_size);
|
2015-11-09 21:30:46 +00:00
|
|
|
if (chunk.detail_hint > 0) {
|
2015-08-23 18:22:37 +00:00
|
|
|
result += chunk.detail_hint * chunk.detail_hint;
|
2015-11-09 21:30:46 +00:00
|
|
|
if (canvas) {
|
2015-08-23 18:22:37 +00:00
|
|
|
processChunk(canvas, &chunk);
|
|
|
|
}
|
|
|
|
}
|
2015-11-09 21:30:46 +00:00
|
|
|
if (interrupted) {
|
2015-08-23 18:22:37 +00:00
|
|
|
return result;
|
2013-06-26 15:28:21 +00:00
|
|
|
}
|
|
|
|
|
2016-01-10 13:27:32 +00:00
|
|
|
getChunk(renderer, &chunk, cx + radius_int - chunk_size * i, cz + radius_int, chunk_size);
|
2015-11-09 21:30:46 +00:00
|
|
|
if (chunk.detail_hint > 0) {
|
2015-08-23 18:22:37 +00:00
|
|
|
result += chunk.detail_hint * chunk.detail_hint;
|
2015-11-09 21:30:46 +00:00
|
|
|
if (canvas) {
|
2015-08-23 18:22:37 +00:00
|
|
|
processChunk(canvas, &chunk);
|
|
|
|
}
|
|
|
|
}
|
2015-11-09 21:30:46 +00:00
|
|
|
if (interrupted) {
|
2015-08-23 18:22:37 +00:00
|
|
|
return result;
|
2013-06-26 15:28:21 +00:00
|
|
|
}
|
|
|
|
|
2016-01-10 13:27:32 +00:00
|
|
|
getChunk(renderer, &chunk, cx - radius_ext, cz + radius_int - chunk_size * i, chunk_size);
|
2015-11-09 21:30:46 +00:00
|
|
|
if (chunk.detail_hint > 0) {
|
2015-08-23 18:22:37 +00:00
|
|
|
result += chunk.detail_hint * chunk.detail_hint;
|
2015-11-09 21:30:46 +00:00
|
|
|
if (canvas) {
|
2015-08-23 18:22:37 +00:00
|
|
|
processChunk(canvas, &chunk);
|
|
|
|
}
|
|
|
|
}
|
2015-11-09 21:30:46 +00:00
|
|
|
if (interrupted) {
|
2015-08-23 18:22:37 +00:00
|
|
|
return result;
|
2013-06-26 15:28:21 +00:00
|
|
|
}
|
2012-12-09 17:49:28 +00:00
|
|
|
}
|
|
|
|
|
2015-12-17 00:13:20 +00:00
|
|
|
if (radius_int > 20.0 && chunk_count % 64 == 0 && to_double(chunk_factor) < radius_int / 20.0) {
|
2012-12-09 17:49:28 +00:00
|
|
|
chunk_count /= 2;
|
|
|
|
chunk_factor *= 2;
|
|
|
|
}
|
|
|
|
chunk_count += 2;
|
2013-06-26 15:28:21 +00:00
|
|
|
chunk_size = base_chunk_size * chunk_factor;
|
2012-12-09 17:49:28 +00:00
|
|
|
radius_int = radius_ext;
|
|
|
|
radius_ext += chunk_size;
|
|
|
|
}
|
2015-08-23 18:22:37 +00:00
|
|
|
|
|
|
|
return result;
|
2012-12-09 17:49:28 +00:00
|
|
|
}
|
2013-06-26 15:28:21 +00:00
|
|
|
|
2015-11-09 21:30:46 +00:00
|
|
|
void TerrainRasterizer::processChunk(CanvasPortion *canvas, TerrainChunkInfo *chunk) {
|
2014-08-19 09:32:23 +00:00
|
|
|
tessellateChunk(canvas, chunk, chunk->detail_hint);
|
2013-06-26 15:28:21 +00:00
|
|
|
}
|
|
|
|
|
2015-11-09 21:30:46 +00:00
|
|
|
int TerrainRasterizer::prepareRasterization() {
|
2015-08-23 18:22:37 +00:00
|
|
|
// TODO Chunks could be saved and reused in rasterizeToCanvas
|
2016-01-10 13:27:32 +00:00
|
|
|
return performTessellation(NULL);
|
2015-08-23 18:22:37 +00:00
|
|
|
}
|
|
|
|
|
2015-11-09 21:30:46 +00:00
|
|
|
void TerrainRasterizer::rasterizeToCanvas(CanvasPortion *canvas) {
|
2016-01-10 13:27:32 +00:00
|
|
|
performTessellation(canvas);
|
2013-06-26 15:28:21 +00:00
|
|
|
}
|
2014-08-18 10:17:16 +00:00
|
|
|
|
2015-11-09 21:30:46 +00:00
|
|
|
Color TerrainRasterizer::shadeFragment(const CanvasFragment &fragment, const CanvasFragment *) const {
|
2014-08-18 10:17:16 +00:00
|
|
|
Vector3 point = fragment.getLocation();
|
|
|
|
double precision = renderer->getPrecision(_getPoint(renderer, point.x, point.z));
|
2016-01-10 13:27:32 +00:00
|
|
|
return renderer->getTerrainRenderer()->getFinalColor(point.x, point.z, precision);
|
2014-08-18 10:17:16 +00:00
|
|
|
}
|