diff --git a/aephysics/dynamic_tree/module.ae b/aephysics/dynamic_tree/module.ae index ac4589d..9c7b245 100644 --- a/aephysics/dynamic_tree/module.ae +++ b/aephysics/dynamic_tree/module.ae @@ -134,8 +134,14 @@ node_height(n: DynamicTreeNode) -> int { return n.height } +// An empty node passes for a leaf, so only its bounds keep a traversal out: +// inverted infinities, which no box overlaps, as the reference's are. The +// float limits were not enough: a query box out to them overlapped, and the +// empty node's proxy index read far past the proxies. make_empty_node() -> DynamicTreeNode { - return DynamicTreeNode { aabb: math.aabb_empty(), flag_index: EMPTY_NODE, height: 0 } + inf = math.infinity() + return DynamicTreeNode { aabb: AABB { lower: Vec3 { x: inf, y: inf, z: inf }, upper: Vec3 { x: 0.0 - inf, y: 0.0 - inf, z: 0.0 - inf } }, + flag_index: EMPTY_NODE, height: 0 } } // The pair below the root, or the root itself when it is a leaf. diff --git a/aephysics/math/module.ae b/aephysics/math/module.ae index 1c66ff0..4780952 100644 --- a/aephysics/math/module.ae +++ b/aephysics/math/module.ae @@ -37,7 +37,7 @@ exports ( EPSILON, TINY, MAX_FLOAT, HUGE, vec2, vec3, quat, quat_vs, qv, transform, matrix3, aabb, plane, vec3_zero, vec3_one, vec3_axis_x, vec3_axis_y, vec3_axis_z, - quat_identity, transform_identity, mat3_zero, mat3_identity, aabb_empty, + quat_identity, transform_identity, mat3_zero, mat3_identity, aabb_empty, infinity, min_int, max_int, clamp_int, abs_float, min_float, max_float, clamp_float, lerp_float, sqrt_float, atan2_approx, compute_cos_sin, sin_approx, cos_approx, unwind_angle, add, sub, mul, neg, dot, length, length_squared, distance, distance_squared, @@ -182,6 +182,13 @@ transform_identity() -> Transform { return Transform { p: vec3_zero(), q: quat_i mat3_zero() -> Matrix3 { return Matrix3 { cx: vec3_zero(), cy: vec3_zero(), cz: vec3_zero() } } mat3_identity() -> Matrix3 { return Matrix3 { cx: vec3_axis_x(), cy: vec3_axis_y(), cz: vec3_axis_z() } } // The box no point is in: a union with it is the other box. +// IEEE positive infinity: past every finite double, as an empty tree node's +// bounds must be so no finite query box overlaps them (b3MakeEmptyNode). +infinity() -> float { + zero = 0.0 + return 1.0 / zero +} + aabb_empty() -> AABB { return AABB { lower: Vec3 { x: MAX_FLOAT, y: MAX_FLOAT, z: MAX_FLOAT }, upper: Vec3 { x: 0.0 - MAX_FLOAT, y: 0.0 - MAX_FLOAT, z: 0.0 - MAX_FLOAT } } diff --git a/tests/test_dynamic_tree.ae b/tests/test_dynamic_tree.ae index 0d21fca..d0d434d 100644 --- a/tests/test_dynamic_tree.ae +++ b/tests/test_dynamic_tree.ae @@ -160,6 +160,13 @@ test_leaf_root() { ensure("leaf root: the proxy", hit.proxy_id == proxy_id) ensure("leaf root: its user data", hit.user_data == (7 as long)) + // A box out to the float limits: the empty node beside the leaf root is + // still skipped (its bounds are inverted infinities, not the limits). + hit.count = 0 + everything = math.aabb(math.vec3(0.0 - math.MAX_FLOAT, 0.0 - math.MAX_FLOAT, 0.0 - math.MAX_FLOAT), math.vec3(math.MAX_FLOAT, math.MAX_FLOAT, math.MAX_FLOAT)) + dynamic_tree.tree_query(&tree, everything, dynamic_tree.default_mask_bits(), false, leaf_root_visit, (&hit) as ptr) + ensure("leaf root: everything finds only the proxy (${hit.count})", hit.count == 1 && hit.proxy_id == proxy_id) + hit.count = 0 miss_box = math.aabb(math.vec3(4.0, 4.0, 4.0), math.vec3(5.0, 5.0, 5.0)) dynamic_tree.tree_query(&tree, miss_box, dynamic_tree.default_mask_bits(), false, leaf_root_visit, (&hit) as ptr)