Builtin Library
- ..
- .metadata202 B
- README.md5.1 KB
- init.luau35.4 KB
- pack.module
README.md
sim.particles (procNode)
The particle solver: every particle from every source wired in, held on the GPU and stepped over the graph's timeline,
born, pushed by the forces, moved, bounced off the colliders and aged until it dies. It is built on the runtime's
g:state and g:step and steps at a fixed 1/60 s, so the result at a frame depends on the frames cooked before it in
the same node cache (a Generator's play, or proc.eval called frame after frame with one nodeCache).
Every particle from every source lives in one set of device columns at the solver's capacity, a slot per particle for
its life. At each step the free slots are numbered and handed to the sources in order, and a free slot whose number a
source owns is born from it with draws keyed by the source's seed and its birth serial; the forces are fields
evaluated where each particle stands; velocity takes the forces, gravity and drag, then position moves; the move
is met against the shape table of the colliders (which starts with one plane too far below to be reached, so it is
never empty), and a hit pushes the particle out with bounce and friction; age and life advance, and a particle whose
lifetime is up dies. A source whose points move between presents births along the path they travelled, by perMeter
for the distance and at each step's place in the present (sim.spawn).
Inputs
sources(List<Record>): the sim.spawn records particles are born from, all in one solver. Each names thestreamits particles carry.forces(List<Record>, default empty): the sim.force records that push the particles; they sum.colliders(List<Record>, default empty): the sim.collider and sim.scene_colliders records the particles bounce off.capacity(Float, default 10000, min 1): the most particles alive at once. A birth past it is dropped.gravity(Vec3, default (0, 0, 0)): an acceleration every particle takes beside the forces, in m/s^2.drag(Float, default 0, min 0): the share of its velocity a particle loses per second.seed(Float, default 0): which of the possible sequences of births every source makes.trails(Bool, default false): on, each particle remembers where it has been, and thetrailsoutput holds a curve through its lasttrailLengthpositions; off, nothing is remembered andtrailsis empty.trailLength(Float, default 16, 2 to 512): how many positions each particle remembers whiletrailsis on, one for each fixed step, newest first.
Outputs
A call answers a record by output name: g.sim.particles({ ... }).points.
points(Points): the particles alive now, carryingP,v,age,life(0 at birth to 1 at death),lifetime,seed(a draw in 0..1 for each particle),size,id,stream(the index of the particle's source insources; the detail'slabels.streamlists the sources' stream names in that order) andslot, the place it holds in the solver for its life.births(Points): the particles born in the present, at the place and velocity they were born with, carrying the same attributes aspoints.deaths(Points): the particles that died in the present, as they stood when they died.hits(Points): the particles that hit a collider in the present, carryingP,v(the velocity going into the hit),N(the surface normal),collider(the place of the collider record incolliders),id,streamandslot.trails(Curves): withtrailson, one open curve per particle alive, through its remembered positions newest first; its points carryP,trail(0 at the newest to 1 at the oldest),particle(the particle'sid),size,life,seedandstream. geometry.ribbon lays a ribbon along it.
The events are kept for each slot over a present: a slot is born and dies at most once in a present and a dead slot is held until the next one, so births and deaths are one row for each particle, and a particle that bounces more than once in a present is one hit, the last. All four point outputs are held at the solver's capacity, with how many are live counted on the device.
Stamped attributes
Every number or vector point attribute a source's points carry, other than the particle's own (P, N, id, v, age, life, lifetime, seed, size, stream, alive, slot), is a state column of the solver (stamped_<name>, typed as the first source carrying it holds it), taken at birth from the point the particle is born at and held for its life; a source whose points do not carry it gives zero. The names are read from the sources while the graph is planned. points, births and deaths carry each one under its own name.
Example
local source = g.sim.spawn({ points = g.points.grid({ countX = 1, countZ = 1, y = 1 }), rate = 60,
velocity = vector.create(0, 3, 0), jitter = 1 })
local floor = g.sim.collider({ shape = "plane" })
local sim = g.sim.particles({ sources = { source }, colliders = { floor }, gravity = vector.create(0, -9.8, 0),
capacity = 2000 })
g:output("particles", sim.points)