Merge branch 'clouds_walking'

This commit is contained in:
Michaël Lemaire 2013-06-02 21:50:16 +02:00
commit 6355de2e5e
11 changed files with 648 additions and 323 deletions

2
TODO
View file

@ -1,4 +1,5 @@
Technology Preview 2 :
- Implement perspective correction for coordinate mapping of rasterized polygons.
- Finalize terrain editor.
=> Add a generation dialog for base noise (overwriting changes).
- Get rid of noise dialogs, for simpler settings.
@ -15,6 +16,7 @@ Technology Preview 2 :
Technlogy Preview 3 :
- Start an undo/redo system ?
- Alter aerial perspective using estimation of the amount of light left after cloud layers traversal.
- Add a map preview to terrain editor.
- Better time selection widget for atmosphere.
- Clouds should keep distance to ground.

View file

@ -1,5 +1,6 @@
#include "formclouds.h"
#include "rendering/clouds/clo_preview.h"
#include "rendering/tools/color.h"
#include "rendering/tools/euclid.h"
#include "rendering/scenery.h"
@ -12,24 +13,22 @@ class PreviewCloudsCoverage:public BasePreview
public:
PreviewCloudsCoverage(QWidget* parent, CloudsLayerDefinition* layer):BasePreview(parent)
{
_renderer = cloudsCreatePreviewCoverageRenderer();
_renderer = cloudsPreviewCoverageCreateRenderer();
_3d = true;
_original_layer = layer;
_preview_definition = (CloudsDefinition*)CloudsDefinitionClass.create();
addToggle("3d", tr("Perspective"), true);
configScaling(100.0, 1000.0, 20.0, 200.0);
}
~PreviewCloudsCoverage()
{
CloudsDefinitionClass.destroy(_preview_definition);
rendererDelete(_renderer);
}
protected:
Color getColor(double x, double y)
{
return cloudsGetPreviewCoverage(_renderer, x, y, scaling, _3d);
return cloudsPreviewCoverageGetPixel(_renderer, x, y, scaling, _3d);
}
virtual void toggleChangeEvent(QString key, bool value)
{
@ -41,15 +40,12 @@ protected:
}
void updateData()
{
layersDeleteLayer(_preview_definition->layers, 0);
layersAddLayer(_preview_definition->layers, _original_layer);
CloudsRendererClass.bind(_renderer, _preview_definition);
cloudsPreviewCoverageBindLayer(_renderer, _original_layer);
}
private:
Renderer* _renderer;
CloudsLayerDefinition* _original_layer;
CloudsDefinition* _preview_definition;
bool _3d;
};
@ -59,27 +55,28 @@ public:
PreviewCloudsColor(QWidget* parent, CloudsLayerDefinition* layer):BasePreview(parent)
{
_original_layer = layer;
_preview_definition = (CloudsDefinition*)CloudsDefinitionClass.create();
_renderer = cloudsCreatePreviewColorRenderer();
_renderer = cloudsPreviewMaterialCreateRenderer();
configScaling(0.5, 2.0, 0.1, 2.0);
}
~PreviewCloudsColor()
{
rendererDelete(_renderer);
}
protected:
Color getColor(double x, double y)
{
return cloudsGetPreviewColor(_renderer, x, y);
return cloudsPreviewMaterialGetPixel(_renderer, x, y);
}
void updateData()
{
layersDeleteLayer(_preview_definition->layers, 0);
layersAddLayer(_preview_definition->layers, _original_layer);
CloudsRendererClass.bind(_renderer, _preview_definition);
cloudsPreviewMaterialBindLayer(_renderer, _original_layer);
}
private:
Renderer* _renderer;
CloudsLayerDefinition* _original_layer;
CloudsDefinition* _preview_definition;
};
/**************** Form ****************/

View file

