2013-02-27 16:38:27 +00:00
|
|
|
#include "private.h"
|
|
|
|
|
2013-11-03 14:46:39 +00:00
|
|
|
#include <cmath>
|
2013-02-27 16:38:27 +00:00
|
|
|
#include "../tools.h"
|
|
|
|
#include "../renderer.h"
|
2013-11-03 14:46:39 +00:00
|
|
|
#include "NoiseGenerator.h"
|
2013-02-27 16:38:27 +00:00
|
|
|
|
|
|
|
static HeightInfo _FAKE_HEIGHT_INFO = {0.0, 0.0, 0.0};
|
|
|
|
|
|
|
|
/******************** Fake ********************/
|
|
|
|
static HeightInfo _fakeGetHeightInfo(Renderer* renderer)
|
|
|
|
{
|
|
|
|
UNUSED(renderer);
|
|
|
|
return _FAKE_HEIGHT_INFO;
|
|
|
|
}
|
|
|
|
|
|
|
|
static double _fakeGetHeight(Renderer* renderer, double x, double z)
|
|
|
|
{
|
|
|
|
UNUSED(renderer);
|
|
|
|
UNUSED(x);
|
|
|
|
UNUSED(z);
|
|
|
|
return 0.0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static WaterResult _fakeGetResult(Renderer* renderer, double x, double z)
|
|
|
|
{
|
|
|
|
WaterResult result;
|
|
|
|
|
2013-03-03 17:05:30 +00:00
|
|
|
UNUSED(renderer);
|
|
|
|
|
2013-02-27 16:38:27 +00:00
|
|
|
result.location.x = x;
|
|
|
|
result.location.y = 0.0;
|
|
|
|
result.location.z = z;
|
|
|
|
|
|
|
|
result.base = COLOR_BLUE;
|
|
|
|
result.reflected = COLOR_BLACK;
|
|
|
|
result.refracted = COLOR_BLACK;
|
|
|
|
result.foam = COLOR_BLACK;
|
|
|
|
result.final = COLOR_BLUE;
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
/******************** Helpers ********************/
|
2013-05-11 21:34:30 +00:00
|
|
|
static inline double _getHeight(WaterDefinition* definition, double base_height, double x, double z)
|
2013-02-27 16:38:27 +00:00
|
|
|
{
|
2013-11-03 14:46:39 +00:00
|
|
|
return base_height + definition->_waves_noise->get2DTotal(x, z);
|
2013-02-27 16:38:27 +00:00
|
|
|
}
|
|
|
|
|
2013-05-11 21:34:30 +00:00
|
|
|
static inline Vector3 _getNormal(WaterDefinition* definition, double base_height, Vector3 base, double detail)
|
2013-02-27 16:38:27 +00:00
|
|
|
{
|
|
|
|
Vector3 back, right;
|
|
|
|
double x, z;
|
|
|
|
|
|
|
|
x = base.x;
|
|
|
|
z = base.z;
|
|
|
|
|
|
|
|
back.x = x;
|
2013-05-11 21:34:30 +00:00
|
|
|
back.y = _getHeight(definition, base_height, x, z + detail);
|
2013-02-27 16:38:27 +00:00
|
|
|
back.z = z + detail;
|
|
|
|
back = v3Sub(back, base);
|
|
|
|
|
|
|
|
right.x = x + detail;
|
2013-05-11 21:34:30 +00:00
|
|
|
right.y = _getHeight(definition, base_height, x + detail, z);
|
2013-02-27 16:38:27 +00:00
|
|
|
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));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-03-07 14:07:12 +00:00
|
|
|
static inline Color _getFoamMask(Renderer* renderer, WaterDefinition* definition, Vector3 location, Vector3 normal, double detail)
|
2013-02-27 16:38:27 +00:00
|
|
|
{
|
2013-03-07 14:07:12 +00:00
|
|
|
Color result;
|
2013-02-27 16:38:27 +00:00
|
|
|
double foam_factor, normal_diff, location_offset;
|
2013-05-11 21:34:30 +00:00
|
|
|
double base_height;
|
2013-02-27 16:38:27 +00:00
|
|
|
|
2013-05-11 21:34:30 +00:00
|
|
|
base_height = renderer->terrain->getWaterHeight(renderer);
|
2013-02-27 16:38:27 +00:00
|
|
|
location_offset = 2.0 * detail;
|
|
|
|
|
|
|
|
foam_factor = 0.0;
|
|
|
|
location.x += location_offset;
|
2013-05-11 21:34:30 +00:00
|
|
|
normal_diff = 1.0 - v3Dot(normal, _getNormal(definition, base_height, location, detail));
|
2013-02-27 16:38:27 +00:00
|
|
|
if (normal_diff > foam_factor)
|
|
|
|
{
|
|
|
|
foam_factor = normal_diff;
|
|
|
|
}
|
|
|
|
location.x -= location_offset * 2.0;
|
2013-05-11 21:34:30 +00:00
|
|
|
normal_diff = 1.0 - v3Dot(normal, _getNormal(definition, base_height, location, detail));
|
2013-02-27 16:38:27 +00:00
|
|
|
if (normal_diff > foam_factor)
|
|
|
|
{
|
|
|
|
foam_factor = normal_diff;
|
|
|
|
}
|
|
|
|
location.x += location_offset;
|
|
|
|
location.z -= location_offset;
|
2013-05-11 21:34:30 +00:00
|
|
|
normal_diff = 1.0 - v3Dot(normal, _getNormal(definition, base_height, location, detail));
|
2013-02-27 16:38:27 +00:00
|
|
|
if (normal_diff > foam_factor)
|
|
|
|
{
|
|
|
|
foam_factor = normal_diff;
|
|
|
|
}
|
|
|
|
location.z += location_offset * 2.0;
|
2013-05-11 21:34:30 +00:00
|
|
|
normal_diff = 1.0 - v3Dot(normal, _getNormal(definition, base_height, location, detail));
|
2013-02-27 16:38:27 +00:00
|
|
|
if (normal_diff > foam_factor)
|
|
|
|
{
|
|
|
|
foam_factor = normal_diff;
|
|
|
|
}
|
|
|
|
|
|
|
|
foam_factor *= 10.0;
|
|
|
|
if (foam_factor > 1.0)
|
|
|
|
{
|
|
|
|
foam_factor = 1.0;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (foam_factor <= 1.0 - definition->foam_coverage)
|
|
|
|
{
|
2013-03-07 14:07:12 +00:00
|
|
|
return COLOR_TRANSPARENT;
|
2013-02-27 16:38:27 +00:00
|
|
|
}
|
|
|
|
foam_factor = (foam_factor - (1.0 - definition->foam_coverage)) * definition->foam_coverage;
|
|
|
|
|
2013-03-07 14:07:12 +00:00
|
|
|
/* TODO Re-use base lighting status */
|
|
|
|
result = renderer->applyLightingToSurface(renderer, location, normal, &definition->foam_material);
|
2013-03-07 14:55:37 +00:00
|
|
|
result.r *= 2.0;
|
|
|
|
result.g *= 2.0;
|
|
|
|
result.b *= 2.0;
|
2013-02-27 16:38:27 +00:00
|
|
|
|
|
|
|
/* TODO This should be configurable */
|
|
|
|
if (foam_factor > 0.2)
|
|
|
|
{
|
|
|
|
result.a = 0.8;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
result.a = 0.8 * (foam_factor / 0.2);
|
|
|
|
}
|
2013-03-07 14:07:12 +00:00
|
|
|
|
|
|
|
return result;
|
2013-02-27 16:38:27 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static int _alterLight(Renderer* renderer, LightDefinition* light, Vector3 at)
|
|
|
|
{
|
|
|
|
WaterDefinition* definition = renderer->water->definition;
|
|
|
|
double factor;
|
2013-05-11 21:34:30 +00:00
|
|
|
double base_height;
|
2013-02-27 16:38:27 +00:00
|
|
|
|
2013-05-11 21:34:30 +00:00
|
|
|
base_height = renderer->terrain->getWaterHeight(renderer);
|
|
|
|
if (at.y < base_height)
|
2013-02-27 16:38:27 +00:00
|
|
|
{
|
2013-03-03 18:10:14 +00:00
|
|
|
if (light->direction.y <= -0.00001)
|
2013-02-27 16:38:27 +00:00
|
|
|
{
|
2013-05-11 21:34:30 +00:00
|
|
|
factor = (base_height - at.y) / (-light->direction.y * definition->lighting_depth);
|
2013-02-27 16:38:27 +00:00
|
|
|
if (factor > 1.0)
|
|
|
|
{
|
|
|
|
factor = 1.0;
|
|
|
|
}
|
2013-03-03 18:10:14 +00:00
|
|
|
factor = 1.0 - factor;
|
2013-02-27 16:38:27 +00:00
|
|
|
|
|
|
|
light->color.r *= factor;
|
|
|
|
light->color.g *= factor;
|
|
|
|
light->color.b *= factor;
|
2013-03-03 18:10:14 +00:00
|
|
|
light->reflection *= factor;
|
2013-02-27 16:38:27 +00:00
|
|
|
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
light->color = COLOR_BLACK;
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/******************** Real ********************/
|
|
|
|
static HeightInfo _realGetHeightInfo(Renderer* renderer)
|
|
|
|
{
|
|
|
|
WaterDefinition* definition = renderer->water->definition;
|
|
|
|
HeightInfo info;
|
|
|
|
double noise_minvalue, noise_maxvalue;
|
|
|
|
|
2013-05-11 21:34:30 +00:00
|
|
|
info.base_height = renderer->terrain->getWaterHeight(renderer);
|
2013-11-03 14:46:39 +00:00
|
|
|
definition->_waves_noise->getRange(&noise_minvalue, &noise_maxvalue);
|
2013-05-11 21:34:30 +00:00
|
|
|
info.min_height = info.base_height + noise_minvalue;
|
|
|
|
info.max_height = info.base_height + noise_maxvalue;
|
2013-02-27 16:38:27 +00:00
|
|
|
|
|
|
|
return info;
|
|
|
|
}
|
|
|
|
|
|
|
|
static double _realGetHeight(Renderer* renderer, double x, double z)
|
|
|
|
{
|
2013-05-11 21:34:30 +00:00
|
|
|
return _getHeight(renderer->water->definition, renderer->terrain->getWaterHeight(renderer), x, z);
|
2013-02-27 16:38:27 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static WaterResult _realGetResult(Renderer* renderer, double x, double z)
|
|
|
|
{
|
|
|
|
WaterDefinition* definition = renderer->water->definition;
|
|
|
|
WaterResult result;
|
|
|
|
RayCastingResult refracted;
|
2013-03-03 17:05:30 +00:00
|
|
|
Vector3 location, normal, look_direction;
|
2013-03-07 14:07:12 +00:00
|
|
|
Color color, foam;
|
2013-05-11 21:34:30 +00:00
|
|
|
double base_height, detail, depth;
|
|
|
|
|
|
|
|
base_height = renderer->terrain->getWaterHeight(renderer);
|
2013-02-27 16:38:27 +00:00
|
|
|
|
|
|
|
location.x = x;
|
2013-05-11 21:34:30 +00:00
|
|
|
location.y = _getHeight(definition, base_height, x, z);
|
2013-02-27 16:38:27 +00:00
|
|
|
location.z = z;
|
|
|
|
result.location = location;
|
|
|
|
|
|
|
|
detail = renderer->getPrecision(renderer, location) * 0.1;
|
|
|
|
if (detail < 0.00001)
|
|
|
|
{
|
|
|
|
detail = 0.00001;
|
|
|
|
}
|
|
|
|
|
2013-05-11 21:34:30 +00:00
|
|
|
normal = _getNormal(definition, base_height, location, detail);
|
2013-03-03 17:05:30 +00:00
|
|
|
look_direction = v3Normalize(v3Sub(location, renderer->getCameraLocation(renderer, location)));
|
|
|
|
|
|
|
|
/* Reflection */
|
|
|
|
if (definition->reflection == 0.0)
|
|
|
|
{
|
|
|
|
result.reflected = COLOR_BLACK;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
result.reflected = renderer->rayWalking(renderer, location, _reflectRay(look_direction, normal), 1, 0, 1, 1).hit_color;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Transparency/refraction */
|
|
|
|
if (definition->transparency == 0.0)
|
2013-02-27 16:38:27 +00:00
|
|
|
{
|
2013-03-03 17:05:30 +00:00
|
|
|
result.refracted = COLOR_BLACK;
|
2013-02-27 16:38:27 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2013-03-09 11:06:39 +00:00
|
|
|
Color depth_color = definition->depth_color;
|
2013-03-03 17:05:30 +00:00
|
|
|
refracted = renderer->rayWalking(renderer, location, _refractRay(look_direction, normal), 1, 0, 1, 1);
|
|
|
|
depth = v3Norm(v3Sub(location, refracted.hit_location));
|
2013-03-09 11:06:39 +00:00
|
|
|
colorLimitPower(&depth_color, colorGetPower(&refracted.hit_color));
|
2013-03-03 17:05:30 +00:00
|
|
|
if (depth > definition->transparency_depth)
|
|
|
|
{
|
2013-03-09 11:06:39 +00:00
|
|
|
result.refracted = depth_color;
|
2013-03-03 17:05:30 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
depth /= definition->transparency_depth;
|
2013-03-09 11:06:39 +00:00
|
|
|
result.refracted.r = refracted.hit_color.r * (1.0 - depth) + depth_color.r * depth;
|
|
|
|
result.refracted.g = refracted.hit_color.g * (1.0 - depth) + depth_color.g * depth;
|
|
|
|
result.refracted.b = refracted.hit_color.b * (1.0 - depth) + depth_color.b * depth;
|
2013-03-03 17:05:30 +00:00
|
|
|
result.refracted.a = 1.0;
|
|
|
|
}
|
2013-02-27 16:38:27 +00:00
|
|
|
}
|
|
|
|
|
2013-03-03 17:05:30 +00:00
|
|
|
/* Lighting from environment */
|
|
|
|
color = renderer->applyLightingToSurface(renderer, location, normal, &definition->material);
|
2013-02-27 16:38:27 +00:00
|
|
|
|
2013-03-03 17:05:30 +00:00
|
|
|
color.r += result.reflected.r * definition->reflection + result.refracted.r * definition->transparency;
|
|
|
|
color.g += result.reflected.g * definition->reflection + result.refracted.g * definition->transparency;
|
|
|
|
color.b += result.reflected.b * definition->reflection + result.refracted.b * definition->transparency;
|
2013-02-27 16:38:27 +00:00
|
|
|
|
2013-03-03 17:05:30 +00:00
|
|
|
/* Merge with foam */
|
2013-03-07 14:07:12 +00:00
|
|
|
foam = _getFoamMask(renderer, definition, location, normal, detail);
|
|
|
|
colorMask(&color, &foam);
|
2013-02-27 16:38:27 +00:00
|
|
|
|
2013-03-03 17:05:30 +00:00
|
|
|
/* Bring color to the camera */
|
2013-02-27 16:38:27 +00:00
|
|
|
color = renderer->applyMediumTraversal(renderer, location, color);
|
|
|
|
|
2013-09-18 15:10:34 +00:00
|
|
|
result.base = definition->material._rgb;
|
2013-02-27 16:38:27 +00:00
|
|
|
result.final = color;
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
/******************** Renderer ********************/
|
|
|
|
static WaterRenderer* _createRenderer()
|
|
|
|
{
|
|
|
|
WaterRenderer* result;
|
|
|
|
|
2013-11-02 15:43:43 +00:00
|
|
|
result = new WaterRenderer;
|
|
|
|
result->definition = (WaterDefinition*)WaterDefinitionClass.create();
|
2013-02-27 16:38:27 +00:00
|
|
|
|
|
|
|
result->getHeightInfo = _fakeGetHeightInfo;
|
|
|
|
result->getHeight = _fakeGetHeight;
|
|
|
|
result->getResult = _fakeGetResult;
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void _deleteRenderer(WaterRenderer* renderer)
|
|
|
|
{
|
|
|
|
WaterDefinitionClass.destroy(renderer->definition);
|
2013-11-02 15:43:43 +00:00
|
|
|
delete renderer;
|
2013-02-27 16:38:27 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void _bindRenderer(Renderer* renderer, WaterDefinition* definition)
|
|
|
|
{
|
|
|
|
WaterDefinitionClass.copy(definition, renderer->water->definition);
|
|
|
|
|
|
|
|
renderer->water->getHeightInfo = _realGetHeightInfo;
|
|
|
|
renderer->water->getHeight = _realGetHeight;
|
|
|
|
renderer->water->getResult = _realGetResult;
|
|
|
|
|
|
|
|
lightingManagerRegisterFilter(renderer->lighting, (FuncLightingAlterLight)_alterLight, renderer);
|
|
|
|
}
|
|
|
|
|
2013-02-28 15:34:47 +00:00
|
|
|
StandardRenderer WaterRendererClass = {
|
2013-02-27 16:38:27 +00:00
|
|
|
(FuncObjectCreate)_createRenderer,
|
|
|
|
(FuncObjectDelete)_deleteRenderer,
|
|
|
|
(FuncObjectBind)_bindRenderer
|
|
|
|
};
|