From 1c0c93479ec5f8d66d0f747146cf2370e81a1c73 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C3=ABl=20Lemaire?= Date: Fri, 31 May 2013 21:36:59 +0200 Subject: [PATCH] clouds_walking: Added entry refinement --- src/rendering/clouds/clo_walking.c | 77 ++++++++++++++++++++++++++++-- src/rendering/clouds/clo_walking.h | 15 ++++-- src/testing/test_clouds.c | 29 ++++++++--- 3 files changed, 107 insertions(+), 14 deletions(-) diff --git a/src/rendering/clouds/clo_walking.c b/src/rendering/clouds/clo_walking.c index 5dd6672..42846c5 100644 --- a/src/rendering/clouds/clo_walking.c +++ b/src/rendering/clouds/clo_walking.c @@ -36,6 +36,7 @@ struct CloudsWalker double max_length; double step_size; + int started; CloudWalkerStepInfo last_segment; CloudWalkingNextAction next_action; @@ -107,10 +108,9 @@ CloudsWalker* cloudsCreateWalker(Renderer* renderer, CloudsLayerDefinition* laye result->cursor = 0.0; result->step_size = 1.0; + result->started = 0; result->last_segment.renderer = renderer; result->last_segment.layer = layer; - result->last_segment.walked_distance = 0.0; - result->last_segment.end = start; result->next_action.order = CLOUD_WALKING_CONTINUE; @@ -128,8 +128,64 @@ void cloudsSetStepSize(CloudsWalker* walker, double step) walker->step_size = step; } +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.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; + } + } + else + { + /* Looking for exit */ + 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; + } + } +} + 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; @@ -139,15 +195,28 @@ int cloudsWalkerPerformStep(CloudsWalker* walker) { /* TODO Limit to end */ walker->last_segment.start = walker->last_segment.end; - walker->last_segment.walked_distance = walker->cursor; walker->cursor += walker->step_size; - walker->last_segment.end = v3Add(walker->start, v3Scale(walker->diff, walker->cursor / walker->max_length)); + _getPoint(walker, walker->cursor, &walker->last_segment.end); walker->last_segment.length = walker->step_size; 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); + walker->last_segment.length = walker->last_segment.end.distance_from_start - walker->last_segment.start.distance_from_start; + walker->next_action.order = CLOUD_WALKING_CONTINUE; + return 1; + } else { /* TODO */ diff --git a/src/rendering/clouds/clo_walking.h b/src/rendering/clouds/clo_walking.h index 49366da..5aaa812 100644 --- a/src/rendering/clouds/clo_walking.h +++ b/src/rendering/clouds/clo_walking.h @@ -13,6 +13,13 @@ extern "C" { #endif +typedef struct +{ + double distance_from_start; + Vector3 location; + double global_density; +} CloudWalkerPoint; + /** * Information on a segment yielded by walking. */ @@ -21,9 +28,8 @@ typedef struct Renderer* renderer; CloudsLayerDefinition* layer; - double walked_distance; - Vector3 start; - Vector3 end; + CloudWalkerPoint start; + CloudWalkerPoint end; double length; /*int refined; @@ -91,6 +97,9 @@ 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 */ diff --git a/src/testing/test_clouds.c b/src/testing/test_clouds.c index add42e7..d00de74 100644 --- a/src/testing/test_clouds.c +++ b/src/testing/test_clouds.c @@ -224,23 +224,38 @@ START_TEST(test_clouds_walking) result = cloudsWalkerPerformStep(walker); segment = cloudsWalkerGetLastSegment(walker); ck_assert_int_eq(result, 1); - ck_assert_double_eq(segment->walked_distance, 0.0); - ck_assert_vector_values(segment->start, -0.4, 0.0, 0.0); - ck_assert_vector_values(segment->end, -0.1, 0.0, 0.0); 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_double_eq(segment->walked_distance, 0.3); - ck_assert_vector_values(segment->start, -0.1, 0.0, 0.0); - ck_assert_vector_values(segment->end, 0.2, 0.0, 0.0); 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); - /* TODO */ + 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_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); /* Clean up */ cloudsDeleteWalker(walker);