Added cloud walker subdivision system

This commit is contained in:
Michaël Lemaire 2013-07-03 13:06:08 +02:00
parent 5a5067e745
commit d8695803b4
3 changed files with 252 additions and 45 deletions

View file

@ -35,10 +35,15 @@ struct CloudsWalker
double cursor;
double max_length;
double step_size;
int skip_void;
int started;
CloudWalkerStepInfo last_segment;
int subdivision_current;
int subdivision_count;
CloudWalkerStepInfo subdivision_parent;
CloudWalkingNextAction next_action;
};
@ -107,8 +112,10 @@ CloudsWalker* cloudsCreateWalker(Renderer* renderer, CloudsLayerDefinition* laye
result->max_length = v3Norm(result->diff);
result->cursor = 0.0;
result->step_size = 1.0;
result->skip_void = 0;
result->started = 0;
result->subdivision_count = 0;
result->last_segment.renderer = renderer;
result->last_segment.layer = layer;
@ -135,6 +142,11 @@ void cloudsWalkerSetStepSize(CloudsWalker* walker, double step)
}
}
void cloudsWalkerSetVoidSkipping(CloudsWalker* walker, int enabled)
{
walker->skip_void = enabled;
}
static void _getPoint(CloudsWalker* walker, double cursor, CloudWalkerPoint* out_point)
{
out_point->distance_from_start = cursor;
@ -187,52 +199,114 @@ static void _refineSegment(CloudsWalker* walker, double start_cursor, double sta
int cloudsWalkerPerformStep(CloudsWalker* walker)
{
int result = -1;
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)
while (result < 0)
{
walker->next_action.order = CLOUD_WALKING_STOP;
return 0;
if (walker->next_action.order == CLOUD_WALKING_STOP || walker->cursor >= walker->max_length)
{
walker->next_action.order = CLOUD_WALKING_STOP;
result = 0;
}
else if (walker->subdivision_count > 0)
{
if (walker->subdivision_current >= walker->subdivision_count)
{
/* Exit subdivision */
walker->subdivision_count = 0;
walker->last_segment = walker->subdivision_parent;
walker->next_action.order = CLOUD_WALKING_CONTINUE;
walker->cursor = walker->subdivision_parent.end.distance_from_start;
/* Recursive call to progress */
result = cloudsWalkerPerformStep(walker);
}
else
{
/* Continue subdivision */
walker->last_segment.start = walker->last_segment.end;
walker->cursor += walker->subdivision_parent.length / (double)walker->subdivision_count;
_getPoint(walker, walker->cursor, &walker->last_segment.end);
walker->last_segment.length = walker->subdivision_parent.length / (double)walker->subdivision_count;
walker->last_segment.refined = 0;
walker->last_segment.subdivided = walker->subdivision_count;
walker->subdivision_current++;
result = 1;
}
}
else if (walker->next_action.order == CLOUD_WALKING_CONTINUE)
{
/* TODO Limit to lookup 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;
walker->last_segment.subdivided = 0;
result = 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->last_segment.subdivided = 0;
walker->next_action.order = CLOUD_WALKING_CONTINUE;
result = 1;
}
else if (walker->next_action.order == CLOUD_WALKING_SUBDIVIDE)
{
/* Starting subdivision */
walker->subdivision_count = walker->next_action.max_segments;
walker->subdivision_current = 0;
walker->subdivision_parent = walker->last_segment;
walker->cursor = walker->subdivision_parent.start.distance_from_start;
/* Copy parent segment start, to be used as first subdivided segment start */
walker->last_segment.end = walker->subdivision_parent.start;
/* Recursive call to get first subdivided segment */
cloudsWalkerPerformStep(walker);
result = 1;
}
else
{
/* Unknown order... */
result = 0;
}
/* Check if we need to loop */
if (result > 0 && walker->skip_void && walker->last_segment.start.global_density == 0.0 && walker->last_segment.end.global_density == 0.0)
{
/* Last segment is considered void, and skipping is enabled */
result = -1;
}
}
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;
}
return result;
}
void cloudsWalkerOrderStop(CloudsWalker* walker)
@ -242,14 +316,20 @@ void cloudsWalkerOrderStop(CloudsWalker* walker)
void cloudsWalkerOrderRefine(CloudsWalker* walker, double precision)
{
walker->next_action.order = CLOUD_WALKING_REFINE;
walker->next_action.precision = precision;
if (walker->subdivision_count == 0)
{
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;
if (walker->subdivision_count == 0)
{
walker->next_action.order = CLOUD_WALKING_SUBDIVIDE;
walker->next_action.max_segments = max_segments;
}
}
CloudWalkerStepInfo* cloudsWalkerGetLastSegment(CloudsWalker* walker)

View file

@ -33,8 +33,7 @@ typedef struct
double length;
int refined;
/*int subdivision_level;
double precision_asked;*/
int subdivided;
void* data;
} CloudWalkerStepInfo;
@ -79,6 +78,14 @@ void cloudsDeleteWalker(CloudsWalker* walker);
*/
void cloudsWalkerSetStepSize(CloudsWalker* walker, double step);
/**
* Set the void skipping mode.
*
* @param walker The walker to configure
* @param enabled 1 to enable the void skipping, 0 to disable
*/
void cloudsWalkerSetVoidSkipping(CloudsWalker* walker, int enabled);
/**
* Perform a single step.
*
@ -108,6 +115,8 @@ void cloudsWalkerOrderRefine(CloudsWalker* walker, double precision);
/**
* Order the walker to subdivide the previous segment in smaller segments.
*
* Next steps will yield subdivided segments. Once subdivided segments have been processed, normal walking
* will resume automatically.
* @param walker The walker to use
* @param max_segments Maximal number of segments
*/

