Michaël Lemaire
9a580dcf7d
git-svn-id: https://subversion.assembla.com/svn/thunderk/paysages@206 b1fd45b6-86a6-48da-8261-f70d1f35bdcc
354 lines
9.4 KiB
C
354 lines
9.4 KiB
C
#include "shared/types.h"
|
|
#include "shared/functions.h"
|
|
#include "shared/constants.h"
|
|
#include "shared/globals.h"
|
|
|
|
#include "water.h"
|
|
#include "terrain.h"
|
|
|
|
#include <math.h>
|
|
|
|
static WaterDefinition _definition;
|
|
static WaterQuality _quality;
|
|
static WaterEnvironment _environment;
|
|
|
|
static RayCastingResult _reflectionFunction(Vector3 start, Vector3 direction)
|
|
{
|
|
RayCastingResult result;
|
|
|
|
if (!terrainProjectRay(start, direction, &result.hit_location, &result.hit_color))
|
|
{
|
|
result.hit_color = skyProjectRay(start, direction);
|
|
/* TODO hit_location */
|
|
}
|
|
|
|
result.hit = 1;
|
|
return result;
|
|
}
|
|
|
|
static RayCastingResult _refractionFunction(Vector3 start, Vector3 direction)
|
|
{
|
|
RayCastingResult result;
|
|
|
|
result.hit = terrainProjectRay(start, direction, &result.hit_location, &result.hit_color);
|
|
|
|
return result;
|
|
}
|
|
|
|
void waterInit()
|
|
{
|
|
_definition = waterCreateDefinition();
|
|
|
|
/* TODO quality */
|
|
|
|
_environment.reflection_function = _reflectionFunction;
|
|
_environment.refraction_function = _refractionFunction;
|
|
_environment.toggle_fog = 1;
|
|
_environment.toggle_shadows = 1;
|
|
}
|
|
|
|
void waterSave(FILE* f)
|
|
{
|
|
toolsSaveDouble(f, _definition.height);
|
|
colorSave(_definition.main_color, f);
|
|
colorSave(_definition.depth_color, f);
|
|
toolsSaveDouble(f, _definition.transparency_depth);
|
|
toolsSaveDouble(f, _definition.transparency);
|
|
toolsSaveDouble(f, _definition.reflection);
|
|
noiseSave(_definition.height_noise, f);
|
|
}
|
|
|
|
void waterLoad(FILE* f)
|
|
{
|
|
_definition.height = toolsLoadDouble(f);
|
|
_definition.main_color = colorLoad(f);
|
|
_definition.depth_color = colorLoad(f);
|
|
_definition.transparency_depth = toolsLoadDouble(f);
|
|
_definition.transparency = toolsLoadDouble(f);
|
|
_definition.reflection = toolsLoadDouble(f);
|
|
noiseLoad(_definition.height_noise, f);
|
|
}
|
|
|
|
WaterDefinition waterCreateDefinition()
|
|
{
|
|
WaterDefinition result;
|
|
|
|
result.height = -1000.0;
|
|
result.height_noise = noiseCreateGenerator();
|
|
|
|
return result;
|
|
}
|
|
|
|
void waterDeleteDefinition(WaterDefinition definition)
|
|
{
|
|
noiseDeleteGenerator(definition.height_noise);
|
|
}
|
|
|
|
void waterCopyDefinition(WaterDefinition source, WaterDefinition* destination)
|
|
{
|
|
NoiseGenerator* noise;
|
|
|
|
noise = destination->height_noise;
|
|
*destination = source;
|
|
destination->height_noise = noise;
|
|
noiseCopy(source.height_noise, destination->height_noise);
|
|
}
|
|
|
|
void waterSetDefinition(WaterDefinition config)
|
|
{
|
|
waterCopyDefinition(config, &_definition);
|
|
}
|
|
|
|
WaterDefinition waterGetDefinition()
|
|
{
|
|
return _definition;
|
|
}
|
|
|
|
void waterSetQuality(WaterQuality quality)
|
|
{
|
|
_quality = quality;
|
|
|
|
_quality.detail_boost = (_quality.detail_boost < 0.1) ? 0.1 : _quality.detail_boost;
|
|
}
|
|
|
|
WaterQuality waterGetQuality()
|
|
{
|
|
return _quality;
|
|
}
|
|
|
|
static inline double _getHeight(WaterDefinition* definition, double x, double z, double detail)
|
|
{
|
|
return definition->height + noiseGet2DDetail(definition->height_noise, x, z, detail);
|
|
}
|
|
|
|
static inline Vector3 _getNormal(WaterDefinition* definition, Vector3 base, double detail)
|
|
{
|
|
Vector3 back, right;
|
|
double x, z;
|
|
|
|
x = base.x;
|
|
z = base.z;
|
|
|
|
back.x = x;
|
|
back.y = _getHeight(definition, x, z + detail, detail);
|
|
back.z = z + detail;
|
|
back = v3Sub(back, base);
|
|
|
|
right.x = x + detail;
|
|
right.y = _getHeight(definition, x + detail, z, detail);
|
|
right.z = z;
|
|
right = v3Sub(right, base);
|
|
|
|
return v3Normalize(v3Cross(back, right));
|
|
}
|
|
|
|
static inline Vector3 _reflectRay(Vector3 incoming, Vector3 normal)
|
|
{
|
|
double c;
|
|
|
|
c = v3Dot(normal, v3Scale(incoming, -1.0));
|
|
return v3Add(incoming, v3Scale(normal, 2.0 * c));
|
|
}
|
|
|
|
static inline Vector3 _refractRay(Vector3 incoming, Vector3 normal)
|
|
{
|
|
double c1, c2, f;
|
|
|
|
f = 1.0 / 1.33;
|
|
c1 = v3Dot(normal, v3Scale(incoming, -1.0));
|
|
c2 = sqrt(1.0 - pow(f, 2.0) * (1.0 - pow(c1, 2.0)));
|
|
if (c1 >= 0.0)
|
|
{
|
|
return v3Add(v3Scale(incoming, f), v3Scale(normal, f * c1 - c2));
|
|
}
|
|
else
|
|
{
|
|
return v3Add(v3Scale(incoming, f), v3Scale(normal, c2 - f * c1));
|
|
}
|
|
}
|
|
|
|
WaterResult waterGetColorCustom(Vector3 location, Vector3 look, WaterDefinition* definition, WaterQuality* quality, WaterEnvironment* environment)
|
|
{
|
|
WaterResult result;
|
|
RayCastingResult refracted;
|
|
Vector3 normal;
|
|
Color color;
|
|
double shadowed, detail, depth;
|
|
|
|
if (definition == NULL)
|
|
{
|
|
definition = &_definition;
|
|
}
|
|
if (quality == NULL)
|
|
{
|
|
quality = &_quality;
|
|
}
|
|
if (environment == NULL)
|
|
{
|
|
environment = &_environment;
|
|
}
|
|
|
|
if (quality->force_detail != 0.0)
|
|
{
|
|
detail = quality->force_detail;
|
|
}
|
|
else
|
|
{
|
|
detail = renderGetPrecision(location) / quality->detail_boost;
|
|
}
|
|
|
|
location.y = _getHeight(definition, location.x, location.z, detail);
|
|
result.location = location;
|
|
|
|
normal = _getNormal(definition, location, detail);
|
|
look = v3Normalize(look);
|
|
result.reflected = environment->reflection_function(location, _reflectRay(look, normal)).hit_color;
|
|
refracted = environment->refraction_function(location, _refractRay(look, normal));
|
|
depth = v3Norm(v3Sub(location, refracted.hit_location));
|
|
if (depth > definition->transparency_depth)
|
|
{
|
|
result.refracted = definition->depth_color;
|
|
}
|
|
else
|
|
{
|
|
depth /= definition->transparency_depth;
|
|
result.refracted.r = refracted.hit_color.r * (1.0 - depth) + definition->depth_color.r * depth;
|
|
result.refracted.g = refracted.hit_color.g * (1.0 - depth) + definition->depth_color.g * depth;
|
|
result.refracted.b = refracted.hit_color.b * (1.0 - depth) + definition->depth_color.b * depth;
|
|
result.refracted.a = 1.0;
|
|
}
|
|
|
|
color.r = definition->main_color.r * (1.0 - definition->transparency) + result.reflected.r * definition->reflection + result.refracted.r * definition->transparency;
|
|
color.g = definition->main_color.g * (1.0 - definition->transparency) + result.reflected.g * definition->reflection + result.refracted.g * definition->transparency;
|
|
color.b = definition->main_color.b * (1.0 - definition->transparency) + result.reflected.b * definition->reflection + result.refracted.b * definition->transparency;
|
|
color.a = 1.0;
|
|
|
|
if (environment->toggle_shadows)
|
|
{
|
|
shadowed = terrainGetShadow(location, sun_direction_inv);
|
|
}
|
|
else
|
|
{
|
|
shadowed = 0.0;
|
|
}
|
|
color = lightingApply(location, normal, shadowed, color, 0.8, 0.6);
|
|
if (environment->toggle_fog)
|
|
{
|
|
color = fogApplyToLocation(location, color);
|
|
}
|
|
|
|
result.base = definition->main_color;
|
|
result.final = color;
|
|
|
|
return result;
|
|
}
|
|
|
|
Color waterGetColor(Vector3 location, Vector3 look)
|
|
{
|
|
return waterGetColorCustom(location, look, &_definition, &_quality, &_environment).final;
|
|
}
|
|
|
|
static int _postProcessFragment(RenderFragment* fragment)
|
|
{
|
|
fragment->vertex.color = waterGetColor(fragment->vertex.location, v3Sub(fragment->vertex.location, camera_location));
|
|
return 1;
|
|
}
|
|
|
|
static Vertex _getFirstPassVertex(double x, double z, double precision)
|
|
{
|
|
Vertex result;
|
|
double value;
|
|
|
|
result.location.x = x;
|
|
result.location.y = _getHeight(&_definition, x, z, 0.0);
|
|
result.location.z = z;
|
|
value = sin(x) * sin(x) * cos(z) * cos(z);
|
|
result.color.r = 0.0;
|
|
result.color.g = value;
|
|
result.color.b = value;
|
|
result.color.a = 1.0;
|
|
result.normal.x = result.normal.y = result.normal.z = 0.0;
|
|
result.callback = _postProcessFragment;
|
|
|
|
return result;
|
|
}
|
|
|
|
static void _renderQuad(double x, double z, double size)
|
|
{
|
|
Vertex v1, v2, v3, v4;
|
|
|
|
v1 = _getFirstPassVertex(x, z, size);
|
|
v2 = _getFirstPassVertex(x, z + size, size);
|
|
v3 = _getFirstPassVertex(x + size, z + size, size);
|
|
v4 = _getFirstPassVertex(x + size, z, size);
|
|
renderPushQuad(&v1, &v2, &v3, &v4);
|
|
}
|
|
|
|
double waterGetLightFactor(Vector3 location)
|
|
{
|
|
double factor;
|
|
|
|
if (location.y < _definition.height)
|
|
{
|
|
if (sun_direction_inv.y > 0.00001)
|
|
{
|
|
factor = (_definition.height - location.y) / (sun_direction_inv.y * 3.0);
|
|
if (factor > 1.0)
|
|
{
|
|
factor = 1.0;
|
|
}
|
|
return 1.0 - 0.8 * factor;
|
|
}
|
|
else
|
|
{
|
|
return 0.0;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
return 1.0;
|
|
}
|
|
}
|
|
|
|
void waterRender(RenderProgressCallback callback)
|
|
{
|
|
int chunk_factor, chunk_count, i;
|
|
double cx = camera_location.x;
|
|
double cz = camera_location.z;
|
|
double radius_int, radius_ext, base_chunk_size, chunk_size;
|
|
|
|
base_chunk_size = 2.0 / (double)render_quality;
|
|
|
|
chunk_factor = 1;
|
|
chunk_count = 2;
|
|
radius_int = 0.0;
|
|
radius_ext = base_chunk_size;
|
|
chunk_size = base_chunk_size;
|
|
|
|
while (radius_ext < 1000.0)
|
|
{
|
|
if (!callback(radius_ext / 1000.0))
|
|
{
|
|
return;
|
|
}
|
|
|
|
for (i = 0; i < chunk_count - 1; i++)
|
|
{
|
|
_renderQuad(cx - radius_ext + chunk_size * i, cz - radius_ext, chunk_size);
|
|
_renderQuad(cx + radius_int, cz - radius_ext + chunk_size * i, chunk_size);
|
|
_renderQuad(cx + radius_int - chunk_size * i, cz + radius_int, chunk_size);
|
|
_renderQuad(cx - radius_ext, cz + radius_int - chunk_size * i, chunk_size);
|
|
}
|
|
|
|
if (radius_int > 20.0 && chunk_count % 64 == 0 && (double)chunk_factor < radius_int / 20.0)
|
|
{
|
|
chunk_count /= 2;
|
|
chunk_factor *= 2;
|
|
}
|
|
chunk_count += 2;
|
|
chunk_size = base_chunk_size * chunk_factor;
|
|
radius_int = radius_ext;
|
|
radius_ext += chunk_size;
|
|
}
|
|
}
|
|
|