paysages : Started clouds refactoring with unit tests (WIP).
git-svn-id: https://subversion.assembla.com/svn/thunderk/paysages@576 b1fd45b6-86a6-48da-8261-f70d1f35bdcc
This commit is contained in:
parent
6420103652
commit
3fdb42ba0e
14 changed files with 629 additions and 317 deletions
3
Makefile
3
Makefile
|
@ -32,7 +32,8 @@ debug:
|
||||||
release:
|
release:
|
||||||
make BUILDMODE=release all
|
make BUILDMODE=release all
|
||||||
|
|
||||||
tests: all
|
tests:
|
||||||
|
make BUILDMODE=${BUILDMODE} all
|
||||||
LD_LIBRARY_PATH=${BUILDPATH} CK_DEFAULT_TIMEOUT=30 ${BUILDPATH}/paysages-tests
|
LD_LIBRARY_PATH=${BUILDPATH} CK_DEFAULT_TIMEOUT=30 ${BUILDPATH}/paysages-tests
|
||||||
|
|
||||||
run_cli: all
|
run_cli: all
|
||||||
|
|
66
src/rendering/clouds/clo_density.c
Normal file
66
src/rendering/clouds/clo_density.c
Normal file
|
@ -0,0 +1,66 @@
|
||||||
|
#include "clo_density.h"
|
||||||
|
|
||||||
|
#include "../tools.h"
|
||||||
|
|
||||||
|
double cloudsGetLayerCoverage(CloudsLayerDefinition* layer, Vector3 location)
|
||||||
|
{
|
||||||
|
if (layer->base_coverage == 0.0)
|
||||||
|
{
|
||||||
|
return 0.0;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
double coverage = noiseGet2DTotal(layer->_coverage_noise, location.x / layer->shape_scaling, location.z / layer->shape_scaling);
|
||||||
|
coverage -= (1.0 - layer->base_coverage);
|
||||||
|
|
||||||
|
coverage *= curveGetValue(layer->_coverage_by_altitude, (location.y - layer->lower_altitude) / layer->thickness);
|
||||||
|
|
||||||
|
return (coverage <= 0.0) ? 0.0 : coverage;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
double cloudsGetLayerDensity(CloudsLayerDefinition* layer, Vector3 location, double coverage)
|
||||||
|
{
|
||||||
|
if (coverage == 0.0)
|
||||||
|
{
|
||||||
|
return 0.0;
|
||||||
|
}
|
||||||
|
else if (coverage == 1.0)
|
||||||
|
{
|
||||||
|
return 1.0;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
double density = noiseGet3DTotal(layer->_shape_noise, location.x / layer->shape_scaling, location.y / layer->shape_scaling, location.z / layer->shape_scaling);
|
||||||
|
density -= (0.5 - coverage);
|
||||||
|
return (density <= 0.0) ? 0.0 : density;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static double _fakeGetDensity(Renderer* renderer, CloudsLayerDefinition* layer, Vector3 location)
|
||||||
|
{
|
||||||
|
UNUSED(layer);
|
||||||
|
UNUSED(renderer);
|
||||||
|
UNUSED(location);
|
||||||
|
|
||||||
|
return 0.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void cloudsBindFakeDensityToRenderer(CloudsRenderer* renderer)
|
||||||
|
{
|
||||||
|
renderer->getLayerDensity = _fakeGetDensity;
|
||||||
|
}
|
||||||
|
|
||||||
|
static double _realGetDensity(Renderer* renderer, CloudsLayerDefinition* layer, Vector3 location)
|
||||||
|
{
|
||||||
|
UNUSED(renderer);
|
||||||
|
|
||||||
|
double coverage = cloudsGetLayerCoverage(layer, location);
|
||||||
|
|
||||||
|
return cloudsGetLayerDensity(layer, location, coverage);
|
||||||
|
}
|
||||||
|
|
||||||
|
void cloudsBindRealDensityToRenderer(CloudsRenderer* renderer)
|
||||||
|
{
|
||||||
|
renderer->getLayerDensity = _realGetDensity;
|
||||||
|
}
|
45
src/rendering/clouds/clo_density.h
Normal file
45
src/rendering/clouds/clo_density.h
Normal file
|
@ -0,0 +1,45 @@
|
||||||
|
#ifndef _PAYSAGES_CLOUDS_DENSITY_H_
|
||||||
|
#define _PAYSAGES_CLOUDS_DENSITY_H_
|
||||||
|
|
||||||
|
#include "public.h"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Coverage/density management in a cloud layer.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C"
|
||||||
|
{
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the local coverage of a cloud layer [0.0;1.0]
|
||||||
|
*
|
||||||
|
* 0.0 means no cloud is present.
|
||||||
|
* 1.0 means full layer.
|
||||||
|
*/
|
||||||
|
double cloudsGetLayerCoverage(CloudsLayerDefinition* layer, Vector3 location);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the local density of a cloud layer at a given point [0.0;1.0].
|
||||||
|
*
|
||||||
|
* 0.0 means no cloud is present.
|
||||||
|
* 1.0 means full density (deep inside cloud).
|
||||||
|
*/
|
||||||
|
double cloudsGetLayerDensity(CloudsLayerDefinition* layer, Vector3 location, double coverage);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Bind fake density functions to a renderer.
|
||||||
|
*/
|
||||||
|
void cloudsBindFakeDensityToRenderer(CloudsRenderer* renderer);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Bind real density functions to a renderer.
|
||||||
|
*/
|
||||||
|
void cloudsBindRealDensityToRenderer(CloudsRenderer* renderer);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
|
@ -3,6 +3,7 @@
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include "../tools.h"
|
#include "../tools.h"
|
||||||
#include "../renderer.h"
|
#include "../renderer.h"
|
||||||
|
#include "clo_density.h"
|
||||||
|
|
||||||
/******************** Fake ********************/
|
/******************** Fake ********************/
|
||||||
static int _fakeAlterLight(Renderer* renderer, LightDefinition* light, Vector3 location)
|
static int _fakeAlterLight(Renderer* renderer, LightDefinition* light, Vector3 location)
|
||||||
|
@ -23,21 +24,6 @@ static Color _fakeGetColor(Renderer* renderer, Color base, Vector3 start, Vector
|
||||||
return base;
|
return base;
|
||||||
}
|
}
|
||||||
|
|
||||||
static CloudsInfo _fakeGetLayerInfo(Renderer* renderer, CloudsLayerDefinition* layer, Vector3 location)
|
|
||||||
{
|
|
||||||
UNUSED(renderer);
|
|
||||||
UNUSED(layer);
|
|
||||||
UNUSED(location);
|
|
||||||
|
|
||||||
CloudsInfo result;
|
|
||||||
|
|
||||||
result.inside = 0;
|
|
||||||
result.density = 0.0;
|
|
||||||
result.distance_to_edge = 1.0;
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
/******************** Real ********************/
|
/******************** Real ********************/
|
||||||
/*static int _cmpLayer(const void* layer1, const void* layer2)
|
/*static int _cmpLayer(const void* layer1, const void* layer2)
|
||||||
{
|
{
|
||||||
|
@ -89,7 +75,8 @@ static CloudsRenderer* _createRenderer()
|
||||||
|
|
||||||
result->getColor = _fakeGetColor;
|
result->getColor = _fakeGetColor;
|
||||||
result->alterLight = (FuncLightingAlterLight)_fakeAlterLight;
|
result->alterLight = (FuncLightingAlterLight)_fakeAlterLight;
|
||||||
result->getLayerInfo = _fakeGetLayerInfo;
|
|
||||||
|
cloudsBindFakeDensityToRenderer(result);
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
@ -106,7 +93,8 @@ static void _bindRenderer(Renderer* renderer, CloudsDefinition* definition)
|
||||||
|
|
||||||
renderer->clouds->getColor = _getColor;
|
renderer->clouds->getColor = _getColor;
|
||||||
renderer->clouds->alterLight = (FuncLightingAlterLight)_alterLight;
|
renderer->clouds->alterLight = (FuncLightingAlterLight)_alterLight;
|
||||||
renderer->clouds->getLayerInfo = cloudsGetLayerInfo;
|
|
||||||
|
cloudsBindRealDensityToRenderer(renderer->clouds);
|
||||||
|
|
||||||
lightingManagerRegisterFilter(renderer->lighting, (FuncLightingAlterLight)_alterLight, renderer);
|
lightingManagerRegisterFilter(renderer->lighting, (FuncLightingAlterLight)_alterLight, renderer);
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
* Clouds tools.
|
* Clouds tools.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include "clo_walking.h"
|
||||||
#include "../renderer.h"
|
#include "../renderer.h"
|
||||||
#include "../tools.h"
|
#include "../tools.h"
|
||||||
|
|
||||||
|
@ -46,179 +47,6 @@ static inline Vector3 _getNormal(CloudsLayerDefinition* layer, Vector3 position,
|
||||||
return v3Normalize(result);
|
return v3Normalize(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Optimize the search limits in a layer.
|
|
||||||
*
|
|
||||||
* @param layer The cloud layer
|
|
||||||
* @param start Start of the search to optimize
|
|
||||||
* @param end End of the search to optimize
|
|
||||||
* @return 0 if the search is useless
|
|
||||||
*/
|
|
||||||
static int _optimizeSearchLimits(CloudsLayerDefinition* layer, Vector3* start, Vector3* end)
|
|
||||||
{
|
|
||||||
Vector3 diff;
|
|
||||||
|
|
||||||
if (start->y > layer->lower_altitude + layer->thickness)
|
|
||||||
{
|
|
||||||
if (end->y >= layer->lower_altitude + layer->thickness)
|
|
||||||
{
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
diff = v3Sub(*end, *start);
|
|
||||||
*start = v3Add(*start, v3Scale(diff, (layer->lower_altitude + layer->thickness - start->y) / diff.y));
|
|
||||||
if (end->y < layer->lower_altitude)
|
|
||||||
{
|
|
||||||
*end = v3Add(*end, v3Scale(diff, (layer->lower_altitude - end->y) / diff.y));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (start->y < layer->lower_altitude)
|
|
||||||
{
|
|
||||||
if (end->y <= layer->lower_altitude)
|
|
||||||
{
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
diff = v3Sub(*end, *start);
|
|
||||||
*start = v3Add(*start, v3Scale(diff, (layer->lower_altitude - start->y) / diff.y));
|
|
||||||
if (end->y >= layer->lower_altitude + layer->thickness)
|
|
||||||
{
|
|
||||||
*end = v3Add(*end, v3Scale(diff, (layer->lower_altitude + layer->thickness - end->y) / diff.y));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else /* start is inside layer */
|
|
||||||
{
|
|
||||||
diff = v3Sub(*end, *start);
|
|
||||||
if (end->y > layer->lower_altitude + layer->thickness)
|
|
||||||
{
|
|
||||||
*end = v3Add(*start, v3Scale(diff, (layer->lower_altitude + layer->thickness - start->y) / diff.y));
|
|
||||||
}
|
|
||||||
else if (end->y < layer->lower_altitude)
|
|
||||||
{
|
|
||||||
*end = v3Add(*start, v3Scale(diff, (layer->lower_altitude - start->y) / diff.y));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* TODO Limit the search length */
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Go through the cloud layer to find segments (parts of the lookup that are likely to contain cloud).
|
|
||||||
*
|
|
||||||
* @param definition The cloud layer
|
|
||||||
* @param renderer The renderer environment
|
|
||||||
* @param start Start position of the lookup (already optimized)
|
|
||||||
* @param direction Normalized direction of the lookup
|
|
||||||
* @param max_segments Maximum number of segments to collect
|
|
||||||
* @param max_inside_length Maximum length to spend inside the cloud
|
|
||||||
* @param max_total_length Maximum lookup length
|
|
||||||
* @param inside_length Resulting length inside cloud (sum of all segments length)
|
|
||||||
* @param total_length Resulting lookup length
|
|
||||||
* @param out_segments Allocated space to fill found segments
|
|
||||||
* @return Number of segments found
|
|
||||||
*/
|
|
||||||
static int _getPrimarySegments(CloudsLayerDefinition* definition, Renderer* renderer, Vector3 start, Vector3 direction, int max_segments, double max_inside_length, double max_total_length, double* inside_length, double* total_length, CloudSegment* out_segments)
|
|
||||||
{
|
|
||||||
int inside, segment_count;
|
|
||||||
double current_total_length, current_inside_length;
|
|
||||||
double step_length, segment_length;
|
|
||||||
Vector3 walker, step, segment_start;
|
|
||||||
CloudsInfo info;
|
|
||||||
double render_precision;
|
|
||||||
|
|
||||||
if (max_segments <= 0)
|
|
||||||
{
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
render_precision = 1.005 - 0.01 * (double)(renderer->render_quality * renderer->render_quality);
|
|
||||||
if (render_precision > max_total_length / 10.0)
|
|
||||||
{
|
|
||||||
render_precision = max_total_length / 10.0;
|
|
||||||
}
|
|
||||||
else if (render_precision < max_total_length / 10000.0)
|
|
||||||
{
|
|
||||||
render_precision = max_total_length / 10000.0;
|
|
||||||
}
|
|
||||||
|
|
||||||
segment_count = 0;
|
|
||||||
current_total_length = 0.0;
|
|
||||||
current_inside_length = 0.0;
|
|
||||||
segment_length = 0.0;
|
|
||||||
walker = start;
|
|
||||||
info = renderer->clouds->getLayerInfo(renderer, definition, start);
|
|
||||||
inside = info.inside;
|
|
||||||
step = v3Scale(direction, render_precision);
|
|
||||||
|
|
||||||
do
|
|
||||||
{
|
|
||||||
walker = v3Add(walker, step);
|
|
||||||
step_length = v3Norm(step);
|
|
||||||
current_total_length += step_length;
|
|
||||||
|
|
||||||
if (current_total_length >= max_total_length || current_inside_length > max_inside_length)
|
|
||||||
{
|
|
||||||
info.distance_to_edge = 0.0;
|
|
||||||
info.inside = 0;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
info = renderer->clouds->getLayerInfo(renderer, definition, walker);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (info.inside)
|
|
||||||
{
|
|
||||||
if (inside)
|
|
||||||
{
|
|
||||||
/* inside the cloud */
|
|
||||||
segment_length += step_length;
|
|
||||||
current_inside_length += step_length;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
/* entering the cloud */
|
|
||||||
segment_length = step_length;
|
|
||||||
segment_start = walker;
|
|
||||||
current_inside_length += segment_length;
|
|
||||||
/* TODO Refine entry position */
|
|
||||||
|
|
||||||
inside = 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (inside)
|
|
||||||
{
|
|
||||||
/* exiting the cloud */
|
|
||||||
segment_length += step_length;
|
|
||||||
current_inside_length += step_length;
|
|
||||||
|
|
||||||
out_segments->start = segment_start;
|
|
||||||
out_segments->end = walker;
|
|
||||||
out_segments->length = segment_length;
|
|
||||||
out_segments++;
|
|
||||||
if (++segment_count >= max_segments)
|
|
||||||
{
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
/* TODO Refine exit position */
|
|
||||||
|
|
||||||
inside = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
step = v3Scale(direction, (info.distance_to_edge < render_precision) ? render_precision : info.distance_to_edge);
|
|
||||||
} while (inside || (walker.y <= definition->lower_altitude + definition->thickness + 0.001 && walker.y >= definition->lower_altitude - 0.001 && current_total_length < max_total_length && current_inside_length < max_inside_length));
|
|
||||||
|
|
||||||
*total_length = current_total_length;
|
|
||||||
*inside_length = current_inside_length;
|
|
||||||
return segment_count;
|
|
||||||
}
|
|
||||||
|
|
||||||
static Color _applyLayerLighting(CloudsLayerDefinition* definition, Renderer* renderer, Vector3 location, double detail)
|
static Color _applyLayerLighting(CloudsLayerDefinition* definition, Renderer* renderer, Vector3 location, double detail)
|
||||||
{
|
{
|
||||||
Vector3 normal;
|
Vector3 normal;
|
||||||
|
@ -262,32 +90,22 @@ static Color _applyLayerLighting(CloudsLayerDefinition* definition, Renderer* re
|
||||||
Color cloudsApplyLayer(CloudsLayerDefinition* definition, Color base, Renderer* renderer, Vector3 start, Vector3 end)
|
Color cloudsApplyLayer(CloudsLayerDefinition* definition, Color base, Renderer* renderer, Vector3 start, Vector3 end)
|
||||||
{
|
{
|
||||||
int segment_count;
|
int segment_count;
|
||||||
double max_length, total_length, inside_length;
|
|
||||||
Vector3 direction;
|
|
||||||
Color col;
|
Color col;
|
||||||
CloudSegment segments[MAX_SEGMENT_COUNT];
|
CloudPrimarySegment segments[MAX_SEGMENT_COUNT];
|
||||||
|
|
||||||
if (!_optimizeSearchLimits(definition, &start, &end))
|
segment_count = cloudsGetLayerPrimarySegments(renderer, definition, start, end, MAX_SEGMENT_COUNT, segments);
|
||||||
{
|
|
||||||
return base;
|
|
||||||
}
|
|
||||||
|
|
||||||
direction = v3Sub(end, start);
|
|
||||||
max_length = v3Norm(direction);
|
|
||||||
direction = v3Normalize(direction);
|
|
||||||
|
|
||||||
segment_count = _getPrimarySegments(definition, renderer, start, direction, MAX_SEGMENT_COUNT, definition->transparencydepth * (double)renderer->render_quality, max_length, &inside_length, &total_length, segments);
|
|
||||||
/* TODO Crawl in segments for render */
|
/* TODO Crawl in segments for render */
|
||||||
|
|
||||||
col = definition->material.base;
|
col = definition->material.base;
|
||||||
if (definition->transparencydepth == 0 || inside_length >= definition->transparencydepth)
|
col.a = 0.0;
|
||||||
|
/*if (definition->transparencydepth == 0 || inside_length >= definition->transparencydepth)
|
||||||
{
|
{
|
||||||
col.a = 1.0;
|
col.a = 1.0;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
col.a = inside_length / definition->transparencydepth;
|
col.a = inside_length / definition->transparencydepth;
|
||||||
}
|
}*/
|
||||||
|
|
||||||
col = renderer->atmosphere->applyAerialPerspective(renderer, start, col).final;
|
col = renderer->atmosphere->applyAerialPerspective(renderer, start, col).final;
|
||||||
|
|
||||||
|
@ -298,10 +116,10 @@ Color cloudsApplyLayer(CloudsLayerDefinition* definition, Color base, Renderer*
|
||||||
|
|
||||||
Color cloudsLayerFilterLight(CloudsLayerDefinition* definition, Renderer* renderer, Color light, Vector3 location, Vector3 light_location, Vector3 direction_to_light)
|
Color cloudsLayerFilterLight(CloudsLayerDefinition* definition, Renderer* renderer, Color light, Vector3 location, Vector3 light_location, Vector3 direction_to_light)
|
||||||
{
|
{
|
||||||
double inside_depth, total_depth, factor;
|
/*double inside_depth, total_depth, factor;
|
||||||
CloudSegment segments[MAX_SEGMENT_COUNT];
|
CloudSegment segments[MAX_SEGMENT_COUNT];
|
||||||
|
|
||||||
if (!_optimizeSearchLimits(definition, &location, &light_location))
|
if (!cloudsOptimizeWalkingBounds(definition, &location, &light_location))
|
||||||
{
|
{
|
||||||
return light;
|
return light;
|
||||||
}
|
}
|
||||||
|
@ -325,93 +143,7 @@ Color cloudsLayerFilterLight(CloudsLayerDefinition* definition, Renderer* render
|
||||||
|
|
||||||
light.r = light.r * factor;
|
light.r = light.r * factor;
|
||||||
light.g = light.g * factor;
|
light.g = light.g * factor;
|
||||||
light.b = light.b * factor;
|
light.b = light.b * factor;*/
|
||||||
|
|
||||||
return light;
|
return light;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
* Get the coverage factor at the given location [0.0;1.0].
|
|
||||||
* 0.0 means no cloud is present.
|
|
||||||
* 1.0 means full layer.
|
|
||||||
*/
|
|
||||||
static inline double _getLayerCoverage(CloudsLayerDefinition* layer, double x, double z)
|
|
||||||
{
|
|
||||||
if (layer->base_coverage == 0.0)
|
|
||||||
{
|
|
||||||
return 0.0;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
double coverage = noiseGet2DTotal(layer->_coverage_noise, x / layer->shape_scaling, z / layer->shape_scaling);
|
|
||||||
coverage -= (1.0 - layer->base_coverage);
|
|
||||||
return (coverage <= 0.0) ? 0.0 : coverage;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Get the local density factor at the given location [0.0;1.0].
|
|
||||||
* 0.0 means no cloud is present.
|
|
||||||
* 1.0 means full density (deep inside cloud).
|
|
||||||
*/
|
|
||||||
static inline double _getLayerDensity(CloudsLayerDefinition* layer, Vector3 location, double coverage)
|
|
||||||
{
|
|
||||||
if (coverage == 0.0)
|
|
||||||
{
|
|
||||||
return 0.0;
|
|
||||||
}
|
|
||||||
else if (coverage == 1.0)
|
|
||||||
{
|
|
||||||
return 1.0;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
double density = noiseGet3DTotal(layer->_shape_noise, location.x / layer->shape_scaling, location.y / layer->shape_scaling, location.z / layer->shape_scaling);
|
|
||||||
density -= (0.5 - coverage);
|
|
||||||
return (density <= 0.0) ? 0.0 : density;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
CloudsInfo cloudsGetLayerInfo(Renderer* renderer, CloudsLayerDefinition* layer, Vector3 location)
|
|
||||||
{
|
|
||||||
CloudsInfo result;
|
|
||||||
|
|
||||||
UNUSED(renderer);
|
|
||||||
|
|
||||||
result.density = 0.0;
|
|
||||||
result.distance_to_edge = 0.1;
|
|
||||||
|
|
||||||
/* Get coverage info */
|
|
||||||
double coverage = _getLayerCoverage(layer, location.x, location.z);
|
|
||||||
if (coverage <= 0.0)
|
|
||||||
{
|
|
||||||
/* TODO Distance to edge */
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
/* Apply altitude to coverage */
|
|
||||||
coverage *= curveGetValue(layer->_coverage_by_altitude, (location.y - layer->lower_altitude) / layer->thickness);
|
|
||||||
if (coverage <= 0.0)
|
|
||||||
{
|
|
||||||
/* TODO Distance to edge */
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
/* Get local density */
|
|
||||||
result.density = _getLayerDensity(layer, location, coverage);
|
|
||||||
if (result.density <= 0)
|
|
||||||
{
|
|
||||||
/* TODO Distance to edge */
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
/* TODO Distance to edge */
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
result.inside = (result.density > 0.0);
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
157
src/rendering/clouds/clo_walking.c
Normal file
157
src/rendering/clouds/clo_walking.c
Normal file
|
@ -0,0 +1,157 @@
|
||||||
|
#include "clo_walking.h"
|
||||||
|
|
||||||
|
#include "../renderer.h"
|
||||||
|
|
||||||
|
int cloudsOptimizeWalkingBounds(CloudsLayerDefinition* layer, Vector3* start, Vector3* end)
|
||||||
|
{
|
||||||
|
Vector3 diff;
|
||||||
|
|
||||||
|
if (start->y > layer->lower_altitude + layer->thickness)
|
||||||
|
{
|
||||||
|
if (end->y >= layer->lower_altitude + layer->thickness)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
diff = v3Sub(*end, *start);
|
||||||
|
*start = v3Add(*start, v3Scale(diff, (layer->lower_altitude + layer->thickness - start->y) / diff.y));
|
||||||
|
if (end->y < layer->lower_altitude)
|
||||||
|
{
|
||||||
|
*end = v3Add(*end, v3Scale(diff, (layer->lower_altitude - end->y) / diff.y));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (start->y < layer->lower_altitude)
|
||||||
|
{
|
||||||
|
if (end->y <= layer->lower_altitude)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
diff = v3Sub(*end, *start);
|
||||||
|
*start = v3Add(*start, v3Scale(diff, (layer->lower_altitude - start->y) / diff.y));
|
||||||
|
if (end->y >= layer->lower_altitude + layer->thickness)
|
||||||
|
{
|
||||||
|
*end = v3Add(*end, v3Scale(diff, (layer->lower_altitude + layer->thickness - end->y) / diff.y));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else /* start is inside layer */
|
||||||
|
{
|
||||||
|
diff = v3Sub(*end, *start);
|
||||||
|
if (end->y > layer->lower_altitude + layer->thickness)
|
||||||
|
{
|
||||||
|
*end = v3Add(*start, v3Scale(diff, (layer->lower_altitude + layer->thickness - start->y) / diff.y));
|
||||||
|
}
|
||||||
|
else if (end->y < layer->lower_altitude)
|
||||||
|
{
|
||||||
|
*end = v3Add(*start, v3Scale(diff, (layer->lower_altitude - start->y) / diff.y));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* TODO Limit the search length */
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int cloudsGetLayerPrimarySegments(Renderer* renderer, CloudsLayerDefinition* layer, Vector3 start, Vector3 end, int max_segments, CloudPrimarySegment* out_segments)
|
||||||
|
{
|
||||||
|
int inside, segment_count;
|
||||||
|
double step_length, segment_length;
|
||||||
|
Vector3 diff, walker, segment_start;
|
||||||
|
double render_precision, density;
|
||||||
|
double diff_length, progress;
|
||||||
|
|
||||||
|
if (max_segments <= 0)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!cloudsOptimizeWalkingBounds(layer, &start, &end))
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
diff = v3Sub(end, start);
|
||||||
|
diff_length = v3Norm(diff);
|
||||||
|
|
||||||
|
if (diff_length < 0.000001)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
render_precision = 1.005 - 0.01 * (double)(renderer->render_quality * renderer->render_quality);
|
||||||
|
/*if (render_precision > max_total_length / 10.0)
|
||||||
|
{
|
||||||
|
render_precision = max_total_length / 10.0;
|
||||||
|
}
|
||||||
|
else if (render_precision < max_total_length / 10000.0)
|
||||||
|
{
|
||||||
|
render_precision = max_total_length / 10000.0;
|
||||||
|
}*/
|
||||||
|
|
||||||
|
segment_count = 0;
|
||||||
|
segment_length = 0.0;
|
||||||
|
density = renderer->clouds->getLayerDensity(renderer, layer, start);
|
||||||
|
progress = 0.0;
|
||||||
|
step_length = render_precision;
|
||||||
|
inside = (density > 0.0);
|
||||||
|
|
||||||
|
do
|
||||||
|
{
|
||||||
|
progress += step_length;
|
||||||
|
walker = v3Add(start, v3Scale(diff, progress / diff_length));
|
||||||
|
|
||||||
|
if (progress >= diff_length)
|
||||||
|
{
|
||||||
|
density = 0.0;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
density = renderer->clouds->getLayerDensity(renderer, layer, walker);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (density > 0.0)
|
||||||
|
{
|
||||||
|
if (inside)
|
||||||
|
{
|
||||||
|
/* inside the cloud */
|
||||||
|
segment_length += step_length;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* entering the cloud */
|
||||||
|
segment_length = step_length;
|
||||||
|
segment_start = v3Add(start, v3Scale(diff, (progress - step_length) / diff_length));
|
||||||
|
/* TODO Refine entry position */
|
||||||
|
|
||||||
|
inside = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (inside)
|
||||||
|
{
|
||||||
|
/* exiting the cloud */
|
||||||
|
segment_length += step_length;
|
||||||
|
|
||||||
|
out_segments->enter = segment_start;
|
||||||
|
out_segments->exit = walker;
|
||||||
|
out_segments->length = segment_length;
|
||||||
|
out_segments++;
|
||||||
|
if (++segment_count >= max_segments)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
/* TODO Refine exit position */
|
||||||
|
|
||||||
|
inside = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/* step = v3Scale(direction, (info.distance_to_edge < render_precision) ? render_precision : info.distance_to_edge); */
|
||||||
|
}
|
||||||
|
while (inside || (walker.y <= layer->lower_altitude + layer->thickness + 0.001 && walker.y >= layer->lower_altitude - 0.001 && progress < diff_length));
|
||||||
|
|
||||||
|
return segment_count;
|
||||||
|
}
|
50
src/rendering/clouds/clo_walking.h
Normal file
50
src/rendering/clouds/clo_walking.h
Normal file
|
@ -0,0 +1,50 @@
|
||||||
|
#ifndef _PAYSAGES_CLOUDS_WALKING_H_
|
||||||
|
#define _PAYSAGES_CLOUDS_WALKING_H_
|
||||||
|
|
||||||
|
#include "public.h"
|
||||||
|
#include "../tools/euclid.h"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Functions to walk through a cloud layer.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C"
|
||||||
|
{
|
||||||
|
#endif
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
Vector3 enter;
|
||||||
|
Vector3 exit;
|
||||||
|
double length;
|
||||||
|
} CloudPrimarySegment;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Optimize the search limits in a layer.
|
||||||
|
*
|
||||||
|
* @param layer The cloud layer
|
||||||
|
* @param start Start of the search to optimize
|
||||||
|
* @param end End of the search to optimize
|
||||||
|
* @return 0 if the search is useless
|
||||||
|
*/
|
||||||
|
int cloudsOptimizeWalkingBounds(CloudsLayerDefinition* layer, Vector3* start, Vector3* end);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Go through the cloud layer to find segments (parts of the lookup that are likely to contain cloud).
|
||||||
|
*
|
||||||
|
* @param renderer The renderer environment
|
||||||
|
* @param layer The cloud layer
|
||||||
|
* @param start Start position of the lookup
|
||||||
|
* @param end End position of the lookup
|
||||||
|
* @param max_segments Maximum number of segments to collect
|
||||||
|
* @param out_segments Allocated space to fill found segments
|
||||||
|
* @return Number of segments found
|
||||||
|
*/
|
||||||
|
int cloudsGetLayerPrimarySegments(Renderer* renderer, CloudsLayerDefinition* layer, Vector3 start, Vector3 end, int max_segments, CloudPrimarySegment* out_segments);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
|
@ -6,18 +6,9 @@
|
||||||
#define CLOUDS_MAX_LAYERS 6
|
#define CLOUDS_MAX_LAYERS 6
|
||||||
#define MAX_SEGMENT_COUNT 100
|
#define MAX_SEGMENT_COUNT 100
|
||||||
|
|
||||||
typedef struct
|
|
||||||
{
|
|
||||||
Vector3 start;
|
|
||||||
Vector3 end;
|
|
||||||
double length;
|
|
||||||
} CloudSegment;
|
|
||||||
|
|
||||||
void cloudsLayerValidateDefinition(CloudsLayerDefinition* definition);
|
void cloudsLayerValidateDefinition(CloudsLayerDefinition* definition);
|
||||||
|
|
||||||
Color cloudsLayerFilterLight(CloudsLayerDefinition* definition, Renderer* renderer, Color light, Vector3 location, Vector3 light_location, Vector3 direction_to_light);
|
Color cloudsLayerFilterLight(CloudsLayerDefinition* definition, Renderer* renderer, Color light, Vector3 location, Vector3 light_location, Vector3 direction_to_light);
|
||||||
Color cloudsApplyLayer(CloudsLayerDefinition* definition, Color base, Renderer* renderer, Vector3 start, Vector3 end);
|
Color cloudsApplyLayer(CloudsLayerDefinition* definition, Color base, Renderer* renderer, Vector3 start, Vector3 end);
|
||||||
|
|
||||||
CloudsInfo cloudsGetLayerInfo(Renderer* renderer, CloudsLayerDefinition* layer, Vector3 location);
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -59,15 +59,8 @@ typedef struct
|
||||||
Layers* layers;
|
Layers* layers;
|
||||||
} CloudsDefinition;
|
} CloudsDefinition;
|
||||||
|
|
||||||
typedef struct
|
|
||||||
{
|
|
||||||
int inside;
|
|
||||||
double density;
|
|
||||||
double distance_to_edge;
|
|
||||||
} CloudsInfo;
|
|
||||||
|
|
||||||
typedef Color (*FuncCloudsGetColor)(Renderer* renderer, Color base, Vector3 start, Vector3 end);
|
typedef Color (*FuncCloudsGetColor)(Renderer* renderer, Color base, Vector3 start, Vector3 end);
|
||||||
typedef CloudsInfo (*FuncCloudsGetLayerInfo)(Renderer* renderer, CloudsLayerDefinition* layer, Vector3 location);
|
typedef double (*FuncCloudsGetLayerDensity)(Renderer* renderer, CloudsLayerDefinition* layer, Vector3 location);
|
||||||
|
|
||||||
typedef struct
|
typedef struct
|
||||||
{
|
{
|
||||||
|
@ -75,7 +68,7 @@ typedef struct
|
||||||
|
|
||||||
FuncCloudsGetColor getColor;
|
FuncCloudsGetColor getColor;
|
||||||
FuncLightingAlterLight alterLight;
|
FuncLightingAlterLight alterLight;
|
||||||
FuncCloudsGetLayerInfo getLayerInfo;
|
FuncCloudsGetLayerDensity getLayerDensity;
|
||||||
} CloudsRenderer;
|
} CloudsRenderer;
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -77,6 +77,11 @@ extern Vector3 VECTOR_SOUTH;
|
||||||
extern Vector3 VECTOR_EAST;
|
extern Vector3 VECTOR_EAST;
|
||||||
extern Vector3 VECTOR_WEST;
|
extern Vector3 VECTOR_WEST;
|
||||||
|
|
||||||
|
static inline Vector3 v3(double x, double y, double z)
|
||||||
|
{
|
||||||
|
Vector3 result = {x, y, z};
|
||||||
|
return result;
|
||||||
|
};
|
||||||
void v3Save(PackStream* stream, Vector3* v);
|
void v3Save(PackStream* stream, Vector3* v);
|
||||||
void v3Load(PackStream* stream, Vector3* v);
|
void v3Load(PackStream* stream, Vector3* v);
|
||||||
Vector3 v3Translate(Vector3 v1, double x, double y, double z);
|
Vector3 v3Translate(Vector3 v1, double x, double y, double z);
|
||||||
|
|
|
@ -35,10 +35,30 @@ static inline int _double_not_equals(double x, double y)
|
||||||
{
|
{
|
||||||
return fabs(x - y) >= 0.00000000001;
|
return fabs(x - y) >= 0.00000000001;
|
||||||
}
|
}
|
||||||
|
static inline int _double_greater(double x, double y)
|
||||||
|
{
|
||||||
|
return _double_not_equals(x, y) || (x > y);
|
||||||
|
}
|
||||||
|
static inline int _double_greater_or_equal(double x, double y)
|
||||||
|
{
|
||||||
|
return _double_equals(x, y) || (x >= y);
|
||||||
|
}
|
||||||
|
static inline int _double_less(double x, double y)
|
||||||
|
{
|
||||||
|
return _double_not_equals(x, y) || (x < y);
|
||||||
|
}
|
||||||
|
static inline int _double_less_or_equal(double x, double y)
|
||||||
|
{
|
||||||
|
return _double_equals(x, y) || (x <= y);
|
||||||
|
}
|
||||||
|
|
||||||
#define _ck_assert_double(F, X, O, Y) ck_assert_msg(F(X, Y), "Assertion '"#X#O#Y"' failed: "#X"=%f, "#Y"=%f", X, Y)
|
#define _ck_assert_double(F, X, O, Y) ck_assert_msg(F(X, Y), "Assertion '"#X#O#Y"' failed: "#X"=%f, "#Y"=%f", X, Y)
|
||||||
#define ck_assert_double_eq(X, Y) _ck_assert_double(_double_equals, X, ==, Y)
|
#define ck_assert_double_eq(X, Y) _ck_assert_double(_double_equals, X, ==, Y)
|
||||||
#define ck_assert_double_ne(X, Y) _ck_assert_double(_double_not_equals, X, !=, Y)
|
#define ck_assert_double_ne(X, Y) _ck_assert_double(_double_not_equals, X, !=, Y)
|
||||||
|
#define ck_assert_double_gt(X, Y) _ck_assert_double(_double_greater, X, >, Y)
|
||||||
|
#define ck_assert_double_lt(X, Y) _ck_assert_double(_double_greater_or_equal, X, >=, Y)
|
||||||
|
#define ck_assert_double_gte(X, Y) _ck_assert_double(_double_less, X, <, Y)
|
||||||
|
#define ck_assert_double_lte(X, Y) _ck_assert_double(_double_less_or_equal, X, <=, Y)
|
||||||
|
|
||||||
|
|
||||||
/***** Generic comparison assertions *****/
|
/***** Generic comparison assertions *****/
|
||||||
|
@ -56,4 +76,9 @@ static inline char* _ck_gen_##_type_##_str(char* buffer, _type_ X) { \
|
||||||
static char _ck_gen_strbuf1[101];
|
static char _ck_gen_strbuf1[101];
|
||||||
static char _ck_gen_strbuf2[101];
|
static char _ck_gen_strbuf2[101];
|
||||||
|
|
||||||
|
|
||||||
|
/***** Some builtin comparisons *****/
|
||||||
|
#define ck_assert_double_in_range(_double_, _x_, _y_) ck_assert_double_gte(_double_, _x_);ck_assert_double_lte(_double_, _y_)
|
||||||
|
#define ck_assert_vector_values(_vector_, _x_, _y_, _z_) ck_assert_double_eq(_vector_.x, _x_);ck_assert_double_eq(_vector_.y, _y_);ck_assert_double_eq(_vector_.z, _z_)
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -1,21 +1,30 @@
|
||||||
#include <check.h>
|
#include <check.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
#include "rendering/main.h"
|
||||||
|
|
||||||
extern void test_euclid_case(Suite* s);
|
extern void test_euclid_case(Suite* s);
|
||||||
extern void test_camera_case(Suite* s);
|
extern void test_camera_case(Suite* s);
|
||||||
|
extern void test_clouds_case(Suite* s);
|
||||||
|
|
||||||
int main(int argc, char** argv)
|
int main(int argc, char** argv)
|
||||||
{
|
{
|
||||||
int number_failed;
|
int number_failed;
|
||||||
Suite *s = suite_create("rendering");
|
Suite *s = suite_create("rendering");
|
||||||
|
|
||||||
|
paysagesInit();
|
||||||
|
|
||||||
/* TODO Find a way to automate this */
|
/* TODO Find a way to automate this */
|
||||||
test_euclid_case(s);
|
test_euclid_case(s);
|
||||||
test_camera_case(s);
|
test_camera_case(s);
|
||||||
|
test_clouds_case(s);
|
||||||
|
|
||||||
SRunner *sr = srunner_create(s);
|
SRunner *sr = srunner_create(s);
|
||||||
srunner_run_all(sr, CK_NORMAL);
|
srunner_run_all(sr, CK_NORMAL);
|
||||||
number_failed = srunner_ntests_failed(sr);
|
number_failed = srunner_ntests_failed(sr);
|
||||||
srunner_free(sr);
|
srunner_free(sr);
|
||||||
|
|
||||||
|
paysagesQuit();
|
||||||
|
|
||||||
return (number_failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE;
|
return (number_failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE;
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,8 +2,6 @@
|
||||||
|
|
||||||
#include "rendering/camera.h"
|
#include "rendering/camera.h"
|
||||||
|
|
||||||
#define _checkVector(_vector_, _x_, _y_, _z_) ck_assert_double_eq(_vector_.x, _x_);ck_assert_double_eq(_vector_.y, _y_);ck_assert_double_eq(_vector_.z, _z_)
|
|
||||||
|
|
||||||
START_TEST(test_camera_definition)
|
START_TEST(test_camera_definition)
|
||||||
{
|
{
|
||||||
CameraDefinition* cam;
|
CameraDefinition* cam;
|
||||||
|
@ -12,8 +10,8 @@ START_TEST(test_camera_definition)
|
||||||
cameraSetLocationCoords(cam, 0.0, 1.0, 1.0);
|
cameraSetLocationCoords(cam, 0.0, 1.0, 1.0);
|
||||||
cameraSetTargetCoords(cam, 0.0, 0.0, 0.0);
|
cameraSetTargetCoords(cam, 0.0, 0.0, 0.0);
|
||||||
|
|
||||||
_checkVector(cameraGetLocation(cam), 0.0, 1.0, 1.0);
|
ck_assert_vector_values(cameraGetLocation(cam), 0.0, 1.0, 1.0);
|
||||||
_checkVector(cameraGetTarget(cam), 0.0, 0.0, 0.0);
|
ck_assert_vector_values(cameraGetTarget(cam), 0.0, 0.0, 0.0);
|
||||||
|
|
||||||
cameraDeleteDefinition(cam);
|
cameraDeleteDefinition(cam);
|
||||||
}
|
}
|
||||||
|
|
252
src/testing/test_clouds.c
Normal file
252
src/testing/test_clouds.c
Normal file
|
@ -0,0 +1,252 @@
|
||||||
|
#include "testing/common.h"
|
||||||
|
|
||||||
|
#include "rendering/renderer.h"
|
||||||
|
#include "rendering/tools.h"
|
||||||
|
#include "rendering/clouds/public.h"
|
||||||
|
#include "rendering/clouds/clo_density.h"
|
||||||
|
#include "rendering/clouds/clo_walking.h"
|
||||||
|
|
||||||
|
START_TEST(test_clouds_density)
|
||||||
|
{
|
||||||
|
/* Setup */
|
||||||
|
double x, y, z;
|
||||||
|
CloudsLayerDefinition* layer;
|
||||||
|
layer = cloudsGetLayerType().callback_create();
|
||||||
|
|
||||||
|
/* Test default coverage (empty) */
|
||||||
|
for (x = -10.0; x < 10.0; x += 10.0)
|
||||||
|
{
|
||||||
|
for (y = -10.0; y < 10.0; y += 10.0)
|
||||||
|
{
|
||||||
|
for (z = -10.0; z < 10.0; z += 10.0)
|
||||||
|
{
|
||||||
|
ck_assert_double_eq(cloudsGetLayerCoverage(layer, v3(x, y, z)), 0.0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Test coverage by altitude */
|
||||||
|
layer->base_coverage = 1.0;
|
||||||
|
layer->lower_altitude = -1.0;
|
||||||
|
layer->thickness = 2.0;
|
||||||
|
cloudsGetLayerType().callback_validate(layer);
|
||||||
|
layer->base_coverage = 1.0;
|
||||||
|
noiseForceValue(layer->_coverage_noise, 1.0);
|
||||||
|
for (x = -10.0; x < 10.0; x += 10.0)
|
||||||
|
{
|
||||||
|
for (z = -10.0; z < 10.0; z += 10.0)
|
||||||
|
{
|
||||||
|
ck_assert_double_eq(cloudsGetLayerCoverage(layer, v3(x, 0.0, z)), 1.0);
|
||||||
|
ck_assert_double_eq(cloudsGetLayerCoverage(layer, v3(x, 0.5, z)), 0.5);
|
||||||
|
ck_assert_double_eq(cloudsGetLayerCoverage(layer, v3(x, 1.0, z)), 0.0);
|
||||||
|
ck_assert_double_eq(cloudsGetLayerCoverage(layer, v3(x, 1.5, z)), 0.0);
|
||||||
|
ck_assert_double_eq(cloudsGetLayerCoverage(layer, v3(x, -0.5, z)), 0.5);
|
||||||
|
ck_assert_double_eq(cloudsGetLayerCoverage(layer, v3(x, -1.0, z)), 0.0);
|
||||||
|
ck_assert_double_eq(cloudsGetLayerCoverage(layer, v3(x, -1.5, z)), 0.0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
layer->base_coverage = 0.5;
|
||||||
|
noiseForceValue(layer->_coverage_noise, 1.0);
|
||||||
|
for (x = -10.0; x < 10.0; x += 10.0)
|
||||||
|
{
|
||||||
|
for (z = -10.0; z < 10.0; z += 10.0)
|
||||||
|
{
|
||||||
|
ck_assert_double_eq(cloudsGetLayerCoverage(layer, v3(x, 0.0, z)), 0.5);
|
||||||
|
ck_assert_double_eq(cloudsGetLayerCoverage(layer, v3(x, 0.5, z)), 0.25);
|
||||||
|
ck_assert_double_eq(cloudsGetLayerCoverage(layer, v3(x, 1.0, z)), 0.0);
|
||||||
|
ck_assert_double_eq(cloudsGetLayerCoverage(layer, v3(x, 1.5, z)), 0.0);
|
||||||
|
ck_assert_double_eq(cloudsGetLayerCoverage(layer, v3(x, -0.5, z)), 0.25);
|
||||||
|
ck_assert_double_eq(cloudsGetLayerCoverage(layer, v3(x, -1.0, z)), 0.0);
|
||||||
|
ck_assert_double_eq(cloudsGetLayerCoverage(layer, v3(x, -1.5, z)), 0.0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
layer->base_coverage = 1.0;
|
||||||
|
noiseForceValue(layer->_coverage_noise, 0.5);
|
||||||
|
for (x = -10.0; x < 10.0; x += 10.0)
|
||||||
|
{
|
||||||
|
for (z = -10.0; z < 10.0; z += 10.0)
|
||||||
|
{
|
||||||
|
ck_assert_double_eq(cloudsGetLayerCoverage(layer, v3(x, 0.0, z)), 0.5);
|
||||||
|
ck_assert_double_eq(cloudsGetLayerCoverage(layer, v3(x, 0.5, z)), 0.25);
|
||||||
|
ck_assert_double_eq(cloudsGetLayerCoverage(layer, v3(x, 1.0, z)), 0.0);
|
||||||
|
ck_assert_double_eq(cloudsGetLayerCoverage(layer, v3(x, 1.5, z)), 0.0);
|
||||||
|
ck_assert_double_eq(cloudsGetLayerCoverage(layer, v3(x, -0.5, z)), 0.25);
|
||||||
|
ck_assert_double_eq(cloudsGetLayerCoverage(layer, v3(x, -1.0, z)), 0.0);
|
||||||
|
ck_assert_double_eq(cloudsGetLayerCoverage(layer, v3(x, -1.5, z)), 0.0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* TODO Test fake renderer */
|
||||||
|
|
||||||
|
/* TODO Test real renderer */
|
||||||
|
|
||||||
|
/* Teardown */
|
||||||
|
cloudsGetLayerType().callback_delete(layer);
|
||||||
|
}
|
||||||
|
END_TEST
|
||||||
|
|
||||||
|
START_TEST(test_clouds_walking_boundaries)
|
||||||
|
{
|
||||||
|
Vector3 start, end;
|
||||||
|
int result;
|
||||||
|
CloudsLayerDefinition* layer;
|
||||||
|
layer = cloudsGetLayerType().callback_create();
|
||||||
|
layer->base_coverage = 1.0;
|
||||||
|
layer->lower_altitude = -1.0;
|
||||||
|
layer->thickness = 2.0;
|
||||||
|
cloudsGetLayerType().callback_validate(layer);
|
||||||
|
|
||||||
|
/* Basic cases */
|
||||||
|
start = v3(0.0, -3.0, 0.0);
|
||||||
|
end = v3(0.0, -2.0, 0.0);
|
||||||
|
result = cloudsOptimizeWalkingBounds(layer, &start, &end);
|
||||||
|
ck_assert_int_eq(result, 0);
|
||||||
|
|
||||||
|
start = v3(0.0, 2.0, 0.0);
|
||||||
|
end = v3(0.0, 3.0, 0.0);
|
||||||
|
result = cloudsOptimizeWalkingBounds(layer, &start, &end);
|
||||||
|
ck_assert_int_eq(result, 0);
|
||||||
|
|
||||||
|
start = v3(0.0, -2.0, 0.0);
|
||||||
|
end = v3(0.0, 2.0, 0.0);
|
||||||
|
result = cloudsOptimizeWalkingBounds(layer, &start, &end);
|
||||||
|
ck_assert_int_eq(result, 1);
|
||||||
|
ck_assert_vector_values(start, 0.0, -1.0, 0.0);
|
||||||
|
ck_assert_vector_values(end, 0.0, 1.0, 0.0);
|
||||||
|
|
||||||
|
start = v3(0.0, 0.0, 0.0);
|
||||||
|
end = v3(0.0, 2.0, 0.0);
|
||||||
|
result = cloudsOptimizeWalkingBounds(layer, &start, &end);
|
||||||
|
ck_assert_int_eq(result, 1);
|
||||||
|
ck_assert_vector_values(start, 0.0, 0.0, 0.0);
|
||||||
|
ck_assert_vector_values(end, 0.0, 1.0, 0.0);
|
||||||
|
|
||||||
|
start = v3(0.0, -2.0, 0.0);
|
||||||
|
end = v3(0.0, 0.0, 0.0);
|
||||||
|
result = cloudsOptimizeWalkingBounds(layer, &start, &end);
|
||||||
|
ck_assert_int_eq(result, 1);
|
||||||
|
ck_assert_vector_values(start, 0.0, -1.0, 0.0);
|
||||||
|
ck_assert_vector_values(end, 0.0, 0.0, 0.0);
|
||||||
|
|
||||||
|
/* Basic cases (inverted) */
|
||||||
|
start = v3(0.0, -2.0, 0.0);
|
||||||
|
end = v3(0.0, -3.0, 0.0);
|
||||||
|
result = cloudsOptimizeWalkingBounds(layer, &start, &end);
|
||||||
|
ck_assert_int_eq(result, 0);
|
||||||
|
|
||||||
|
start = v3(0.0, 3.0, 0.0);
|
||||||
|
end = v3(0.0, 2.0, 0.0);
|
||||||
|
result = cloudsOptimizeWalkingBounds(layer, &start, &end);
|
||||||
|
ck_assert_int_eq(result, 0);
|
||||||
|
|
||||||
|
start = v3(0.0, 2.0, 0.0);
|
||||||
|
end = v3(0.0, -2.0, 0.0);
|
||||||
|
result = cloudsOptimizeWalkingBounds(layer, &start, &end);
|
||||||
|
ck_assert_int_eq(result, 1);
|
||||||
|
ck_assert_vector_values(start, 0.0, 1.0, 0.0);
|
||||||
|
ck_assert_vector_values(end, 0.0, -1.0, 0.0);
|
||||||
|
|
||||||
|
start = v3(0.0, 2.0, 0.0);
|
||||||
|
end = v3(0.0, 0.0, 0.0);
|
||||||
|
result = cloudsOptimizeWalkingBounds(layer, &start, &end);
|
||||||
|
ck_assert_int_eq(result, 1);
|
||||||
|
ck_assert_vector_values(start, 0.0, 1.0, 0.0);
|
||||||
|
ck_assert_vector_values(end, 0.0, 0.0, 0.0);
|
||||||
|
|
||||||
|
start = v3(0.0, 0.0, 0.0);
|
||||||
|
end = v3(0.0, -2.0, 0.0);
|
||||||
|
result = cloudsOptimizeWalkingBounds(layer, &start, &end);
|
||||||
|
ck_assert_int_eq(result, 1);
|
||||||
|
ck_assert_vector_values(start, 0.0, 0.0, 0.0);
|
||||||
|
ck_assert_vector_values(end, 0.0, -1.0, 0.0);
|
||||||
|
|
||||||
|
/* Horizontal cases */
|
||||||
|
start = v3(0.0, 2.0, 0.0);
|
||||||
|
end = v3(10.0, 2.0, 0.0);
|
||||||
|
result = cloudsOptimizeWalkingBounds(layer, &start, &end);
|
||||||
|
ck_assert_int_eq(result, 0);
|
||||||
|
|
||||||
|
start = v3(0.0, 1.00001, 0.0);
|
||||||
|
end = v3(10.0, 1.00001, 0.0);
|
||||||
|
result = cloudsOptimizeWalkingBounds(layer, &start, &end);
|
||||||
|
ck_assert_int_eq(result, 0);
|
||||||
|
|
||||||
|
start = v3(0.0, -1.00001, 0.0);
|
||||||
|
end = v3(10.0, -1.00001, 0.0);
|
||||||
|
result = cloudsOptimizeWalkingBounds(layer, &start, &end);
|
||||||
|
ck_assert_int_eq(result, 0);
|
||||||
|
|
||||||
|
start = v3(0.0, -2.0, 0.0);
|
||||||
|
end = v3(10.0, -2.0, 0.0);
|
||||||
|
result = cloudsOptimizeWalkingBounds(layer, &start, &end);
|
||||||
|
ck_assert_int_eq(result, 0);
|
||||||
|
|
||||||
|
start = v3(0.0, 0.0, 0.0);
|
||||||
|
end = v3(10.0, 0.0, 0.0);
|
||||||
|
result = cloudsOptimizeWalkingBounds(layer, &start, &end);
|
||||||
|
ck_assert_int_eq(result, 1);
|
||||||
|
ck_assert_vector_values(start, 0.0, 0.0, 0.0);
|
||||||
|
ck_assert_vector_values(end, 10.0, 0.0, 0.0);
|
||||||
|
|
||||||
|
cloudsGetLayerType().callback_delete(layer);
|
||||||
|
}
|
||||||
|
END_TEST
|
||||||
|
|
||||||
|
static double _getLayerDensitySinX(Renderer* renderer, CloudsLayerDefinition* layer, Vector3 location)
|
||||||
|
{
|
||||||
|
UNUSED(renderer);
|
||||||
|
UNUSED(layer);
|
||||||
|
double density = sin(location.x * (2.0 * M_PI));
|
||||||
|
return (density > 0.0) ? density : 0.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
START_TEST(test_clouds_primary_segments)
|
||||||
|
{
|
||||||
|
int segment_count, i;
|
||||||
|
CloudPrimarySegment segments[10];
|
||||||
|
|
||||||
|
CloudsLayerDefinition* layer;
|
||||||
|
layer = cloudsGetLayerType().callback_create();
|
||||||
|
layer->lower_altitude = -1.0;
|
||||||
|
layer->thickness = 2.0;
|
||||||
|
cloudsGetLayerType().callback_validate(layer);
|
||||||
|
|
||||||
|
Renderer* renderer;
|
||||||
|
renderer = rendererCreate();
|
||||||
|
|
||||||
|
renderer->render_quality = 8;
|
||||||
|
renderer->clouds->getLayerDensity = _getLayerDensitySinX;
|
||||||
|
|
||||||
|
segment_count = cloudsGetLayerPrimarySegments(renderer, layer, v3(-0.4, 0.0, 0.0), v3(1.9, 0.0, 0.0), 10, segments);
|
||||||
|
ck_assert_int_eq(segment_count, 2);
|
||||||
|
for (i = 0; i < segment_count; i++)
|
||||||
|
{
|
||||||
|
ck_assert_double_eq(segments[i].enter.y, 0.0);
|
||||||
|
ck_assert_double_eq(segments[i].enter.z, 0.0);
|
||||||
|
ck_assert_double_eq(segments[i].exit.y, 0.0);
|
||||||
|
ck_assert_double_eq(segments[i].exit.z, 0.0);
|
||||||
|
}
|
||||||
|
ck_assert_double_in_range(segments[0].enter.x, -0.5, 0.0);
|
||||||
|
ck_assert_double_in_range(segments[0].exit.x, 0.5, 1.0);
|
||||||
|
ck_assert_double_in_range(segments[0].length, 0.5, 1.5);
|
||||||
|
ck_assert_double_gte(segments[1].enter.x, segments[0].exit.x);
|
||||||
|
ck_assert_double_in_range(segments[1].enter.x, 0.5, 1.0);
|
||||||
|
ck_assert_double_in_range(segments[1].exit.x, 1.5, 2.0);
|
||||||
|
ck_assert_double_in_range(segments[1].length, 0.5, 1.5);
|
||||||
|
|
||||||
|
cloudsGetLayerType().callback_delete(layer);
|
||||||
|
rendererDelete(renderer);
|
||||||
|
}
|
||||||
|
END_TEST
|
||||||
|
|
||||||
|
START_TEST(test_clouds_preview_color)
|
||||||
|
{
|
||||||
|
Renderer* renderer = cloudsCreatePreviewColorRenderer();
|
||||||
|
|
||||||
|
/* TODO Test the density overriding */
|
||||||
|
|
||||||
|
rendererDelete(renderer);
|
||||||
|
}
|
||||||
|
END_TEST
|
||||||
|
|
||||||
|
TEST_CASE(clouds, test_clouds_density, test_clouds_walking_boundaries, test_clouds_primary_segments, test_clouds_preview_color)
|
Loading…
Reference in a new issue