View file

@ -215,7 +215,7 @@ START_TEST(test_clouds_walking)
renderer->render_quality = 8;
renderer->clouds->getLayerDensity = _getLayerDensitySinX;
CloudsWalker* walker = cloudsCreateWalker(renderer, layer, v3(-0.4, 0.0, 0.0), v3(10.0, 0.0, 0.0));
CloudsWalker* walker = cloudsCreateWalker(renderer, layer, v3(-0.4, 0.0, 0.0), v3(1.75, 0.0, 0.0));
CloudWalkerStepInfo* segment;
int result;
@ -225,6 +225,7 @@ START_TEST(test_clouds_walking)
segment = cloudsWalkerGetLastSegment(walker);
ck_assert_int_eq(result, 1);
ck_assert_false(segment->refined);
ck_assert_false(segment->subdivided);
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);
@ -238,6 +239,7 @@ START_TEST(test_clouds_walking)
segment = cloudsWalkerGetLastSegment(walker);
ck_assert_int_eq(result, 1);
ck_assert_false(segment->refined);
ck_assert_false(segment->subdivided);
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);
@ -252,6 +254,7 @@ START_TEST(test_clouds_walking)
segment = cloudsWalkerGetLastSegment(walker);
ck_assert_int_eq(result, 1);
ck_assert_true(segment->refined);
ck_assert_false(segment->subdivided);
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);
@ -266,6 +269,7 @@ START_TEST(test_clouds_walking)
segment = cloudsWalkerGetLastSegment(walker);
ck_assert_int_eq(result, 1);
ck_assert_false(segment->refined);
ck_assert_false(segment->subdivided);
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);
@ -280,13 +284,127 @@ START_TEST(test_clouds_walking)
segment = cloudsWalkerGetLastSegment(walker);
ck_assert_int_eq(result, 1);
ck_assert_true(segment->refined);
ck_assert_false(segment->subdivided);
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);
ck_assert_double_eq(segment->end.global_density, 0.0);
/* Find next entry point by skipping blank */
cloudsWalkerSetVoidSkipping(walker, 1);
cloudsWalkerSetStepSize(walker, 0.2);
result = cloudsWalkerPerformStep(walker);
segment = cloudsWalkerGetLastSegment(walker);
ck_assert_int_eq(result, 1);
ck_assert_false(segment->refined);
ck_assert_false(segment->subdivided);
ck_assert_double_eq(segment->length, 0.2);
ck_assert_double_in_range(segment->start.distance_from_start, 1.2, 1.4);
ck_assert_double_in_range(segment->start.location.x, 0.8, 1.0);
ck_assert_double_eq(segment->start.global_density, 0.0);
ck_assert_double_in_range(segment->end.distance_from_start, 1.4, 1.6);
ck_assert_double_in_range(segment->end.location.x, 1.0, 1.2);
ck_assert_double_gt(segment->end.global_density, 0.0);
/* Refine 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_false(segment->subdivided);
ck_assert_double_in_range(segment->length, 0.0, 0.2);
ck_assert_double_in_range(segment->start.distance_from_start, 1.4, 1.41);
ck_assert_double_in_range(segment->start.location.x, 1.0, 1.01);
ck_assert_double_gt(segment->start.global_density, 0.0);
ck_assert_double_in_range(segment->end.distance_from_start, 1.41, 1.6);
ck_assert_double_in_range(segment->end.location.x, 1.01, 1.2);
ck_assert_double_gt(segment->end.global_density, 0.0);
/* Subdivide entry for more detail */
CloudWalkerStepInfo parent = *segment;
cloudsWalkerOrderSubdivide(walker, 3);
result = cloudsWalkerPerformStep(walker);
segment = cloudsWalkerGetLastSegment(walker);
ck_assert_int_eq(result, 1);
ck_assert_false(segment->refined);
ck_assert_true(segment->subdivided);
ck_assert_double_eq(segment->length, parent.length / 3.0);
ck_assert_double_eq(segment->start.distance_from_start, parent.start.distance_from_start);
ck_assert_double_eq(segment->start.location.x, parent.start.location.x);
ck_assert_double_eq(segment->end.distance_from_start, parent.start.distance_from_start + segment->length);
ck_assert_double_eq(segment->end.location.x, parent.start.location.x + segment->length);
result = cloudsWalkerPerformStep(walker);
segment = cloudsWalkerGetLastSegment(walker);
ck_assert_int_eq(result, 1);
ck_assert_false(segment->refined);
ck_assert_true(segment->subdivided);
ck_assert_double_eq(segment->length, parent.length / 3.0);
ck_assert_double_eq(segment->start.distance_from_start, parent.start.distance_from_start + segment->length);
ck_assert_double_eq(segment->start.location.x, parent.start.location.x + segment->length);
ck_assert_double_eq(segment->end.distance_from_start, parent.start.distance_from_start + 2.0 * segment->length);
ck_assert_double_eq(segment->end.location.x, parent.start.location.x + 2.0 * segment->length);
result = cloudsWalkerPerformStep(walker);
segment = cloudsWalkerGetLastSegment(walker);
ck_assert_int_eq(result, 1);
ck_assert_false(segment->refined);
ck_assert_true(segment->subdivided);
ck_assert_double_eq(segment->length, parent.length / 3.0);
ck_assert_double_eq(segment->start.distance_from_start, parent.start.distance_from_start + 2.0 * segment->length);
ck_assert_double_eq(segment->start.location.x, parent.start.location.x + 2.0 * segment->length);
ck_assert_double_eq(segment->end.distance_from_start, parent.end.distance_from_start);
ck_assert_double_eq(segment->end.location.x, parent.end.location.x);
/* After subdividing, normal walking resumes */
result = cloudsWalkerPerformStep(walker);
segment = cloudsWalkerGetLastSegment(walker);
ck_assert_int_eq(result, 1);
ck_assert_false(segment->refined);
ck_assert_false(segment->subdivided);
ck_assert_double_eq(segment->length, 0.2);
ck_assert_double_in_range(segment->start.distance_from_start, 1.41, 1.6);
ck_assert_double_in_range(segment->start.location.x, 1.01, 1.2);
ck_assert_double_gt(segment->start.global_density, 0.0);
ck_assert_double_in_range(segment->end.distance_from_start, 1.61, 1.8);
ck_assert_double_in_range(segment->end.location.x, 1.21, 1.4);
ck_assert_double_gt(segment->end.global_density, 0.0);
/* Exiting cloud again */
cloudsWalkerSetStepSize(walker, 0.3);
result = cloudsWalkerPerformStep(walker);
segment = cloudsWalkerGetLastSegment(walker);
ck_assert_int_eq(result, 1);
ck_assert_false(segment->refined);
ck_assert_false(segment->subdivided);
ck_assert_double_eq(segment->length, 0.3);
ck_assert_double_in_range(segment->start.distance_from_start, 1.61, 1.8);
ck_assert_double_in_range(segment->start.location.x, 1.21, 1.4);
ck_assert_double_gt(segment->start.global_density, 0.0);
ck_assert_double_in_range(segment->end.distance_from_start, 1.91, 2.1);
ck_assert_double_in_range(segment->end.location.x, 1.5, 1.7);
ck_assert_double_eq(segment->end.global_density, 0.0);
/* A step in the void without skipping */
cloudsWalkerSetVoidSkipping(walker, 0);
result = cloudsWalkerPerformStep(walker);
segment = cloudsWalkerGetLastSegment(walker);
ck_assert_int_eq(result, 1);
ck_assert_false(segment->refined);
ck_assert_false(segment->subdivided);
ck_assert_double_eq(segment->length, 0.3);
ck_assert_double_in_range(segment->start.distance_from_start, 1.91, 2.1);
ck_assert_double_in_range(segment->start.location.x, 1.5, 1.7);
ck_assert_double_eq(segment->start.global_density, 0.0);
ck_assert_double_in_range(segment->end.distance_from_start, 2.21, 2.4);
ck_assert_double_in_range(segment->end.location.x, 1.8, 2.0);
ck_assert_double_eq(segment->end.global_density, 0.0);
/* Walker reached the lookup segment's end, it should stop */
result = cloudsWalkerPerformStep(walker);
ck_assert_int_eq(result, 0);
/* Clean up */
cloudsDeleteWalker(walker);