Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 23 additions & 14 deletions aephysics/physics_world/module.ae
Original file line number Diff line number Diff line change
Expand Up @@ -1096,9 +1096,12 @@ accept_shape(shape_id: ShapeId, context: ptr) -> bool { return true }
keep_cast(shape_id: ShapeId, point: Vec3, normal: Vec3, fraction: float, user_material_id: long, triangle_index: int, child_index: int, context: ptr) -> float { return 1.0 }
accept_planes(shape_id: ShapeId, planes: PlaneResult[], count: int, context: ptr) -> bool { return true }

query_recorder(context: @keep ptr) -> QueryRecorder {
// A recorder with no visitor and no context yet: each query sets the
// caller's on its own local, which it lends to the internal query only,
// so the caller's context is borrowed, never kept (#179).
query_recorder() -> QueryRecorder {
return QueryRecorder { payload: dynamics.rec_payload_create(), overlap: accept_shape, cast: keep_cast, planes: accept_planes,
has_overlap: false, context: context, count: 0 }
has_overlap: false, context: null, count: 0 }
}

// Whether the query asked of `id` is one to record: the world records and
Expand Down Expand Up @@ -1166,9 +1169,10 @@ record_plane_visit(shape_id: ShapeId, planes: PlaneResult[], count: int, context

// Every shape whose fat bounds overlap the box, through the visitor
// until it returns false; the tree visits made.
overlap_aabb(id: WorldId, box: AABB, filter: QueryFilter, visitor: fn(ShapeId, ptr) -> bool, context: @keep ptr) -> TreeStats {
overlap_aabb(id: WorldId, box: AABB, filter: QueryFilter, visitor: fn(ShapeId, ptr) -> bool, context: ptr) -> TreeStats {
if recording_query(id) == false { return overlap_aabb_internal(id, box, filter, visitor, context) }
q = query_recorder(context)
q = query_recorder()
q.context = context
q.overlap = visitor
q.has_overlap = true
dynamics.rec_aabb(q.payload, box)
Expand All @@ -1183,9 +1187,10 @@ overlap_aabb(id: WorldId, box: AABB, filter: QueryFilter, visitor: fn(ShapeId, p
}

// Every shape a convex proxy (given relative to the origin) overlaps.
overlap_shape(id: WorldId, origin: Vec3, proxy: ShapeProxy, filter: QueryFilter, visitor: fn(ShapeId, ptr) -> bool, context: @keep ptr) -> TreeStats {
overlap_shape(id: WorldId, origin: Vec3, proxy: ShapeProxy, filter: QueryFilter, visitor: fn(ShapeId, ptr) -> bool, context: ptr) -> TreeStats {
if recording_query(id) == false { return overlap_shape_internal(id, origin, proxy, filter, visitor, context) }
q = query_recorder(context)
q = query_recorder()
q.context = context
q.overlap = visitor
q.has_overlap = true
dynamics.rec_vec3(q.payload, origin)
Expand All @@ -1203,12 +1208,13 @@ overlap_shape(id: WorldId, origin: Vec3, proxy: ShapeProxy, filter: QueryFilter,
// The planes of every shape the character mover's capsule (relative to
// the origin) touches, a shape's planes at a time (up to 64), through
// the visitor until it returns false.
collide_mover(id: WorldId, origin: Vec3, mover: Capsule, filter: QueryFilter, visitor: fn(ShapeId, PlaneResult[], int, ptr) -> bool, context: @keep ptr) {
collide_mover(id: WorldId, origin: Vec3, mover: Capsule, filter: QueryFilter, visitor: fn(ShapeId, PlaneResult[], int, ptr) -> bool, context: ptr) {
if recording_query(id) == false {
collide_mover_internal(id, origin, mover, filter, visitor, context)
return
}
q = query_recorder(context)
q = query_recorder()
q.context = context
q.planes = visitor
dynamics.rec_vec3(q.payload, origin)
dynamics.rec_capsule_value(q.payload, mover)
Expand All @@ -1224,9 +1230,10 @@ collide_mover(id: WorldId, origin: Vec3, mover: Capsule, filter: QueryFilter, vi
// visitor, whose return clips the ray (0 stops it, 1 keeps everything,
// -1 ignores that shape); the tree visits made.
cast_ray(id: WorldId, origin: Vec3, translation: Vec3, filter: QueryFilter,
visitor: fn(ShapeId, Vec3, Vec3, float, long, int, int, ptr) -> float, context: @keep ptr) -> TreeStats {
visitor: fn(ShapeId, Vec3, Vec3, float, long, int, int, ptr) -> float, context: ptr) -> TreeStats {
if recording_query(id) == false { return cast_ray_internal(id, origin, translation, filter, visitor, context) }
q = query_recorder(context)
q = query_recorder()
q.context = context
q.cast = visitor
dynamics.rec_vec3(q.payload, origin)
dynamics.rec_vec3(q.payload, translation)
Expand Down Expand Up @@ -1270,9 +1277,10 @@ rec_ray_result(payload: *Recording, result: RayResult) {
// A convex proxy (relative to the origin) swept along the translation,
// each hit through the visitor as the ray cast's.
cast_shape(id: WorldId, origin: Vec3, proxy: ShapeProxy, translation: Vec3, filter: QueryFilter,
visitor: fn(ShapeId, Vec3, Vec3, float, long, int, int, ptr) -> float, context: @keep ptr) -> TreeStats {
visitor: fn(ShapeId, Vec3, Vec3, float, long, int, int, ptr) -> float, context: ptr) -> TreeStats {
if recording_query(id) == false { return cast_shape_internal(id, origin, proxy, translation, filter, visitor, context) }
q = query_recorder(context)
q = query_recorder()
q.context = context
q.cast = visitor
dynamics.rec_vec3(q.payload, origin)
dynamics.rec_shape_proxy(q.payload, proxy)
Expand All @@ -1291,9 +1299,10 @@ cast_shape(id: WorldId, origin: Vec3, proxy: ShapeProxy, translation: Vec3, filt
// touching a shape (its filter asked per shape when given). Recorded, the
// filter is asked through the recorder for every candidate, given or not,
// so a replay sees the same decisions.
cast_mover(id: WorldId, origin: Vec3, mover: Capsule, translation: Vec3, filter: QueryFilter, has_filter: bool, filter_fn: fn(ShapeId, ptr) -> bool, context: @keep ptr) -> float {
cast_mover(id: WorldId, origin: Vec3, mover: Capsule, translation: Vec3, filter: QueryFilter, has_filter: bool, filter_fn: fn(ShapeId, ptr) -> bool, context: ptr) -> float {
if recording_query(id) == false { return cast_mover_internal(id, origin, mover, translation, filter, has_filter, filter_fn, context) }
q = query_recorder(context)
q = query_recorder()
q.context = context
q.overlap = filter_fn
q.has_overlap = has_filter
dynamics.rec_vec3(q.payload, origin)
Expand Down
18 changes: 18 additions & 0 deletions tests/test_physics_world.ae
Original file line number Diff line number Diff line change
Expand Up @@ -2057,6 +2057,19 @@ count_planes(id: ShapeId, planes: PlaneResult[], count: int, context: ptr) -> bo
return true
}

// #179: a caller's borrowed context reaches every world query without
// @keep, since a query only lends it to its own recorder for the call.
// The context here is a plain ptr on purpose: if a query required @keep
// again, this file would stop building. The queries count into it.
borrowed_queries(world_id: WorldId, filter: QueryFilter, context: ptr) {
physics_world.cast_ray(world_id, math.vec3_zero(), math.vec3(20.0, 0.0, 0.0), filter, count_and_clip, context)
physics_world.overlap_aabb(world_id, AABB { lower: math.vec3(3.0, 0.0 - 1.0, 0.0 - 1.0), upper: math.vec3(11.5, 1.0, 1.0) }, filter, count_overlap, context)
physics_world.overlap_shape(world_id, math.vec3(10.0, 0.0, 0.0), point_proxy(math.vec3_zero(), 1.5), filter, count_overlap, context)
physics_world.cast_shape(world_id, math.vec3_zero(), point_proxy(math.vec3_zero(), 0.25), math.vec3(20.0, 0.0, 0.0), filter, count_and_clip, context)
physics_world.cast_mover(world_id, math.vec3_zero(), Capsule { center1: math.vec3_zero(), center2: math.vec3(0.0, 1.0, 0.0), radius: 0.25 }, math.vec3(10.0, 0.0, 0.0), filter, true, count_overlap, context)
physics_world.collide_mover(world_id, math.vec3(4.0, 0.0, 0.0), Capsule { center1: math.vec3(0.0, 0.0 - 0.5, 0.0), center2: math.vec3(0.0, 0.5, 0.0), radius: 0.3 }, filter, count_planes, context)
}

test_world_queries() {
wdef = dynamics.default_world_def()
world_id = dynamics.create_world(&wdef)
Expand Down Expand Up @@ -2097,6 +2110,11 @@ test_world_queries() {
counter[0] = 0
physics_world.overlap_shape(world_id, math.vec3(10.0, 5.0, 0.0), point_proxy(math.vec3_zero(), 1.5), filter, count_overlap, counter_block)
ensure("world overlap shape: none above", counter[0] == 0)
// A borrowed context (no @keep) through all six queries: the ray's 3,
// the box's 2, the proxy's 1 and the cast's 3, plus the mover's (#179).
counter[0] = 0
borrowed_queries(world_id, filter, counter_block)
ensure("world queries: a borrowed context reaches all six (${counter[0]})", counter[0] >= 9)
// A shape cast along the row: three hits when nothing clips.
counter[0] = 0
physics_world.cast_shape(world_id, math.vec3_zero(), point_proxy(math.vec3_zero(), 0.25), math.vec3(20.0, 0.0, 0.0), filter, count_and_clip, counter_block)
Expand Down
Loading