clouds_walking: Started using walker in rendering
This commit is contained in:
parent
aefc3cacdd
commit
1ede3de8d5
6 changed files with 183 additions and 35 deletions
2
TODO
2
TODO
|
@ -1,4 +1,5 @@
|
||||||
Technology Preview 2 :
|
Technology Preview 2 :
|
||||||
|
- Implement perspective correction for coordinate mapping of rasterized polygons.
|
||||||
- Finalize terrain editor.
|
- Finalize terrain editor.
|
||||||
=> Add a generation dialog for base noise (overwriting changes).
|
=> Add a generation dialog for base noise (overwriting changes).
|
||||||
- Get rid of noise dialogs, for simpler settings.
|
- Get rid of noise dialogs, for simpler settings.
|
||||||
|
@ -15,6 +16,7 @@ Technology Preview 2 :
|
||||||
|
|
||||||
Technlogy Preview 3 :
|
Technlogy Preview 3 :
|
||||||
- Start an undo/redo system ?
|
- 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.
|
- Add a map preview to terrain editor.
|
||||||
- Better time selection widget for atmosphere.
|
- Better time selection widget for atmosphere.
|
||||||
- Clouds should keep distance to ground.
|
- Clouds should keep distance to ground.
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
#include "private.h"
|
#include "private.h"
|
||||||
|
|
||||||
|
#include <assert.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include "../tools.h"
|
#include "../tools.h"
|
||||||
#include "../renderer.h"
|
#include "../renderer.h"
|
||||||
|
@ -26,32 +27,117 @@ static Color _fakeGetColor(Renderer* renderer, Color base, Vector3 start, Vector
|
||||||
}
|
}
|
||||||
|
|
||||||
/******************** Real ********************/
|
/******************** Real ********************/
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
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)
|
static int _alterLight(Renderer* renderer, LightDefinition* light, Vector3 location)
|
||||||
{
|
{
|
||||||
#if 0
|
|
||||||
CloudsDefinition* definition = renderer->clouds->definition;
|
CloudsDefinition* definition = renderer->clouds->definition;
|
||||||
int i, n;
|
int i, n;
|
||||||
|
|
||||||
|
AccumulatedLightData data;
|
||||||
|
data.out_scattering = 0.0;
|
||||||
|
data.light_power = colorGetPower(&light->color);
|
||||||
|
|
||||||
/* TODO Iter layers in sorted order */
|
/* TODO Iter layers in sorted order */
|
||||||
n = layersCount(definition->layers);
|
n = layersCount(definition->layers);
|
||||||
for (i = 0; i < n; i++)
|
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));
|
CloudsLayerDefinition* layer = (CloudsLayerDefinition*)layersGetLayer(renderer->clouds->definition->layers, i);
|
||||||
/* TODO Reduce light->reflection too */
|
Vector3 ostart, oend;
|
||||||
|
|
||||||
|
ostart = location;
|
||||||
|
oend = v3Add(location, v3Scale(light->direction, -10000.0));
|
||||||
|
if (!cloudsOptimizeWalkingBounds(layer, &ostart, &oend))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
return n > 0;
|
else
|
||||||
#endif
|
{
|
||||||
return 0;
|
CloudsWalker* walker;
|
||||||
|
|
||||||
|
walker = cloudsCreateWalker(renderer, layer, ostart, oend);
|
||||||
|
cloudsWalkerSetStepSize(walker, -1.0);
|
||||||
|
cloudsStartWalking(walker, _walkerFilterCallback, &data);
|
||||||
|
cloudsDeleteWalker(walker);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
typedef struct
|
||||||
{
|
{
|
||||||
Color result;
|
double out_scattering; /* Amount of light scattered away by heavy particles */
|
||||||
|
Color in_scattering; /* Amount of light redirected toward the viewer */
|
||||||
} AccumulatedMaterialData;
|
} 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)
|
static void _walkerMaterialCallback(CloudsWalker* walker)
|
||||||
{
|
{
|
||||||
/*AccumulatedMaterialData* data = (AccumulatedMaterialData*)segment->data;*/
|
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)
|
static Color _getColor(Renderer* renderer, Color base, Vector3 start, Vector3 end)
|
||||||
|
@ -81,13 +167,25 @@ static Color _getColor(Renderer* renderer, Color base, Vector3 start, Vector3 en
|
||||||
{
|
{
|
||||||
CloudsWalker* walker;
|
CloudsWalker* walker;
|
||||||
AccumulatedMaterialData data;
|
AccumulatedMaterialData data;
|
||||||
data.result = COLOR_TRANSPARENT;
|
data.out_scattering = 0.0;
|
||||||
|
data.in_scattering = COLOR_BLACK;
|
||||||
|
|
||||||
walker = cloudsCreateWalker(renderer, layer, start, end);
|
walker = cloudsCreateWalker(renderer, layer, ostart, oend);
|
||||||
|
cloudsWalkerSetStepSize(walker, -1.0);
|
||||||
cloudsStartWalking(walker, _walkerMaterialCallback, &data);
|
cloudsStartWalking(walker, _walkerMaterialCallback, &data);
|
||||||
cloudsDeleteWalker(walker);
|
cloudsDeleteWalker(walker);
|
||||||
|
|
||||||
colorMask(&base, &data.result);
|
/* 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->applyMediumTraversal(renderer, ostart, base);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -122,11 +122,18 @@ void cloudsDeleteWalker(CloudsWalker* walker)
|
||||||
free(walker);
|
free(walker);
|
||||||
}
|
}
|
||||||
|
|
||||||
void cloudsSetStepSize(CloudsWalker* walker, double step)
|
void cloudsWalkerSetStepSize(CloudsWalker* walker, double step)
|
||||||
|
{
|
||||||
|
if (step > 0.0)
|
||||||
{
|
{
|
||||||
/* TODO Negative step => automatic */
|
|
||||||
walker->step_size = step;
|
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)
|
static void _getPoint(CloudsWalker* walker, double cursor, CloudWalkerPoint* out_point)
|
||||||
{
|
{
|
||||||
|
@ -147,33 +154,33 @@ static void _refineSegment(CloudsWalker* walker, double start_cursor, double sta
|
||||||
if (start_density == 0.0)
|
if (start_density == 0.0)
|
||||||
{
|
{
|
||||||
/* Looking for entry */
|
/* Looking for entry */
|
||||||
if (middle.global_density == 0.0)
|
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);
|
_refineSegment(walker, middle.distance_from_start, middle.global_density, end_cursor, end_density, precision, result);
|
||||||
}
|
}
|
||||||
else if (middle.distance_from_start - start_cursor > precision)
|
|
||||||
{
|
|
||||||
_refineSegment(walker, start_cursor, start_density, middle.distance_from_start, middle.global_density, precision, result);
|
|
||||||
}
|
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
*result = middle;
|
_refineSegment(walker, start_cursor, start_density, middle.distance_from_start, middle.global_density, precision, result);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
/* Looking for exit */
|
/* Looking for exit */
|
||||||
if (middle.global_density == 0.0)
|
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);
|
_refineSegment(walker, start_cursor, start_density, middle.distance_from_start, middle.global_density, precision, result);
|
||||||
}
|
}
|
||||||
else if (end_cursor - middle.distance_from_start > precision)
|
|
||||||
{
|
|
||||||
_refineSegment(walker, middle.distance_from_start, middle.global_density, end_cursor, end_density, precision, result);
|
|
||||||
}
|
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
*result = middle;
|
_refineSegment(walker, middle.distance_from_start, middle.global_density, end_cursor, end_density, precision, result);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -200,6 +207,7 @@ int cloudsWalkerPerformStep(CloudsWalker* walker)
|
||||||
|
|
||||||
_getPoint(walker, walker->cursor, &walker->last_segment.end);
|
_getPoint(walker, walker->cursor, &walker->last_segment.end);
|
||||||
walker->last_segment.length = walker->step_size;
|
walker->last_segment.length = walker->step_size;
|
||||||
|
walker->last_segment.refined = 0;
|
||||||
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
@ -212,9 +220,12 @@ int cloudsWalkerPerformStep(CloudsWalker* walker)
|
||||||
walker->last_segment.end.distance_from_start,
|
walker->last_segment.end.distance_from_start,
|
||||||
walker->last_segment.end.global_density,
|
walker->last_segment.end.global_density,
|
||||||
walker->next_action.precision,
|
walker->next_action.precision,
|
||||||
&walker->last_segment.start);
|
(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.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;
|
walker->next_action.order = CLOUD_WALKING_CONTINUE;
|
||||||
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
@ -248,6 +259,7 @@ CloudWalkerStepInfo* cloudsWalkerGetLastSegment(CloudsWalker* walker)
|
||||||
|
|
||||||
void cloudsStartWalking(CloudsWalker* walker, FuncCloudsWalkingCallback callback, void* data)
|
void cloudsStartWalking(CloudsWalker* walker, FuncCloudsWalkingCallback callback, void* data)
|
||||||
{
|
{
|
||||||
|
walker->last_segment.data = data;
|
||||||
while (cloudsWalkerPerformStep(walker))
|
while (cloudsWalkerPerformStep(walker))
|
||||||
{
|
{
|
||||||
callback(walker);
|
callback(walker);
|
||||||
|
|
|
@ -32,8 +32,8 @@ typedef struct
|
||||||
CloudWalkerPoint end;
|
CloudWalkerPoint end;
|
||||||
double length;
|
double length;
|
||||||
|
|
||||||
/*int refined;
|
int refined;
|
||||||
int subdivision_level;
|
/*int subdivision_level;
|
||||||
double precision_asked;*/
|
double precision_asked;*/
|
||||||
|
|
||||||
void* data;
|
void* data;
|
||||||
|
@ -77,7 +77,7 @@ void cloudsDeleteWalker(CloudsWalker* walker);
|
||||||
* @param walker The walker to configure
|
* @param walker The walker to configure
|
||||||
* @param step The step length, negative for automatic
|
* @param step The step length, negative for automatic
|
||||||
*/
|
*/
|
||||||
void cloudsSetStepSize(CloudsWalker* walker, double step);
|
void cloudsWalkerSetStepSize(CloudsWalker* walker, double step);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Perform a single step.
|
* Perform a single step.
|
||||||
|
|
|
@ -27,6 +27,11 @@ static inline void _add_methods_to_case(TCase* tc, ...)
|
||||||
suite_add_tcase(s, 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)
|
static inline int _double_equals(double x, double y)
|
||||||
{
|
{
|
||||||
return fabs(x - y) < 0.00000000001;
|
return fabs(x - y) < 0.00000000001;
|
||||||
|
|
|
@ -220,10 +220,11 @@ START_TEST(test_clouds_walking)
|
||||||
int result;
|
int result;
|
||||||
|
|
||||||
/* First step */
|
/* First step */
|
||||||
cloudsSetStepSize(walker, 0.3);
|
cloudsWalkerSetStepSize(walker, 0.3);
|
||||||
result = cloudsWalkerPerformStep(walker);
|
result = cloudsWalkerPerformStep(walker);
|
||||||
segment = cloudsWalkerGetLastSegment(walker);
|
segment = cloudsWalkerGetLastSegment(walker);
|
||||||
ck_assert_int_eq(result, 1);
|
ck_assert_int_eq(result, 1);
|
||||||
|
ck_assert_false(segment->refined);
|
||||||
ck_assert_double_eq(segment->length, 0.3);
|
ck_assert_double_eq(segment->length, 0.3);
|
||||||
ck_assert_double_eq(segment->start.distance_from_start, 0.0);
|
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_vector_values(segment->start.location, -0.4, 0.0, 0.0);
|
||||||
|
@ -236,6 +237,7 @@ START_TEST(test_clouds_walking)
|
||||||
result = cloudsWalkerPerformStep(walker);
|
result = cloudsWalkerPerformStep(walker);
|
||||||
segment = cloudsWalkerGetLastSegment(walker);
|
segment = cloudsWalkerGetLastSegment(walker);
|
||||||
ck_assert_int_eq(result, 1);
|
ck_assert_int_eq(result, 1);
|
||||||
|
ck_assert_false(segment->refined);
|
||||||
ck_assert_double_eq(segment->length, 0.3);
|
ck_assert_double_eq(segment->length, 0.3);
|
||||||
ck_assert_double_eq(segment->start.distance_from_start, 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_vector_values(segment->start.location, -0.1, 0.0, 0.0);
|
||||||
|
@ -249,14 +251,43 @@ START_TEST(test_clouds_walking)
|
||||||
result = cloudsWalkerPerformStep(walker);
|
result = cloudsWalkerPerformStep(walker);
|
||||||
segment = cloudsWalkerGetLastSegment(walker);
|
segment = cloudsWalkerGetLastSegment(walker);
|
||||||
ck_assert_int_eq(result, 1);
|
ck_assert_int_eq(result, 1);
|
||||||
ck_assert_double_in_range(segment->length, 0.19, 0.21);
|
ck_assert_true(segment->refined);
|
||||||
ck_assert_double_in_range(segment->start.distance_from_start, 0.39, 0.41);
|
ck_assert_double_in_range(segment->length, 0.19, 0.20);
|
||||||
ck_assert_double_in_range(segment->start.location.x, -0.01, 0.01);
|
ck_assert_double_in_range(segment->start.distance_from_start, 0.40, 0.41);
|
||||||
/* TODO Check segment->start.global_density */
|
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_double_eq(segment->end.distance_from_start, 0.6);
|
||||||
ck_assert_vector_values(segment->end.location, 0.2, 0.0, 0.0);
|
ck_assert_vector_values(segment->end.location, 0.2, 0.0, 0.0);
|
||||||
ck_assert_double_gt(segment->end.global_density, 0.9);
|
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 */
|
/* Clean up */
|
||||||
cloudsDeleteWalker(walker);
|
cloudsDeleteWalker(walker);
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue