The per-frame sweep over physics, character, vehicles and nav found three fact makers that made a record per fact: contacts and splashes, the character's and the vehicles'. Each keeps a pool now, as wildlife's and npc's do (q_unheld). phys_sink, asked on every removal, built two new lists each time; it filters into a kept spare pair and swaps. ludic.nav's paths and crowds already allocate nothing, and none of the four builds a string per call. What remains at load, reset or a world swap is not per frame. vehicle_feed takes its state mut. vehicles_test: 1000 fed facts, drained turn by turn, use at most 4 records. All packages 421. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
99 lines
5 KiB
Text
99 lines
5 KiB
Text
# shapes.ludic - the world opened and closed, and shapes: made in C, kept here, named by an int.
|
|
# A shape may be used by any number of bodies; phys_close frees them all.
|
|
|
|
# a world for up to max_bodies (threads 0: the cores less one, at most four); false if C refused
|
|
export function phys_open(physics_st: mut PhysicsState, max_bodies: int, threads: int) -> bool {
|
|
phys_close(physics_st)
|
|
ph_buffers()
|
|
physics_st.ph_world = jph_world_new(max_bodies, threads)
|
|
physics_st.ph_gen += 1
|
|
physics_st.ph_acc = 0.0
|
|
physics_st.ph_steps = 0
|
|
return physics_st.ph_world != null
|
|
}
|
|
|
|
export function phys_is_open(physics_st: PhysicsState) -> bool { return physics_st.ph_world != null }
|
|
# which world this is: a body, shape or walker id kept from an older one means nothing now
|
|
export function phys_generation(physics_st: PhysicsState) -> int { return physics_st.ph_gen }
|
|
|
|
# everything let go: the world, every body in it and every shape (a world swap, a quit)
|
|
export function phys_close(physics_st: mut PhysicsState) -> void {
|
|
if physics_st.ph_walkers != null {
|
|
for i in 0 .. len(physics_st.ph_walkers) {
|
|
if physics_st.ph_walkers[i] != null { jph_char_free(physics_st.ph_walkers[i]) }
|
|
}
|
|
}
|
|
physics_st.ph_walkers = new []pointer
|
|
if physics_st.ph_world != null { jph_world_free(physics_st.ph_world) }
|
|
physics_st.ph_world = null
|
|
if physics_st.ph_shapes != null {
|
|
for i in 0 .. len(physics_st.ph_shapes) {
|
|
if physics_st.ph_shapes[i] != null { jph_shape_free(physics_st.ph_shapes[i]) }
|
|
}
|
|
}
|
|
physics_st.ph_shapes = new []pointer
|
|
List.clear(physics_st.ph_floats)
|
|
List.clear(physics_st.ph_float_k)
|
|
if physics_st.ph_facts != null { q_clear(physics_st.ph_facts) }
|
|
}
|
|
|
|
function ph_keep(physics_st: mut PhysicsState, s: pointer) -> int {
|
|
if s == null { return -1 }
|
|
ph_buffers()
|
|
push(physics_st.ph_shapes, s)
|
|
return len(physics_st.ph_shapes) - 1
|
|
}
|
|
|
|
function ph_shape(physics_st: PhysicsState, id: int) -> pointer {
|
|
if physics_st.ph_shapes == null or id < 0 or id >= len(physics_st.ph_shapes) { return null }
|
|
return physics_st.ph_shapes[id]
|
|
}
|
|
|
|
# a crate: half extents in metres
|
|
export function phys_box(physics_st: mut PhysicsState, hx: float, hy: float, hz: float) -> int { return ph_keep(physics_st, jph_shape_box(hx, hy, hz)) }
|
|
export function phys_sphere(physics_st: mut PhysicsState, r: float) -> int { return ph_keep(physics_st, jph_shape_sphere(r)) }
|
|
# a trunk or a body: upright, its straight part 2 * half_h tall, centred on its origin
|
|
export function phys_capsule(physics_st: mut PhysicsState, half_h: float, r: float) -> int { return ph_keep(physics_st, jph_shape_capsule(half_h, r)) }
|
|
# a post: upright, 2 * half_h tall, centred on its origin
|
|
export function phys_cylinder(physics_st: mut PhysicsState, half_h: float, r: float) -> int { return ph_keep(physics_st, jph_shape_cylinder(half_h, r)) }
|
|
# a boulder: a dome r across and h tall, standing on its origin
|
|
export function phys_dome(physics_st: mut PhysicsState, r: float, h: float) -> int { return ph_keep(physics_st, jph_shape_dome(r, h)) }
|
|
|
|
# a rock: the convex hull of n points (x, y, z) about its origin, its edges rounded by 2 cm
|
|
export function phys_convex(physics_st: mut PhysicsState, v: []float, n: int) -> int {
|
|
if n < 4 or len(v) < n * 3 { return -1 }
|
|
return ph_keep(physics_st, jph_shape_convex(v, n, 0.02))
|
|
}
|
|
# another shape k times its size, so one hull serves every boulder of its shape
|
|
export function phys_scaled(physics_st: mut PhysicsState, shape: int, k: float) -> int {
|
|
let s = ph_shape(physics_st, shape)
|
|
if s == null { return -1 }
|
|
return ph_keep(physics_st, jph_shape_scaled(s, k))
|
|
}
|
|
|
|
# another shape moved and turned about y inside its body (a post whose foot is its origin)
|
|
export function phys_offset(physics_st: mut PhysicsState, shape: int, x: float, y: float, z: float, yaw: float) -> int {
|
|
let s = ph_shape(physics_st, shape)
|
|
if s == null { return -1 }
|
|
return ph_keep(physics_st, jph_shape_offset(s, x, y, z, yaw))
|
|
}
|
|
|
|
# n x n heights, row by row along z, the first at (ox, oz), cell metres apart; PHYS_HOLE is none
|
|
export const PHYS_HOLE: float = 340282346638528859811704183484516925440.0
|
|
export function phys_heightfield(physics_st: mut PhysicsState, h: []float, n: int, ox: float, oz: float, cell: float) -> int {
|
|
if len(h) < n * n { return -1 }
|
|
return ph_keep(physics_st, jph_shape_heightfield(h, n, ox, oz, cell))
|
|
}
|
|
|
|
# the ground as a renderer draws a cubic B-spline height map: tex is n x n texels, the first
|
|
# texel's centre at (ox, oz), cell metres apart; the smoothing is done in C
|
|
export function phys_heightfield_smooth(physics_st: mut PhysicsState, tex: []float, n: int, ox: float, oz: float, cell: float) -> int {
|
|
if len(tex) < n * n { return -1 }
|
|
return ph_keep(physics_st, jph_shape_heightfield_bspline(tex, n, ox, oz, cell))
|
|
}
|
|
|
|
# a dock or a cabin: v holds nv points (x, y, z), tri holds nt triangles (i, j, k)
|
|
export function phys_mesh(physics_st: mut PhysicsState, v: []float, nv: int, tri: []int, nt: int) -> int {
|
|
if len(v) < nv * 3 or len(tri) < nt * 3 { return -1 }
|
|
return ph_keep(physics_st, jph_shape_mesh(v, nv, tri, nt))
|
|
}
|