Design a new side-scrolling biome that evokes the toxic-beautiful, spore-laden, insect-haunted forest mood you want, while fitting the current v-type rendering model:
- one coherent background system, not a parallel special case
- config-driven level tuning through
level_*.cfg - shader-heavy ambience with deterministic procedural structure
- strong silhouette readability for gameplay under the existing CRT/post stack
This should be treated as a homage in mood, scale, and atmosphere, not a direct reproduction of any specific shot or asset.
The current codebase already supports the right architectural shape for this:
- data-driven background selection in
src/leveldef.c - per-theme tuning values in
src/leveldef.h - dedicated GPU environment passes in
src/main.c - custom fragment shaders for
underwater,fire, andiceinsrc/shaders/
That matters because the forest should be implemented as one more environment family in the same system:
- add
background=forest - add
forest.*tuning keys - add one main forest shader pass
- optionally add one low-res flora prepass, similar in spirit to the underwater kelp path
That is the cleanest route and respects the repo rule to extend existing systems rather than inventing a second environment framework.
The level should feel like a dead world that has overgrown into something sacred and hostile.
Primary visual traits:
- towering fungal trunks and shell-like stalks
- dense airborne spores that read like glowing snow, ash, and pollen at once
- translucent membranes, caps, and fronds that catch backlight
- insect movement that feels ritualistic rather than merely aggressive
- muted earth below, luminous poison above
- cathedral-scale negative space between giant growths
The strongest reference cues are not literal objects. They are:
- layered depth from foreground silhouettes against glowing haze
- irregular organic repetition
- drifting particulate density
- color contrast between rot and bioluminescence
- the sense that the forest is alive and digesting the ruins
The screen needs a few large, readable forms before fine detail matters:
- giant trunks with widened bases
- umbrella caps and ribbed fungal fans
- root arches bridging across lanes
- hanging tendrils and veil-like membranes
If these silhouettes work in pure black against a bright mist, the biome will already feel convincing.
Spores are the unifying motion layer:
- far field: fine dust haze
- mid field: drifting soft disks and filaments
- near field: large focus-pulling motes and streaks
This should not read like generic smoke. It needs softer edges, slower inertia, and subtle luminescent response.
Use light sparingly:
- cap rims
- underside membranes
- spore pockets
- vein structures in bark/fungus
- occasional alien pollen bursts
The dark mass should dominate. Glow is the accent that gives the forest its spiritual/unnatural quality.
Foreground darkness, midground structure, and backlit haze are what produce the filmic look.
The player should move through:
- near black silhouettes in front
- readable combat space in the middle
- luminous fog and giant forms behind
Almost everything should move, but very little should move quickly:
- spore drift
- tendril sway
- membrane breathing
- cap pulsing
- insect nest quiver
Fast motion should be reserved for enemies, attacks, and occasional set-piece reactions.
Follow the same pattern used by underwater, fire, and ice.
Probable implementation touch points:
src/leveldef.c- parse
background=forest - parse
forest.*tuning keys
- parse
src/leveldef.h- add forest tuning fields to
leveldef_level
- add forest tuning fields to
src/main.c- add
create_forest_resources(...) - add
record_gpu_forest(...) - optionally add
record_gpu_forest_flora(...)
- add
src/shaders/forest.frag- main composite shader
src/shaders/forest_flora.frag- optional low-res flora prepass
CMakeLists.txt- add shader build rules
This keeps the environment system unified and lets the level be authored exactly like current themed levels.
Pure hand-authored art will be slow and hard to iterate. Pure procedural generation will look muddy if not tightly art-directed. The best result here is hybrid:
- authored palette direction
- procedural large-form generation
- shader-based atmospheric detail
- deterministic seeded variation
Recommended split:
- offline or startup-generated silhouettes for major flora
- shader-generated haze, glow, and spores
- optional hand-authored masks or seed maps later, only if needed
L-systems are useful here, but mostly for generating believable growth skeletons:
- trunk branching
- hanging roots
- fungal rib structures
- vein networks
Do not expect raw L-system lines alone to look beautiful. The trick is:
- generate a branching scaffold
- convert it into widened ribbons, caps, ribs, and membranes
- shade it with backlight, thickness, translucency, and depth fog
That is where the "GPU shader magic" matters.
The forest should be composed as three logical layers.
Purpose:
- establish depth
- carry global color mood
- hold god rays and distant giant forms
Content:
- low-frequency fog fields
- distant canopy shapes
- faint bioluminescent clouds
- slow drifting spores
Implementation:
- full-screen fragment shader
- mostly noise-driven and cheap
Purpose:
- sell scale and identity
- provide large readable organic shapes
Content:
- trunks
- fungal caps
- arching roots
- membrane sheets
- clustered nest shapes
Implementation:
- ideally a low-res prepass texture or batched procedural silhouette field
- then composited in main forest pass
This is where an L-system or space-colonization-generated flora field pays off.
Purpose:
- push depth
- create cinematic framing
- make the player feel inside the biome
Content:
- dark foreground fronds
- near spore motes
- hanging strands crossing the view
- occasional bright membrane flashes
Implementation:
- a small number of high-contrast overlay shapes
- near-field particle layer
The underwater system already demonstrates a useful pattern:
- a low-res auxiliary render target for structured content
- a main full-screen shader that composites atmosphere and detail
That pattern maps well to a forest.
Recommended approach:
forest_flora.frag- renders large trunks, caps, tendrils, and membranes into a lower-res texture
- deterministic, camera-aware, tiled in world space
forest.frag- samples the flora texture
- adds haze, spores, bloom accents, backlight, and atmospheric grading
This gives:
- stable large forms
- cheap screen coverage
- good art direction
- controllable performance
Use multiple grammars, each specialized to a silhouette family. One grammar for everything will produce repetition.
Use for:
- main trunk forms
- giant umbrella caps
- layered shelf fungus
Example grammar sketch:
axiom: F
rules:
F -> FF-[+F+F+M]+[--F-M]
M -> [ccc]
Interpretation:
Fgrows branch segments+and-rotate[and]push/pop transformMspawns a membrane or cap clustercemits cap rib arcs rather than branch lines
Suggested generation controls:
- branch angle:
12..26 deg - length decay:
0.72..0.88 - width decay:
0.76..0.92 - depth:
3..6
Use for:
- hanging tendrils
- draping moss-like membranes
- cocoon curtains
Better than a strict tree grammar:
- generate anchored spline strands
- apply gravity bias
- modulate with curl noise
- add membrane bridges between neighboring strands
Use for:
- lane framing
- giant rib-like roots
- foreground silhouettes
Good method:
- two growth points seeded far apart
- connect them with biased branching
- fit a smoothed centerline spline
- widen into ribbon geometry
Use for:
- glowing translucent cap undersides
- radial shell structures
- leaf-like alien fans
Method:
- radial skeleton with uneven angular spacing
- secondary branches from each rib
- SDF thickness for translucency and rim lighting
For the largest plants, space colonization is often better than classic L-systems because it grows toward target regions and creates less obviously recursive shapes.
Use it for:
- canopy trunks reaching open pockets
- root systems seeking around combat lanes
- membrane ribs stretching toward attractor clouds
Algorithm sketch:
- Scatter attractor points inside a target volume.
- Start from one or more trunk seeds.
- For each attractor, find the nearest growth node within influence radius.
- Accumulate growth directions per node.
- Spawn new nodes in averaged directions.
- Remove attractors that are reached.
- Repeat until attractors are exhausted.
Useful properties:
- naturally avoids uniform branching
- can preserve gameplay corridors by leaving no-attractor regions
- easy to shape around authored safe lanes
This is likely the best generator for the most iconic giant flora silhouettes.
The generated structure should not stay as thin lines.
Convert skeletons into:
- widened spline ribbons for trunks and roots
- variable-radius capsules for stems
- fan SDFs for caps
- ribbon sheets between nearby tendrils for membranes
Important rendering trick:
- thickness should vary with branch age, depth layer, and local curvature
Example:
radius(node) = base_radius * age01^0.8 * depth_scale * curvature_relax
Where:
age01is normalized distance from tip to rootdepth_scalemakes far layers thinnercurvature_relaxprevents tight bends from ballooning
This is the main ambience effect.
Technique:
- layered FBM plus Worley noise plus advected particle highlights
Math sketch:
uv0 = world_uv * spore_scale
warp = vec2(fbm(uv0 + t*w0), fbm(uv0*1.7 - t*w1))
uv1 = uv0 + warp_amp * (warp*2 - 1)
mist = fbm(uv1)
cells = worley_edge(uv1 * cell_scale)
clusters = smoothstep(cluster_lo, cluster_hi, 1 - cells)
spore_density = mist * 0.65 + clusters * 0.35
Use cases:
- mist controls broad coverage
- Worley clusters create colony-like pockets
- animated warp keeps the air alive
Rendering note:
- keep near and far spore frequencies separate
- near spores should drift faster across screen space
- far spores should move with world space and camera parallax
This effect sells the alien beauty.
For each cap or membrane SDF:
d = sdf_membrane(p)
edge = 1 - smoothstep(0, rim_w, abs(d))
thickness = thickness_map(p)
backlight = saturate(dot(light_dir, membrane_normal)*0.5 + 0.5)
translucency = edge * thickness * backlight
Then color mix:
membrane_color = mix(dead_ochre, toxic_teal, translucency)
glow = translucency * pulse
This should be subtle. Too much emissive turns the scene into neon fantasy instead of poisonous beauty.
The fog should not be uniform. It should build around plant masses and underneath the canopy.
Method:
- derive an occlusion proxy from flora alpha or signed distance
- increase fog density near large forms
- darken the upper canopy and brighten shafts behind gaps
Math sketch:
fog_base = fbm(fog_uv)
fog_occ = smoothstep(occ_lo, occ_hi, flora_mass)
fog = fog_base * mix(0.7, 1.3, fog_occ)
This makes the world feel thick and inhabited.
This is a strong effect if kept restrained.
Cheap 2D method:
- Build a canopy mask from far flora silhouettes.
- Pick a directional light vector.
- Sample along that vector a few times.
- Accumulate openness.
- Multiply by haze density.
Sketch:
ray = 0
for i in 0..N-1:
suv = uv + light_dir * step_len * i
ray += 1 - canopy_mask(suv)
ray /= N
godray = ray * haze * ray_strength
Recommended:
- low sample count:
6..10 - animate light angle very slightly over long intervals
- fade rays in foreground-heavy areas so gameplay stays legible
Use a dedicated particle effect for hero motes rather than trying to do everything in the background shader.
Behavior:
- large spores drift with buoyancy and lateral curl
- some rotate and pulse
- some stretch into short streaks when camera speed changes
Motion model:
vel += curl_noise(pos * curl_scale, t * curl_speed) * curl_amp * dt
vel += wind * dt
vel *= 1 - drag * dt
pos += vel * dt
Visual model:
- soft disc
- double-ring shell
- faint inner nucleus
- optional chromatic fringe only at high glow moments
Use only a small count of near spores. The background shader can handle the dense fine particulate field.
To avoid flat silhouettes, add internal texture modulation.
For trunks:
- ridge noise aligned along branch tangent
- slow pulse in internal vein mask
- edge brightening from backlight
For caps:
- radial banding
- rib SDFs
- local translucency
- occasional darker bruise zones
This can all be done without actual textured sprites if the shapes are derived from distance fields or widened spline masks.
The most important trick is not detail generation. It is controlled backlighting.
If the scene is lit as:
- dark foreground
- luminous haze behind forms
- thin glowing rims on translucent structures
then even simple geometry will feel rich.
Do not use too many saturated colors. Limit the palette to a few anchored zones:
- bark/rot: deep umber, smoke brown, cold charcoal
- toxic life: dusty teal, mold green, pale cyan
- spore glow: bone white, faint gold, soft turquoise
Suggested palette anchors:
#120D0A#2D2018#5A4B32#6E8660#7AB8A6#C7E6D7
Keep the brightest values rare.
Near-black flora in front of the player sells scale better than adding more detail to the background.
Use:
- root arches entering from corners
- cap silhouettes crossing the top edge
- thin tendrils swaying in front of open space
These can be simple masks and still be extremely effective.
Static procedural scenes die immediately. Every subsystem should have a different slow phase:
- haze drift
- membrane pulse
- cap breathing
- tendril sway
- spore cluster migration
Use deterministic seeds per layer or tile so repetition is broken without randomness flicker.
The level will feel artificial if variation comes only from noise values. Variation should come from changes in shape:
- wide cap clusters versus thin reeds
- root ribs versus umbrella crowns
- open glades versus dense curtains
- dead trunks versus glowing young growth
That is why structure generation matters more than piling on more noise octaves.
Add a compact set of level-driven controls, following the current theme-key model.
Suggested fields for leveldef_level:
forest_spore_densityforest_spore_scaleforest_spore_drift_speedforest_haze_alphaforest_canopy_densityforest_parallax_strengthforest_flora_densityforest_branch_wobble_ampforest_branch_wobble_speedforest_membrane_glowforest_biolume_pulse_freqforest_godray_strengthforest_root_arch_densityforest_foreground_occluder_alpha
Suggested config names:
background=forest
forest.spore_density=1.150
forest.spore_scale=1.000
forest.spore_drift_speed=0.850
forest.haze_alpha=0.580
forest.canopy_density=0.900
forest.parallax_strength=1.100
forest.flora_density=1.050
forest.branch_wobble_amp=0.700
forest.branch_wobble_speed=0.650
forest.membrane_glow=0.520
forest.biolume_pulse_freq=0.440
forest.godray_strength=0.300
forest.root_arch_density=0.650
forest.foreground_occluder_alpha=0.520This is enough to make multiple variants:
- dim poisonous forest
- pale luminous spore grove
- root-choked tunnel biome
- flooded fungal basin
The industry plan already points toward a useful pattern: generate runtime assets from source data rather than hand-maintaining giant arrays.
For forest content, a generator could emit:
src/generated/forest_flora_lut.h- stem seeds
- cap descriptors
- membrane links
- tile-local spline control points
- per-layer seed tables
Advantages:
- deterministic results
- easy visual iteration
- no runtime heavy generation cost
- keeps runtime code simple
If fully procedural shapes do not give enough art direction, author a few source silhouettes as SVG and deform them procedurally at runtime:
- giant cap
- shell fan
- ribbed trunk
- hanging membrane
Then:
- pick variant by tile seed
- bend and scale in shader or CPU placement
- layer with procedural fog/spores
This hybrid approach often gives a better first shippable result than pure L-system output.
Another strong route is a tiny atlas of procedural motifs:
- cap
- pod
- rib fan
- cocoon
- veined leaf
Generate or author them once as SDFs, then:
- stamp them into the flora prepass
- warp them
- light them consistently
This can dramatically improve art direction without needing large texture assets.
The background must not fight gameplay readability.
Recommendations:
- keep combat lane brightness fairly stable in the center band
- push the strongest glows above and below the player path
- use enemy palette shifts sparingly; hostile silhouettes must remain readable
- avoid full-screen dense fog during bullet-heavy sections
Enemy types that fit this biome well:
- moth-like or beetle-like swarmers
- cocoon mines
- pollen burst hazards
- root arc traps
- glider insects with translucent wings
The atmosphere should imply an ecosystem, not just a color swap.
- a low-combat section
- dense floating spores
- huge backlit caps
- slow god rays
- occasional membrane pulse
This is the "beauty shot" zone.
- giant crossing arches form the level ceiling
- negative space lanes between rib roots
- distant luminous sacs breathe in sync
- the forest looks torn open
- exposed glowing interior veins
- more aggressive insect pressure
- haze turns from pale to sickly amber-green
- fewer glows
- heavy dust field
- enormous fossilized fungi silhouettes
- occasional surviving luminous colonies
These can all be different parameterizations of the same base system.
Goal:
- get the mood on screen quickly with minimal tech risk
Implement:
background=forest- one full-screen
forest.frag - layered spore haze
- distant canopy silhouettes from noise masks
- foreground occluder masks
- restrained god rays
This will already establish a strong mood, even before true generated flora exists.
Goal:
- add iconic giant structures
Implement:
- low-res
forest_florarender target - deterministic seeded trunk/cap/tendril generator
- world-space tiling
- alpha and emissive channels for later composite
This is the stage where the biome starts feeling special rather than just well-shaded.
Goal:
- make flora look authored and biologically plausible
Implement:
- L-system grammar library
- space-colonization generator for giant forms
- membrane bridges and vein fans
- cap translucency lighting
This is where the environment becomes worthy of being a signature level type.
Goal:
- add the magic and motion that players remember
Implement:
- large spore particles
- set-piece pulses
- nest breathing
- localized biolum bursts
- hazard-linked atmospheric responses
This biome can get expensive fast if everything becomes layered transparency.
Rules to keep it under control:
- keep the large-form flora in one low-res prepass when possible
- cap FBM octaves at
3..5 - prefer tiled world-space determinism over thousands of dynamic sprites
- reserve high-frequency detail for near field only
- use a small number of god-ray samples
- keep overdraw low in the center combat band
Most of the visual payoff should come from:
- silhouette
- compositing
- color grading
- slow motion
not from brute-force detail.
If the goal is the strongest result for the effort, the most effective stack is:
- giant generated fungal silhouettes in a low-res flora prepass
- a full-screen forest shader for spore haze, depth, and grading
- translucent membrane rim lighting on selected structures
- a small near-field particle layer for hero spores
- strong foreground framing
L-systems should help drive the skeletons of the megaflora, but they should not be the whole solution. The beauty will come from how those skeletons are widened, layered, backlit, fogged, and pulsed.
If you want one implementation path to pursue first, pursue this:
- Add a new
forestbackground family besideunderwater,fire, andice. - Build
forest.fragfirst to establish haze, palette, god rays, and distant canopy. - Reuse the underwater low-res auxiliary-pass pattern to create
forest_flora.frag. - Generate tileable flora descriptors from either:
- a small L-system library, or
- a space-colonization generator for large trunks plus simpler procedural caps.
- Composite the flora texture with spore fog and membrane rim lighting.
That route is technically aligned with the current engine, visually ambitious, and realistic to iterate without needing a huge hand-painted asset pipeline.