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>
This commit is contained in:
parent
b6c4d703a2
commit
f048ffb6ac
11 changed files with 140 additions and 87 deletions
|
|
@ -7,6 +7,11 @@ import "ludic.base"
|
|||
import "ludic.physics"
|
||||
program CharacterTest {
|
||||
numbers float
|
||||
function wpose(physics_st: mut PhysicsState, id: int) -> PhysWalk {
|
||||
let p = new PhysWalk
|
||||
if not phys_walker_pose(physics_st, id, p) { return null }
|
||||
return p
|
||||
}
|
||||
|
||||
# the world: x in 4..8 is a deck 45 cm up; z < -10 a cliff; z > 10 a gentle rise; x > 20 the lake
|
||||
function fk_height(x: float, z: float) -> float {
|
||||
|
|
@ -87,7 +92,7 @@ program CharacterTest {
|
|||
function fk_walk(physics_st: mut PhysicsState, character_test_st: mut CharacterTestState, x: float, y: float, z: float, vx: float, vz: float, jump: float, step: float, slope: float, dt: float) -> int {
|
||||
if character_test_st.walker < 0 { character_test_st.walker = fk_jolt(physics_st) }
|
||||
let g = phys_walker_move(physics_st, character_test_st.walker, x, y, z, vx, vz, jump, step, slope, dt)
|
||||
let p = phys_walker_pose(physics_st, character_test_st.walker)
|
||||
let p = wpose(physics_st, character_test_st.walker)
|
||||
character_test_st.wx = p.x
|
||||
character_test_st.wy = p.y
|
||||
character_test_st.wz = p.z
|
||||
|
|
|
|||
|
|
@ -26,6 +26,9 @@ import "ludic.physics"
|
|||
|
||||
## The API
|
||||
|
||||
A query fills a record the caller keeps (`p`, `h`) and allocates nothing: it is asked every frame, and
|
||||
Ludic gives no allocation back.
|
||||
|
||||
| | |
|
||||
| --- | --- |
|
||||
| `phys_open(st, max_bodies, threads) -> bool`, `phys_close(st)`, `phys_is_open(st)` | a world, and everything in it let go |
|
||||
|
|
@ -34,15 +37,15 @@ import "ludic.physics"
|
|||
| `phys_ground_add`, `phys_static_add`, `phys_kinematic_add`, `phys_body_add(..., mass)` | bodies: the ground, still things, what the game moves (the player), what falls and floats |
|
||||
| `phys_settle(st)` | after adding many still things, before the first query |
|
||||
| `phys_remove(st, id)`, `phys_count(st)` | gone for good |
|
||||
| `phys_pose(st, id) -> PhysPose`, `phys_yaw_of(p)` | position, rotation, velocity |
|
||||
| `phys_pose(st, id, p) -> bool`, `phys_yaw_of(p)` | position, rotation, velocity |
|
||||
| `phys_place`, `phys_impulse`, `phys_set_velocity`, `phys_set_kinematic_target`, `phys_awake` | moving a body |
|
||||
| `phys_ray(st, o, d, mask) -> PhysHit` | the first thing along a ray |
|
||||
| `phys_ray(st, o, d, mask, h) -> bool` | the first thing along a ray |
|
||||
| `phys_top_at(st, x, z, r, from_y, depth, mask) -> float` | what a foot would land on (`CharacterGround.top_at`), `PHYS_NONE` if nothing |
|
||||
| `phys_push(st, x, z, r, y0, y1, mask) -> PhysPush` | the move that stands an upright body clear (`CharacterGround.push`) |
|
||||
| `phys_push(st, x, z, r, y0, y1, mask, p) -> int` | the move that stands an upright body clear (`CharacterGround.push`) |
|
||||
| `phys_resolve(st, x, z, r, y0, y1, mask) -> bool`, `phys_resolved_x` / `_z` | pushed clear in two passes, the place kept (`CharacterGround.push`, `pushed_x` / `_z`); a thing wholly under the feet or over the head is not in the way |
|
||||
| `phys_step_top(st, x, z, r, feet, reach, mask) -> float` | the highest top a foot could step up to, anything taller than `feet + reach` left out as a wall |
|
||||
| `phys_overlap(st, x, y, z, r, mask) -> []int` | the bodies a ball touches |
|
||||
| `phys_walker_add(st, r, h, x, y, z, max_slope, mass)`, `phys_walker_step(st, id, dt, vx, vz, jump) -> ground`, `phys_walker_pose -> PhysWalk`, `phys_walker_place`, `phys_walker_limits(step_up, stick)`, `phys_walker_remove` | a walking body (Jolt's CharacterVirtual): gravity, landing, steps, slopes, following the ground down; it shoves dynamic things and rays find its `body` |
|
||||
| `phys_overlap(st, x, y, z, r, mask) -> int`, `phys_overlap_id(st, i)` | how many bodies a ball touches, and each one |
|
||||
| `phys_walker_add(st, r, h, x, y, z, max_slope, mass)`, `phys_walker_step(st, id, dt, vx, vz, jump) -> ground`, `phys_walker_pose(st, id, p) -> bool`, `phys_walker_place`, `phys_walker_limits(step_up, stick)`, `phys_walker_remove` | a walking body (Jolt's CharacterVirtual): gravity, landing, steps, slopes, following the ground down; it shoves dynamic things and rays find its `body` |
|
||||
| `phys_force`, `phys_torque`, `phys_angular_impulse`, `phys_spin_y`, `phys_set_pose(st, id, pose)` | an oar's stroke and a turn; a guest's copy put where the host said |
|
||||
| `phys_float(st, id, buoyancy, linear_drag, angular_drag)`, `phys_sink`, `phys_floating` | a body buoyed each step against `PhysWater` and carried by its current |
|
||||
| `phys_facts(st)` | `PhysFact`: `PHYS_HIT` (two bodies met at `ph_hard` m/s or more), `PHYS_SPLASH` (a floating body reached water) |
|
||||
|
|
|
|||
|
|
@ -43,17 +43,16 @@ export function phys_remove(physics_st: mut PhysicsState, id: int) -> void {
|
|||
jph_body_remove(physics_st.ph_world, id)
|
||||
}
|
||||
|
||||
# where a body is, how it is turned and how fast it goes; null when there is no such body
|
||||
export function phys_pose(physics_st: mut PhysicsState, id: int) -> PhysPose {
|
||||
if physics_st.ph_world == null { return null }
|
||||
ph_buffers()
|
||||
if jph_body_read(physics_st.ph_world, id, physics_st.ph_vals) != 0 { return null }
|
||||
# where a body is, how it is turned and how fast it goes, into the caller's own record (a query asked
|
||||
# every frame allocates nothing: there is no GC); false, and p untouched, when there is no such body
|
||||
export function phys_pose(physics_st: mut PhysicsState, id: int, p: PhysPose) -> bool {
|
||||
if physics_st.ph_world == null { return false }
|
||||
if jph_body_read(physics_st.ph_world, id, physics_st.ph_vals) != 0 { return false }
|
||||
let v = physics_st.ph_vals
|
||||
let p = new PhysPose
|
||||
p.x = v[0]; p.y = v[1]; p.z = v[2]
|
||||
p.qx = v[3]; p.qy = v[4]; p.qz = v[5]; p.qw = v[6]
|
||||
p.vx = v[7]; p.vy = v[8]; p.vz = v[9]
|
||||
return p
|
||||
return true
|
||||
}
|
||||
|
||||
# the heading a pose's rotation gives, in radians about y
|
||||
|
|
|
|||
|
|
@ -12,9 +12,9 @@ export function phys_hull_add(physics_st: mut PhysicsState, x: float, y: float,
|
|||
# a stroke of `forward` newtons along the bow and a turn of `turn` newton-metres about y, over the
|
||||
# next step
|
||||
export function phys_row(physics_st: mut PhysicsState, id: int, forward: float, turn: float) -> void {
|
||||
let p = phys_pose(physics_st, id)
|
||||
if p == null { return }
|
||||
let yaw = phys_yaw_of(p)
|
||||
if physics_st.ph_world == null or jph_body_read(physics_st.ph_world, id, physics_st.ph_vals) != 0 { return }
|
||||
let v = physics_st.ph_vals
|
||||
let yaw = Math.atan2(2.0 * (v[6] * v[4] + v[3] * v[5]), 1.0 - 2.0 * (v[4] * v[4] + v[3] * v[3]))
|
||||
phys_force(physics_st, id, -Math.sin(yaw) * forward, 0.0, -Math.cos(yaw) * forward)
|
||||
phys_torque(physics_st, id, 0.0, turn, 0.0)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,18 +12,19 @@ export property PhysHit {
|
|||
nz: float = 0.0
|
||||
}
|
||||
|
||||
# a ray from (ox, oy, oz) along (dx, dy, dz), whose length is the reach
|
||||
export function phys_ray(physics_st: mut PhysicsState, ox: float, oy: float, oz: float, dx: float, dy: float, dz: float, mask: int) -> PhysHit {
|
||||
let h = new PhysHit
|
||||
if physics_st.ph_world == null { return h }
|
||||
ph_buffers()
|
||||
# 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 h }
|
||||
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 h
|
||||
return true
|
||||
}
|
||||
|
||||
# a ball r across let down from from_y at most depth metres: the height of what it lands on, or
|
||||
|
|
@ -45,15 +46,13 @@ export function phys_step_top(physics_st: PhysicsState, x: float, z: float, r: f
|
|||
return y
|
||||
}
|
||||
|
||||
# the bodies a ball at (x, y, z) touches, in the order Jolt sorts them
|
||||
export function phys_overlap(physics_st: mut PhysicsState, x: float, y: float, z: float, r: float, mask: int) -> []int {
|
||||
let out = new []int
|
||||
if physics_st.ph_world == null { return out }
|
||||
ph_buffers()
|
||||
let n = jph_overlap(physics_st.ph_world, x, y, z, r, mask, physics_st.ph_ids, PH_CAP)
|
||||
for i in 0 .. n { push(out, physics_st.ph_ids[i]) }
|
||||
return out
|
||||
# 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
|
||||
|
|
@ -63,14 +62,18 @@ export property PhysPush {
|
|||
|
||||
# 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) -> PhysPush {
|
||||
let p = new PhysPush
|
||||
if physics_st.ph_world == null { return p }
|
||||
ph_buffers()
|
||||
p.n = jph_push(physics_st.ph_world, x, z, r, y0, y1, mask, physics_st.ph_vals)
|
||||
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
|
||||
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
|
||||
|
|
@ -82,10 +85,9 @@ export function phys_resolve(physics_st: mut PhysicsState, x: float, z: float, r
|
|||
var pz = z
|
||||
var moved = false
|
||||
for pass in 0 .. 2 {
|
||||
let p = phys_push(physics_st, px, pz, r, y0, y1, mask)
|
||||
if p.n > 0 {
|
||||
px += p.x
|
||||
pz += p.z
|
||||
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
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,6 +4,11 @@ import "ludic.physics"
|
|||
import "ludic.base"
|
||||
program HullShapeTest {
|
||||
numbers float
|
||||
function ray(physics_st: mut PhysicsState, ox: float, oy: float, oz: float, dx: float, dy: float, dz: float, mask: int) -> PhysHit {
|
||||
let h = new PhysHit
|
||||
phys_ray(physics_st, ox, oy, oz, dx, dy, dz, mask, h)
|
||||
return h
|
||||
}
|
||||
|
||||
# a cube two metres across, its centre two metres along x from its origin
|
||||
function cube(physics_st: mut PhysicsState) -> int {
|
||||
|
|
@ -15,7 +20,7 @@ program HullShapeTest {
|
|||
}
|
||||
return phys_convex(physics_st, v, 8)
|
||||
}
|
||||
function down(physics_st: mut PhysicsState, x: float, z: float) -> PhysHit { return phys_ray(physics_st, x, 10.0, z, 0.0, -20.0, 0.0, PHYS_M_ALL) }
|
||||
function down(physics_st: mut PhysicsState, x: float, z: float) -> PhysHit { return ray(physics_st, x, 10.0, z, 0.0, -20.0, 0.0, PHYS_M_ALL) }
|
||||
|
||||
test "a hull stands where its points are, and too few points are no shape" (physics_st: mut PhysicsState) {
|
||||
expect(phys_open(physics_st, 16, 1))
|
||||
|
|
|
|||
|
|
@ -5,6 +5,11 @@ import "ludic.physics"
|
|||
import "ludic.base"
|
||||
program MotionTest {
|
||||
numbers float
|
||||
function pose(physics_st: mut PhysicsState, id: int) -> PhysPose {
|
||||
let p = new PhysPose
|
||||
if not phys_pose(physics_st, id, p) { return null }
|
||||
return p
|
||||
}
|
||||
|
||||
function lake_h(x: float, z: float) -> float {
|
||||
if x > 20.0 { return 0.0 }
|
||||
|
|
@ -45,7 +50,7 @@ program MotionTest {
|
|||
let crate = phys_body_add(physics_st, phys_box(physics_st, 0.5, 0.25, 0.5), 30.0, 2.0, 0.0, 0.0, 60.0)
|
||||
phys_float(physics_st, crate, 1.2, 2.0, 2.0)
|
||||
sim(physics_st, 8.0)
|
||||
let p = phys_pose(physics_st, crate)
|
||||
let p = pose(physics_st, crate)
|
||||
expect(p.y > -0.4)
|
||||
expect(p.y < 0.4)
|
||||
expect(Math.abs(p.vy) < 0.2)
|
||||
|
|
@ -55,7 +60,7 @@ program MotionTest {
|
|||
phys_sink(physics_st, crate)
|
||||
expect(not phys_floating(physics_st, crate))
|
||||
sim(physics_st, 4.0)
|
||||
expect(phys_pose(physics_st, crate).y < -2.5)
|
||||
expect(pose(physics_st, crate).y < -2.5)
|
||||
phys_close(physics_st)
|
||||
}
|
||||
|
||||
|
|
@ -87,7 +92,7 @@ program MotionTest {
|
|||
phys_set_kinematic_target(physics_st, me, 2.0 + float(i + 1) * 0.03, 0.95, 0.0, 0.0)
|
||||
phys_step(physics_st)
|
||||
}
|
||||
expect(phys_pose(physics_st, crate).x > 5.0)
|
||||
expect(pose(physics_st, crate).x > 5.0)
|
||||
phys_close(physics_st)
|
||||
}
|
||||
|
||||
|
|
@ -114,7 +119,7 @@ program MotionTest {
|
|||
for i in 0 .. 12 { last = phys_body_add(physics_st, b, 3.0 + float(i % 3) * 0.3, 2.0 + float(i) * 0.7, float(i % 2) * 0.2, float(i) * 0.4, 8.0) }
|
||||
phys_impulse(physics_st, last, 20.0, 0.0, 5.0)
|
||||
sim(physics_st, 4.0)
|
||||
let p = phys_pose(physics_st, last)
|
||||
let p = pose(physics_st, last)
|
||||
phys_close(physics_st)
|
||||
return p
|
||||
}
|
||||
|
|
@ -132,21 +137,21 @@ program MotionTest {
|
|||
world(physics_st)
|
||||
let b = phys_hull_add(physics_st, 40.0, 0.5, 0.0, 1.5707963, 0.6, 0.25, 1.9, 180.0, 4.0, 1.0)
|
||||
sim(physics_st, 4.0)
|
||||
let p0 = phys_pose(physics_st, b)
|
||||
let p0 = pose(physics_st, b)
|
||||
expect(p0.y > -0.2)
|
||||
expect(p0.y < 0.2)
|
||||
for i in 0 .. 240 {
|
||||
phys_row(physics_st, b, 420.0, 0.0)
|
||||
phys_step(physics_st)
|
||||
}
|
||||
let p1 = phys_pose(physics_st, b)
|
||||
let p1 = pose(physics_st, b)
|
||||
expect(p1.x < 34.0)
|
||||
expect(phys_bow_speed(p1) > 1.0)
|
||||
for i in 0 .. 120 {
|
||||
phys_row(physics_st, b, 0.0, 520.0)
|
||||
phys_step(physics_st)
|
||||
}
|
||||
expect(Math.abs(phys_yaw_of(phys_pose(physics_st, b)) - phys_yaw_of(p1)) > 0.5)
|
||||
expect(Math.abs(phys_yaw_of(pose(physics_st, b)) - phys_yaw_of(p1)) > 0.5)
|
||||
phys_close(physics_st)
|
||||
world(physics_st)
|
||||
let c = phys_hull_add(physics_st, 24.0, 0.5, 0.0, 1.5707963, 0.6, 0.25, 1.9, 180.0, 4.0, 1.0)
|
||||
|
|
@ -154,7 +159,7 @@ program MotionTest {
|
|||
phys_row(physics_st, c, 420.0, 0.0)
|
||||
phys_step(physics_st)
|
||||
}
|
||||
expect(phys_pose(physics_st, c).x > 19.5)
|
||||
expect(pose(physics_st, c).x > 19.5)
|
||||
phys_close(physics_st)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,6 +5,21 @@ import "ludic.physics"
|
|||
import "ludic.base"
|
||||
program PhysicsTest {
|
||||
numbers float
|
||||
function pose(physics_st: mut PhysicsState, id: int) -> PhysPose {
|
||||
let p = new PhysPose
|
||||
if not phys_pose(physics_st, id, p) { return null }
|
||||
return p
|
||||
}
|
||||
function ray(physics_st: mut PhysicsState, ox: float, oy: float, oz: float, dx: float, dy: float, dz: float, mask: int) -> PhysHit {
|
||||
let h = new PhysHit
|
||||
phys_ray(physics_st, ox, oy, oz, dx, dy, dz, mask, h)
|
||||
return h
|
||||
}
|
||||
function push_of(physics_st: mut PhysicsState, x: float, z: float, r: float, y0: float, y1: float, mask: int) -> PhysPush {
|
||||
let p = new PhysPush
|
||||
phys_push(physics_st, x, z, r, y0, y1, mask, p)
|
||||
return p
|
||||
}
|
||||
|
||||
# the lake: everything east of x = 20, its surface at y = 0, drifting north at 0.5 m/s
|
||||
function lake_h(x: float, z: float) -> float {
|
||||
|
|
@ -38,15 +53,15 @@ program PhysicsTest {
|
|||
let id = phys_body_add(physics_st, b, 0.0, 4.0, 0.0, 0.0, 20.0)
|
||||
expect(id != -1)
|
||||
sim(physics_st, 3.0)
|
||||
let p = phys_pose(physics_st, id)
|
||||
let p = pose(physics_st, id)
|
||||
expect(p != null)
|
||||
expect(Math.abs(p.y - 0.5) < 0.05)
|
||||
expect(Math.abs(p.vy) < 0.05)
|
||||
let h = phys_ray(physics_st, 0.0, 10.0, 0.0, 0.0, -20.0, 0.0, PHYS_M_ALL)
|
||||
let h = ray(physics_st, 0.0, 10.0, 0.0, 0.0, -20.0, 0.0, PHYS_M_ALL)
|
||||
expect_eq(h.body, id)
|
||||
expect(Math.abs(h.y - 1.0) < 0.05)
|
||||
expect(h.ny > 0.9)
|
||||
let g = phys_ray(physics_st, 5.0, 10.0, 5.0, 0.0, -20.0, 0.0, PHYS_M_ALL)
|
||||
let g = ray(physics_st, 5.0, 10.0, 5.0, 0.0, -20.0, 0.0, PHYS_M_ALL)
|
||||
expect(g.body != -1)
|
||||
expect(g.body != id)
|
||||
expect(Math.abs(g.y) < 0.01)
|
||||
|
|
@ -59,14 +74,13 @@ program PhysicsTest {
|
|||
let id = phys_static_add(physics_st, post, 2.0, 0.0, 2.0, 0.0)
|
||||
phys_settle(physics_st)
|
||||
let n = phys_count(physics_st)
|
||||
let near = phys_overlap(physics_st, 2.0, 1.0, 2.0, 0.5, PHYS_M_STATIC)
|
||||
expect_eq(len(near), 1)
|
||||
expect_eq(near[0], id)
|
||||
expect_eq(len(phys_overlap(physics_st, 2.0, 1.0, 2.0, 0.5, PHYS_M_GROUND)), 0)
|
||||
expect_eq(phys_overlap(physics_st, 2.0, 1.0, 2.0, 0.5, PHYS_M_STATIC), 1)
|
||||
expect_eq(phys_overlap_id(physics_st, 0), id)
|
||||
expect_eq(phys_overlap(physics_st, 2.0, 1.0, 2.0, 0.5, PHYS_M_GROUND), 0)
|
||||
phys_remove(physics_st, id)
|
||||
expect_eq(phys_count(physics_st), n - 1)
|
||||
expect_eq(len(phys_overlap(physics_st, 2.0, 1.0, 2.0, 0.5, PHYS_M_STATIC)), 0)
|
||||
expect(phys_pose(physics_st, id) == null)
|
||||
expect_eq(phys_overlap(physics_st, 2.0, 1.0, 2.0, 0.5, PHYS_M_STATIC), 0)
|
||||
expect(pose(physics_st, id) == null)
|
||||
phys_close(physics_st)
|
||||
}
|
||||
|
||||
|
|
@ -86,11 +100,11 @@ program PhysicsTest {
|
|||
let post = phys_offset(physics_st, phys_cylinder(physics_st, 1.0, 0.3), 0.0, 1.0, 0.0, 0.0)
|
||||
phys_static_add(physics_st, post, 0.0, 0.0, 0.0, 0.0)
|
||||
phys_settle(physics_st)
|
||||
let p = phys_push(physics_st, 0.5, 0.0, 0.4, 0.1, 1.8, PHYS_M_STATIC)
|
||||
let p = push_of(physics_st, 0.5, 0.0, 0.4, 0.1, 1.8, PHYS_M_STATIC)
|
||||
expect_eq(p.n, 1)
|
||||
expect(p.x > 0.1)
|
||||
expect(Math.abs(p.z) < 0.05)
|
||||
let clear = phys_push(physics_st, 3.0, 0.0, 0.4, 0.1, 1.8, PHYS_M_STATIC)
|
||||
let clear = push_of(physics_st, 3.0, 0.0, 0.4, 0.1, 1.8, PHYS_M_STATIC)
|
||||
expect_eq(clear.n, 0)
|
||||
phys_close(physics_st)
|
||||
}
|
||||
|
|
@ -141,8 +155,8 @@ program PhysicsTest {
|
|||
expect(g >= 0)
|
||||
phys_ground_add(physics_st, g)
|
||||
phys_settle(physics_st)
|
||||
let odd = phys_ray(physics_st, 5.5, 50.0, 10.5, 0.0, -100.0, 0.0, PHYS_M_GROUND)
|
||||
let even = phys_ray(physics_st, 6.5, 50.0, 10.5, 0.0, -100.0, 0.0, PHYS_M_GROUND)
|
||||
let odd = ray(physics_st, 5.5, 50.0, 10.5, 0.0, -100.0, 0.0, PHYS_M_GROUND)
|
||||
let even = ray(physics_st, 6.5, 50.0, 10.5, 0.0, -100.0, 0.0, PHYS_M_GROUND)
|
||||
expect(Math.abs(odd.y - 2.0) < 0.01)
|
||||
expect(Math.abs(even.y - 1.0) < 0.01)
|
||||
phys_close(physics_st)
|
||||
|
|
|
|||
|
|
@ -4,6 +4,16 @@ import "ludic.physics"
|
|||
import "ludic.base"
|
||||
program WalkerTest {
|
||||
numbers float
|
||||
function pose(physics_st: mut PhysicsState, id: int) -> PhysPose {
|
||||
let p = new PhysPose
|
||||
if not phys_pose(physics_st, id, p) { return null }
|
||||
return p
|
||||
}
|
||||
function wpose(physics_st: mut PhysicsState, id: int) -> PhysWalk {
|
||||
let p = new PhysWalk
|
||||
if not phys_walker_pose(physics_st, id, p) { return null }
|
||||
return p
|
||||
}
|
||||
|
||||
function flat(physics_st: mut PhysicsState) -> void {
|
||||
expect(phys_open(physics_st, 1024, 1))
|
||||
|
|
@ -20,11 +30,11 @@ program WalkerTest {
|
|||
flat(physics_st)
|
||||
let me = phys_walker_add(physics_st, 0.35, 1.8, 0.0, 1.0, 0.0, 50.0, 70.0)
|
||||
walk(physics_st, me, 1.0, 0.0, 0.0)
|
||||
let p = phys_walker_pose(physics_st, me)
|
||||
let p = wpose(physics_st, me)
|
||||
expect_eq(p.ground, PHYS_ON_GROUND)
|
||||
expect(Math.abs(p.y) < 0.05)
|
||||
walk(physics_st, me, 2.0, 3.0, 0.0)
|
||||
let q = phys_walker_pose(physics_st, me)
|
||||
let q = wpose(physics_st, me)
|
||||
expect(Math.abs(q.x - 6.0) < 0.2)
|
||||
expect(q.body >= 0)
|
||||
phys_close(physics_st)
|
||||
|
|
@ -40,12 +50,12 @@ program WalkerTest {
|
|||
let a = phys_walker_add(physics_st, 0.35, 1.8, 0.0, 0.0, 0.0, 50.0, 70.0)
|
||||
phys_walker_limits(physics_st, a, 0.55, 0.5, 50.0)
|
||||
walk(physics_st, a, 1.5, 2.0, 0.0)
|
||||
let pa = phys_walker_pose(physics_st, a)
|
||||
let pa = wpose(physics_st, a)
|
||||
expect(pa.x > 2.5)
|
||||
expect(Math.abs(pa.y - 0.3) < 0.05)
|
||||
let b = phys_walker_add(physics_st, 0.35, 1.8, 0.0, 0.0, 10.0, 50.0, 70.0)
|
||||
walk(physics_st, b, 2.0, 2.0, 0.0)
|
||||
let pb = phys_walker_pose(physics_st, b)
|
||||
let pb = wpose(physics_st, b)
|
||||
expect(pb.x < 1.7)
|
||||
expect(Math.abs(pb.y) < 0.05)
|
||||
phys_close(physics_st)
|
||||
|
|
@ -57,11 +67,11 @@ program WalkerTest {
|
|||
walk(physics_st, me, 0.5, 0.0, 0.0)
|
||||
phys_walker_step(physics_st, me, 1.0 / 60.0, 0.0, 0.0, 5.0)
|
||||
walk(physics_st, me, 0.25, 0.0, 0.0)
|
||||
let up = phys_walker_pose(physics_st, me)
|
||||
let up = wpose(physics_st, me)
|
||||
expect_eq(up.ground, PHYS_IN_AIR)
|
||||
expect(up.y > 0.5)
|
||||
walk(physics_st, me, 1.5, 0.0, 0.0)
|
||||
let down = phys_walker_pose(physics_st, me)
|
||||
let down = wpose(physics_st, me)
|
||||
expect_eq(down.ground, PHYS_ON_GROUND)
|
||||
expect(Math.abs(down.y) < 0.05)
|
||||
phys_close(physics_st)
|
||||
|
|
@ -75,12 +85,12 @@ program WalkerTest {
|
|||
phys_walker_step(physics_st, me, 1.0 / 60.0, 2.0, 0.0, 0.0)
|
||||
phys_step(physics_st)
|
||||
}
|
||||
expect(phys_pose(physics_st, crate).x > 2.5)
|
||||
expect(pose(physics_st, crate).x > 2.5)
|
||||
phys_walker_place(physics_st, me, -10.0, 0.0, -10.0)
|
||||
let p = phys_walker_pose(physics_st, me)
|
||||
let p = wpose(physics_st, me)
|
||||
expect(Math.abs(p.x + 10.0) < 0.01)
|
||||
phys_walker_remove(physics_st, me)
|
||||
expect(phys_walker_pose(physics_st, me) == null)
|
||||
expect(wpose(physics_st, me) == null)
|
||||
phys_close(physics_st)
|
||||
}
|
||||
|
||||
|
|
@ -94,11 +104,11 @@ program WalkerTest {
|
|||
phys_step(physics_st)
|
||||
}
|
||||
expect(phys_spin_y(physics_st, hull) > 0.1)
|
||||
expect(phys_pose(physics_st, hull).z > 0.5)
|
||||
let p = phys_pose(physics_st, hull)
|
||||
expect(pose(physics_st, hull).z > 0.5)
|
||||
let p = pose(physics_st, hull)
|
||||
p.x = 5.0
|
||||
phys_set_pose(physics_st, hull, p)
|
||||
expect(Math.abs(phys_pose(physics_st, hull).x - 5.0) < 0.01)
|
||||
expect(Math.abs(pose(physics_st, hull).x - 5.0) < 0.01)
|
||||
phys_close(physics_st)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -53,19 +53,18 @@ export function phys_walker_step(physics_st: mut PhysicsState, id: int, dt: floa
|
|||
return jph_char_step(physics_st.ph_world, k, dt, vx, vz, jump)
|
||||
}
|
||||
|
||||
export function phys_walker_pose(physics_st: mut PhysicsState, id: int) -> PhysWalk {
|
||||
# where a walker is, into the caller's record; false when there is no such walker
|
||||
export function phys_walker_pose(physics_st: mut PhysicsState, id: int, p: PhysWalk) -> bool {
|
||||
let k = ph_walker(physics_st, id)
|
||||
if k == null { return null }
|
||||
ph_buffers()
|
||||
if k == null { return false }
|
||||
jph_char_read(k, physics_st.ph_vals)
|
||||
let v = physics_st.ph_vals
|
||||
let p = new PhysWalk
|
||||
p.x = v[0]; p.y = v[1]; p.z = v[2]
|
||||
p.vx = v[3]; p.vy = v[4]; p.vz = v[5]
|
||||
p.ground = int(v[6])
|
||||
p.ny = v[7]
|
||||
p.body = int(v[8])
|
||||
return p
|
||||
return true
|
||||
}
|
||||
|
||||
# put it somewhere, standing still: a teleport, a swap, getting off a boat
|
||||
|
|
@ -77,11 +76,12 @@ export function phys_walker_place(physics_st: mut PhysicsState, id: int, x: floa
|
|||
# a body the game keeps elsewhere, moved by its walker: put where the body is if something else
|
||||
# moved it (a teleport, a ride, a swim), then one step under the given limits; the ground after it
|
||||
export function phys_walker_move(physics_st: mut PhysicsState, id: int, x: float, y: float, z: float, vx: float, vz: float, jump: float, step: float, max_slope: float, dt: float) -> int {
|
||||
let p = phys_walker_pose(physics_st, id)
|
||||
if p == null { return PHYS_IN_AIR }
|
||||
let dx = p.x - x
|
||||
let dy = p.y - y
|
||||
let dz = p.z - z
|
||||
let k = ph_walker(physics_st, id)
|
||||
if k == null { return PHYS_IN_AIR }
|
||||
jph_char_read(k, physics_st.ph_vals)
|
||||
let dx = physics_st.ph_vals[0] - x
|
||||
let dy = physics_st.ph_vals[1] - y
|
||||
let dz = physics_st.ph_vals[2] - z
|
||||
if dx * dx + dy * dy + dz * dz > 0.0025 { phys_walker_place(physics_st, id, x, y, z) }
|
||||
phys_walker_limits(physics_st, id, step, 0.5, max_slope)
|
||||
return phys_walker_step(physics_st, id, dt, vx, vz, jump)
|
||||
|
|
|
|||
|
|
@ -8,6 +8,16 @@ import "ludic.base"
|
|||
import "ludic.physics"
|
||||
program VehiclesTest {
|
||||
numbers float
|
||||
function pose(physics_st: mut PhysicsState, id: int) -> PhysPose {
|
||||
let p = new PhysPose
|
||||
if not phys_pose(physics_st, id, p) { return null }
|
||||
return p
|
||||
}
|
||||
function wpose(physics_st: mut PhysicsState, id: int) -> PhysWalk {
|
||||
let p = new PhysWalk
|
||||
if not phys_walker_pose(physics_st, id, p) { return null }
|
||||
return p
|
||||
}
|
||||
|
||||
function fk_ground(x: float, z: float) -> float {
|
||||
if x > 10.0 { return -(x - 10.0) }
|
||||
|
|
@ -95,7 +105,7 @@ program VehiclesTest {
|
|||
fk_lake(physics_st)
|
||||
if vehicles_test_st.legs < 0 { vehicles_test_st.legs = phys_walker_add(physics_st, 0.8, 1.7, x, y, z, 40.0, 450.0) }
|
||||
phys_walker_move(physics_st, vehicles_test_st.legs, x, y, z, vx, vz, 0.0, 0.45, 40.0, dt)
|
||||
let p = phys_walker_pose(physics_st, vehicles_test_st.legs)
|
||||
let p = wpose(physics_st, vehicles_test_st.legs)
|
||||
vehicles_test_st.lx = p.x
|
||||
vehicles_test_st.ly = p.y
|
||||
vehicles_test_st.lz = p.z
|
||||
|
|
@ -120,7 +130,7 @@ program VehiclesTest {
|
|||
function fk_drift(physics_st: mut PhysicsState, vehicles_test_st: VehiclesTestState, nid: int, fx: float, fz: float) -> void { phys_force(physics_st, vehicles_test_st.hull, fx, 0.0, fz) }
|
||||
function fk_read(physics_st: mut PhysicsState, vehicles_test_st: mut VehiclesTestState, nid: int) -> bool {
|
||||
if vehicles_test_st.hull < 0 or vehicles_test_st.hull_nid != nid { return false }
|
||||
let p = phys_pose(physics_st, vehicles_test_st.hull)
|
||||
let p = pose(physics_st, vehicles_test_st.hull)
|
||||
if p == null { return false }
|
||||
vehicles_test_st.hx = p.x
|
||||
vehicles_test_st.hy = p.y
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue