Skip to content

Commit 0de7b88

Browse files
Anarchidclaude
andcommitted
Overdrive cables: remove dead weight from the cable feature (redo)
Redo of the pre-tent-rework cleanup (16be8d501 on cable-cutting) against current master — the original predates the tent geometry, the coverage-slot ghost sentinel move and the cableTangent budget work, so it was re-derived item by item rather than merged. No intended behaviour change; the live render path is untouched. - Bubble-phase integration (dead): the FS advects bubbles purely from gameTime via the crossfaded speed ladder; the CPU-side integrated phase it never read is gone (widthOfCapacity, flowToSpeed, BUBBLE_* constants, per-edge bubblePhase/Speed/AnchorTime, both catch-up loops, bubbleBakeTime, the commented-out bakeTime uniform plumbing and the FS's dead bakeTime declaration). Vertex payload shrinks 10 -> 9 floats: vertGrid is now vec3 (eff, flow, isOwnAlly/ghost flag) end-to-end — Lua generators + both VBO Defines, VS attribute, GS DataVS and ghost checks (.w -> .z); the FS already read the flag from .z since the tangent budget commit. - Grid-efficiency send: EFF_EPSILON/lastSentEff were never read. Deleted, and the comment that claimed eff changes force a re-send now documents the actual behaviour (eff hue refreshes on the FORCE_SEND_TICKS cadence). - geomCache: invalidation bookkeeping for a cache that was never implemented. Removed the table and all four .valid sites. - Dead CPU-router remnants: redeclared math locals, unused BRANCH_*/router constants (SEG_LENGTH, NOISE_AMP_ABS, MERGE_ANGLE, STEM_FRACTION, Lua GROWTH_RATE/WITHER_RATE), Hash/HashUnit/GetTrunkWidth/NoisyPath/ normalizeAngle, MarkGridDirty, GetNodePcurrent (and now-orphaned generatorDefs), the prange column, spGetGroundHeight/MAP_* captures, two unused spec captures, the 'old angle clustering' tombstone comment. - Shaders: drop the twig tipR/twigHWt/CONE_TIP_WIDTH chain (twig is a 3-vert triangle; tip emitted as tip3D, geometry identical — header vert counts fixed 4 -> 3); delete the unreachable t > 0.90 FS discard (t <= EDGE_BUFFER = 0.55 by construction); refresh the comments that described the removed CPU phase anchoring. luacheck: 79 -> 74 warnings; all remaining are the gl/GL unsynced false-positives and the pre-existing 'edges' shadowing. luac5.1 -p clean. Shader compile/link still needs a dev run to confirm (GLSL can't be checked offline). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
1 parent f44852c commit 0de7b88

4 files changed

Lines changed: 97 additions & 331 deletions

File tree

LuaRules/Gadgets/Shaders/gfx_overdrive_cables.frag.glsl

Lines changed: 12 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@
66
uniform sampler2D infoTex;
77
uniform sampler2D cableTex;
88
uniform float gameTime;
9-
uniform float bakeTime;
109
uniform float enableFlow; // 1.0 = full bubble pass; 0.0 = static cables (no animation)
1110
uniform float ghostsEnabled; // 1.0 = ghost branch active; 0.0 = enemy OOL discards immediately
1211
uniform sampler2DShadow shadowTex; // engine shadow map ($shadow); sampled for shadow reception
@@ -26,7 +25,7 @@ in DataGS {
2625
float width;
2726
vec2 cableUV;
2827
vec2 timeData;
29-
vec3 gridData; // was vec4; .z was unused, dropped to fund cableTangent (see GS BUDGET NOTE). Old .w (isOwnAlly / ghost flag) is now .z.
28+
vec3 gridData; // (gridEfficiency, flow, isOwnAlly / ghost flag); see GS BUDGET NOTE
3029
vec3 cableTangent; // smooth per-vertex cable along-direction; interpolated → drives the lit frame below
3130
float spawnAlongMain; // overloaded: main ribbon → lenPerSeg; twig → twig-along
3231
flat int gsSlot;
@@ -39,9 +38,10 @@ in DataGS {
3938
// Pure aesthetic constants; nothing here changes geometry or topology.
4039
// =====================================================================
4140

42-
// Grow/wither animation rates (elmos/s) — must match unsynced GROWTH_RATE/
43-
// WITHER_RATE so the CPU-side bubble phase anchor and the FS-side growth
44-
// front sweep at the same speed.
41+
// Grow/wither animation rates (elmos/s). The CPU only supplies appear/wither
42+
// timestamps (timeData); the fronts sweep entirely FS-side. The unsynced
43+
// WITHER_HOLD_FRAMES must stay generous relative to worst-case cable length /
44+
// WITHER_RATE so geometry outlives the animation.
4545
const float GROWTH_RATE = 250.0;
4646
const float WITHER_RATE = 400.0;
4747

@@ -100,8 +100,9 @@ const float SIZE_4 = 1.32;
100100
// dimFactor instead but always render.
101101
const float ENEMY_LOS_CUT = 0.1;
102102

103-
// Bubble flow mapping. Must mirror Lua flowToSpeed() exactly for CPU-baked
104-
// phase anchoring + FS extrapolation to remain continuous across baking.
103+
// Bubble flow mapping. Advection is FS-only: phase derives purely from
104+
// gameTime via the crossfaded speed ladder in main() — nothing is
105+
// CPU-integrated, so there is no bake to stay continuous across.
105106
const float MAX_SPEED = 130.0;
106107
const float MAX_DENSITY_FLOW = 100.0;
107108
const float MIN_TRUNK_W = 3.0;
@@ -176,10 +177,9 @@ float hash1(float n) {
176177
// Shading: faint inner glow + Fresnel rim + small offset highlight, all with
177178
// smoothstep edges to avoid pixelation at oblique camera angles. Returns
178179
// (body, specular).
179-
// `phase` is the integrated travel distance baked + extrapolated by the
180-
// caller (CPU integrates ∫ speed dt, shader extrapolates the last segment
181-
// with the current speed). Subtracting from `along` advects bubbles smoothly
182-
// across speed changes.
180+
// `phase` is the travel distance the caller derives from gameTime (via the
181+
// crossfaded speed ladder in main(), so subtle flow changes can't teleport
182+
// bubbles). Subtracting it from `along` advects the bubbles.
183183
//
184184
// Returns vec3: (body, specular, halo). Caller composites all three with
185185
// possibly different colour weights for richer look.
@@ -299,7 +299,6 @@ float getShadowCoeff(vec3 wp, float NdotL) {
299299
void main() {
300300
float v = cableUV.y * EDGE_BUFFER;
301301
float t = abs(v);
302-
if (t > 0.90) discard;
303302

304303
// Visual grow/wither: cableUV.x is distance along cable in elmos.
305304
// Growth front advances from u=0 forward.
@@ -566,14 +565,12 @@ void main() {
566565
// - Two layered streams of bubbles (big + small) with random per-bubble
567566
// size + cross-axis offset, so the cable looks like a real bubbly
568567
// slurry instead of a metronome of identical dots.
569-
// Bubble speed/density mapping. MUST match the CPU's flowToSpeed for the
570-
// integrated phase anchoring to stay consistent.
571568
//
572569
// Cable thickness conveys capacity (orthogonal);
573570
float flow = gridData.y;
574571
// Linear thickness divisor: a cable 4× thicker than min gets its flow
575572
// signal scaled to 1/4 before the sqrt → ~0.5× visual liveliness. Slight
576-
// negative bias for thick cables, matching the CPU's flowToSpeed.
573+
// negative bias for thick cables ("wide pipe, same flow, calmer look").
577574
float thicknessRatio = max(1.0, width / MIN_TRUNK_W);
578575
float effFlow = max(flow, 0.0) / thicknessRatio;
579576
float flowFactor = min(1.0, sqrt(effFlow / MAX_DENSITY_FLOW));

LuaRules/Gadgets/Shaders/gfx_overdrive_cables.geom.glsl

Lines changed: 30 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
// max_vertices budget, so we can:
1010
// invocation 0 → left tent slope (ridge→ground, SEGMENTS+1 × 2 verts)
1111
// invocation 1 → right tent slope (ridge→ground, SEGMENTS+1 × 2 verts)
12-
// invocations 2..N-1 → one twig each (4 verts), conditional on a hash
12+
// invocations 2..N-1 → one twig each (3 verts), conditional on a hash
1313
// Splitting the cross-section into two single-sheet slopes (one per invocation)
1414
// is what lets the full raised-ridge tent fit: each slope gets its own
1515
// GL_MAX_GEOMETRY_OUTPUT_COMPONENTS budget, so neither busts the 1024 ceiling a
@@ -21,7 +21,7 @@ layout (lines, invocations = 5) in;
2121
// 46 verts/invocation fits the min-spec total-components budget once the
2222
// per-vertex cableTangent varying is included (46 × 22 = 1012 ≤ 1024). The
2323
// tent-slope invocations use the full 46 (= 23 boundaries × 2); twig
24-
// invocations use 4.
24+
// invocations use 3.
2525
layout (triangle_strip, max_vertices = 46) out;
2626

2727
uniform sampler2D heightmapTex;
@@ -42,7 +42,7 @@ layout (std430, binding = 6) coherent buffer cableCoverageBuffer {
4242
in DataVS {
4343
vec2 vsWorldXZ;
4444
vec3 vsCableData;
45-
vec4 vsGridData;
45+
vec3 vsGridData;
4646
flat int vsSlot;
4747
} dataIn[];
4848

@@ -52,11 +52,12 @@ in DataVS {
5252
// pulse animation). For main-ribbon fragments (isBranch<0.5) it carries the
5353
// cable's len-per-segment so the FS can derive a per-segment bit index for
5454
// the coverage SSBO. The two semantics are disjoint by isBranch so no conflict.
55-
// BUDGET NOTE: `gridData` is a vec3 (was vec4 — the .z component was never read
56-
// by the FS). Shrinking it reclaimed one component so `cableTangent` could be
57-
// added without busting GL_MAX_GEOMETRY_OUTPUT_COMPONENTS' total budget: with
58-
// the tangent the per-vertex count is 22 (incl. gl_Position), so max_vertices
59-
// drops 50→46 to keep 46 × 22 = 1012 ≤ 1024 (min-spec).
55+
// BUDGET NOTE: `gridData` is a vec3 (eff, flow, isOwnAlly/ghost flag). It was a
56+
// vec4 whose .z carried a CPU-integrated bubble phase the FS never read;
57+
// dropping that float (now removed end-to-end — the CPU sends 3 floats)
58+
// reclaimed the component that funds `cableTangent`: with the tangent the
59+
// per-vertex count is 22 (incl. gl_Position), so max_vertices is 46 to keep
60+
// 46 × 22 = 1012 ≤ 1024 (min-spec GL_MAX_GEOMETRY_OUTPUT_COMPONENTS).
6061
out DataGS {
6162
vec3 worldPos;
6263
float capacity;
@@ -162,14 +163,13 @@ const int PLACEMENT_OVERSAMPLE = 2;
162163
const int MAX_GRID = MAX_SEGMENTS * 2; // local-array bound for the scan
163164
const float KINK_GAIN = 0.15;
164165

165-
// Twig parameters mirror the Lua-side BRANCH_* constants.
166+
// Twig parameters (chance/geometry of the small side branches).
166167
const float BRANCH_CHANCE = 0.85;
167168
const float BRANCH_LEN_MIN = 6.0;
168169
const float BRANCH_LEN_MAX = 8.0;
169170
const float BRANCH_ANGLE_MIN = 1.2;
170171
const float BRANCH_ANGLE_MAX = 1.5;
171172
const float BRANCH_WIDTH = 2.1;
172-
const float CONE_TIP_WIDTH = 0.0;
173173
const float BRANCH_WIDTH_TWIG_LENGTH_FACTOR = 2.0;
174174

175175
// Clearance over the heightmap, applied along the cable's chord-averaged surface
@@ -196,14 +196,14 @@ float gOutSpawnAlong = 0.0;
196196
int gOutSlot = -1;
197197

198198
void emitVtx(vec3 wp, vec3 tangent3D, vec2 cuv,
199-
float w, vec4 grid, vec2 td, float cap) {
199+
float w, vec3 grid, vec2 td, float cap) {
200200
worldPos = wp;
201201
capacity = cap;
202202
isBranch = gOutBranch;
203203
width = w;
204204
cableUV = cuv;
205205
timeData = td;
206-
gridData = grid.xyw; // .z dropped (unused by FS); see DataGS BUDGET NOTE
206+
gridData = grid;
207207
cableTangent = tangent3D; // smooth along-dir → FS interpolates the lit frame
208208
spawnAlongMain = gOutSpawnAlong;
209209
gsSlot = gOutSlot;
@@ -455,7 +455,7 @@ int placeRibbonVertices(vec2 a, vec2 d, vec2 perpAB, float lenAB, float arcDh,
455455
// verts/boundary), so the full tent only costs one extra invocation.
456456
void emitTentHalf(float side, vec2 a, vec2 d, vec2 perpAB,
457457
float halfW, float widthVal, float effAmp, float seed,
458-
vec4 gridD, vec2 timeD, float cap, int numSeg, float arcDh) {
458+
vec3 gridD, vec2 timeD, float cap, int numSeg, float arcDh) {
459459
gOutBranch = 0.0;
460460
float tentHeight = halfW * TENT_HEIGHT_FACTOR;
461461
// `along` is fed into the FS as cableUV.x and drives bubble advection.
@@ -594,7 +594,7 @@ void emitTentHalf(float side, vec2 a, vec2 d, vec2 perpAB,
594594
// hash says "no twig here" — leaving an empty primitive, which is a no-op.
595595
void emitTwig(vec2 a, vec2 d, vec2 perpAB,
596596
float halfMainW, float widthVal, float effAmp, float seed,
597-
vec4 gridD, vec2 timeD, float cap, float tCenter,
597+
vec3 gridD, vec2 timeD, float cap, float tCenter,
598598
float spawnAlongMain, int twigIdx, float arcDh, int numSeg,
599599
vec3 railPrev, vec3 railCenter) {
600600
// Resolve spawn point on the wiggly main path at tCenter so twigs root on
@@ -624,19 +624,14 @@ void emitTwig(vec2 a, vec2 d, vec2 perpAB,
624624
float bLen = BRANCH_LEN_MIN + widthVal*BRANCH_WIDTH_TWIG_LENGTH_FACTOR +
625625
gsHashU(spawn.x, spawn.y, twigSeed + 3.0) * (BRANCH_LEN_MAX - BRANCH_LEN_MIN);
626626

627+
// The twig is a 3-vertex triangle: a full-width root edge tapering to a
628+
// point at tip3D. The WIDTH varying passed to the FS stays UNIFORM at
629+
// `twigW` along the whole twig, so bubble math sees constant halfWidthE
630+
// and bubble radius/spacing don't change with along position; the visible
631+
// bubble simply projects smaller near the point. This decouples "bubble
632+
// flow looks uniform" from "twig has a cone shape".
627633
float twigW = max(2.5, widthVal * BRANCH_WIDTH);
628634
float twigHWr = min(twigW, widthVal * 0.55) * BRANCH_WIDTH;
629-
// Geometric cone taper at 0.45 — visible shape narrows toward the tip
630-
// (looks like a branch, not a tube). The WIDTH varying we pass to the FS
631-
// stays UNIFORM at `twigW` along the entire twig, so bubble math sees
632-
// constant halfWidthE and bubble radius/spacing don't change with along
633-
// position. The visible bubble naturally fits the tapered geometry: in v
634-
// space the bubble keeps the same cross-axis extent (relative to the
635-
// cable's UV cross), which projects to a smaller world-cross at the
636-
// thinner tip. At the very end the cable's `t > 0.9` cross discard clips
637-
// any bubble that runs off the tip. This decouples "bubble flow looks
638-
// uniform" from "twig has cone shape".
639-
float twigHWt = twigHWr * CONE_TIP_WIDTH;
640635

641636
// Build the twig as a flat ribbon in the slope's local tangent plane at
642637
// the spawn point. This way, viewing perpendicular to the slope, the twig
@@ -689,19 +684,17 @@ void emitTwig(vec2 a, vec2 d, vec2 perpAB,
689684

690685
vec3 rootL = root3D - twigPerp3D * twigHWr;
691686
vec3 rootR = root3D + twigPerp3D * twigHWr;
692-
vec3 tipL = tip3D - twigPerp3D * twigHWt;
693-
vec3 tipR = tip3D + twigPerp3D * twigHWt;
694687

695688
// cableUV.x carries the cable-wide along distance so the FS growth gate
696689
// hides this twig until the main growth front has reached spawnAlongMain.
697690
// vsTangent for twigs is the twigDir3D (the twig's along-direction); the
698691
// FS derives perp3D from cross(worldUp, vsTangent) so cylindrical lighting
699692
// follows the twig's pointing direction.
700693
gOutBranch = 1.0;
701-
gOutSpawnAlong = spawnAlongMain; // shared by all 4 twig vertices; lets FS compute twig-local along
702-
emitVtx(rootL, twigDir3D, vec2(spawnAlongMain, -1.0), twigW, gridD, timeD, cap);
703-
emitVtx(rootR, twigDir3D, vec2(spawnAlongMain, 1.0), twigW, gridD, timeD, cap);
704-
emitVtx(tipL, twigDir3D, vec2(spawnAlongMain + bLen, -1.0), twigW, gridD, timeD, cap);
694+
gOutSpawnAlong = spawnAlongMain; // shared by all 3 twig vertices; lets FS compute twig-local along
695+
emitVtx(rootL, twigDir3D, vec2(spawnAlongMain, -1.0), twigW, gridD, timeD, cap);
696+
emitVtx(rootR, twigDir3D, vec2(spawnAlongMain, 1.0), twigW, gridD, timeD, cap);
697+
emitVtx(tip3D, twigDir3D, vec2(spawnAlongMain + bLen, -1.0), twigW, gridD, timeD, cap);
705698
EndPrimitive();
706699
gOutSpawnAlong = 0.0;
707700
}
@@ -723,15 +716,15 @@ void main() {
723716

724717
float cap = dataIn[0].vsCableData.x;
725718
vec2 timeD = dataIn[0].vsCableData.yz;
726-
vec4 gridD = dataIn[0].vsGridData;
719+
vec3 gridD = dataIn[0].vsGridData;
727720

728-
// Ghost edges (gridData.w = -1.0) emit the same two tent slopes as live
721+
// Ghost edges (gridData.z = -1.0) emit the same two tent slopes as live
729722
// (no twigs), using the SAME wiggly path so the live→ghost transition has
730723
// no visual snap. Ghost FS path is fast (no lighting/bubble math), and the
731724
// GS still skips the 3 twig invocations. Coverage updates use the live
732725
// atomicAnd path with the wiggly samples → consistent with what the player
733726
// visually sees.
734-
bool isGhostEdge = gridD.w < -0.5;
727+
bool isGhostEdge = gridD.z < -0.5;
735728
if (isGhostEdge && gl_InvocationID > 1) return;
736729

737730
float widthVal = MIN_TRUNK_WIDTH +
@@ -797,8 +790,8 @@ void main() {
797790

798791
if (gl_InvocationID == 0) {
799792
// Coverage SSBO update — once per cable per frame, not per fragment.
800-
// Live edges (gridData.w >= -0.5) atomicOr bits for segments currently
801-
// in LOS; ghost edges (gridData.w < -0.5) atomicAnd to clear bits the
793+
// Live edges (gridData.z >= -0.5) atomicOr bits for segments currently
794+
// in LOS; ghost edges (gridData.z < -0.5) atomicAnd to clear bits the
802795
// player has re-scouted. Sampling along the actual wiggly path keeps
803796
// reveal accurate even with arc bias on slopes.
804797
//
@@ -814,7 +807,7 @@ void main() {
814807
// second per-frame update would risk double-clearing ghost bits.
815808
#if !defined(SHADOW_PASS) && !defined(DEFERRED_PASS)
816809
int slot = dataIn[0].vsSlot;
817-
bool isGhost = gridD.w < -0.5;
810+
bool isGhost = gridD.z < -0.5;
818811
// Hard gate on the user-facing ghosts toggle — skip ALL coverage
819812
// bookkeeping (the n-tap LOS scan, atomic ops, even the SSBO read)
820813
// when ghosts are off. Restores live-only perf parity with pre-slice-1.

LuaRules/Gadgets/Shaders/gfx_overdrive_cables.vert.glsl

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010

1111
layout (location = 0) in vec2 vertPos; // (x, z) world coords
1212
layout (location = 1) in vec3 vertData; // (capacity, appearTime, witherTime)
13-
layout (location = 2) in vec4 vertGrid; // (gridEfficiency, flow, bubblePhase, isOwnAlly)
13+
layout (location = 2) in vec3 vertGrid; // (gridEfficiency, flow, isOwnAlly / ghost flag)
1414
layout (location = 3) in float vertSlot; // coverage SSBO slot (-1 = disabled)
1515

1616
out gl_PerVertex {
@@ -20,7 +20,7 @@ out gl_PerVertex {
2020
out DataVS {
2121
vec2 vsWorldXZ;
2222
vec3 vsCableData;
23-
vec4 vsGridData;
23+
vec3 vsGridData;
2424
flat int vsSlot;
2525
};
2626

0 commit comments

Comments
 (0)