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 :
|
||||
- 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.
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
#include "private.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdlib.h>
|
||||
#include "../tools.h"
|
||||
#include "../renderer.h"
|
||||
|
@ -26,32 +27,117 @@ static Color _fakeGetColor(Renderer* renderer, Color base, Vector3 start, Vector
|
|||
}
|
||||
|
||||
/******************** 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)
|
||||
{
|
||||
#if 0
|
||||
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;
|
||||
#endif
|
||||
return 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
|
||||
{
|
||||
Color result;
|
||||
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)
|
||||
{
|
||||
/*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)
|
||||
|
@ -81,13 +167,25 @@ static Color _getColor(Renderer* renderer, Color base, Vector3 start, Vector3 en
|
|||
{
|
||||
CloudsWalker* walker;
|
||||
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);
|
||||
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,10 +122,17 @@ void cloudsDeleteWalker(CloudsWalker* walker)
|
|||
free(walker);
|
||||
}
|
||||
|
||||
void cloudsSetStepSize(CloudsWalker* walker, double step)
|
||||
void cloudsWalkerSetStepSize(CloudsWalker* walker, double step)
|
||||
{
|
||||
/* TODO Negative step => automatic */
|
||||
walker->step_size = 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)
|
||||
|
@ -147,33 +154,33 @@ static void _refineSegment(CloudsWalker* walker, double start_cursor, double sta
|
|||
if (start_density == 0.0)
|
||||
{
|
||||
/* 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);
|
||||
}
|
||||
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
|
||||
{
|
||||
*result = middle;
|
||||
_refineSegment(walker, start_cursor, start_density, middle.distance_from_start, middle.global_density, precision, result);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/* 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);
|
||||
}
|
||||
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
|
||||
{
|
||||
*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);
|
||||
walker->last_segment.length = walker->step_size;
|
||||
walker->last_segment.refined = 0;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
@ -212,9 +220,12 @@ int cloudsWalkerPerformStep(CloudsWalker* walker)
|
|||
walker->last_segment.end.distance_from_start,
|
||||
walker->last_segment.end.global_density,
|
||||
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.refined = 1;
|
||||
|
||||
walker->next_action.order = CLOUD_WALKING_CONTINUE;
|
||||
|
||||
return 1;
|
||||
}
|
||||
else
|
||||
|
@ -248,6 +259,7 @@ CloudWalkerStepInfo* cloudsWalkerGetLastSegment(CloudsWalker* walker)
|
|||
|
||||
void cloudsStartWalking(CloudsWalker* walker, FuncCloudsWalkingCallback callback, void* data)
|
||||
{
|
||||
walker->last_segment.data = data;
|
||||
while (cloudsWalkerPerformStep(walker))
|
||||
{
|
||||
callback(walker);
|
||||
|
|
|
@ -32,8 +32,8 @@ typedef struct
|
|||
CloudWalkerPoint end;
|
||||
double length;
|
||||
|
||||
/*int refined;
|
||||
int subdivision_level;
|
||||
int refined;
|
||||
/*int subdivision_level;
|
||||
double precision_asked;*/
|
||||
|
||||
void* data;
|
||||
|
@ -77,7 +77,7 @@ void cloudsDeleteWalker(CloudsWalker* walker);
|
|||
* @param walker The walker to configure
|
||||
* @param step The step length, negative for automatic
|
||||
*/
|
||||
void cloudsSetStepSize(CloudsWalker* walker, double step);
|
||||
void cloudsWalkerSetStepSize(CloudsWalker* walker, double step);
|
||||
|
||||
/**
|
||||
* Perform a single step.
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -220,10 +220,11 @@ START_TEST(test_clouds_walking)
|
|||
int result;
|
||||
|
||||
/* First step */
|
||||
cloudsSetStepSize(walker, 0.3);
|
||||
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);
|
||||
|
@ -236,6 +237,7 @@ START_TEST(test_clouds_walking)
|
|||
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);
|
||||
|
@ -249,14 +251,43 @@ START_TEST(test_clouds_walking)
|
|||
result = cloudsWalkerPerformStep(walker);
|
||||
segment = cloudsWalkerGetLastSegment(walker);
|
||||
ck_assert_int_eq(result, 1);
|
||||
ck_assert_double_in_range(segment->length, 0.19, 0.21);
|
||||
ck_assert_double_in_range(segment->start.distance_from_start, 0.39, 0.41);
|
||||
ck_assert_double_in_range(segment->start.location.x, -0.01, 0.01);
|
||||
/* TODO Check segment->start.global_density */
|
||||
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);
|
||||
|
||||
|
|
Loading…
Reference in a new issue