@ -18,7 +18,7 @@ Color _fakeApplyLightingToSurface(Renderer* renderer, Vector3 location, Vector3
return COLOR_WHITE;
}
Renderer* cloudsCreatePreviewCoverageRenderer()
Renderer* cloudsPreviewCoverageCreateRenderer()
{
Renderer* result = rendererCreate();
result->render_quality = 5;
@ -26,7 +26,15 @@ Renderer* cloudsCreatePreviewCoverageRenderer()
return result;
}
Color cloudsGetPreviewCoverage(Renderer* renderer, double x, double y, double scaling, int perspective)
void cloudsPreviewCoverageBindLayer(Renderer* renderer, CloudsLayerDefinition* layer)
{
CloudsDefinition* definition = (CloudsDefinition*)CloudsDefinitionClass.create();
layersAddLayer(definition->layers, layer);
CloudsRendererClass.bind(renderer, definition);
CloudsDefinitionClass.destroy(definition);
}
Color cloudsPreviewCoverageGetPixel(Renderer* renderer, double x, double y, double scaling, int perspective)
{
if (perspective)
{
@ -55,25 +63,91 @@ Color cloudsGetPreviewCoverage(Renderer* renderer, double x, double y, double sc
}
}
Renderer* cloudsCreatePreviewColorRenderer()
static void _getLightingStatus(Renderer* renderer, LightStatus* status, Vector3 normal, int opaque)
{
LightDefinition light;
UNUSED(renderer);
UNUSED(normal);
UNUSED(opaque);
light.color.r = 1.0;
light.color.g = 1.0;
light.color.b = 1.0;
light.direction.x = -1.0;
light.direction.y = -0.5;
light.direction.z = 1.0;
light.direction = v3Normalize(light.direction);
light.altered = 1;
light.reflection = 0.0;
lightingPushLight(status, &light);
light.color.r = 0.2;
light.color.g = 0.2;
light.color.b = 0.2;
light.direction.x = 1.0;
light.direction.y = -0.5;
light.direction.z = -1.0;
light.direction = v3Normalize(light.direction);
light.altered = 0;
light.reflection = 0.0;
lightingPushLight(status, &light);
}
Renderer* cloudsPreviewMaterialCreateRenderer()
{
Renderer* result = rendererCreate();
result->render_quality = 8;
result->atmosphere->getLightingStatus = _getLightingStatus;
return result;
}
Color cloudsGetPreviewColor(Renderer* renderer, double x, double y)
static double _getDensity(Renderer* renderer, CloudsLayerDefinition* layer, Vector3 location)
{
UNUSED(renderer);
UNUSED(layer);
double distance = v3Norm(location);
if (distance > 1.0)
{
return 0.0;
}
else if (distance < 0.8)
{
return 1.0;
}
else
{
return (1.0 - distance) / 0.2;
}
}
void cloudsPreviewMaterialBindLayer(Renderer* renderer, CloudsLayerDefinition* layer)
{
CloudsDefinition* definition = (CloudsDefinition*)CloudsDefinitionClass.create();
layersAddLayer(definition->layers, layer);
CloudsRendererClass.bind(renderer, definition);
CloudsDefinitionClass.destroy(definition);
layer = layersGetLayer(renderer->clouds->definition->layers, 0);
layer->lower_altitude = -1.0;
layer->thickness = 2.0;
renderer->clouds->getLayerDensity = _getDensity;
}
Color cloudsPreviewMaterialGetPixel(Renderer* renderer, double x, double y)
{
Vector3 start, end;
double thickness = 0.5;
double thickness = 2.0;
start.x = x * thickness * 0.5;
start.y = -y * thickness * 0.5;
start.z = thickness * 0.5;
start.z = y * thickness * 0.5;
start.y = thickness * 0.5;
end.x = start.x;
end.y = start.y;
end.z = -start.z;
end.z = start.z;
end.y = -start.y;
return renderer->clouds->getColor(renderer, COLOR_BLUE, start, end);
}

View file

@ -0,0 +1,28 @@
#ifndef _PAYSAGES_CLOUDS_PREVIEW_H_
#define _PAYSAGES_CLOUDS_PREVIEW_H_
#include "public.h"
#include "../tools/euclid.h"
/**
* Cloud preview helpers.
*/
#ifdef __cplusplus
extern "C"
{
#endif
Renderer* cloudsPreviewCoverageCreateRenderer();
void cloudsPreviewCoverageBindLayer(Renderer* renderer, CloudsLayerDefinition* layer);
Color cloudsPreviewCoverageGetPixel(Renderer* renderer, double x, double y, double scaling, int perspective);
Renderer* cloudsPreviewMaterialCreateRenderer();
void cloudsPreviewMaterialBindLayer(Renderer* renderer, CloudsLayerDefinition* layer);
Color cloudsPreviewMaterialGetPixel(Renderer* renderer, double x, double y);
#ifdef __cplusplus
}
#endif
#endif

View file

@ -1,9 +1,11 @@
#include "private.h"
#include <assert.h>
#include <stdlib.h>
#include "../tools.h"
#include "../renderer.h"
#include "clo_density.h"
#include "clo_walking.h"
/******************** Fake ********************/
static int _fakeAlterLight(Renderer* renderer, LightDefinition* light, Vector3 location)
@ -25,24 +27,117 @@ static Color _fakeGetColor(Renderer* renderer, Color base, Vector3 start, Vector
}
/******************** Real ********************/
/*static int _cmpLayer(const void* layer1, const void* layer2)
typedef struct
{
return (((CloudsLayerDefinition*)layer1)->lower_altitude > ((CloudsLayerDefinition*)layer2)->lower_altitude) ? -1 : 1;
}*/
double light_power;
double out_scattering; /* Amount of light scattered away by heavy particles */
} AccumulatedLightData;
static void _walkerFilterCallback(CloudsWalker* walker)
{
CloudWalkerStepInfo* segment = cloudsWalkerGetLastSegment(walker);
AccumulatedLightData* data = (AccumulatedLightData*)segment->data;
assert(data != NULL);
double density_integral = segment->length * (segment->start.global_density + segment->end.global_density) / 2.0;
data->out_scattering += 0.3 * density_integral;
if (data->out_scattering > data->light_power)
{
cloudsWalkerOrderStop(walker);
}
}
static int _alterLight(Renderer* renderer, LightDefinition* light, Vector3 location)
{
CloudsDefinition* definition = renderer->clouds->definition;
int i, n;
AccumulatedLightData data;
data.out_scattering = 0.0;
data.light_power = colorGetPower(&light->color);
/* TODO Iter layers in sorted order */
n = layersCount(definition->layers);
for (i = 0; i < n; i++)
{
light->color = cloudsLayerFilterLight(layersGetLayer(definition->layers, i), renderer, light->color, location, v3Add(location, v3Scale(light->direction, -10000.0)), v3Scale(light->direction, -1.0));
/* TODO Reduce light->reflection too */
CloudsLayerDefinition* layer = (CloudsLayerDefinition*)layersGetLayer(renderer->clouds->definition->layers, i);
Vector3 ostart, oend;
ostart = location;
oend = v3Add(location, v3Scale(light->direction, -10000.0));
if (!cloudsOptimizeWalkingBounds(layer, &ostart, &oend))
{
continue;
}
else
{
CloudsWalker* walker;
walker = cloudsCreateWalker(renderer, layer, ostart, oend);
cloudsWalkerSetStepSize(walker, -1.0);
cloudsStartWalking(walker, _walkerFilterCallback, &data);
cloudsDeleteWalker(walker);
}
}
return n > 0;
double max_power = colorGetPower(&light->color) - data.out_scattering;
if (max_power < 0.0)
{
light->color = COLOR_BLACK;
}
else
{
colorLimitPower(&light->color, max_power);
}
return data.out_scattering > 0.0;
}
typedef struct
{
double out_scattering; /* Amount of light scattered away by heavy particles */
Color in_scattering; /* Amount of light redirected toward the viewer */
} AccumulatedMaterialData;
static inline void _applyOutScattering(Color* col, double out_scattering)
{
if (out_scattering >= 1.0)
{
col->r = col->g = col->b = 0.0;
}
else
{
col->r *= (1.0 - out_scattering);
col->g *= (1.0 - out_scattering);
col->b *= (1.0 - out_scattering);
}
}
static void _walkerMaterialCallback(CloudsWalker* walker)
{
CloudWalkerStepInfo* segment = cloudsWalkerGetLastSegment(walker);
AccumulatedMaterialData* data = (AccumulatedMaterialData*)segment->data;
Renderer* renderer = segment->renderer;
CloudsLayerDefinition* layer = segment->layer;
assert(data != NULL);
double density_integral = segment->length * (segment->start.global_density + segment->end.global_density) / 2.0;
data->out_scattering += 0.5 * density_integral;
Color in_scattering = renderer->applyLightingToSurface(renderer, segment->start.location, VECTOR_UP, &layer->material);
in_scattering.r *= density_integral * 5.0;
in_scattering.g *= density_integral * 5.0;
in_scattering.b *= density_integral * 5.0;
_applyOutScattering(&in_scattering, data->out_scattering);
data->in_scattering.r += in_scattering.r;
data->in_scattering.g += in_scattering.g;
data->in_scattering.b += in_scattering.b;
}
static Color _getColor(Renderer* renderer, Color base, Vector3 start, Vector3 end)
@ -59,7 +154,39 @@ static Color _getColor(Renderer* renderer, Color base, Vector3 start, Vector3 en
/* TODO Iter layers in sorted order */
for (i = 0; i < n; i++)
{
base = cloudsApplyLayer(layersGetLayer(definition->layers, i), base, renderer, start, end);
CloudsLayerDefinition* layer = (CloudsLayerDefinition*)layersGetLayer(renderer->clouds->definition->layers, i);
Vector3 ostart, oend;
ostart = start;
oend = end;
if (!cloudsOptimizeWalkingBounds(layer, &ostart, &oend))
{
continue;
}
else
{
CloudsWalker* walker;
AccumulatedMaterialData data;
data.out_scattering = 0.0;
data.in_scattering = COLOR_BLACK;
walker = cloudsCreateWalker(renderer, layer, ostart, oend);
cloudsWalkerSetStepSize(walker, -1.0);
cloudsStartWalking(walker, _walkerMaterialCallback, &data);
cloudsDeleteWalker(walker);
/* Apply final out_scattering to base */
_applyOutScattering(&base, data.out_scattering);
/* Apply in_scattering */
base.r += data.in_scattering.r;
base.g += data.in_scattering.g;
base.b += data.in_scattering.b;
/* Apply aerial perspective approximation */
/* TODO This should be done at cloud entry */
base = renderer->atmosphere->applyAerialPerspective(renderer, ostart, base).final;
}
}
return base;

View file

@ -1,149 +0,0 @@
#include "private.h"
/*
* Clouds tools.
*/
#include "clo_walking.h"
#include "../renderer.h"
#include "../tools.h"
static inline Vector3 _getNormal(CloudsLayerDefinition* layer, Vector3 position, double detail)
{
Vector3 result = {0.0, 0.0, 0.0};
/*Vector3 dposition;
double val, dval;
val = _getDistanceToBorder(layer, position);
dposition.x = position.x + detail;
dposition.y = position.y;
dposition.z = position.z;
dval = val - _getDistanceToBorder(layer, dposition);
result.x += dval;
dposition.x = position.x - detail;
dval = val - _getDistanceToBorder(layer, dposition);
result.x -= dval;
dposition.x = position.x;
dposition.y = position.y + detail;
dval = val - _getDistanceToBorder(layer, dposition);
result.y += dval;
dposition.y = position.y - detail;
dval = val - _getDistanceToBorder(layer, dposition);
result.y -= dval;
dposition.y = position.y;
dposition.z = position.z + detail;
dval = val - _getDistanceToBorder(layer, dposition);
result.z += dval;
dposition.z = position.z - detail;
dval = val - _getDistanceToBorder(layer, dposition);
result.z -= dval;*/
return v3Normalize(result);
}
static Color _applyLayerLighting(CloudsLayerDefinition* definition, Renderer* renderer, Vector3 location, double detail)
{
Vector3 normal;
Color col1, col2;
LightStatus* lighting;
normal = _getNormal(definition, location, 3.0);
if (renderer->render_quality > 3)
{
normal = v3Add(normal, _getNormal(definition, location, 2.0));
normal = v3Add(normal, _getNormal(definition, location, 1.0));
}
if (renderer->render_quality > 5)
{
normal = v3Add(normal, _getNormal(definition, location, 0.5));
}
if (renderer->render_quality > 8)
{
normal = v3Add(normal, _getNormal(definition, location, 0.75));
normal = v3Add(normal, _getNormal(definition, location, 1.25));
normal = v3Add(normal, _getNormal(definition, location, 2.5));
}
normal = v3Scale(v3Normalize(normal), definition->hardness);
return renderer->applyLightingToSurface(renderer, location, normal, &definition->material);
lighting = lightingCreateStatus(renderer->lighting, location, renderer->getCameraLocation(renderer, location));
renderer->atmosphere->getLightingStatus(renderer, lighting, normal, 0);
col1 = lightingApplyStatus(lighting, normal, &definition->material);
col2 = lightingApplyStatus(lighting, v3Scale(normal, -1.0), &definition->material);
lightingDeleteStatus(lighting);
col1.r = (col1.r + col2.r) / 2.0;
col1.g = (col1.g + col2.g) / 2.0;
col1.b = (col1.b + col2.b) / 2.0;
col1.a = (col1.a + col2.a) / 2.0;
return col1;
}
Color cloudsApplyLayer(CloudsLayerDefinition* definition, Color base, Renderer* renderer, Vector3 start, Vector3 end)
{
int segment_count;
Color col;
CloudPrimarySegment segments[MAX_SEGMENT_COUNT];
segment_count = cloudsGetLayerPrimarySegments(renderer, definition, start, end, MAX_SEGMENT_COUNT, segments);
/* TODO Crawl in segments for render */
col = definition->material.base;
/*if (definition->transparencydepth == 0 || inside_length >= definition->transparencydepth)
{
col.a = 1.0;
}
else
{
col.a = inside_length / definition->transparencydepth;
}*/
col = renderer->atmosphere->applyAerialPerspective(renderer, start, col).final;
col.a = 0.0;
colorMask(&base, &col);
return base;
}
Color cloudsLayerFilterLight(CloudsLayerDefinition* definition, Renderer* renderer, Color light, Vector3 location, Vector3 light_location, Vector3 direction_to_light)
{
/*double inside_depth, total_depth, factor;
CloudSegment segments[MAX_SEGMENT_COUNT];
if (!cloudsOptimizeWalkingBounds(definition, &location, &light_location))
{
return light;
}
_getPrimarySegments(definition, renderer, location, direction_to_light, MAX_SEGMENT_COUNT, definition->lighttraversal, v3Norm(v3Sub(light_location, location)), &inside_depth, &total_depth, segments);
if (definition->lighttraversal < 0.0001)
{
factor = 0.0;
}
else
{
factor = inside_depth / definition->lighttraversal;
if (factor > 1.0)
{
factor = 1.0;
}
}
factor = 1.0 - (1.0 - definition->minimumlight) * factor;
light.r = light.r * factor;
light.g = light.g * factor;
light.b = light.b * factor;*/
return light;
}

View file

@ -2,6 +2,47 @@
#include "../renderer.h"
/**
* Control of the next walking order.
*/
typedef enum
{
CLOUD_WALKING_CONTINUE,
CLOUD_WALKING_STOP,
CLOUD_WALKING_REFINE,
CLOUD_WALKING_SUBDIVIDE
} CloudWalkingOrder;
/**
* Additional info for walking orders.
*/
typedef struct
{
CloudWalkingOrder order;
double precision;
int max_segments;
} CloudWalkingNextAction;
/*
* Private structure for the walker.
*/
struct CloudsWalker
{
Vector3 start;
Vector3 end;
Vector3 diff;
double cursor;
double max_length;
double step_size;
int started;
CloudWalkerStepInfo last_segment;
CloudWalkingNextAction next_action;
};
int cloudsOptimizeWalkingBounds(CloudsLayerDefinition* layer, Vector3* start, Vector3* end)
{
Vector3 diff;
@ -51,107 +92,176 @@ int cloudsOptimizeWalkingBounds(CloudsLayerDefinition* layer, Vector3* start, Ve
}
}
/* TODO Limit the search length */
return 1;
}
int cloudsGetLayerPrimarySegments(Renderer* renderer, CloudsLayerDefinition* layer, Vector3 start, Vector3 end, int max_segments, CloudPrimarySegment* out_segments)
CloudsWalker* cloudsCreateWalker(Renderer* renderer, CloudsLayerDefinition* layer, Vector3 start, Vector3 end)
{
int inside, segment_count;
double step_length, segment_length;
Vector3 diff, walker, segment_start;
double render_precision, density;
double diff_length, progress;
CloudsWalker* result;
if (max_segments <= 0)
{
return 0;
}
result = (CloudsWalker*)malloc(sizeof (CloudsWalker));
if (!cloudsOptimizeWalkingBounds(layer, &start, &end))
{
return 0;
}
result->start = start;
result->end = end;
result->diff = v3Sub(end, start);
result->max_length = v3Norm(result->diff);
result->cursor = 0.0;
result->step_size = 1.0;
diff = v3Sub(end, start);
diff_length = v3Norm(diff);
result->started = 0;
result->last_segment.renderer = renderer;
result->last_segment.layer = layer;
if (diff_length < 0.000001)
{
return 0;
}
result->next_action.order = CLOUD_WALKING_CONTINUE;
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;
return result;
}
void cloudsDeleteWalker(CloudsWalker* walker)
{
free(walker);
}
void cloudsWalkerSetStepSize(CloudsWalker* walker, double step)
{
if (step > 0.0)
{
walker->step_size = step;
}
else
{
/* TODO Automatic settings (using rendering quality and cloud feature size) */
walker->step_size = 1.0;
}
}
static void _getPoint(CloudsWalker* walker, double cursor, CloudWalkerPoint* out_point)
{
out_point->distance_from_start = cursor;
out_point->location = v3Add(walker->start, v3Scale(walker->diff, out_point->distance_from_start / walker->max_length));
Renderer* renderer = walker->last_segment.renderer;
CloudsLayerDefinition* layer = walker->last_segment.layer;
out_point->global_density = renderer->clouds->getLayerDensity(renderer, layer, out_point->location);
}
static void _refineSegment(CloudsWalker* walker, double start_cursor, double start_density, double end_cursor, double end_density, double precision, CloudWalkerPoint* result)
{
CloudWalkerPoint middle;
_getPoint(walker, (start_cursor + end_cursor) / 2.0, &middle);
if (start_density == 0.0)
{
/* Looking for entry */
if (middle.distance_from_start - start_cursor < precision)
{
*result = middle;
}
else if (middle.global_density == 0.0)
{
_refineSegment(walker, middle.distance_from_start, middle.global_density, end_cursor, end_density, precision, result);
}
else
{
_refineSegment(walker, start_cursor, start_density, middle.distance_from_start, middle.global_density, precision, result);
}
}
else
{
/* Looking for exit */
if (end_cursor - middle.distance_from_start < precision)
{
*result = middle;
}
else if (middle.global_density == 0.0)
{
_refineSegment(walker, start_cursor, start_density, middle.distance_from_start, middle.global_density, precision, result);
}
else
{
_refineSegment(walker, middle.distance_from_start, middle.global_density, end_cursor, end_density, precision, result);
}
}
}
int cloudsWalkerPerformStep(CloudsWalker* walker)
{
if (!walker->started)
{
_getPoint(walker, 0.0, &walker->last_segment.end);
walker->started = 1;
}
if (walker->next_action.order == CLOUD_WALKING_STOP || walker->cursor >= walker->max_length)
{
walker->next_action.order = CLOUD_WALKING_STOP;
return 0;
}
else if (walker->next_action.order == CLOUD_WALKING_CONTINUE)
{
/* TODO Limit to end */
walker->last_segment.start = walker->last_segment.end;
walker->cursor += walker->step_size;
_getPoint(walker, walker->cursor, &walker->last_segment.end);
walker->last_segment.length = walker->step_size;
walker->last_segment.refined = 0;
return 1;
}
else if (walker->next_action.order == CLOUD_WALKING_REFINE)
{
/* Refine segment with dichotomy */
_refineSegment(walker,
walker->last_segment.start.distance_from_start,
walker->last_segment.start.global_density,
walker->last_segment.end.distance_from_start,
walker->last_segment.end.global_density,
walker->next_action.precision,
(walker->last_segment.start.global_density == 0.0) ? (&walker->last_segment.start) : (&walker->last_segment.end));
walker->last_segment.length = walker->last_segment.end.distance_from_start - walker->last_segment.start.distance_from_start;
walker->last_segment.refined = 1;
walker->next_action.order = CLOUD_WALKING_CONTINUE;
return 1;
}
else
{
/* TODO */
return 0;
}
}
void cloudsWalkerOrderStop(CloudsWalker* walker)
{
walker->next_action.order = CLOUD_WALKING_STOP;
}
void cloudsWalkerOrderRefine(CloudsWalker* walker, double precision)
{
walker->next_action.order = CLOUD_WALKING_REFINE;
walker->next_action.precision = precision;
}
void cloudsWalkerOrderSubdivide(CloudsWalker* walker, double max_segments)
{
walker->next_action.order = CLOUD_WALKING_SUBDIVIDE;
walker->next_action.max_segments = max_segments;
}
CloudWalkerStepInfo* cloudsWalkerGetLastSegment(CloudsWalker* walker)
{
return &walker->last_segment;
}
void cloudsStartWalking(CloudsWalker* walker, FuncCloudsWalkingCallback callback, void* data)
{
walker->last_segment.data = data;
while (cloudsWalkerPerformStep(walker))
{
callback(walker);
}
}

View file

@ -15,10 +15,33 @@ extern "C"
typedef struct
{
Vector3 enter;
Vector3 exit;
double distance_from_start;
Vector3 location;
double global_density;
} CloudWalkerPoint;
/**
* Information on a segment yielded by walking.
*/
typedef struct
{
Renderer* renderer;
CloudsLayerDefinition* layer;
CloudWalkerPoint start;
CloudWalkerPoint end;
double length;
} CloudPrimarySegment;
int refined;
/*int subdivision_level;
double precision_asked;*/
void* data;
} CloudWalkerStepInfo;
typedef struct CloudsWalker CloudsWalker;
typedef void (*FuncCloudsWalkingCallback)(CloudsWalker* walker);
/**
* Optimize the search limits in a layer.
@ -31,17 +54,81 @@ typedef struct
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).
* Create a cloud walker.
*
* @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
* For better performance, the segment should by optimized using cloudsOptimizeWalkingBounds.
* @param renderer Renderer context
* @param layer The cloud layer to traverse
* @param start Start of the walk
* @param end End of the walk
*/
int cloudsGetLayerPrimarySegments(Renderer* renderer, CloudsLayerDefinition* layer, Vector3 start, Vector3 end, int max_segments, CloudPrimarySegment* out_segments);
CloudsWalker* cloudsCreateWalker(Renderer* renderer, CloudsLayerDefinition* layer, Vector3 start, Vector3 end);
/**
* Delete a cloud walker.
*
* @param walker The walker to free
*/
void cloudsDeleteWalker(CloudsWalker* walker);
/**
* Define the segment size for next steps.
*
* @param walker The walker to configure
* @param step The step length, negative for automatic
*/
void cloudsWalkerSetStepSize(CloudsWalker* walker, double step);
/**
* Perform a single step.
*
* @param walker The walker to use
* @return 1 to continue the loop, 0 to stop
*/
int cloudsWalkerPerformStep(CloudsWalker* walker);
/**
* Order the walker to stop.
*
* @param walker The walker to use
*/
void cloudsWalkerOrderStop(CloudsWalker* walker);
/**
* Order the walker to refine the search for cloud entry or exit.
*
* The refinement will next yield a shorter version of the segment, containing only the cloud-inside portion, with a
* tolerance fixed by precision. For an entry point, this will discard the part before cloud entry. For en exit point,
* the portion after this point will be part of the next step, as normal walking resumes.
* @param walker The walker to use
* @param precision Precision wanted for the refinement
*/
void cloudsWalkerOrderRefine(CloudsWalker* walker, double precision);
/**
* Order the walker to subdivide the previous segment in smaller segments.
*
* @param walker The walker to use
* @param max_segments Maximal number of segments
*/
void cloudsWalkerOrderSubdivide(CloudsWalker* walker, double max_segments);
/**
* Get the last segment information.
*
* @param walker The walker to use
*/
CloudWalkerStepInfo* cloudsWalkerGetLastSegment(CloudsWalker* walker);
/**
* Start walking automatically through a segment.
*
* The callback will be called with each segment found, giving info and asking for desired alteration on walking.
* @param walker The walker to use
* @param callback Callback to be called with each found segment
* @param data User data that will be passed back in the callback
*/
void cloudsStartWalking(CloudsWalker* walker, FuncCloudsWalkingCallback callback, void* data);
#ifdef __cplusplus
}

