On this page
tecs.gfx.particles
GPU particle effects, emitter playback, pool sizing, and rendering limits.
An entity represents an emitter. The GPU owns individual particles, so game code controls an effect and its emitter rather than inspecting particles one at a time.
local sparks <const> = tecs.gfx.particles.newEffect({
name = "game.sparks",
capacity = 256,
schedule = {rate = 120, duration = 0.3},
spawn = {shape = "disc", width = 4, outward = true},
initial = {
lifetime = {min = 0.2, max = 0.5},
speed = {min = 40, max = 120},
size = 3,
},
update = {
drag = 2.0,
size = tecs.gfx.particles.newCurve({
{0.0, 1.0},
{1.0, 0.0},
}),
},
})
world:addPlugin(tecs.gfx.particles.plugin({
renderer = app.renderer,
capacity = 4096,
}))
world:spawn(
tecs.Transform2D(120, 80, 0, 1, 0, 1, 1),
tecs.gfx.particles.ParticleEmitter({effect = sparks})
)An immutable Effect describes schedule, spawn, initial state, updates, and rendering. A ParticleEmitter names the effect and carries playback state. Effect names form a snapshot compatibility surface.
Emission and motion
Schedules combine rate, delay, duration, looping, and timed bursts. duration = 0 keeps a cycle open. An emission that finds no free effect slot either drops the new particle or replaces the oldest, according to overflow.
Spawn shapes include point, line, rectangle, rectangle edge, disc, ring, and cone. World-space particles detach from later emitter movement. Local-space particles follow the emitter for their full life. inheritVelocity applies only in world space.
Curves and gradients sample normalized age from zero to one. Numeric properties accept a constant or a {min, max} range.
Playback
play starts or resumes emission. stop resets the schedule while live particles drain. pause holds the schedule and live field. clear kills the field without changing emission. restart resets schedule and randomness. burst queues a count for the next step.
finished derives its answer from the schedule and the longest lifetime. estimatedCount integrates the schedule over the mean lifetime, and may differ after overflow or a truncated burst. Game code cannot inspect, move, kill, or count individual particles.
Pool and snapshots
The plugin fixes world capacity and maximum emitters at installation. Each emitter reserves its effect capacity. An emitter that cannot fit draws nothing and logs the refusal.
Snapshots store effect name, seed, configuration, and playback state. They do not store individual particles, so a restored emitter starts empty and refills.
Blending
render.blend selects "alpha", "additive", or "opaque", and defaults to "alpha". The first two draw in the forward pass over the composited image, depth tested and not written, so alpha means what it says: a gradient that ends transparent fades out, and "additive" adds light instead of covering, which is what fire, glow, and sparks want. "opaque" keeps the effect in the G-buffer.
A blended effect is not in the G-buffer, so it casts no shadow and no light's occluder mask sees it, and it is not shadowed by one either. It also draws after compositing, so all of one effect's particles composite as one group in pool-slot order rather than being sorted against each other: an effect names one layer and its particles take one depth within it. Sorting within a pool is a separate feature.
One blended emitter puts the frame's forward lane to work, which is five compute passes and a draw a world with nothing blended in it does not pay. A world of "opaque" effects pays none of it, which is the other reason that name exists.
Module contents
Constructors
| Constructor | Description |
|---|---|
newCurve |
Compiles keyframes into a curve over normalized age. |
newEffect |
Registers an immutable effect under options.name and shares it among every emitter using that name. |
newGradient |
Compiles keyframes into a color gradient over normalized age. |
Types
| Type | Kind | Description |
|---|---|---|
Color |
type | Represents a color accepted by particle authoring fields. |
Curve |
record | Stores a compiled scalar curve. |
Draining |
record | A slot range whose emitter is gone, held until its last particle can no longer be alive. |
Effect |
record | The handle newEffect returns and a ParticleEmitter names. |
EffectOptions |
record | Configures newEffect. |
EmitterOptions |
record | Configures a ParticleEmitter. |
EmitterState |
enum | The three values an emitter's state takes. |
Gradient |
record | Stores a compiled color gradient. |
Holding |
record | One emitter's place in the pool, as the pool remembers it. |
InitialOptions |
record | Configures a particle's initial properties. |
ParticleEmitter |
record | Controls an effect's playback state and per-instance scales. |
Pool |
record | Represents a world's installed particle pool. |
PoolOptions |
record | Configures plugin. |
Range |
record | The two-bound form of a Value. |
RenderOptions |
record | Configures how particles draw. |
ScheduleOptions |
record | Configures when particles emit. |
SpawnOptions |
record | Configures where particles spawn. |
UpdateOptions |
record | Configures how a particle changes over its normalized lifetime. |
Value |
type | Accepted wherever a property may vary per particle. |
Functions
| Function | Kind | Description |
|---|---|---|
find |
Static | Returns the effect registered under name, or nil when none exists. |
names |
Static | Returns every registered effect name in registration order. |
plugin |
Static | Installs the pool on a world. |
poolOf |
Static | Returns the pool installed on a world, or nil. |
reset |
Static | Forgets every registered effect, so a spec can register a set again. |
Values
| Value | Type | Description |
|---|---|---|
CURVE_SAMPLES |
integer |
Read-only. Reports how many samples one compiled curve or gradient holds. |
EFFECT |
{string : integer} |
Read-only. Reports each field's offset within an effect record. |
EFFECT_FLOATS |
integer |
Read-only. Reports how many floats each effect record contains. |
EMITTER |
{string : integer} |
Read-only. Reports each field's float offset within an emitter record, counting from zero. |
EMITTER_FLOATS |
integer |
Read-only. Reports how many floats each emitter record contains. |
STATE_FLOATS |
integer |
Read-only. Reports how many GPU state floats one particle carries. |
Constructors
tecs.gfx.particles.newCurve Static
Compiles keyframes into a curve over normalized age.
function tecs.gfx.particles.newCurve(keys: {CurveKey}): CurveArguments
| Name | Type | Description |
|---|---|---|
keys |
{CurveKey} |
Supplies {age, value} pairs with age from zero through one, in any order. Empty or nil raises, and one key defines a constant. |
Returns
| Type | Description |
|---|---|
Curve |
Returns the piecewise-linear curve resampled at CURVE_SAMPLES points. Ages outside zero through one read as the nearest endpoint. |
tecs.gfx.particles.newEffect Static
Registers an immutable effect under options.name and shares it among every emitter using that name. The function requires a unique name.
function tecs.gfx.particles.newEffect(options: EffectOptions): EffectArguments
| Name | Type | Description |
|---|---|---|
options |
EffectOptions |
Every section but name is optional and defaults to something drawable. |
Returns
| Type | Description |
|---|---|
Effect |
Returns a handle that stays valid until reset. The process-wide registry makes it reachable from every world. |
tecs.gfx.particles.newGradient Static
Compiles keyframes into a color gradient over normalized age.
function tecs.gfx.particles.newGradient(keys: {CurveKey}): GradientArguments
| Name | Type | Description |
|---|---|---|
keys |
{CurveKey} |
Supplies {age, color} pairs under the same rules as newCurve. Alpha fades a blended effect and is ignored by an opaque one. |
Returns
| Type | Description |
|---|---|
Gradient |
A gradient resampled like a curve, interpolating each channel separately, so two colors blend through whatever lies between them componentwise rather than around a color wheel. |
Types
tecs.gfx.particles.Color type
Represents a color accepted by particle authoring fields.
type tecs.gfx.particles.Color = Colortecs.gfx.particles.Curve record
Stores a compiled scalar curve.
record tecs.gfx.particles.Curve
samples: {number}
endtecs.gfx.particles.Curve.samples field
Read-only. Provides evenly spaced samples over normalized age from zero to one.
tecs.gfx.particles.Draining record
A slot range whose emitter is gone, held until its last particle can no longer be alive.
global record tecs.gfx.particles.Draining
base: integer
count: integer
releaseAt: number
blended: boolean
endtecs.gfx.particles.Draining.base field
Engine-owned. Stores the first particle slot awaiting release.
tecs.gfx.particles.Draining.count field
Engine-owned. Stores the number of particle slots awaiting release.
tecs.gfx.particles.Draining.releaseAt field
Engine-owned. Stores the time when the slots become reusable.
tecs.gfx.particles.Draining.blended field
Engine-owned. Reports whether the effect that held this range blends. The pool carries this value because the emitter and its effect are gone while the range's particles are still being drawn.
tecs.gfx.particles.Effect record
The handle newEffect returns and a ParticleEmitter names.
record tecs.gfx.particles.Effect
name: string
index: integer
capacity: integer
blend: string
maxLifetime: number
meanLifetime: number
emitFor: number
rate: number
delay: number
duration: number
looping: boolean
bursts: {number}
endtecs.gfx.particles.Effect.name field
Read-only. Reports the effect name and the only property that outlives the process. Everything a save, a tool or a person identifies an effect by is this.
tecs.gfx.particles.Effect.index field
Read-only. Reports where its record sits in the assembled effect table, counting from one. A position in registration order, so it means nothing outside the process that assigned it and nothing stores one.
tecs.gfx.particles.Effect.capacity field
Read-only. Reports how many live particles one emitter can hold.
tecs.gfx.particles.Effect.blend field
Read-only. Reports the blend mode the effect resolved to, which is one of the three render.blend names and never nil. The pool reads it to say how many of its slots may reach the forward pass.
tecs.gfx.particles.Effect.maxLifetime field
Read-only. Reports the longest particle lifetime in seconds, which decides when an emitter that has stopped emitting has finished.
tecs.gfx.particles.Effect.maxLifetime: numbertecs.gfx.particles.Effect.meanLifetime field
Read-only. Reports the mean particle lifetime in seconds, halfway between the bounds initial.lifetime was authored with. This is the window estimatedCount integrates the schedule over, and it is the steady-state live count divided by the emission rate.
tecs.gfx.particles.Effect.meanLifetime: numbertecs.gfx.particles.Effect.emitFor field
Read-only. Reports seconds from play to the last emission, or -1 for an effect that never stops emitting.
tecs.gfx.particles.Effect.rate field
Read-only. Reports the continuous emission rate used by finished and estimatedCount.
tecs.gfx.particles.Effect.delay field
Read-only. Reports the emission delay in seconds.
tecs.gfx.particles.Effect.duration field
Read-only. Reports the emission duration in seconds.
tecs.gfx.particles.Effect.looping field
Read-only. Reports whether the emission cycle repeats.
tecs.gfx.particles.Effect.bursts field
Read-only. Provides the compiled burst schedule.
tecs.gfx.particles.EffectOptions record
Configures newEffect.
record tecs.gfx.particles.EffectOptions
name: string
capacity: integer
overflow: string
schedule: ScheduleOptions
spawn: SpawnOptions
initial: InitialOptions
update: UpdateOptions
render: RenderOptions
endtecs.gfx.particles.EffectOptions.name field
Caller-writable. Sets the required process-wide unique effect name. Snapshots use this name to identify the effect.
tecs.gfx.particles.EffectOptions.name: stringtecs.gfx.particles.EffectOptions.capacity field
Caller-writable. Sets how many live particles one emitter can hold. Defaults to 256, and under one raises. A steady-state emitter needs at least rate * maxLifetime; a smaller capacity registers, drops the emissions that find no slot, and logs a warning saying so, because a spray thinner than the one authored is otherwise found by eye.
tecs.gfx.particles.EffectOptions.capacity: integertecs.gfx.particles.EffectOptions.overflow field
Caller-writable. Selects "drop" or "replace" when an emission arrives with no free slot: give way, or take the oldest live particle's. Defaults to "replace", and anything else raises.
tecs.gfx.particles.EffectOptions.overflow: stringtecs.gfx.particles.EffectOptions.schedule field
Caller-writable. Configures when the effect emits particles. An absent section takes every default below it.
tecs.gfx.particles.EffectOptions.schedule: ScheduleOptionstecs.gfx.particles.EffectOptions.spawn field
Caller-writable. Configures where the effect spawns particles. An absent section takes every default below it.
tecs.gfx.particles.EffectOptions.spawn: SpawnOptionstecs.gfx.particles.EffectOptions.initial field
Caller-writable. Configures each particle's initial properties. An absent section takes every default below it.
tecs.gfx.particles.EffectOptions.initial: InitialOptionstecs.gfx.particles.EffectOptions.update field
Caller-writable. Configures how particles change over their lifetime. An absent section leaves a particle on its launch velocity.
tecs.gfx.particles.EffectOptions.update: UpdateOptionstecs.gfx.particles.EffectOptions.render field
Caller-writable. Configures how particles draw. An absent section draws white quads on layer one.
tecs.gfx.particles.EffectOptions.render: RenderOptionstecs.gfx.particles.EmitterOptions record
Configures a ParticleEmitter.
record tecs.gfx.particles.EmitterOptions
effect: Effect
state: EmitterState
seed: number
rateScale: number
sizeScale: number
timeScale: number
tint: Color
endtecs.gfx.particles.EmitterOptions.effect field
Caller-writable. Selects the required effect. An absent one raises.
tecs.gfx.particles.EmitterOptions.effect: Effecttecs.gfx.particles.EmitterOptions.state field
Caller-writable. Sets the initial playback state. Defaults to "playing", and anything outside the three raises.
tecs.gfx.particles.EmitterOptions.state: EmitterStatetecs.gfx.particles.EmitterOptions.seed field
Caller-writable. Sets the random seed. Defaults to one.
tecs.gfx.particles.EmitterOptions.seed: numbertecs.gfx.particles.EmitterOptions.rateScale field
Caller-writable. Multiplies the effect's emission rate. Defaults to one.
tecs.gfx.particles.EmitterOptions.rateScale: numbertecs.gfx.particles.EmitterOptions.sizeScale field
Caller-writable. Multiplies every particle's size. Defaults to one.
tecs.gfx.particles.EmitterOptions.sizeScale: numbertecs.gfx.particles.EmitterOptions.timeScale field
Caller-writable. Multiplies the speed of the effect clock. Defaults to one.
tecs.gfx.particles.EmitterOptions.timeScale: numbertecs.gfx.particles.EmitterOptions.tint field
Caller-writable. Multiplies every particle's color. Defaults to none, which multiplies by opaque white.
tecs.gfx.particles.EmitterOptions.tint: Colortecs.gfx.particles.EmitterState enum
The three values an emitter's state takes.
global enum tecs.gfx.particles.EmitterState
"paused"
"playing"
"stopped"
endtecs.gfx.particles.Gradient record
Stores a compiled color gradient.
record tecs.gfx.particles.Gradient
samples: {number}
endtecs.gfx.particles.Gradient.samples field
Read-only. Provides evenly spaced RGBA samples over normalized age, with four floats per sample.
tecs.gfx.particles.Holding record
One emitter's place in the pool, as the pool remembers it.
global record tecs.gfx.particles.Holding
item: ParticleEmitter
index: integer
base: integer
count: integer
tint: Color
tintR: number
tintG: number
tintB: number
tintA: number
endtecs.gfx.particles.Holding.item field
Engine-owned. Stores the emitter assigned to this allocation.
tecs.gfx.particles.Holding.item: ParticleEmittertecs.gfx.particles.Holding.index field
Engine-owned. Stores the reusable emitter index.
tecs.gfx.particles.Holding.base field
Engine-owned. Stores the first particle slot.
tecs.gfx.particles.Holding.count field
Engine-owned. Stores the number of particle slots.
tecs.gfx.particles.Holding.tint field
Engine-owned. Stores the tint as it was last read and what it parsed to. Parsing a color string per emitter per frame would be the one allocation in a loop that otherwise has none, and a tint changes rarely.
tecs.gfx.particles.Holding.tintR field
Engine-owned. Stores the parsed red tint channel.
tecs.gfx.particles.Holding.tintG field
Engine-owned. Stores the parsed green tint channel.
tecs.gfx.particles.Holding.tintB field
Engine-owned. Stores the parsed blue tint channel.
tecs.gfx.particles.Holding.tintA field
Engine-owned. Stores the parsed alpha tint channel.
tecs.gfx.particles.InitialOptions record
Configures a particle's initial properties.
global record tecs.gfx.particles.InitialOptions
lifetime: Value
speed: Value
size: Value
rotation: Value
angularVelocity: Value
accelerationX: Value
accelerationY: Value
color: Color
endtecs.gfx.particles.InitialOptions.lifetime field
Caller-writable. Sets particle lifetime in seconds. Defaults to one, and its upper bound is the effect's maxLifetime.
tecs.gfx.particles.InitialOptions.lifetime: Valuetecs.gfx.particles.InitialOptions.speed field
Caller-writable. Sets initial speed in world units per second. Defaults to zero.
tecs.gfx.particles.InitialOptions.speed: Valuetecs.gfx.particles.InitialOptions.size field
Caller-writable. Sets initial size in world units. Defaults to one.
tecs.gfx.particles.InitialOptions.size: Valuetecs.gfx.particles.InitialOptions.rotation field
Caller-writable. Sets initial rotation in radians. Defaults to zero.
tecs.gfx.particles.InitialOptions.rotation: Valuetecs.gfx.particles.InitialOptions.angularVelocity field
Caller-writable. Sets initial angular velocity in radians per second. Defaults to zero.
tecs.gfx.particles.InitialOptions.angularVelocity: Valuetecs.gfx.particles.InitialOptions.accelerationX field
Caller-writable. Sets constant horizontal acceleration. Independent axes make a plume drift apart instead of translating as a block. Defaults to zero.
tecs.gfx.particles.InitialOptions.accelerationX: Valuetecs.gfx.particles.InitialOptions.accelerationY field
Caller-writable. Sets constant vertical acceleration. Defaults to zero.
tecs.gfx.particles.InitialOptions.accelerationY: Valuetecs.gfx.particles.InitialOptions.color field
Caller-writable. Sets the color every particle starts with, multiplied by the gradient and by the emitter's tint. Defaults to opaque white.
tecs.gfx.particles.InitialOptions.color: Colortecs.gfx.particles.ParticleEmitter record
Controls an effect's playback state and per-instance scales.
Deliberately small. If an instance could override every effect field then effects would stop being reusable GPU data and every emitter would grow to hold a copy. The effect owns capacity because it determines the pool slot range when the emitter starts. Read-only. Exposes the particle-emitter component.
record tecs.gfx.particles.ParticleEmitter is ecs.Component
effect: Effect
state: EmitterState
seed: number
rateScale: number
sizeScale: number
timeScale: number
tint: Color
burst: function(self, count: number)
clear: function(self)
estimatedCount: function(self): integer
finished: function(self): boolean
pause: function(self)
play: function(self)
restart: function(self)
stop: function(self)
endInterfaces
| Interface |
|---|
ecs.Component |
tecs.gfx.particles.ParticleEmitter.effect field
Caller-writable. Selects the effect before spawning the emitter. It cannot change after spawn because the slot range is the effect's capacity, and changing it would mean reallocating. Despawn and spawn again, which is cheap.
tecs.gfx.particles.ParticleEmitter.effect: Effecttecs.gfx.particles.ParticleEmitter.state field
Caller-writable. Reports whether the emitter plays, pauses, or stops. Set it through play, pause and stop rather than by assignment: those move the cycle's start step and the random generation with it, and a bare write leaves both where the previous state left them, so an emitter written back to "playing" resumes part way through its schedule instead of starting it again.
tecs.gfx.particles.ParticleEmitter.state: EmitterStatetecs.gfx.particles.ParticleEmitter.seed field
Caller-writable. Selects the random sequence. Two emitters with one seed produce one field.
tecs.gfx.particles.ParticleEmitter.seed: numbertecs.gfx.particles.ParticleEmitter.rateScale field
Caller-writable. Multiplies the effect's emission rate.
tecs.gfx.particles.ParticleEmitter.rateScale: numbertecs.gfx.particles.ParticleEmitter.sizeScale field
Caller-writable. Multiplies every particle's size.
tecs.gfx.particles.ParticleEmitter.sizeScale: numbertecs.gfx.particles.ParticleEmitter.timeScale field
Caller-writable. Multiplies the speed of the effect clock.
tecs.gfx.particles.ParticleEmitter.timeScale: numbertecs.gfx.particles.ParticleEmitter.tint field
Caller-writable. Multiplies every particle's color, alpha included, so a tint is how one emitter of a blended effect fades against another. An effect whose render.blend is "opaque" ignores the alpha.
tecs.gfx.particles.ParticleEmitter.tint: Colortecs.gfx.particles.ParticleEmitter:burst Instance
Emits count particles at the next step boundary.
A stopped emitter ignores the call. The emitter truncates a count beyond its free capacity to the number that fits.
function tecs.gfx.particles.ParticleEmitter.burst(self, count: number)Arguments
| Name | Type | Description |
|---|---|---|
self |
ParticleEmitter |
|
count |
number |
Returns
None.
tecs.gfx.particles.ParticleEmitter:clear Instance
Kills every live particle immediately.
Use this for teardown and state transitions. It leaves emission unchanged, so a playing emitter starts filling again on the next step.
function tecs.gfx.particles.ParticleEmitter.clear(self)Arguments
| Name | Type | Description |
|---|---|---|
self |
ParticleEmitter |
Returns
None.
tecs.gfx.particles.ParticleEmitter:estimatedCount Instance
Returns an estimate of how many particles remain alive.
Integrates the schedule over the effect's meanLifetime and treats every emission older than that as expired. An effect whose initial.lifetime is one number has a mean equal to that lifetime, so the answer is exact but for the step the emitter is part way through; a lifetime range makes it an expectation, because the host does not know which particles drew the short lifetimes. A truncated burst or an overflow replacement moves it either way. Reading the exact count back from the GPU would stall the pipeline.
function tecs.gfx.particles.ParticleEmitter.estimatedCount(
self
): integerArguments
| Name | Type | Description |
|---|---|---|
self |
ParticleEmitter |
Returns
| Type | Description |
|---|---|
integer |
tecs.gfx.particles.ParticleEmitter:finished Instance
Returns whether this emitter has stopped emitting and its last particle has died.
Exact, and it costs nothing: it is the schedule plus the longest lifetime against the emitter's own clock, all of which the host holds. This is what almost every "has the explosion finished" question is actually asking, and unlike a count it needs nothing read back from the GPU.
function tecs.gfx.particles.ParticleEmitter.finished(self): booleanArguments
| Name | Type | Description |
|---|---|---|
self |
ParticleEmitter |
Returns
| Type | Description |
|---|---|
boolean |
tecs.gfx.particles.ParticleEmitter:pause Instance
Stops emission and holds. The schedule and the particles both keep their state, and neither ages while this is on.
function tecs.gfx.particles.ParticleEmitter.pause(self)Arguments
| Name | Type | Description |
|---|---|---|
self |
ParticleEmitter |
Returns
None.
tecs.gfx.particles.ParticleEmitter:play Instance
Starts or resumes emission.
From stopped this begins the cycle again; from paused it carries on from where it was, with the steps spent paused not counted against the schedule.
function tecs.gfx.particles.ParticleEmitter.play(self)Arguments
| Name | Type | Description |
|---|---|---|
self |
ParticleEmitter |
Returns
None.
tecs.gfx.particles.ParticleEmitter:restart Instance
Resets the schedule and the random sequence, and starts playing.
Reproducible: a restart of an emitter with the same seed produces the same field, because every particle's draws are a pure function of the seed, the generation, the serial and the property, and all four start over.
function tecs.gfx.particles.ParticleEmitter.restart(self)Arguments
| Name | Type | Description |
|---|---|---|
self |
ParticleEmitter |
Returns
None.
tecs.gfx.particles.ParticleEmitter:stop Instance
Stops emission and resets the schedule. Live particles drain.
The next play starts the cycle from its beginning with a fresh random sequence, which is what makes it reproducible. Killing the field instead is clear, and the two are deliberately separate.
function tecs.gfx.particles.ParticleEmitter.stop(self)Arguments
| Name | Type | Description |
|---|---|---|
self |
ParticleEmitter |
Returns
None.
tecs.gfx.particles.Pool record
Represents a world's installed particle pool.
record tecs.gfx.particles.Pool
capacity: integer
active: function(self): boolean
blended: function(self): integer
casting: function(self): integer
count: function(self): integer
destroy: function(self)
record: function(
self, frame: Frame, instances: Buffer, bounds: Buffer
)
takeDirty: function(self): {integer}
write: function(
self,
floats: loader.CArray,
bounds: loader.CArray,
base: integer,
first: integer,
last: integer
)
endtecs.gfx.particles.Pool.capacity field
Read-only. Reports how many live particles the pool can hold.
tecs.gfx.particles.Pool:active Instance
Whether there is anything to dispatch over.
Published rather than derived, and this is the hazard the design named: an emitter dirties nothing, ever, while its whole field moves every frame. There is no archetype bit to read and no component write to observe, so a gate written as "nothing is dirty" would conclude wrongly every frame an emitter was running. The pool counts its own live emitters instead, and holds the gate open one frame past the last of them so the pass that hides their slots is the one that actually runs.
function tecs.gfx.particles.Pool.active(self): booleanArguments
| Name | Type | Description |
|---|---|---|
self |
Pool |
Returns
| Type | Description |
|---|---|
boolean |
tecs.gfx.particles.Pool:blended Instance
Slots the forward pass may find a particle in.
Reserved slots rather than live particles, and it has to be: what is alive lives in a buffer the CPU never reads, so the honest answer here is the only one that cannot be too small. Too small is the failure that matters, because the backend skips the whole forward lane on a frame nothing said was blended and every particle of every blended effect would then go missing.
A slot with no live particle in it writes the hidden bound, so over-reporting costs the lane five compute passes over slots the mark pass has already rejected, and never a draw.
function tecs.gfx.particles.Pool.blended(self): integerArguments
| Name | Type | Description |
|---|---|---|
self |
Pool |
Returns
| Type | Description |
|---|---|
integer |
tecs.gfx.particles.Pool:casting Instance
Particles never write caster lane signs into their bounds.
function tecs.gfx.particles.Pool.casting(self): integerArguments
| Name | Type | Description |
|---|---|---|
self |
Pool |
Returns
| Type | Description |
|---|---|
integer |
tecs.gfx.particles.Pool:count Instance
function tecs.gfx.particles.Pool.count(self): integerArguments
| Name | Type | Description |
|---|---|---|
self |
Pool |
Returns
| Type | Description |
|---|---|
integer |
tecs.gfx.particles.Pool:destroy Instance
Releases everything the pool owns. Safe to call more than once.
function tecs.gfx.particles.Pool.destroy(self)Arguments
| Name | Type | Description |
|---|---|---|
self |
Pool |
Returns
None.
tecs.gfx.particles.Pool:record Instance
Arguments
| Name | Type | Description |
|---|---|---|
self |
Pool |
|
frame |
Frame |
|
instances |
Buffer |
|
bounds |
Buffer |
Returns
None.
tecs.gfx.particles.Pool:takeDirty Instance
function tecs.gfx.particles.Pool.takeDirty(self): {integer}Arguments
| Name | Type | Description |
|---|---|---|
self |
Pool |
Returns
| Type | Description |
|---|---|
{integer} |
tecs.gfx.particles.Pool:write Instance
Hides the run and remembers where it landed.
Only ever called on a frame the layout moved, since nothing else is ever dirty. What it writes is the hidden slot every reserved run writes, and the simulate pass overwrites the live ones later on the same frame: the flush is recorded before the dispatch, so a relayout cannot lose a particle.
function tecs.gfx.particles.Pool.write(
self,
floats: loader.CArray,
bounds: loader.CArray,
base: integer,
first: integer,
last: integer
)Arguments
| Name | Type | Description |
|---|---|---|
self |
Pool |
|
floats |
loader.CArray |
|
bounds |
loader.CArray |
|
base |
integer |
|
first |
integer |
|
last |
integer |
Returns
None.
tecs.gfx.particles.PoolOptions record
Configures plugin.
record tecs.gfx.particles.PoolOptions
renderer: Renderer
capacity: integer
maxEmitters: integer
endtecs.gfx.particles.PoolOptions.renderer field
Caller-writable. Selects the renderer whose instance buffer the pool uses and whose device its buffers and pipelines are built on. Required, and an absent one raises.
tecs.gfx.particles.PoolOptions.renderer: Renderertecs.gfx.particles.PoolOptions.capacity field
Caller-writable. Sets how many live particles the pool holds across the world. Defaults to 16384. The value remains fixed at install: changing it would move the run, and moving the run lays out the whole scene again.
tecs.gfx.particles.PoolOptions.capacity: integertecs.gfx.particles.PoolOptions.maxEmitters field
Caller-writable. Sets how many emitters the pool holds at once. Defaults to 256. An emitter that arrives past the limit draws nothing and logs the refusal.
tecs.gfx.particles.PoolOptions.maxEmitters: integertecs.gfx.particles.Range record
The two-bound form of a Value.
tecs.gfx.particles.Range.min field
Caller-writable. Sets the inclusive lower bound.
tecs.gfx.particles.Range.max field
Caller-writable. Sets the inclusive upper bound.
tecs.gfx.particles.RenderOptions record
Configures how particles draw.
global record tecs.gfx.particles.RenderOptions
material: string
materialParam: number
sprite: Sprite
sheet: Sheet
tag: string
layer: integer
pivotX: number
pivotY: number
alignment: string
stretch: number
blend: string
clip: integer
endtecs.gfx.particles.RenderOptions.material field
Caller-writable. Selects the material that shades the quad by name. An absent value samples the image array and covers the whole quad.
tecs.gfx.particles.RenderOptions.material: stringtecs.gfx.particles.RenderOptions.materialParam field
Caller-writable. Passes one number to the material, clamped to zero through 0.999 and ignored unless material names one. The instance carries the material as id + param in one float, and the vertex shader reads the integer part as the selector and the fraction as the parameter, so 1.0 would arrive as the next material's id with a parameter of zero. Defaults to zero.
tecs.gfx.particles.RenderOptions.materialParam: numbertecs.gfx.particles.RenderOptions.sprite field
Caller-writable. Selects a registered image from renderer.sprites:sprite. An absent value draws a white quad.
tecs.gfx.particles.RenderOptions.sprite: Spritetecs.gfx.particles.RenderOptions.sheet field
Caller-writable. Selects a sheet to animate instead of a still region. The cycle is played exactly once over each particle's own life and clamps at the end, so a randomized lifetime randomizes the playback speed.
Uses the same frame table an animated entity does, so per-frame durations, reverse and pingpong all arrive already spent.
tecs.gfx.particles.RenderOptions.sheet: Sheettecs.gfx.particles.RenderOptions.tag field
Caller-writable. Selects the sheet tag to animate. An absent value animates the whole sheet. A name the sheet does not carry animates the whole sheet too, and the sheet reports it at error level under the tecs.gfx logger, naming the tags it does carry. Defining the effect still succeeds, so a sheet re-exported without a tag keeps drawing.
tecs.gfx.particles.RenderOptions.tag: stringtecs.gfx.particles.RenderOptions.layer field
Caller-writable. Selects the render layer from one to layers.MAX. Defaults to one, and anything outside the range raises.
tecs.gfx.particles.RenderOptions.layer: integertecs.gfx.particles.RenderOptions.pivotX field
Caller-writable. Sets the horizontal pivot as a fraction of the frame from its top left. Defaults to 0.5, the middle.
tecs.gfx.particles.RenderOptions.pivotX: numbertecs.gfx.particles.RenderOptions.pivotY field
Caller-writable. Sets the vertical pivot as a fraction of the frame from its top edge. Defaults to 0.5, the middle.
tecs.gfx.particles.RenderOptions.pivotY: numbertecs.gfx.particles.RenderOptions.alignment field
Caller-writable. Selects "fixed" or "velocity" alignment. Velocity alignment adds the path's angle to the particle's own rotation, so a spinning spark aligned to its path still spins. Defaults to "fixed", and anything else raises.
tecs.gfx.particles.RenderOptions.alignment: stringtecs.gfx.particles.RenderOptions.stretch field
Caller-writable. Multiplies the along-path axis when aligned to velocity. Defaults to one.
tecs.gfx.particles.RenderOptions.stretch: numbertecs.gfx.particles.RenderOptions.blend field
Caller-writable. Selects "alpha", "additive", or "opaque". Defaults to "alpha", and anything else raises.
"alpha" and "additive" draw in the forward pass over the composited image, depth tested and not written, so a particle is still hidden by geometry in front of it. "alpha" keeps that much less of what is behind it and "additive" adds to it, so a dark additive particle changes nothing and overlapping ones brighten.
"opaque" draws into the G-buffer instead. What it gives up is alpha, which the G-buffer is written with replace and has nowhere to put: a fade to transparent writes opaque over what was behind it, and the only fade left is a size curve reaching zero. What it buys is everything that reads the G-buffer, which is the occluder mask and being shadowed by one.
tecs.gfx.particles.RenderOptions.blend: stringtecs.gfx.particles.RenderOptions.clip field
Caller-writable. Selects the clip region that contains the fragments. Defaults to zero, which is no clipping.
tecs.gfx.particles.RenderOptions.clip: integertecs.gfx.particles.ScheduleOptions record
Configures when particles emit.
global record tecs.gfx.particles.ScheduleOptions
rate: number
duration: number
looping: boolean
delay: number
bursts: {ScheduleBurst}
endtecs.gfx.particles.ScheduleOptions.rate field
Caller-writable. Sets the continuous emission rate in particles per second. Defaults to zero, which emits only what bursts asks for.
tecs.gfx.particles.ScheduleOptions.rate: numbertecs.gfx.particles.ScheduleOptions.duration field
Caller-writable. Sets the emission cycle duration in seconds. Defaults to zero, which leaves the cycle open.
tecs.gfx.particles.ScheduleOptions.duration: numbertecs.gfx.particles.ScheduleOptions.looping field
Caller-writable. Controls whether the emission cycle repeats. Defaults to false.
tecs.gfx.particles.ScheduleOptions.looping: booleantecs.gfx.particles.ScheduleOptions.delay field
Caller-writable. Sets the delay before emission starts in seconds. Defaults to zero.
tecs.gfx.particles.ScheduleOptions.delay: numbertecs.gfx.particles.ScheduleOptions.bursts field
Caller-writable. Defines at most four timed burst counts measured from the start of the cycle. A muzzle flash is one burst at time zero. Defaults to none, and a fifth raises.
tecs.gfx.particles.ScheduleOptions.bursts: {ScheduleBurst}tecs.gfx.particles.SpawnOptions record
Configures where particles spawn.
global record tecs.gfx.particles.SpawnOptions
shape: string
width: number
height: number
arc: number
rotation: number
distribution: string
direction: number
spread: number
outward: boolean
space: string
inheritVelocity: number
endtecs.gfx.particles.SpawnOptions.shape field
Caller-writable. Selects "point", "line", "rectangle", "rectangleEdge", "disc", "ring", or "cone". Defaults to "point", and anything else raises.
tecs.gfx.particles.SpawnOptions.shape: stringtecs.gfx.particles.SpawnOptions.width field
Caller-writable. Sets width for a line or rectangle and radius for a disc, ring, or cone. Defaults to zero.
tecs.gfx.particles.SpawnOptions.width: numbertecs.gfx.particles.SpawnOptions.height field
Caller-writable. Sets height for a rectangle. Every other shape ignores it. Defaults to zero.
tecs.gfx.particles.SpawnOptions.height: numbertecs.gfx.particles.SpawnOptions.arc field
Caller-writable. Sets the sampled sector of a disc, ring, or cone in radians. Defaults to a full turn.
tecs.gfx.particles.SpawnOptions.arc: numbertecs.gfx.particles.SpawnOptions.rotation field
Caller-writable. Rotates the sampled area in radians, independently of launch direction. Defaults to zero.
tecs.gfx.particles.SpawnOptions.rotation: numbertecs.gfx.particles.SpawnOptions.distribution field
Caller-writable. Selects "uniform" or "normal" sampling. A gaussian spread makes a column read as a column rather than as a slab. Defaults to "uniform", and anything else raises.
tecs.gfx.particles.SpawnOptions.distribution: stringtecs.gfx.particles.SpawnOptions.direction field
Caller-writable. Sets the launch direction in radians and centers the full cone on it, so a particle leaves within plus or minus half of spread. Half-angle is the other plausible reading of spread and a mismatch is silent, so it is said here. Defaults to zero.
tecs.gfx.particles.SpawnOptions.direction: numbertecs.gfx.particles.SpawnOptions.spread field
Caller-writable. Sets the full cone width in radians. Defaults to zero, which launches every particle along direction.
tecs.gfx.particles.SpawnOptions.spread: numbertecs.gfx.particles.SpawnOptions.outward field
Caller-writable. Launches along the shape's outward normal rather than along direction. Meaningful for the shapes that have one: disc, ring, cone and a rectangle's edge. Defaults to false.
tecs.gfx.particles.SpawnOptions.outward: booleantecs.gfx.particles.SpawnOptions.space field
Caller-writable. Selects "local" or "world" space. Local particles follow the emitter's transform for their whole life; world particles read it once, at spawn, and then move independently. Neither is a good universal default, so it is explicit and defaults to "world". Anything else raises.
tecs.gfx.particles.SpawnOptions.space: stringtecs.gfx.particles.SpawnOptions.inheritVelocity field
Caller-writable. Sets how much emitter velocity a particle inherits, from zero to one. Defaults to zero, and world space is the only space it means anything in: a local particle is already carried by the emitter, so inheriting would carry it twice.
tecs.gfx.particles.SpawnOptions.inheritVelocity: numbertecs.gfx.particles.UpdateOptions record
Configures how a particle changes over its normalized lifetime.
global record tecs.gfx.particles.UpdateOptions
drag: number
radialAcceleration: Value
tangentialAcceleration: Value
size: Curve
color: Gradient
endtecs.gfx.particles.UpdateOptions.drag field
Caller-writable. Sets damping in units of one over seconds, applied as velocity = velocity / (1 + drag * dt), so it composes with any step. Defaults to zero.
tecs.gfx.particles.UpdateOptions.drag: numbertecs.gfx.particles.UpdateOptions.radialAcceleration field
Caller-writable. Sets acceleration away from the emitter. This and tangentialAcceleration are the difference between a fountain and a vortex. Defaults to zero.
tecs.gfx.particles.UpdateOptions.radialAcceleration: Valuetecs.gfx.particles.UpdateOptions.tangentialAcceleration field
Caller-writable. Sets acceleration at right angles to the emitter. Defaults to zero.
tecs.gfx.particles.UpdateOptions.tangentialAcceleration: Valuetecs.gfx.particles.UpdateOptions.size field
Caller-writable. Multiplies each particle's size over its life. An absent curve multiplies by one, so leaving it out costs nothing.
tecs.gfx.particles.UpdateOptions.size: Curvetecs.gfx.particles.UpdateOptions.color field
Caller-writable. Multiplies each particle's color over its life. An absent gradient multiplies by white, so leaving it out costs nothing. Its alpha fades a blended effect and is ignored by an effect whose render.blend is "opaque".
tecs.gfx.particles.UpdateOptions.color: Gradienttecs.gfx.particles.Value type
Accepted wherever a property may vary per particle.
type tecs.gfx.particles.Value = ValueFunctions
tecs.gfx.particles.find Static
Returns the effect registered under name, or nil when none exists.
function tecs.gfx.particles.find(name: string): EffectArguments
| Name | Type | Description |
|---|---|---|
name |
string |
Nil answers nil rather than raising, so a restore holding no name takes the same path as one holding an unknown name. |
Returns
| Type | Description |
|---|---|
Effect |
The registered effect itself, not a copy, so two callers finding one name share it. Nil when nothing has that name. |
tecs.gfx.particles.names Static
Returns every registered effect name in registration order.
function tecs.gfx.particles.names(): {string}Arguments
None.
Returns
| Type | Description |
|---|---|
{string} |
A fresh table each call, the caller's to keep and to modify. |
tecs.gfx.particles.plugin Static
Installs the pool on a world. Without it, Tecs allocates and dispatches no particle work.
function tecs.gfx.particles.plugin(options: PoolOptions): function(
World
)Arguments
| Name | Type | Description |
|---|---|---|
options |
PoolOptions |
Requires renderer; both capacities have defaults. |
Returns
| Type | Description |
|---|---|
function(World) |
Returns the plugin to install on a world. Installing it on a world that already has a pool does nothing, and installing it on two worlds gives each its own run of the one renderer's buffer. |
tecs.gfx.particles.poolOf Static
Returns the pool installed on a world, or nil. Tests and tooling use this function.
Arguments
| Name | Type | Description |
|---|---|---|
world |
World |
Accepts a world with or without the plugin. |
Returns
| Type | Description |
|---|---|
Pool |
Returns the world's live pool, or nil when the plugin was never installed. The function does not copy the pool. |
tecs.gfx.particles.reset Static
Forgets every registered effect, so a spec can register a set again. Every handle already handed out goes stale with it.
function tecs.gfx.particles.reset()Arguments
None.
Returns
None.
Values
tecs.gfx.particles.CURVE_SAMPLES variable
Read-only. Reports how many samples one compiled curve or gradient holds.
tecs.gfx.particles.CURVE_SAMPLES: integertecs.gfx.particles.EFFECT variable
Read-only. Reports each field's offset within an effect record.
Published because the shaders state the same offsets and a disagreement between the two is silent: it draws something, just not the right thing. spec/particles_spec.lua reads both and holds them to each other, which is the only reason this is on the surface.
tecs.gfx.particles.EFFECT: {string: integer}tecs.gfx.particles.EFFECT_FLOATS variable
Read-only. Reports how many floats each effect record contains.
tecs.gfx.particles.EFFECT_FLOATS: integertecs.gfx.particles.EMITTER variable
Read-only. Reports each field's float offset within an emitter record, counting from zero.
tecs.gfx.particles.EMITTER: {string: integer}tecs.gfx.particles.EMITTER_FLOATS variable
Read-only. Reports how many floats each emitter record contains. An emitter record is much the smaller of the two.
tecs.gfx.particles.EMITTER_FLOATS: integertecs.gfx.particles.STATE_FLOATS variable
Read-only. Reports how many GPU state floats one particle carries.
tecs.gfx.particles.STATE_FLOATS: integer