2013-12-05 15:44:18 +00:00
|
|
|
#include "TerrainRasterizer.h"
|
2012-12-09 17:49:28 +00:00
|
|
|
|
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"
|
2013-12-09 10:59:57 +00:00
|
|
|
#include "ParallelQueue.h"
|
2012-12-09 17:49:28 +00:00
|
|
|
|
2013-12-05 15:44:18 +00:00
|
|
|
TerrainRasterizer::TerrainRasterizer(SoftwareRenderer* renderer):
|
|
|
|
renderer(renderer)
|
|
|
|
{
|
|
|
|
}
|
2012-12-09 17:49:28 +00:00
|
|
|
|
2013-12-08 19:54:34 +00:00
|
|
|
static inline Vector3 _getPoint(SoftwareRenderer* renderer, double x, double z)
|
2012-12-09 17:49:28 +00:00
|
|
|
{
|
2013-12-11 11:46:39 +00:00
|
|
|
return Vector3(x, renderer->getTerrainRenderer()->getHeight(x, z, 1), z);
|
2012-12-09 17:49:28 +00:00
|
|
|
}
|
|
|
|
|
2013-12-11 11:46:39 +00:00
|
|
|
static Color _postProcessFragment(SoftwareRenderer* renderer, const Vector3 &point, void*)
|
2012-12-09 17:49:28 +00:00
|
|
|
{
|
2013-12-11 11:46:39 +00:00
|
|
|
double precision = renderer->getPrecision(_getPoint(renderer, point.x, point.z));
|
2013-12-08 19:54:34 +00:00
|
|
|
return renderer->getTerrainRenderer()->getFinalColor(point, precision);
|
2012-12-09 17:49:28 +00:00
|
|
|
}
|
|
|
|
|
2013-12-08 19:54:34 +00:00
|
|
|
static void _renderQuad(SoftwareRenderer* renderer, double x, double z, double size, double water_height)
|
2012-12-09 17:49:28 +00:00
|
|
|
{
|
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;
|
2013-12-08 19:54:34 +00:00
|
|
|
dv1 = renderer->getTerrainRenderer()->getResult(x, z, 1, 1).location;
|
2012-12-09 17:49:28 +00:00
|
|
|
|
2013-04-06 13:29:12 +00:00
|
|
|
ov2.x = x;
|
|
|
|
ov2.z = z + size;
|
2013-12-08 19:54:34 +00:00
|
|
|
dv2 = renderer->getTerrainRenderer()->getResult(x, z + size, 1, 1).location;
|
2013-04-06 13:29:12 +00:00
|
|
|
|
|
|
|
ov3.x = x + size;
|
|
|
|
ov3.z = z + size;
|
2013-12-08 19:54:34 +00:00
|
|
|
dv3 = renderer->getTerrainRenderer()->getResult(x + size, z + size, 1, 1).location;
|
2013-04-06 13:29:12 +00:00
|
|
|
|
|
|
|
ov4.x = x + size;
|
|
|
|
ov4.z = z;
|
2013-12-08 19:54:34 +00:00
|
|
|
dv4 = renderer->getTerrainRenderer()->getResult(x + size, z, 1, 1).location;
|
2013-04-06 13:29:12 +00:00
|
|
|
|
|
|
|
if (dv1.y > water_height || dv2.y > water_height || dv3.y > water_height || dv4.y > water_height)
|
2012-12-09 17:49:28 +00:00
|
|
|
{
|
2013-12-09 10:59:57 +00:00
|
|
|
renderer->pushDisplacedQuad(dv1, dv2, dv3, dv4, ov1, ov2, ov3, ov4, _postProcessFragment, NULL);
|
2012-12-09 17:49:28 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-12-05 15:44:18 +00:00
|
|
|
void TerrainRasterizer::tessellateChunk(TerrainChunkInfo* chunk, int detail)
|
2013-06-26 15:28:21 +00:00
|
|
|
{
|
2013-06-27 09:47:16 +00:00
|
|
|
if (detail < 1)
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2013-12-08 19:54:34 +00:00
|
|
|
double water_height = renderer->getWaterRenderer()->getHeightInfo().min_height;
|
2013-06-27 09:47:16 +00:00
|
|
|
|
|
|
|
double startx = chunk->point_nw.x;
|
|
|
|
double startz = chunk->point_nw.z;
|
|
|
|
double size = (chunk->point_ne.x - chunk->point_nw.x) / (double)detail;
|
|
|
|
int i, j;
|
|
|
|
|
|
|
|
for (i = 0; i < detail; i++)
|
|
|
|
{
|
|
|
|
for (j = 0; j < detail; j++)
|
|
|
|
{
|
|
|
|
_renderQuad(renderer, startx + (double)i * size, startz + (double)j * size, size, water_height);
|
|
|
|
}
|
|
|
|
}
|
2013-06-26 15:28:21 +00:00
|
|
|
}
|
|
|
|
|
2013-12-08 19:54:34 +00:00
|
|
|
static void _getChunk(SoftwareRenderer* renderer, TerrainRasterizer::TerrainChunkInfo* chunk, double x, double z, double size, int displaced)
|
2013-06-26 15:28:21 +00:00
|
|
|
{
|
2013-12-08 19:54:34 +00:00
|
|
|
chunk->point_nw = renderer->getTerrainRenderer()->getResult(x, z, 1, displaced).location;
|
|
|
|
chunk->point_sw = renderer->getTerrainRenderer()->getResult(x, z + size, 1, displaced).location;
|
|
|
|
chunk->point_se = renderer->getTerrainRenderer()->getResult(x + size, z + size, 1, displaced).location;
|
|
|
|
chunk->point_ne = renderer->getTerrainRenderer()->getResult(x + size, z, 1, displaced).location;
|
2013-06-26 15:28:21 +00:00
|
|
|
|
2013-06-27 09:47:16 +00:00
|
|
|
double displacement_power;
|
|
|
|
if (displaced)
|
|
|
|
{
|
|
|
|
displacement_power = 0.0;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2013-12-08 19:54:34 +00:00
|
|
|
displacement_power = renderer->getTexturesRenderer()->getMaximalDisplacement(renderer->getScenery()->getTextures());
|
2013-06-27 09:47:16 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
BoundingBox box;
|
|
|
|
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)));
|
2013-06-27 09:47:16 +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);
|
2013-06-27 09:47:16 +00:00
|
|
|
if (coverage > 0)
|
2013-06-26 15:28:21 +00:00
|
|
|
{
|
2013-06-27 10:03:55 +00:00
|
|
|
chunk->detail_hint = (int)ceil(sqrt((double)coverage) / (double)(25 - 2 * renderer->render_quality));
|
2013-06-27 09:47:16 +00:00
|
|
|
if (chunk->detail_hint > 5 * renderer->render_quality)
|
|
|
|
{
|
|
|
|
chunk->detail_hint = 5 * renderer->render_quality;
|
|
|
|
}
|
2013-06-26 15:28:21 +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;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-12-05 15:44:18 +00:00
|
|
|
void TerrainRasterizer::getTessellationInfo(int displaced)
|
2012-12-09 17:49:28 +00:00
|
|
|
{
|
2013-06-26 15:28:21 +00:00
|
|
|
TerrainChunkInfo chunk;
|
2012-12-09 17:49:28 +00:00
|
|
|
int chunk_factor, chunk_count, i;
|
2013-12-09 10:59:57 +00:00
|
|
|
Vector3 cam = renderer->getCameraLocation(VECTOR_ZERO);
|
2013-06-26 15:28:21 +00:00
|
|
|
double progress;
|
|
|
|
double radius_int, radius_ext;
|
|
|
|
double base_chunk_size, chunk_size;
|
2012-12-09 17:49:28 +00:00
|
|
|
|
2013-06-26 15:28:21 +00:00
|
|
|
base_chunk_size = 5.0 / (double)renderer->render_quality;
|
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;
|
2013-06-27 10:15:30 +00:00
|
|
|
progress = 0.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);
|
|
|
|
|
2013-07-03 15:48:54 +00:00
|
|
|
while (radius_int < 20000.0)
|
2012-12-09 17:49:28 +00:00
|
|
|
{
|
2013-07-03 15:48:54 +00:00
|
|
|
progress = radius_int / 20000.0;
|
2012-12-09 17:49:28 +00:00
|
|
|
for (i = 0; i < chunk_count - 1; i++)
|
|
|
|
{
|
2013-06-27 09:49:32 +00:00
|
|
|
_getChunk(renderer, &chunk, cx - radius_ext + chunk_size * i, cz - radius_ext, chunk_size, displaced);
|
2013-12-05 15:44:18 +00:00
|
|
|
if (!processChunk(&chunk, progress))
|
2013-06-26 15:28:21 +00:00
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2013-06-27 09:49:32 +00:00
|
|
|
_getChunk(renderer, &chunk, cx + radius_int, cz - radius_ext + chunk_size * i, chunk_size, displaced);
|
2013-12-05 15:44:18 +00:00
|
|
|
if (!processChunk(&chunk, progress))
|
2013-06-26 15:28:21 +00:00
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2013-06-27 09:49:32 +00:00
|
|
|
_getChunk(renderer, &chunk, cx + radius_int - chunk_size * i, cz + radius_int, chunk_size, displaced);
|
2013-12-05 15:44:18 +00:00
|
|
|
if (!processChunk(&chunk, progress))
|
2013-06-26 15:28:21 +00:00
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2013-06-27 09:49:32 +00:00
|
|
|
_getChunk(renderer, &chunk, cx - radius_ext, cz + radius_int - chunk_size * i, chunk_size, displaced);
|
2013-12-05 15:44:18 +00:00
|
|
|
if (!processChunk(&chunk, progress))
|
2013-06-26 15:28:21 +00:00
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
2012-12-09 17:49:28 +00:00
|
|
|
}
|
|
|
|
|
2013-06-26 15:28:21 +00:00
|
|
|
if (radius_int > 20.0 && chunk_count % 64 == 0 && (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;
|
|
|
|
}
|
|
|
|
}
|
2013-06-26 15:28:21 +00:00
|
|
|
|
2013-07-06 22:32:45 +00:00
|
|
|
typedef struct
|
|
|
|
{
|
2013-12-05 15:44:18 +00:00
|
|
|
TerrainRasterizer* rasterizer;
|
|
|
|
TerrainRasterizer::TerrainChunkInfo chunk;
|
2013-07-06 22:32:45 +00:00
|
|
|
} ParallelRasterInfo;
|
|
|
|
|
2013-11-17 22:02:36 +00:00
|
|
|
static int _parallelJobCallback(ParallelQueue*, int, void* data, int stopping)
|
2013-07-06 22:32:45 +00:00
|
|
|
{
|
|
|
|
ParallelRasterInfo* info = (ParallelRasterInfo*)data;
|
|
|
|
|
|
|
|
if (!stopping)
|
|
|
|
{
|
2013-12-05 15:44:18 +00:00
|
|
|
info->rasterizer->tessellateChunk(&info->chunk, info->chunk.detail_hint);
|
2013-07-06 22:32:45 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
free(data);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2013-12-05 15:44:18 +00:00
|
|
|
int TerrainRasterizer::processChunk(TerrainChunkInfo* chunk, double progress)
|
2013-06-26 15:28:21 +00:00
|
|
|
{
|
2013-11-02 15:43:43 +00:00
|
|
|
ParallelRasterInfo* info = new ParallelRasterInfo;
|
2013-07-06 22:32:45 +00:00
|
|
|
|
2013-12-05 15:44:18 +00:00
|
|
|
info->rasterizer = this;
|
2013-07-06 22:32:45 +00:00
|
|
|
info->chunk = *chunk;
|
|
|
|
|
2013-12-09 10:59:57 +00:00
|
|
|
if (!((ParallelQueue*)renderer->customData[0])->addJob(_parallelJobCallback, info))
|
2013-07-06 22:32:45 +00:00
|
|
|
{
|
2013-11-02 15:43:43 +00:00
|
|
|
delete info;
|
2013-07-06 22:32:45 +00:00
|
|
|
}
|
|
|
|
|
2013-06-27 10:15:30 +00:00
|
|
|
renderer->render_progress = 0.05 * progress;
|
2013-06-26 15:28:21 +00:00
|
|
|
return !renderer->render_interrupt;
|
|
|
|
}
|
|
|
|
|
2013-12-05 15:44:18 +00:00
|
|
|
void TerrainRasterizer::renderSurface()
|
2013-06-26 15:28:21 +00:00
|
|
|
{
|
2013-12-09 10:59:57 +00:00
|
|
|
ParallelQueue queue;
|
2013-07-06 22:32:45 +00:00
|
|
|
|
|
|
|
/* TODO Do not use custom data, it could already be used by another module */
|
2013-12-09 10:59:57 +00:00
|
|
|
renderer->customData[0] = &queue;
|
2013-07-06 22:32:45 +00:00
|
|
|
|
2013-06-27 10:15:30 +00:00
|
|
|
renderer->render_progress = 0.0;
|
2013-12-05 15:44:18 +00:00
|
|
|
getTessellationInfo(0);
|
2013-06-27 10:15:30 +00:00
|
|
|
renderer->render_progress = 0.05;
|
2013-07-06 22:32:45 +00:00
|
|
|
|
2013-12-09 10:59:57 +00:00
|
|
|
queue.wait();
|
2013-06-26 15:28:21 +00:00
|
|
|
}
|