ludic/packages/ludic.physics/queries.ludic
Orkuncakilkaya f048ffb6ac ludic.physics: queries fill a record the caller keeps, and allocate nothing
phys_pose, phys_ray, phys_push and phys_walker_pose each made a new record per call, some several times a frame, and Ludic never gives one back. They now fill the caller's PhysPose / PhysHit / PhysPush / PhysWalk and return whether they found anything. phys_overlap returns a count; phys_overlap_id(i) reads each id back. The package's own uses (phys_resolve, phys_row, phys_walker_move) read the values buffer directly. Tests take small allocating helpers. physics 20, character 15, vehicles 8.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-27 23:18:26 +03:00

99 lines
4.4 KiB
Text

# queries.ludic - what is there, asked of the world as it stood after the last step. Each takes a
# mask of what it sees (PHYS_M_*), so a body's own questions can leave out the ground or itself.
export property PhysHit {
body: int = -1 # -1: nothing
fraction: float = 1.0 # how far along the ray, 0 to 1
x: float = 0.0
y: float = 0.0
z: float = 0.0
nx: float = 0.0
ny: float = 1.0
nz: float = 0.0
}
# a ray from (ox, oy, oz) along (dx, dy, dz), whose length is the reach, into the caller's record;
# true when it hit something (h.body -1 and h.fraction 1 when not)
export function phys_ray(physics_st: mut PhysicsState, ox: float, oy: float, oz: float, dx: float, dy: float, dz: float, mask: int, h: PhysHit) -> bool {
h.body = -1
h.fraction = 1.0
if physics_st.ph_world == null { return false }
h.body = jph_ray(physics_st.ph_world, ox, oy, oz, dx, dy, dz, mask, physics_st.ph_vals)
if h.body == -1 { return false }
let v = physics_st.ph_vals
h.fraction = v[0]
h.x = v[1]; h.y = v[2]; h.z = v[3]
h.nx = v[4]; h.ny = v[5]; h.nz = v[6]
return true
}
# a ball r across let down from from_y at most depth metres: the height of what it lands on, or
# PHYS_NONE - what CharacterGround.top_at asks, with the ground left out of the mask
export function phys_top_at(physics_st: PhysicsState, x: float, z: float, r: float, from_y: float, depth: float, mask: int) -> float {
if physics_st.ph_world == null { return PHYS_NONE }
let y = jph_top_at(physics_st.ph_world, x, z, r, from_y, depth, mask)
if y < PHYS_NONE { return PHYS_NONE }
return y
}
# the highest top under an upright body r wide that a foot at `feet` could stand on or step up
# to, or PHYS_NONE: anything whose top is above feet + reach is a wall and left out - what
# CharacterGround.top_at asks when a boulder may be climbed and a cliff may not
export function phys_step_top(physics_st: PhysicsState, x: float, z: float, r: float, feet: float, reach: float, mask: int) -> float {
if physics_st.ph_world == null { return PHYS_NONE }
let y = jph_step_top(physics_st.ph_world, x, z, r, feet, reach, mask)
if y < PHYS_NONE { return PHYS_NONE }
return y
}
# how many bodies a ball at (x, y, z) touches, in the order Jolt sorts them; phys_overlap_id(i)
# reads them back until the next query
export function phys_overlap(physics_st: mut PhysicsState, x: float, y: float, z: float, r: float, mask: int) -> int {
if physics_st.ph_world == null { return 0 }
return jph_overlap(physics_st.ph_world, x, y, z, r, mask, physics_st.ph_ids, PH_CAP)
}
export function phys_overlap_id(physics_st: PhysicsState, i: int) -> int { return physics_st.ph_ids[i] }
export property PhysPush {
x: float = 0.0 # how far to move in x and z to stand clear
z: float = 0.0
n: int = 0 # how many things were in the way
}
# an upright body r wide from y0 to y1 at (x, z): the move that stands it clear - what
# CharacterGround.push asks
export function phys_push(physics_st: mut PhysicsState, x: float, z: float, r: float, y0: float, y1: float, mask: int, p: PhysPush) -> int {
p.n = ph_push(physics_st, x, z, r, y0, y1, mask)
p.x = physics_st.ph_vals[0]
p.z = physics_st.ph_vals[1]
return p.n
}
# how many things are in the way, the move clear in ph_vals[0], [1]
function ph_push(physics_st: mut PhysicsState, x: float, z: float, r: float, y0: float, y1: float, mask: int) -> int {
physics_st.ph_vals[0] = 0.0
physics_st.ph_vals[1] = 0.0
if physics_st.ph_world == null { return 0 }
return jph_push(physics_st.ph_world, x, z, r, y0, y1, mask, physics_st.ph_vals)
}
# a body r wide from y0 to y1 at (x, z) moved clear of what it stands in, two passes over (a push out
# of one thing can put it into another); true when it moved, the place in phys_resolved_x / _z -
# what CharacterGround.push and pushed_x / _z ask. A thing wholly below y0 or above y1 is not in
# the way
export function phys_resolve(physics_st: mut PhysicsState, x: float, z: float, r: float, y0: float, y1: float, mask: int) -> bool {
var px = x
var pz = z
var moved = false
for pass in 0 .. 2 {
if ph_push(physics_st, px, pz, r, y0, y1, mask) > 0 {
px += physics_st.ph_vals[0]
pz += physics_st.ph_vals[1]
moved = true
}
}
physics_st.ph_rx = px
physics_st.ph_rz = pz
return moved
}
export function phys_resolved_x(physics_st: PhysicsState) -> float { return physics_st.ph_rx }
export function phys_resolved_z(physics_st: PhysicsState) -> float { return physics_st.ph_rz }