paysages3d/src/render/software/TerrainRayWalker.cpp

104 lines
2.9 KiB
C++

#include "TerrainRayWalker.h"
#include "SoftwareRenderer.h"
#include "Scenery.h"
#include "TerrainDefinition.h"
#include "TexturesDefinition.h"
#include "TerrainRenderer.h"
#include "TexturesRenderer.h"
TerrainRayWalker::TerrainRayWalker(SoftwareRenderer* renderer):
renderer(renderer)
{
}
void TerrainRayWalker::update()
{
TerrainDefinition* terrain = renderer->getScenery()->getTerrain();
HeightInfo info = terrain->getHeightInfo();
TexturesDefinition* textures = renderer->getScenery()->getTextures();
double disp = textures->getMaximalDisplacement();
ymin = info.min_height - disp;
ymax = info.max_height + disp;
ydispmin = -disp;
ydispmax = disp;
minstep = 0.01 * terrain->scaling / (double)renderer->render_quality;
maxstep = 1.0 * terrain->scaling;
}
bool TerrainRayWalker::startWalking(Vector3 start, const Vector3 &direction, double escaping_factor, double max_length, TerrainHitResult &result)
{
TerrainRenderer* terrain_renderer = renderer->getTerrainRenderer();
TexturesRenderer* textures_renderer = renderer->getTexturesRenderer();
TerrainRenderer::TerrainResult terrain_result;
Vector3 end, displaced;
bool hit;
double diff;
double step_length = minstep;
double walked_length = 0.0;
do
{
// Perform a step
end = start.add(direction.scale(step_length));
// Get the terrain info at end (without textures displacement)
terrain_result = terrain_renderer->getResult(end.x, end.z, true, false);
diff = end.y - terrain_result.location.y;
// If we are very under the terrain, consider a hit
if (diff < ydispmin)
{
hit = true;
}
// If we are close enough to the terrain, apply displacement
else if (diff < ydispmax)
{
displaced = textures_renderer->displaceTerrain(terrain_result);
diff = end.y - displaced.y;
hit = (diff < 0.0);
}
if (hit)
{
// Refine the hit with dichotomy at high quality
/*if (renderer->render_quality > 7)
{
end = refineHit(start, end, step_length);
}*/
// Find an escape
/*if (escaping_factor != 0.0)
{
result.escape_length = findEscape(end, walked_length, escaping_factor, max_length);
}*/
result.hit_location = end;
}
else
{
// Prepare next step
start = end;
walked_length += step_length;
step_length = diff / (double)renderer->render_quality;
if (step_length < minstep)
{
step_length = minstep;
}
else if (step_length > maxstep)
{
step_length = maxstep;
}
}
} while (not hit and start.y < ymax and walked_length < max_length);
return hit;
}