Added cloud walker subdivision system
This commit is contained in:
parent
5a5067e745
commit
d8695803b4
3 changed files with 252 additions and 45 deletions
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
*/
|
||||
|
|
|
@ -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);
|
||||
|
|
Loading…
Reference in a new issue