paysages3d/src/rendering/terrain/ter_render.c
Michaël Lemaire 47de568004 Added scaling adjustements
This is not enough, there are still problems with painting brush offsets.
2013-06-09 23:03:16 +02:00

351 lines
10 KiB
C

#include "private.h"
#include <stdlib.h>
#include <math.h>
#include "../tools.h"
#include "../renderer.h"
/******************** Binding ********************/
static double _fakeGetHeight(Renderer* renderer, double x, double z, int with_painting)
{
UNUSED(renderer);
UNUSED(x);
UNUSED(z);
UNUSED(with_painting);
return 0.0;
}
static double _realGetHeight(Renderer* renderer, double x, double z, int with_painting)
{
return terrainGetInterpolatedHeight(renderer->terrain->definition, x, z, 1, with_painting);
}
static TerrainResult _fakeGetResult(Renderer* renderer, double x, double z, int with_painting, int with_textures)
{
TerrainResult result;
UNUSED(renderer);
UNUSED(x);
UNUSED(z);
UNUSED(with_painting);
UNUSED(with_textures);
result.location.x = x;
result.location.y = 0.0;
result.location.z = z;
result.normal = VECTOR_UP;
return result;
}
static inline Vector3 _getNormal4(Vector3 center, Vector3 north, Vector3 east, Vector3 south, Vector3 west)
{
Vector3 dnorth, deast, dsouth, dwest, normal;
dnorth = v3Sub(north, center);
deast = v3Sub(east, center);
dsouth = v3Sub(south, center);
dwest = v3Sub(west, center);
normal = v3Cross(deast, dnorth);
normal = v3Add(normal, v3Cross(dsouth, deast));
normal = v3Add(normal, v3Cross(dwest, dsouth));
normal = v3Add(normal, v3Cross(dnorth, dwest));
return v3Normalize(normal);
}
static inline Vector3 _getNormal2(Vector3 center, Vector3 east, Vector3 south)
{
return v3Normalize(v3Cross(v3Sub(south, center), v3Sub(east, center)));
}
static TerrainResult _realGetResult(Renderer* renderer, double x, double z, int with_painting, int with_textures)
{
TerrainResult result;
double detail = 0.001; /* TODO */
/* Normal */
Vector3 center, north, east, south, west;
center.x = x;
center.z = z;
center.y = renderer->terrain->getHeight(renderer, center.x, center.z, with_painting);
east.x = x + detail;
east.z = z;
east.y = renderer->terrain->getHeight(renderer, east.x, east.z, with_painting);
south.x = x;
south.z = z + detail;
south.y = renderer->terrain->getHeight(renderer, south.x, south.z, with_painting);
if (renderer->render_quality > 6)
{
west.x = x - detail;
west.z = z;
west.y = renderer->terrain->getHeight(renderer, west.x, west.z, with_painting);
north.x = x;
north.z = z - detail;
north.y = renderer->terrain->getHeight(renderer, north.x, north.z, with_painting);
result.normal = _getNormal4(center, north, east, south, west);
}
else
{
result.normal = _getNormal2(center, east, south);
}
/* Location */
result.location = center;
/* Texture displacement */
if (with_textures)
{
center = renderer->textures->displaceTerrain(renderer, result);
result.location = center;
/* Recompute normal */
if (renderer->render_quality > 6)
{
/* Use 5 points on displaced terrain */
east = renderer->textures->displaceTerrain(renderer, _realGetResult(renderer, east.x, east.z, with_painting, 0));
south = renderer->textures->displaceTerrain(renderer, _realGetResult(renderer, south.x, south.z, with_painting, 0));
west = renderer->textures->displaceTerrain(renderer, _realGetResult(renderer, west.x, west.z, with_painting, 0));
north = renderer->textures->displaceTerrain(renderer, _realGetResult(renderer, north.x, north.z, with_painting, 0));
result.normal = _getNormal4(center, north, east, south, west);
}
else if (renderer->render_quality > 2)
{
/* Use 3 points on displaced terrain */
east = renderer->textures->displaceTerrain(renderer, _realGetResult(renderer, east.x, east.z, with_painting, 0));
south = renderer->textures->displaceTerrain(renderer, _realGetResult(renderer, south.x, south.z, with_painting, 0));
result.normal = _getNormal2(center, east, south);
}
else
{
/* TODO Use texture noise directly, as if terrain was a plane */
}
}
return result;
}
static Color _fakeGetFinalColor(Renderer* renderer, Vector3 location, double precision)
{
UNUSED(renderer);
UNUSED(location);
UNUSED(precision);
return COLOR_GREEN;
}
static Color _realGetFinalColor(Renderer* renderer, Vector3 location, double precision)
{
/* TODO Restore precision control */
TexturesResult textures = renderer->textures->applyToTerrain(renderer, location.x, location.z);
return renderer->applyMediumTraversal(renderer, textures.final_location, textures.final_color);
}
static RayCastingResult _fakeCastRay(Renderer* renderer, Vector3 start, Vector3 direction)
{
UNUSED(renderer);
UNUSED(start);
UNUSED(direction);
RayCastingResult result;
result.hit = 0;
return result;
}
static RayCastingResult _realCastRay(Renderer* renderer, Vector3 start, Vector3 direction)
{
RayCastingResult result;
TerrainDefinition* definition = renderer->terrain->definition;
Vector3 inc_vector;
double inc_value, inc_base, inc_factor, height, diff, lastdiff, length;
direction = v3Normalize(direction);
inc_factor = (double)renderer->render_quality;
inc_base = 1.0;
inc_value = inc_base / inc_factor;
lastdiff = start.y - _realGetHeight(renderer, start.x, start.z, 1);
length = 0.0;
do
{
inc_vector = v3Scale(direction, inc_value);
length += v3Norm(inc_vector);
start = v3Add(start, inc_vector);
height = _realGetHeight(renderer, start.x, start.z, 1);
diff = start.y - height;
if (diff < 0.0)
{
if (fabs(diff - lastdiff) > 0.00001)
{
start = v3Add(start, v3Scale(inc_vector, -diff / (diff - lastdiff)));
start.y = _realGetHeight(renderer, start.x, start.z, 1);
}
else
{
start.y = height;
}
result.hit = 1;
result.hit_location = start;
result.hit_color = _realGetFinalColor(renderer, start, renderer->getPrecision(renderer, result.hit_location));
return result;
}
if (diff < inc_base / inc_factor)
{
inc_value = inc_base / inc_factor;
}
else if (diff > inc_base)
{
inc_value = inc_base;
}
else
{
inc_value = diff;
}
lastdiff = diff;
}
while (length < 50.0 && start.y <= definition->_max_height);
result.hit = 0;
return result;
}
static int _alterLight(Renderer* renderer, LightDefinition* light, Vector3 location)
{
TerrainDefinition* definition = renderer->terrain->definition;
Vector3 inc_vector, direction_to_light;
double inc_value, inc_base, inc_factor, height, diff, light_factor, smoothing, length;
direction_to_light = v3Scale(light->direction, -1.0);
if (direction_to_light.y < -0.05)
{
light->color = COLOR_BLACK;
return 1;
}
else if (direction_to_light.y < 0.0000)
{
light->color.r *= (0.05 + direction_to_light.y) / 0.05;
light->color.g *= (0.05 + direction_to_light.y) / 0.05;
light->color.b *= (0.05 + direction_to_light.y) / 0.05;
}
inc_factor = (double)renderer->render_quality;
inc_base = definition->height / definition->scaling;
inc_value = inc_base / inc_factor;
smoothing = definition->shadow_smoothing;
light_factor = 1.0;
length = 0.0;
diff = 0.0;
do
{
inc_vector = v3Scale(direction_to_light, inc_value);
length += v3Norm(inc_vector);
location = v3Add(location, inc_vector);
height = renderer->terrain->getResult(renderer, location.x, location.z, 1, 1).location.y;
diff = location.y - height;
if (diff < 0.0)
{
if (length * smoothing > 0.000001)
{
light_factor += diff * v3Norm(inc_vector) / (length * smoothing);
}
else
{
light_factor = 0.0;
}
}
if (diff < inc_base / inc_factor)
{
inc_value = inc_base / inc_factor;
}
else if (diff > inc_base)
{
inc_value = inc_base;
}
else
{
inc_value = diff;
}
}
while (light_factor > 0.0 && length < (10.0 * inc_factor) && location.y <= definition->_max_height);
if (light_factor <= 0.0)
{
light->color = COLOR_BLACK;
return 1;
}
else
{
light->color.r *= light_factor;
light->color.g *= light_factor;
light->color.b *= light_factor;
return 1;
}
}
static double _fakeGetWaterHeight(Renderer* renderer)
{
UNUSED(renderer);
return -1000.0;
}
static double _realGetWaterHeight(Renderer* renderer)
{
return renderer->terrain->definition->water_height * renderer->terrain->definition->height * renderer->terrain->definition->scaling;
}
/******************** Renderer ********************/
static TerrainRenderer* _createRenderer()
{
TerrainRenderer* result;
result = malloc(sizeof (TerrainRenderer));
result->definition = TerrainDefinitionClass.create();
result->castRay = _fakeCastRay;
result->getHeight = _fakeGetHeight;
result->getResult = _fakeGetResult;
result->getFinalColor = _fakeGetFinalColor;
result->getWaterHeight = _fakeGetWaterHeight;
return result;
}
static void _deleteRenderer(TerrainRenderer* renderer)
{
TerrainDefinitionClass.destroy(renderer->definition);
free(renderer);
}
static void _bindRenderer(Renderer* renderer, TerrainDefinition* definition)
{
TerrainDefinitionClass.copy(definition, renderer->terrain->definition);
renderer->terrain->castRay = _realCastRay;
renderer->terrain->getHeight = _realGetHeight;
renderer->terrain->getResult = _realGetResult;
renderer->terrain->getFinalColor = _realGetFinalColor;
renderer->terrain->getWaterHeight = _realGetWaterHeight;
lightingManagerRegisterFilter(renderer->lighting, (FuncLightingAlterLight)_alterLight, renderer);
}
StandardRenderer TerrainRendererClass = {
(FuncObjectCreate)_createRenderer,
(FuncObjectDelete)_deleteRenderer,
(FuncObjectBind)_bindRenderer
};