Michaël Lemaire
e3f692800b
git-svn-id: https://subversion.assembla.com/svn/thunderk/paysages@212 b1fd45b6-86a6-48da-8261-f70d1f35bdcc
374 lines
9.5 KiB
C
374 lines
9.5 KiB
C
#include <stdlib.h>
|
|
#include <math.h>
|
|
#include <string.h>
|
|
|
|
#include "shared/types.h"
|
|
#include "shared/functions.h"
|
|
#include "shared/globals.h"
|
|
#include "shared/constants.h"
|
|
|
|
#include "textures.h"
|
|
#include "water.h"
|
|
#include "terrain.h"
|
|
|
|
static TerrainDefinition _definition;
|
|
static TerrainQuality _quality;
|
|
static TerrainEnvironment _environment;
|
|
static double _max_height = 1.0;
|
|
|
|
void terrainInit()
|
|
{
|
|
_definition = terrainCreateDefinition();
|
|
_max_height = noiseGetMaxValue(_definition.height_noise);
|
|
}
|
|
|
|
void terrainSave(FILE* f)
|
|
{
|
|
int i;
|
|
|
|
noiseSave(_definition.height_noise, f);
|
|
toolsSaveInt(f, _definition.height_modifiers_count);
|
|
for (i = 0; i < _definition.height_modifiers_count; i++)
|
|
{
|
|
modifierSave(_definition.height_modifiers[i], f);
|
|
}
|
|
}
|
|
|
|
void terrainLoad(FILE* f)
|
|
{
|
|
int i;
|
|
|
|
noiseLoad(_definition.height_noise, f);
|
|
_max_height = noiseGetMaxValue(_definition.height_noise);
|
|
|
|
for (i = 0; i < _definition.height_modifiers_count; i++)
|
|
{
|
|
modifierDelete(_definition.height_modifiers[i]);
|
|
}
|
|
_definition.height_modifiers_count = toolsLoadInt(f);
|
|
for (i = 0; i < _definition.height_modifiers_count; i++)
|
|
{
|
|
_definition.height_modifiers[i] = modifierCreate();
|
|
modifierLoad(_definition.height_modifiers[i], f);
|
|
}
|
|
}
|
|
|
|
TerrainDefinition terrainCreateDefinition()
|
|
{
|
|
TerrainDefinition definition;
|
|
|
|
definition.height_noise = noiseCreateGenerator();
|
|
definition.height_modifiers_count = 0;
|
|
|
|
return definition;
|
|
}
|
|
|
|
void terrainDeleteDefinition(TerrainDefinition definition)
|
|
{
|
|
int i;
|
|
|
|
noiseDeleteGenerator(definition.height_noise);
|
|
for (i = 0; i < definition.height_modifiers_count; i++)
|
|
{
|
|
modifierDelete(definition.height_modifiers[i]);
|
|
}
|
|
}
|
|
|
|
void terrainCopyDefinition(TerrainDefinition source, TerrainDefinition* destination)
|
|
{
|
|
int i;
|
|
|
|
noiseCopy(source.height_noise, destination->height_noise);
|
|
|
|
for (i = 0; i < destination->height_modifiers_count; i++)
|
|
{
|
|
modifierDelete(destination->height_modifiers[i]);
|
|
}
|
|
destination->height_modifiers_count = source.height_modifiers_count;
|
|
for (i = 0; i < destination->height_modifiers_count; i++)
|
|
{
|
|
destination->height_modifiers[i] = modifierCreateCopy(source.height_modifiers[i]);
|
|
}
|
|
}
|
|
|
|
void terrainSetDefinition(TerrainDefinition definition)
|
|
{
|
|
terrainCopyDefinition(definition, &_definition);
|
|
_max_height = noiseGetMaxValue(_definition.height_noise);
|
|
/* FIXME _max_height depends on modifiers */
|
|
}
|
|
|
|
TerrainDefinition terrainGetDefinition()
|
|
{
|
|
return _definition;
|
|
}
|
|
|
|
int terrainAddModifier(TerrainDefinition* definition, HeightModifier* modifier)
|
|
{
|
|
if (definition->height_modifiers_count < MAX_HEIGHT_MODIFIER_COUNT)
|
|
{
|
|
definition->height_modifiers[definition->height_modifiers_count] = modifierCreateCopy(modifier);
|
|
return definition->height_modifiers_count++;
|
|
}
|
|
else
|
|
{
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
void terrainDelModifier(TerrainDefinition* definition, int modifier_position)
|
|
{
|
|
if (modifier_position >= 0 && modifier_position < definition->height_modifiers_count)
|
|
{
|
|
/* TODO */
|
|
}
|
|
}
|
|
|
|
void terrainSetQuality(TerrainQuality quality)
|
|
{
|
|
_quality = quality;
|
|
}
|
|
|
|
TerrainQuality terrainGetQuality()
|
|
{
|
|
return _quality;
|
|
}
|
|
|
|
static inline double _getHeight(TerrainDefinition* definition, double x, double z, double detail)
|
|
{
|
|
Vector3 location;
|
|
int i;
|
|
|
|
location.x = x;
|
|
location.y = noiseGet2DDetail(definition->height_noise, x, z, detail);
|
|
location.z = z;
|
|
|
|
for (i = 0; i < definition->height_modifiers_count; i++)
|
|
{
|
|
location = modifierApply(definition->height_modifiers[i], location);
|
|
}
|
|
|
|
return location.y;
|
|
}
|
|
|
|
static inline Vector3 _getPoint(TerrainDefinition* definition, double x, double z, double detail)
|
|
{
|
|
Vector3 result;
|
|
|
|
result.x = x;
|
|
result.y = _getHeight(definition, x, z, detail);
|
|
result.z = z;
|
|
|
|
return result;
|
|
}
|
|
|
|
double terrainGetShadow(Vector3 start, Vector3 direction)
|
|
{
|
|
Vector3 inc_vector;
|
|
double inc_value, inc_base, inc_factor, height, diff, light, smoothing, length, water;
|
|
|
|
direction = v3Normalize(direction);
|
|
inc_factor = (double)render_quality;
|
|
inc_base = 1.0;
|
|
inc_value = inc_base / inc_factor;
|
|
smoothing = 0.03 * inc_factor;;
|
|
|
|
water = waterGetLightFactor(start);
|
|
|
|
light = 1.0;
|
|
length = 0.0;
|
|
do
|
|
{
|
|
inc_vector = v3Scale(direction, inc_value);
|
|
length += v3Norm(inc_vector);
|
|
start = v3Add(start, inc_vector);
|
|
height = _getHeight(&_definition, start.x, start.z, inc_value);
|
|
diff = start.y - height;
|
|
if (diff < 0.0)
|
|
{
|
|
light += diff / smoothing;
|
|
}
|
|
|
|
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 > 0.0 && length < 50.0 && start.y <= _max_height);
|
|
|
|
light *= water;
|
|
if (light < 0.0)
|
|
{
|
|
return 1.0;
|
|
}
|
|
else
|
|
{
|
|
return 1.0 - light;
|
|
}
|
|
}
|
|
|
|
static Color _getColor(TerrainDefinition* definition, Vector3 point, double precision)
|
|
{
|
|
Color color;
|
|
|
|
/* FIXME Environment for textures should be customized */
|
|
color = texturesGetColor(point);
|
|
color = fogApplyToLocation(point, color);
|
|
//color = cloudsApplySegmentResult(color, camera_location, point);
|
|
|
|
return color;
|
|
}
|
|
|
|
int terrainProjectRay(Vector3 start, Vector3 direction, Vector3* hit_point, Color* hit_color)
|
|
{
|
|
Vector3 inc_vector;
|
|
double inc_value, inc_base, inc_factor, height, diff, length;
|
|
|
|
direction = v3Normalize(direction);
|
|
inc_factor = (double)render_quality;
|
|
inc_base = 1.0;
|
|
inc_value = inc_base / inc_factor;
|
|
|
|
length = 0.0;
|
|
do
|
|
{
|
|
inc_vector = v3Scale(direction, inc_value);
|
|
length += v3Norm(inc_vector);
|
|
start = v3Add(start, inc_vector);
|
|
height = _getHeight(&_definition, start.x, start.z, inc_value);
|
|
diff = start.y - height;
|
|
if (diff < 0.0)
|
|
{
|
|
start.y = height;
|
|
*hit_point = start;
|
|
*hit_color = _getColor(&_definition, start, inc_value);
|
|
return 1;
|
|
}
|
|
|
|
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 (length < 50.0 && start.y <= _max_height);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int _postProcessFragment(RenderFragment* fragment)
|
|
{
|
|
Vector3 point;
|
|
double precision;
|
|
|
|
point = fragment->vertex.location;
|
|
precision = renderGetPrecision(point);
|
|
|
|
point = _getPoint(&_definition, point.x, point.z, precision);
|
|
|
|
fragment->vertex.color = _getColor(&_definition, point, precision);
|
|
|
|
return 1;
|
|
}
|
|
|
|
static Vertex _getFirstPassVertex(double x, double z, double detail)
|
|
{
|
|
Vertex result;
|
|
double value;
|
|
|
|
result.location = _getPoint(&_definition, x, z, 0.0);
|
|
value = sin(x) * sin(x) * cos(z) * cos(z);
|
|
result.color.r = value;
|
|
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 terrainGetHeight(double x, double z)
|
|
{
|
|
return _getHeight(&_definition, x, z, 0.0);
|
|
}
|
|
|
|
double terrainGetHeightNormalized(double x, double z)
|
|
{
|
|
return 0.5 + _getHeight(&_definition, x, z, 0.0) / (_max_height * 2.0);
|
|
}
|
|
|
|
Color terrainGetColorCustom(double x, double z, double detail, TerrainDefinition* definition, TerrainQuality* quality, TerrainEnvironment* environment)
|
|
{
|
|
return _getColor(definition, _getPoint(definition, x, z, detail), detail);
|
|
}
|
|
|
|
Color terrainGetColor(double x, double z, double detail)
|
|
{
|
|
return terrainGetColorCustom(x, z, detail, &_definition, &_quality, &_environment);
|
|
}
|
|
|
|
void terrainRender(RenderProgressCallback callback)
|
|
{
|
|
int chunk_factor, chunk_count, i;
|
|
double cx = camera_location.x;
|
|
double cz = camera_location.z;
|
|
double radius_int, radius_ext, chunk_size;
|
|
|
|
chunk_factor = 1;
|
|
chunk_count = 2;
|
|
radius_int = 0.0;
|
|
radius_ext = _quality.min_chunk_size;
|
|
chunk_size = _quality.min_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 (chunk_count % 64 == 0 && chunk_size / radius_int < _quality.visible_chunk_size)
|
|
{
|
|
chunk_count /= 2;
|
|
chunk_factor *= 2;
|
|
/* TODO Fill in gaps with triangles */
|
|
}
|
|
chunk_count += 2;
|
|
chunk_size = _quality.min_chunk_size * chunk_factor;
|
|
radius_int = radius_ext;
|
|
radius_ext += chunk_size;
|
|
}
|
|
}
|