308 lines
12 KiB
Markdown
308 lines
12 KiB
Markdown
# Infrastructure Requirements
|
|
|
|
Foundational data structures and capabilities needed to support all features
|
|
described in REALISM-IDEAS.md. These are the structural primitives — every
|
|
realism feature reduces to needing one or more of these five layers.
|
|
|
|
|
|
## A. Expanded ant state
|
|
|
|
Currently all 4 RGBA Float32 channels are used:
|
|
R = pos.x, G = pos.y, B = angle, A = packed(storage | isCarrying)
|
|
|
|
Most realism features need more per-ant data. This means a second ant
|
|
texture (RGBA Float32, same dimensions, same ping-pong pattern).
|
|
|
|
What needs to live in ant state:
|
|
|
|
personality/threshold float #3 individuality, #4 adaptive noise
|
|
steps since pickup int #1 distance-modulated deposition
|
|
cargo quality float #5 food quality encoding
|
|
path integration dx float #9 path integration
|
|
path integration dy float #9 path integration
|
|
role/caste float #11 caste switching
|
|
colony ID int #12 CHC recognition
|
|
|
|
That's 7 values. Two RGBA textures give 8 floats total, so with some
|
|
packing of ints into float bits (steps, colony ID, carrying flag are all
|
|
small integers) it fits in 2 textures. If the packing gets uncomfortable
|
|
a third texture is an option — WebGL supports up to 16 texture units per
|
|
shader, current shaders use 2-3, so headroom is fine.
|
|
|
|
Layout sketch for texture 2:
|
|
|
|
R = personality (0.0 = pure follower, 1.0 = pure explorer)
|
|
G = cargo quality (0.0 when not carrying, otherwise food quality value)
|
|
B = path integration dx (accumulated displacement since last reset)
|
|
A = packed(path_integration_dy | role | colony_id | steps_since_pickup)
|
|
|
|
The A channel packing is tight. Alternative: move steps_since_pickup into
|
|
texture 1's storage bits (current storage uses ~20 of 31 available bits)
|
|
and give path_integration_dy its own float in texture 2.A. Then role and
|
|
colony_id pack together (role quantized to 8 bits, colony_id in 8 bits =
|
|
16 bits, fits in the fractional part of another channel).
|
|
|
|
Pipeline impact:
|
|
- AntsComputeScene needs to read/write 2 textures per ant instead of 1
|
|
- Ping-pong doubles from 2 textures to 4
|
|
- antsCompute.frag gets a second sampler input and second render target
|
|
(requires MRT — multiple render targets — or a second pass)
|
|
- MRT is available in WebGL2 via gl.drawBuffers(), clean solution
|
|
|
|
Features unlocked: #1, #3, #4, #5, #9, #11, #12
|
|
|
|
|
|
## B. Generalized pheromone system
|
|
|
|
Currently 2 pheromone channels in the world texture:
|
|
G = scentToHome, B = scentToFood
|
|
A = unused (free slot for one more)
|
|
|
|
Minimum viable expansion: put repellent pheromone in world.A. That gives
|
|
3 channels with zero new textures.
|
|
|
|
But alarm pheromone (#6) and multicomponent blends (#8) need more. Options:
|
|
|
|
Option 1: Second world texture (RGBA Float32, same worldSize)
|
|
R = alarm pheromone
|
|
G = blend component A
|
|
B = blend component B
|
|
A = reserved
|
|
|
|
Option 2: Multiplex channels temporally or semantically
|
|
Less clean, harder to reason about, not recommended
|
|
|
|
Going with option 1, total pheromone channels = 6:
|
|
world1.G scentToHome
|
|
world1.B scentToFood
|
|
world1.A repellent
|
|
world2.R alarm
|
|
world2.G blend component A
|
|
world2.B blend component B
|
|
|
|
The blur/diffusion shader must generalize:
|
|
- Currently applies one blur radius and one fade-out factor uniformly
|
|
- Needs per-channel parameters:
|
|
channel decay rate diffusion radius notes
|
|
toHome medium medium standard trail
|
|
toFood medium medium standard trail
|
|
repellent slow (2x) narrow persists longer per biology
|
|
alarm fast wide spreads fast, fades fast
|
|
blend A configurable configurable depends on compound
|
|
blend B configurable configurable depends on compound
|
|
|
|
- Pass these as a uniform array or pack into a small config texture
|
|
- The blur pass runs once but processes all channels with their own params
|
|
|
|
Pipeline impact:
|
|
- worldBlur.frag becomes per-channel parameterized
|
|
- world.frag merges deposits into more channels
|
|
- antsCompute.frag reads more pheromone channels for decision-making
|
|
- antsDiscretize.frag may need more output channels (MRT again, or
|
|
expand discrete texture to RGBA Float32 to carry more deposit types)
|
|
|
|
Features unlocked: #2, #6, #8
|
|
|
|
|
|
## C. World cell metadata
|
|
|
|
Currently world.R uses 3 bits for cell flags:
|
|
bit 0 = hasFood, bit 1 = isHome, bit 2 = isObstacle
|
|
bits 3-31 = unused (29 bits free in float representation)
|
|
|
|
Several features need more per-cell information:
|
|
|
|
terrain type 3-4 bits #7 substrate-dependent decay
|
|
colony ownership 4-8 bits #12 multi-colony territories
|
|
food quality ~8 bits #5 quality encoding (quantized)
|
|
|
|
Terrain type (3 bits = 8 terrain types) and colony ownership (4 bits = 16
|
|
colonies) fit cleanly into world.R's unused bits:
|
|
|
|
bits 0-2: cell type flags (food, home, obstacle) — existing
|
|
bits 3-5: terrain type (0=default, 1-7 = surface variants)
|
|
bits 6-9: colony ID that owns this cell (0=neutral, 1-15 = colonies)
|
|
bits 10-17: food quality (0-255 quantized, only meaningful when hasFood)
|
|
bits 18-31: reserved
|
|
|
|
This keeps everything in a single channel with bit operations the shader
|
|
already uses (the existing code does bitwise AND/OR on world.R).
|
|
|
|
Terrain type feeds into the blur shader — the decay rate per cell becomes:
|
|
effective_decay = base_decay * terrain_decay_multiplier[terrain_type]
|
|
This means the blur shader needs to read the world texture (not just the
|
|
blurred copy) to know each cell's terrain type. Small perf cost but
|
|
the texture is already bound.
|
|
|
|
Food quality feeds into ant pickup behavior — when an ant grabs food, it
|
|
reads the quality bits and stores them in its cargo quality channel
|
|
(ant texture 2.G from section A).
|
|
|
|
Features unlocked: #5, #7, #12
|
|
|
|
|
|
## D. Ant-ant spatial interaction
|
|
|
|
Currently zero. Ants only interact through stigmergy (pheromone trails in
|
|
the world texture). No ant knows about any other ant's position or state.
|
|
|
|
The discrete ants texture maps ant deposits to grid cells but doesn't
|
|
preserve ant identity — it just accumulates pheromone values.
|
|
|
|
To enable ant-ant awareness, options:
|
|
|
|
Option 1: Identity-preserving discrete texture
|
|
Instead of (or in addition to) accumulating pheromone deposits,
|
|
store the ant index of whoever occupies each cell. Multiple ants
|
|
per cell requires either:
|
|
a) Last-write-wins (lossy but simple, GPU-friendly)
|
|
b) Linked list in a buffer (complex, needs WebGL2 atomic ops or
|
|
compute shaders)
|
|
c) Spatial hash with fixed bucket size (e.g. 4 ants per cell,
|
|
pack 4 ant indices into RGBA)
|
|
|
|
Option (a) is probably fine — tandem running only needs to know
|
|
"is there an ant near me" and check one neighbor, not enumerate
|
|
all neighbors.
|
|
|
|
Option 2: Proximity via world texture overlay
|
|
A separate texture where each cell stores the ID (or packed state)
|
|
of an ant occupying it. Ants sample neighboring cells to find nearby
|
|
ants. Radius-1 gives 8 neighbors, radius-2 gives 24.
|
|
|
|
For tandem running: leader deposits its ID in the cell. Follower
|
|
checks adjacent cells for the leader's ID, moves toward it.
|
|
|
|
For CHC recognition: ant reads neighbor cell, extracts colony ID
|
|
from neighbor's state, compares to own colony ID.
|
|
|
|
Implementation:
|
|
- New texture: antsPresenceTexture (RGBA Float32, worldSize x worldSize)
|
|
- R = ant index (or packed ant state subset)
|
|
- G = colony ID of occupant
|
|
- B = role/caste of occupant
|
|
- A = reserved
|
|
- Written during discretize pass, read during ant compute pass
|
|
- Cleared each frame before discretize
|
|
|
|
Tandem running specifics:
|
|
- Leader state: "has follower" flag, movement gated on follower proximity
|
|
- Follower state: "leader index" reference, moves toward leader cell
|
|
- Pairing logic: when a returning forager passes a naive ant, the naive
|
|
ant checks if the forager is available as a leader (not already paired)
|
|
- Race condition on pairing: last-write-wins means two ants might both
|
|
claim the same leader. Acceptable — worst case is a broken tandem that
|
|
reforms next frame.
|
|
|
|
Pipeline impact:
|
|
- New texture in discretize pass
|
|
- antsCompute.frag gets a new sampler for neighbor awareness
|
|
- Possibly a CPU readback for complex pairing logic that's too hard on GPU
|
|
|
|
Features unlocked: #10, #12
|
|
|
|
|
|
## E. Colony-level feedback
|
|
|
|
Currently no aggregate state. Individual ants have no information about the
|
|
colony as a whole — they only react to local pheromone concentrations.
|
|
|
|
Caste switching (#11) and alarm response scaling (#6) need colony-wide stats:
|
|
|
|
forager count how many ants are currently foraging
|
|
scout count how many ants are currently exploring
|
|
total food collected cumulative food delivered to nest
|
|
threat level number of alarm pheromone cells above threshold
|
|
colony size total ants (static, but relevant for ratios)
|
|
|
|
Two approaches:
|
|
|
|
Option 1: GPU reduction
|
|
Run a reduction shader that sums values across the ant texture:
|
|
- Count ants with isCarrying = 1 (foragers returning)
|
|
- Count ants with role > threshold (scouts)
|
|
- Sum food deposits at home cells
|
|
Requires multiple passes halving texture dimensions each time
|
|
(standard parallel reduction). Result lands in a 1x1 texture.
|
|
Read the 1x1 texture as a uniform for the next frame.
|
|
|
|
Pros: stays on GPU, no sync stall
|
|
Cons: multiple passes, more textures, latency (stats are 1 frame old)
|
|
|
|
Option 2: CPU readback
|
|
Use gl.readPixels on the ant texture (or a downsampled version).
|
|
Compute stats on CPU. Upload as uniforms next frame.
|
|
|
|
Pros: simpler to implement, flexible computation
|
|
Cons: GPU-CPU sync stall (readPixels is blocking in WebGL1, async in
|
|
WebGL2 via pixel buffer objects / fences). Could downsample first to
|
|
reduce transfer size.
|
|
|
|
Option 3: Hybrid
|
|
Reduce on GPU to a small texture (e.g. 4x4), read back 16 pixels
|
|
instead of thousands, compute final stats on CPU.
|
|
|
|
This is probably the pragmatic choice.
|
|
|
|
Colony stats feed back as uniforms into antsCompute.frag:
|
|
|
|
uniform float u_foragerRatio; // foragers / total ants
|
|
uniform float u_scoutRatio; // scouts / total ants
|
|
uniform float u_foodCollected; // cumulative food at nest
|
|
uniform float u_threatLevel; // alarm pheromone intensity
|
|
|
|
Individual ants use these to adjust their role variable:
|
|
- If foragerRatio is high and scoutRatio is low, some foragers transition
|
|
toward scouting (role variable drifts)
|
|
- If threatLevel is high, nearby ants shift toward defensive behavior
|
|
- Transitions are gradual (continuous role variable, not discrete switch)
|
|
|
|
Features unlocked: #6, #11
|
|
|
|
|
|
## Dependency graph
|
|
|
|
Which infrastructure layers does each feature need?
|
|
|
|
feature A B C D E
|
|
ant pher cell a2a agg
|
|
#1 distance-modulated deposition x
|
|
#2 negative pheromone x
|
|
#3 individual thresholds x
|
|
#4 adaptive noise x
|
|
#5 food quality encoding x x
|
|
#6 alarm pheromone x x
|
|
#7 substrate-dependent decay x x
|
|
#8 multicomponent blends x
|
|
#9 path integration x
|
|
#10 tandem running x x
|
|
#11 caste switching x x
|
|
#12 CHC recognition x x x
|
|
|
|
Layer A (expanded ant state) unlocks: 9 of 12 features
|
|
Layer B (generalized pheromones) : 4 of 12 features
|
|
Layer C (cell metadata) : 3 of 12 features
|
|
Layer D (ant-ant interaction) : 2 of 12 features
|
|
Layer E (colony feedback) : 2 of 12 features
|
|
|
|
Suggested build order based on feature unlock count and dependency:
|
|
1. Layer A — second ant texture + MRT (unlocks most features)
|
|
2. Layer B — pheromone channel expansion + per-channel blur params
|
|
3. Layer C — bit-packed cell metadata (small change, high leverage)
|
|
4. Layer E — colony aggregate readback (needed before caste switching)
|
|
5. Layer D — spatial neighbor queries (hardest, fewest features, do last)
|
|
|
|
|
|
## WebGL2 requirements
|
|
|
|
Several infrastructure pieces depend on WebGL2 features:
|
|
|
|
MRT (multiple render targets) layers A, D
|
|
gl.drawBuffers() writing 2+ textures per pass
|
|
pixel buffer objects layer E async readback
|
|
integer textures (optional) cleaner bit packing in layers A, C
|
|
|
|
The simulation already uses three.js WebGLRenderer. Confirm it's running
|
|
in WebGL2 mode (three.js defaults to WebGL2 when available). If targeting
|
|
WebGL1 fallback, MRT requires WEBGL_draw_buffers extension and some
|
|
features may not be feasible.
|