Optionalcapacity: numberNumber of particle rows the arrays can hold.
Where each particle was when the current simulation step began.
The step is fixed while frames are not, so a frame is usually drawn some way past the last step. The renderer closes that gap by continuing the motion the last step produced — and the only way to know that motion for every kind of behavior, not just the ones that move a particle by its velocity, is to have kept where it started.
Size and colour at the start of the current step, for the same reason as
previousPosition. These are read off a curve at age / life
rather than integrated, and at the lifetimes an explosion uses — a fifth
of a second, a dozen steps in total — a whole step of fade lands on one
frame and none on the next.
Velocity at the start of the current step. A stretched billboard's shape is its velocity — the streak points along it and is as long as it — so a velocity that only changes on steps makes the sprite's outline pop at the step rate even while its position moves smoothly.
Empty until keepPreviousVelocity asks for it, because only that one render mode reads it and an unused column is not free: it sits between the columns that are hot and costs them their locality.
age, life and speedModifier interleaved, SCALAR_STRIDE per row.
Double precision, unlike the vector columns: age accumulates += delta
for the particle's whole life and is compared against life to decide
death, so rounding it to float32 shifts when particles die and makes
tightly balanced emitters flicker.
Static ReadonlyAGEOffsets within a scalars row.
Static ReadonlyLIFEStatic ReadonlySCALAR_Values per row in scalars.
Static ReadonlySPEED_Grows the arrays so count rows fit, preserving the existing contents.
Number of rows that must be addressable.
true when the arrays were replaced, meaning every particle
bound to this store has to be re-bound.
Starts keeping previousVelocity. Harmless to call every step; returns true on the call that allocated, which replaces the array and so leaves every particle's view of it pointing at the old one.
Column storage for the vector-valued particle attributes.
The three-component and four-component fields of a particle used to be six separate objects per particle, so walking a system meant chasing pointers across the heap. Here each attribute is one flat
Float32Arrayshared by every particle in a system, and a particle holds views into its own row. The numbers a simulation step touches are then contiguous and prefetchable, whileparticle.position.xkeeps working exactly as before.Scalar attributes (age, life, rotation, …) stay as plain properties on the particle: a monomorphic own property is already a single load, so moving them here would add an indirection rather than remove one.