Builtin Library
README.md
core
Core boid simulation primitives — the spatial grid for O(1) neighbour
queries, a struct-of-arrays boid storage container, and the per-boid
force / integration math used by the outer boids.module flock loop.
Exports
Core.SpatialGrid.new(cellSize: number) -> SpatialGridInstance— construct a grid.SpatialGrid:clear()— wipe every cell.SpatialGrid:insert(index, x, y, z)— drop a boid index into the cell containing(x, y, z).SpatialGrid:query(px, py, pz, radius, positions, results) -> number— fillresultswith neighbour indices and return the count.Core.BoidData.new(capacity: number) -> BoidDataInstance— construct an empty struct-of-arrays container.BoidData:add(entityId, px, py, pz, vx, vy, vz) -> number— append a boid and return its index.BoidData:remove(index)— mark a boid dead.Core.calculateFlockForces(boidIdx, data, neighbors, neighborCount, sepW, aliW, cohW, sepDist, viewAngleCos) -> (number, number, number)— combined separation + alignment + cohesion force.Core.integrate(data, index, fx, fy, fz, dt, minSpeed, maxSpeed, maxForce)— clamp, integrate, mutatedatain place.Core.velocityToQuaternion(vx, vy, vz) -> (number, number, number, number)— yaw/pitch quaternion from velocity direction.Core.v3len,Core.v3dist,Core.v3normalize,Core.v3clampMag— scalar-form vec3 helpers.
Types:
Vec3Triple = { number }— vec3 as an{ x, y, z }array.SpatialGridInstance—{ cellSize, cells, _invCell, clear, insert, query }.BoidDataInstance—{ count, positions, velocities, entityIds, alive, add, remove }.
Usage
local Core = require("@builtin::modules.boids.core")
local grid = Core.SpatialGrid.new(5.0)
local data = Core.BoidData.new(50)
local i = data:add(eid, px, py, pz, vx, vy, vz)
grid:clear()
grid:insert(i, px, py, pz)
local neighbors = {}
local n = grid:query(px, py, pz, 5.0, data.positions, neighbors)
local fx, fy, fz = Core.calculateFlockForces(i, data, neighbors, n, 1.5, 1.0, 1.0, 2.0, viewAngleCos)
Core.integrate(data, i, fx, fy, fz, dt, 1.5, 4.0, 8.0)
local qx, qy, qz, qw = Core.velocityToQuaternion(vx, vy, vz)
Notes
- All math is scalar-form (no allocated
vec3tables in the hot path); intermediate positions and velocities live in pre-allocated{ x, y, z }arrays inside theBoidDatacontainer. SpatialGrid:queryoverwritesresultsstarting at index 1 and returns the count, so callers can recycle the same buffer between frames without per-call allocation. Entries beyond the returned count are stale and must not be read.Core.integratemutates the suppliedBoidDataentry in place.BoidData:removedoes not compact the underlying arrays — dead slots stay in place and are skipped on iteration viaalive[i].