View file

@ -80,12 +80,6 @@ LayerType cloudsGetLayerType();
void cloudsAutoPreset(CloudsDefinition* definition, CloudsPreset preset);
void cloudsLayerAutoPreset(CloudsLayerDefinition* definition, CloudsLayerPreset preset);
Renderer* cloudsCreatePreviewCoverageRenderer();
Color cloudsGetPreviewCoverage(Renderer* renderer, double x, double y, double scaling, int perspective);
Renderer* cloudsCreatePreviewColorRenderer();
Color cloudsGetPreviewColor(Renderer* renderer, double x, double y);
#ifdef __cplusplus
}
#endif

View file

@ -27,6 +27,11 @@ static inline void _add_methods_to_case(TCase* tc, ...)
suite_add_tcase(s, tc); \
}
/***** Boolean assertions *****/
#define ck_assert_true(_X_) ck_assert_int_ne((_X_), 0)
#define ck_assert_false(_X_) ck_assert_int_eq((_X_), 0)
/***** Floating point assertions *****/
static inline int _double_equals(double x, double y)
{
return fabs(x - y) < 0.00000000001;

View file

@ -200,11 +200,9 @@ static double _getLayerDensitySinX(Renderer* renderer, CloudsLayerDefinition* la
return (density > 0.0) ? density : 0.0;
}
START_TEST(test_clouds_primary_segments)
START_TEST(test_clouds_walking)
{
int segment_count, i;
CloudPrimarySegment segments[10];
/* Init */
CloudsLayerDefinition* layer;
layer = cloudsGetLayerType().callback_create();
layer->lower_altitude = -1.0;
@ -217,36 +215,88 @@ START_TEST(test_clouds_primary_segments)
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);
CloudsWalker* walker = cloudsCreateWalker(renderer, layer, v3(-0.4, 0.0, 0.0), v3(10.0, 0.0, 0.0));
CloudWalkerStepInfo* segment;
int result;
/* First step */
cloudsWalkerSetStepSize(walker, 0.3);
result = cloudsWalkerPerformStep(walker);
segment = cloudsWalkerGetLastSegment(walker);
ck_assert_int_eq(result, 1);
ck_assert_false(segment->refined);
ck_assert_double_eq(segment->length, 0.3);
ck_assert_double_eq(segment->start.distance_from_start, 0.0);
ck_assert_vector_values(segment->start.location, -0.4, 0.0, 0.0);
ck_assert_double_eq(segment->start.global_density, 0.0);
ck_assert_double_eq(segment->end.distance_from_start, 0.3);
ck_assert_vector_values(segment->end.location, -0.1, 0.0, 0.0);
ck_assert_double_eq(segment->end.global_density, 0.0);
/* Second step */
result = cloudsWalkerPerformStep(walker);
segment = cloudsWalkerGetLastSegment(walker);
ck_assert_int_eq(result, 1);
ck_assert_false(segment->refined);
ck_assert_double_eq(segment->length, 0.3);
ck_assert_double_eq(segment->start.distance_from_start, 0.3);
ck_assert_vector_values(segment->start.location, -0.1, 0.0, 0.0);
ck_assert_double_eq(segment->start.global_density, 0.0);
ck_assert_double_eq(segment->end.distance_from_start, 0.6);
ck_assert_vector_values(segment->end.location, 0.2, 0.0, 0.0);
ck_assert_double_gt(segment->end.global_density, 0.9);
/* Order to refine second step around the entry point */
cloudsWalkerOrderRefine(walker, 0.01);
result = cloudsWalkerPerformStep(walker);
segment = cloudsWalkerGetLastSegment(walker);
ck_assert_int_eq(result, 1);
ck_assert_true(segment->refined);
ck_assert_double_in_range(segment->length, 0.19, 0.20);
ck_assert_double_in_range(segment->start.distance_from_start, 0.40, 0.41);
ck_assert_double_in_range(segment->start.location.x, 0.0, 0.01);
ck_assert_double_gt(segment->start.global_density, 0.0);
ck_assert_double_eq(segment->end.distance_from_start, 0.6);
ck_assert_vector_values(segment->end.location, 0.2, 0.0, 0.0);
ck_assert_double_gt(segment->end.global_density, 0.9);
/* Third step, change step size */
cloudsWalkerSetStepSize(walker, 0.4);
result = cloudsWalkerPerformStep(walker);
segment = cloudsWalkerGetLastSegment(walker);
ck_assert_int_eq(result, 1);
ck_assert_false(segment->refined);
ck_assert_double_eq(segment->length, 0.4);
ck_assert_double_eq(segment->start.distance_from_start, 0.6);
ck_assert_vector_values(segment->start.location, 0.2, 0.0, 0.0);
ck_assert_double_gt(segment->start.global_density, 0.9);
ck_assert_double_eq(segment->end.distance_from_start, 1.0);
ck_assert_vector_values(segment->end.location, 0.6, 0.0, 0.0);
ck_assert_double_eq(segment->end.global_density, 0.0);
/* Refine exit point */
cloudsWalkerOrderRefine(walker, 0.001);
result = cloudsWalkerPerformStep(walker);
segment = cloudsWalkerGetLastSegment(walker);
ck_assert_int_eq(result, 1);
ck_assert_true(segment->refined);
ck_assert_double_in_range(segment->length, 0.3, 0.301);
ck_assert_double_eq(segment->start.distance_from_start, 0.6);
ck_assert_vector_values(segment->start.location, 0.2, 0.0, 0.0);
ck_assert_double_gt(segment->start.global_density, 0.9);
ck_assert_double_in_range(segment->end.distance_from_start, 0.9, 0.901);
ck_assert_double_in_range(segment->end.location.x, 0.5, 0.501);
ck_assert_double_lt(segment->end.global_density, 0.1);
/* Clean up */
cloudsDeleteWalker(walker);
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)
TEST_CASE(clouds,
test_clouds_density,
test_clouds_walking_boundaries,
test_clouds_walking)