Land the six shield-panel sources, ported to built-in and compiling
Cut 1 of docs/shield-panel-cut.md: land ShieldPanel.cs, ShieldPanel.shader,
ShieldSim.compute, ShieldCommon.hlsl, TilingBuilder.cs and Tilings.cs under
Assets/Shaders/Compute/ShieldField/, alongside Lightning/ and Slime/.
Port (URP -> built-in, three substitutions per the map's §2.1 probe):
- drop the UniversalPipeline SubShader tag, add IgnoreProjector
- swap the URP Core.hlsl include for UnityCG.cginc
- TransformWorldToHClip -> UnityWorldToClipPos (3 call sites)
Drawing collapse (D2/D5): RenderParams has no shaderPass member in
6000.3.24f1, and a multi-pass material draws every SubShader pass from one
indirect call regardless, so the three per-pass args buffers and draw calls
collapse to one args buffer sized to the widest pass (Outline) and one
Graphics.RenderPrimitivesIndirect call. Deleted: argsOutline, argsConduit,
drawFill/drawOutline/drawConduits and their branches.
Also deleted: ShieldPanelTester (the click harness the operator ruled
against; it also can't run — activeInputHandler is New Input System only)
and [ExecuteAlways] (incompatible with a pool per D13; Rebuild stays on the
context menu).
Defects fixed:
- D1: TilingBuilder.cs's inner `dist` shadowed the outer one (CS0136);
renamed to nbrDist.
- D3: CellStatic was 15 floats against a declared Stride of 16; added pad2
on both the HLSL and C# sides.
- D4: `centroid` is reserved in the compute compiler; renamed the
ShieldCommon.hlsl field to `center` (kept the C# field name as-is; it's
not reserved there and the two sides don't need matching names, only
matching layout).
- D6: fixed the comment on _BreakBudget — it's two elements (count + cap),
not the "single element" the old comment claimed.
- D12: the two-element uint[] Update() allocated every frame is now a
cached field, cheaper still under a pool of many live panels.
- D18 (R5 — operator ruling, not deferred to a follow-up): Penrose ignored
panelRadius entirely — a fixed authored iteration count meant the tiling
was just uniformly rescaled, not resampled, so cell count never grew with
the panel. Every other tiling here uses cellSize to hold local cell size
constant while radius extends coverage; Penrose now derives its iteration
count the same way (each substitution divides edge length by phi, solved
for the count that brings a unit edge to ~cellSize once scaled to
radius). This retires the now-meaningless `penroseIterations` field and
the `subdivisions` parameter it threaded through Tilings.Generate.
- Also removed a dead `idx` counter in Tilings.Voronoi (D8's mention of
dead code; the Vector2.zero sentinel ambiguity itself is unfixed, same as
D7/D19 — Voronoi and TriHex stay recorded defects, not fixed, per R5).
Not fixed here, per the map: D7 (Voronoi is unusably slow) and D19 (TriHex
has zero dual edges) are recorded, not fixed. D9/D10 (fracture tuning) are
Cut 5. D17 (growth gate blocks the spawning hit) and D20 (_PanelSpawnTime
hardcoded to 0) are Cut 3.
Repo-specific fix not in the map: Assets/Scripts/UI/ContextMenu.cs defines
a global `ContextMenu` MonoBehaviour that shadows UnityEngine.ContextMenu,
so `[ContextMenu("Rebuild")]` doesn't compile here (CS0616) even though it
did in the map's throwaway probe project. Used the fully-qualified
`[UnityEngine.ContextMenu(...)]` instead.
Adds Assets/Scripts/Editor/ShieldPanelCut1Verify.cs, a batchmode probe (no
NUnit path exists per the map's Q7 — Tests.asmdef can't see
Assembly-CSharp): asserts zero shader/compute compile errors and that
Penrose cell count actually scales with panelRadius (D18). Also adds
tests/mutation_tests_shield_panel_cut1.py, which mutates the D18 fix, the
D4 fix, and the shaderPass deletion in turn and asserts the probe goes red
each time, restoring the source after each case (own file per the
convention mutation_tests_addressables_cut.py established, so as not to
touch item-provenance's or the addressables cut's harnesses).
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-18 06:08:04 +02:00
|
|
|
using System.Collections.Generic;
|
|
|
|
|
using UnityEngine;
|
|
|
|
|
using UnityEngine.Rendering;
|
|
|
|
|
|
|
|
|
|
namespace ShieldField
|
|
|
|
|
{
|
|
|
|
|
public enum FracturePattern { Radial = 0, Star = 1, Lance = 2, Spiral = 3, Crystal = 4, Fizzle = 5 }
|
|
|
|
|
|
|
|
|
|
public class ShieldPanel : MonoBehaviour
|
|
|
|
|
{
|
|
|
|
|
// ---------------- authoring ----------------
|
|
|
|
|
[Header("Tiling")]
|
|
|
|
|
public TilingKind tiling = TilingKind.PenroseP3;
|
|
|
|
|
public float panelRadius = 5f;
|
|
|
|
|
public float cellSize = 0.35f;
|
|
|
|
|
public bool mergeRhombs = true; // Penrose triangles -> P3 rhombs
|
|
|
|
|
public bool takeDual = false; // render the dual tiling instead
|
|
|
|
|
public int seed = 1337;
|
|
|
|
|
|
|
|
|
|
[Header("Assets")]
|
|
|
|
|
public ComputeShader sim;
|
|
|
|
|
public Shader panelShader;
|
|
|
|
|
|
|
|
|
|
[Header("Materialisation")]
|
|
|
|
|
public float growDuration = 0.35f;
|
|
|
|
|
public float wavefrontSpeed = 8f;
|
|
|
|
|
public float delayJitter = 0.06f;
|
|
|
|
|
|
|
|
|
|
[Header("Wave")]
|
|
|
|
|
[Tooltip("c^2. Raise for a faster shock; watch the CFL warning in the console.")]
|
|
|
|
|
public float waveSpeed2 = 26f;
|
|
|
|
|
public float damping = 1.6f;
|
|
|
|
|
public float stressClamp = 4f;
|
Cut 5: hold the rim, clamp v, fix fracture's clock, tune to spec
R7 (operator ruling): the panel's emitters hold its edge, so the rim is no
longer a free surface. Deletes the openW ghost term end to end -- its only
consumer in ShieldSim.compute's Laplacian, and the accumulation loop in
TilingBuilder that built it -- rather than zeroing it at runtime. A broken
cell's own faces are untouched (KWaveStep's per-edge `solid` check already
treats a broken/ungrown neighbour as free), so the dicing cascade survives.
D9: v was unclamped while u was, so a hard hit drove v far past what one
substep's u-clamp could absorb, pinning u in compression indefinitely (the
measured collapse above energy ~60). Added `_VelClamp` and clamp v to it
every substep in KWaveStep, exposed as ShieldPanel.velocityClamp (100).
D10: KFracture dispatched once per frame but read `_Dt` still holding the
substep dt from the wave loop, so damage and temper erosion accrued at
1/substeps of the intended rate. Fracture's clock is the frame it settles
once per, not the substep, so it now gets the frame dt -- one line, not the
five-extra-dispatches-per-panel alternative (dispatch count is the measured
per-panel cost at up to 12 live panels).
D15: zero-init BuildLine's output struct so the compute compiler's dataflow
analysis stops flagging every branch as a potential non-initializer.
Tuning (ShieldPanel.cs Material header): tensileStrength 0.9->0.2,
temperErosion 2.2->12, damageGain 6->100, breakAt 0.75->0.08,
storedEnergyGain 0.8->1 -- makes a solid hit dice a local cluster around
impact, a weak hit leave nothing, and repeated weak hits erode temper
substantially without breaking anything outright.
Adds ShieldPanelCut5Verify (batchmode probe, same pattern as Cuts 1-3):
pins monotonic peak tension against energy (no D9-style collapse), solid
vs. weak hit outcomes, temper erosion under repeated sub-critical hits,
breaks clustering at the impact rather than the rim (R7), and a broken
cell's neighbours breaking more often than distant cells (the cascade).
D9 and D10 mutations were manually reverted and confirmed to fail their
respective checks; R7's reintroduced-rim mutation did not reliably fail
the cluster check within this probe's energy/step budget -- noted as a
known gap rather than papered over.
The map's own operator-level check ("repeated hits on the same spot break
through on a later hit than the first") is left to play: a closing hit
landed after several prior weak hits on one live panel instance measured
a materially lower peak tension than the same energy against a fresh
panel, a reproducible multi-hit interaction this cut did not chase to
ground.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-18 20:04:05 +02:00
|
|
|
[Tooltip("D9: bounds dU/dt so a hard hit cannot pin the field in compression forever. " +
|
|
|
|
|
"Too low flattens the wave; too high reproduces the saturation defect.")]
|
|
|
|
|
public float velocityClamp = 100f;
|
Land the six shield-panel sources, ported to built-in and compiling
Cut 1 of docs/shield-panel-cut.md: land ShieldPanel.cs, ShieldPanel.shader,
ShieldSim.compute, ShieldCommon.hlsl, TilingBuilder.cs and Tilings.cs under
Assets/Shaders/Compute/ShieldField/, alongside Lightning/ and Slime/.
Port (URP -> built-in, three substitutions per the map's §2.1 probe):
- drop the UniversalPipeline SubShader tag, add IgnoreProjector
- swap the URP Core.hlsl include for UnityCG.cginc
- TransformWorldToHClip -> UnityWorldToClipPos (3 call sites)
Drawing collapse (D2/D5): RenderParams has no shaderPass member in
6000.3.24f1, and a multi-pass material draws every SubShader pass from one
indirect call regardless, so the three per-pass args buffers and draw calls
collapse to one args buffer sized to the widest pass (Outline) and one
Graphics.RenderPrimitivesIndirect call. Deleted: argsOutline, argsConduit,
drawFill/drawOutline/drawConduits and their branches.
Also deleted: ShieldPanelTester (the click harness the operator ruled
against; it also can't run — activeInputHandler is New Input System only)
and [ExecuteAlways] (incompatible with a pool per D13; Rebuild stays on the
context menu).
Defects fixed:
- D1: TilingBuilder.cs's inner `dist` shadowed the outer one (CS0136);
renamed to nbrDist.
- D3: CellStatic was 15 floats against a declared Stride of 16; added pad2
on both the HLSL and C# sides.
- D4: `centroid` is reserved in the compute compiler; renamed the
ShieldCommon.hlsl field to `center` (kept the C# field name as-is; it's
not reserved there and the two sides don't need matching names, only
matching layout).
- D6: fixed the comment on _BreakBudget — it's two elements (count + cap),
not the "single element" the old comment claimed.
- D12: the two-element uint[] Update() allocated every frame is now a
cached field, cheaper still under a pool of many live panels.
- D18 (R5 — operator ruling, not deferred to a follow-up): Penrose ignored
panelRadius entirely — a fixed authored iteration count meant the tiling
was just uniformly rescaled, not resampled, so cell count never grew with
the panel. Every other tiling here uses cellSize to hold local cell size
constant while radius extends coverage; Penrose now derives its iteration
count the same way (each substitution divides edge length by phi, solved
for the count that brings a unit edge to ~cellSize once scaled to
radius). This retires the now-meaningless `penroseIterations` field and
the `subdivisions` parameter it threaded through Tilings.Generate.
- Also removed a dead `idx` counter in Tilings.Voronoi (D8's mention of
dead code; the Vector2.zero sentinel ambiguity itself is unfixed, same as
D7/D19 — Voronoi and TriHex stay recorded defects, not fixed, per R5).
Not fixed here, per the map: D7 (Voronoi is unusably slow) and D19 (TriHex
has zero dual edges) are recorded, not fixed. D9/D10 (fracture tuning) are
Cut 5. D17 (growth gate blocks the spawning hit) and D20 (_PanelSpawnTime
hardcoded to 0) are Cut 3.
Repo-specific fix not in the map: Assets/Scripts/UI/ContextMenu.cs defines
a global `ContextMenu` MonoBehaviour that shadows UnityEngine.ContextMenu,
so `[ContextMenu("Rebuild")]` doesn't compile here (CS0616) even though it
did in the map's throwaway probe project. Used the fully-qualified
`[UnityEngine.ContextMenu(...)]` instead.
Adds Assets/Scripts/Editor/ShieldPanelCut1Verify.cs, a batchmode probe (no
NUnit path exists per the map's Q7 — Tests.asmdef can't see
Assembly-CSharp): asserts zero shader/compute compile errors and that
Penrose cell count actually scales with panelRadius (D18). Also adds
tests/mutation_tests_shield_panel_cut1.py, which mutates the D18 fix, the
D4 fix, and the shaderPass deletion in turn and asserts the probe goes red
each time, restoring the source after each case (own file per the
convention mutation_tests_addressables_cut.py established, so as not to
touch item-provenance's or the addressables cut's harnesses).
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-18 06:08:04 +02:00
|
|
|
[Range(1, 16)] public int substeps = 6;
|
|
|
|
|
public float fixedSubstepDt = 0.0025f;
|
|
|
|
|
|
|
|
|
|
[Header("Through-thickness echo")]
|
|
|
|
|
[Tooltip("Substeps for one round trip through the plate. Small = fast back-face spall.")]
|
|
|
|
|
[Range(1, 15)] public int echoDelaySteps = 5;
|
|
|
|
|
[Tooltip("Negative: a free back face returns the compressive pulse as tension.")]
|
Cut 5 follow-up: strengthen the echo so weak-hit tension actually clears zero
Cut 5's own probe passed while breaking Cut 3's reuse rule: with R7 holding
the rim, the through-thickness echo (KEcho/_EchoReflect) is the only
remaining source of sub-critical tension for a weak hit, and its magnitude
(-0.65) was weaker than the still-live residual compression velocity left
by the direct KInject kick by the time the echo fired. The echo's own
header comment says a free-surface reflection inverts with unit
magnitude, matching the pre-R7 rim's Dirichlet condition -- -0.65 was a
lossy fraction that could reduce but never flip the sign of u, so a weak
hit's u never crossed positive and KFracture's `u <= 0` guard skipped the
sub-critical erosion branch forever, no matter how many hits landed.
Raised echoReflect from -0.65 to -0.9 (ShieldPanel.cs) -- enough margin to
flip a weak hit's residual compression into net tension without needing a
delay/seeding change; echoDelaySteps and KInject's seeding were not the
defect. All four probes now pass, including Cut 3's reuse rule (temper
0.4180 -> 0.3997 on a reused panel).
Also lands the multi-hit-reuse rule Cut 5's own report flagged as unpinned:
CheckRepeatedWeakHitsBreakWhereOneDoesNot in ShieldPanelCut5Verify.cs.
Energy 15 (not 8, whose peak tension of ~0.14 sits under tensileStrength
0.2 and can never break at any repetition) breaks on hit 2 of a live,
reused panel while a cold single hit -- given equal idle time (720 steps,
matching the multi-hit run's total budget, to rule out "just needed more
time to ring out") -- never breaks. This is the Cut 3 reuse rule and the
temper mechanism producing the operator-facing payoff together.
Verified: ShieldPanelCut1Verify, Cut2Verify, Cut3Verify, Cut5Verify all
PASS via batchmode (graphics device required, not -nographics).
Operator watch: echoReflect at -0.9 is close to the physical "unit
inversion" ceiling implied by the header comment; if later tuning raises
hit energies or lowers damping, watch for the echo overshooting into an
immediate break on a single moderate hit (observed at -1.4 and even -1.0
in earlier tuning passes) rather than the intended multi-hit erosion.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-18 20:22:51 +02:00
|
|
|
public float echoReflect = -0.9f;
|
Land the six shield-panel sources, ported to built-in and compiling
Cut 1 of docs/shield-panel-cut.md: land ShieldPanel.cs, ShieldPanel.shader,
ShieldSim.compute, ShieldCommon.hlsl, TilingBuilder.cs and Tilings.cs under
Assets/Shaders/Compute/ShieldField/, alongside Lightning/ and Slime/.
Port (URP -> built-in, three substitutions per the map's §2.1 probe):
- drop the UniversalPipeline SubShader tag, add IgnoreProjector
- swap the URP Core.hlsl include for UnityCG.cginc
- TransformWorldToHClip -> UnityWorldToClipPos (3 call sites)
Drawing collapse (D2/D5): RenderParams has no shaderPass member in
6000.3.24f1, and a multi-pass material draws every SubShader pass from one
indirect call regardless, so the three per-pass args buffers and draw calls
collapse to one args buffer sized to the widest pass (Outline) and one
Graphics.RenderPrimitivesIndirect call. Deleted: argsOutline, argsConduit,
drawFill/drawOutline/drawConduits and their branches.
Also deleted: ShieldPanelTester (the click harness the operator ruled
against; it also can't run — activeInputHandler is New Input System only)
and [ExecuteAlways] (incompatible with a pool per D13; Rebuild stays on the
context menu).
Defects fixed:
- D1: TilingBuilder.cs's inner `dist` shadowed the outer one (CS0136);
renamed to nbrDist.
- D3: CellStatic was 15 floats against a declared Stride of 16; added pad2
on both the HLSL and C# sides.
- D4: `centroid` is reserved in the compute compiler; renamed the
ShieldCommon.hlsl field to `center` (kept the C# field name as-is; it's
not reserved there and the two sides don't need matching names, only
matching layout).
- D6: fixed the comment on _BreakBudget — it's two elements (count + cap),
not the "single element" the old comment claimed.
- D12: the two-element uint[] Update() allocated every frame is now a
cached field, cheaper still under a pool of many live panels.
- D18 (R5 — operator ruling, not deferred to a follow-up): Penrose ignored
panelRadius entirely — a fixed authored iteration count meant the tiling
was just uniformly rescaled, not resampled, so cell count never grew with
the panel. Every other tiling here uses cellSize to hold local cell size
constant while radius extends coverage; Penrose now derives its iteration
count the same way (each substitution divides edge length by phi, solved
for the count that brings a unit edge to ~cellSize once scaled to
radius). This retires the now-meaningless `penroseIterations` field and
the `subdivisions` parameter it threaded through Tilings.Generate.
- Also removed a dead `idx` counter in Tilings.Voronoi (D8's mention of
dead code; the Vector2.zero sentinel ambiguity itself is unfixed, same as
D7/D19 — Voronoi and TriHex stay recorded defects, not fixed, per R5).
Not fixed here, per the map: D7 (Voronoi is unusably slow) and D19 (TriHex
has zero dual edges) are recorded, not fixed. D9/D10 (fracture tuning) are
Cut 5. D17 (growth gate blocks the spawning hit) and D20 (_PanelSpawnTime
hardcoded to 0) are Cut 3.
Repo-specific fix not in the map: Assets/Scripts/UI/ContextMenu.cs defines
a global `ContextMenu` MonoBehaviour that shadows UnityEngine.ContextMenu,
so `[ContextMenu("Rebuild")]` doesn't compile here (CS0616) even though it
did in the map's throwaway probe project. Used the fully-qualified
`[UnityEngine.ContextMenu(...)]` instead.
Adds Assets/Scripts/Editor/ShieldPanelCut1Verify.cs, a batchmode probe (no
NUnit path exists per the map's Q7 — Tests.asmdef can't see
Assembly-CSharp): asserts zero shader/compute compile errors and that
Penrose cell count actually scales with panelRadius (D18). Also adds
tests/mutation_tests_shield_panel_cut1.py, which mutates the D18 fix, the
D4 fix, and the shaderPass deletion in turn and asserts the probe goes red
each time, restoring the source after each case (own file per the
convention mutation_tests_addressables_cut.py established, so as not to
touch item-provenance's or the addressables cut's harnesses).
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-18 06:08:04 +02:00
|
|
|
|
|
|
|
|
[Header("Material (tempered)")]
|
Cut 5: hold the rim, clamp v, fix fracture's clock, tune to spec
R7 (operator ruling): the panel's emitters hold its edge, so the rim is no
longer a free surface. Deletes the openW ghost term end to end -- its only
consumer in ShieldSim.compute's Laplacian, and the accumulation loop in
TilingBuilder that built it -- rather than zeroing it at runtime. A broken
cell's own faces are untouched (KWaveStep's per-edge `solid` check already
treats a broken/ungrown neighbour as free), so the dicing cascade survives.
D9: v was unclamped while u was, so a hard hit drove v far past what one
substep's u-clamp could absorb, pinning u in compression indefinitely (the
measured collapse above energy ~60). Added `_VelClamp` and clamp v to it
every substep in KWaveStep, exposed as ShieldPanel.velocityClamp (100).
D10: KFracture dispatched once per frame but read `_Dt` still holding the
substep dt from the wave loop, so damage and temper erosion accrued at
1/substeps of the intended rate. Fracture's clock is the frame it settles
once per, not the substep, so it now gets the frame dt -- one line, not the
five-extra-dispatches-per-panel alternative (dispatch count is the measured
per-panel cost at up to 12 live panels).
D15: zero-init BuildLine's output struct so the compute compiler's dataflow
analysis stops flagging every branch as a potential non-initializer.
Tuning (ShieldPanel.cs Material header): tensileStrength 0.9->0.2,
temperErosion 2.2->12, damageGain 6->100, breakAt 0.75->0.08,
storedEnergyGain 0.8->1 -- makes a solid hit dice a local cluster around
impact, a weak hit leave nothing, and repeated weak hits erode temper
substantially without breaking anything outright.
Adds ShieldPanelCut5Verify (batchmode probe, same pattern as Cuts 1-3):
pins monotonic peak tension against energy (no D9-style collapse), solid
vs. weak hit outcomes, temper erosion under repeated sub-critical hits,
breaks clustering at the impact rather than the rim (R7), and a broken
cell's neighbours breaking more often than distant cells (the cascade).
D9 and D10 mutations were manually reverted and confirmed to fail their
respective checks; R7's reintroduced-rim mutation did not reliably fail
the cluster check within this probe's energy/step budget -- noted as a
known gap rather than papered over.
The map's own operator-level check ("repeated hits on the same spot break
through on a later hit than the first") is left to play: a closing hit
landed after several prior weak hits on one live panel instance measured
a materially lower peak tension than the same energy against a fresh
panel, a reproducible multi-hit interaction this cut did not chase to
ground.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-18 20:04:05 +02:00
|
|
|
public float tensileStrength = 0.2f;
|
Land the six shield-panel sources, ported to built-in and compiling
Cut 1 of docs/shield-panel-cut.md: land ShieldPanel.cs, ShieldPanel.shader,
ShieldSim.compute, ShieldCommon.hlsl, TilingBuilder.cs and Tilings.cs under
Assets/Shaders/Compute/ShieldField/, alongside Lightning/ and Slime/.
Port (URP -> built-in, three substitutions per the map's §2.1 probe):
- drop the UniversalPipeline SubShader tag, add IgnoreProjector
- swap the URP Core.hlsl include for UnityCG.cginc
- TransformWorldToHClip -> UnityWorldToClipPos (3 call sites)
Drawing collapse (D2/D5): RenderParams has no shaderPass member in
6000.3.24f1, and a multi-pass material draws every SubShader pass from one
indirect call regardless, so the three per-pass args buffers and draw calls
collapse to one args buffer sized to the widest pass (Outline) and one
Graphics.RenderPrimitivesIndirect call. Deleted: argsOutline, argsConduit,
drawFill/drawOutline/drawConduits and their branches.
Also deleted: ShieldPanelTester (the click harness the operator ruled
against; it also can't run — activeInputHandler is New Input System only)
and [ExecuteAlways] (incompatible with a pool per D13; Rebuild stays on the
context menu).
Defects fixed:
- D1: TilingBuilder.cs's inner `dist` shadowed the outer one (CS0136);
renamed to nbrDist.
- D3: CellStatic was 15 floats against a declared Stride of 16; added pad2
on both the HLSL and C# sides.
- D4: `centroid` is reserved in the compute compiler; renamed the
ShieldCommon.hlsl field to `center` (kept the C# field name as-is; it's
not reserved there and the two sides don't need matching names, only
matching layout).
- D6: fixed the comment on _BreakBudget — it's two elements (count + cap),
not the "single element" the old comment claimed.
- D12: the two-element uint[] Update() allocated every frame is now a
cached field, cheaper still under a pool of many live panels.
- D18 (R5 — operator ruling, not deferred to a follow-up): Penrose ignored
panelRadius entirely — a fixed authored iteration count meant the tiling
was just uniformly rescaled, not resampled, so cell count never grew with
the panel. Every other tiling here uses cellSize to hold local cell size
constant while radius extends coverage; Penrose now derives its iteration
count the same way (each substitution divides edge length by phi, solved
for the count that brings a unit edge to ~cellSize once scaled to
radius). This retires the now-meaningless `penroseIterations` field and
the `subdivisions` parameter it threaded through Tilings.Generate.
- Also removed a dead `idx` counter in Tilings.Voronoi (D8's mention of
dead code; the Vector2.zero sentinel ambiguity itself is unfixed, same as
D7/D19 — Voronoi and TriHex stay recorded defects, not fixed, per R5).
Not fixed here, per the map: D7 (Voronoi is unusably slow) and D19 (TriHex
has zero dual edges) are recorded, not fixed. D9/D10 (fracture tuning) are
Cut 5. D17 (growth gate blocks the spawning hit) and D20 (_PanelSpawnTime
hardcoded to 0) are Cut 3.
Repo-specific fix not in the map: Assets/Scripts/UI/ContextMenu.cs defines
a global `ContextMenu` MonoBehaviour that shadows UnityEngine.ContextMenu,
so `[ContextMenu("Rebuild")]` doesn't compile here (CS0616) even though it
did in the map's throwaway probe project. Used the fully-qualified
`[UnityEngine.ContextMenu(...)]` instead.
Adds Assets/Scripts/Editor/ShieldPanelCut1Verify.cs, a batchmode probe (no
NUnit path exists per the map's Q7 — Tests.asmdef can't see
Assembly-CSharp): asserts zero shader/compute compile errors and that
Penrose cell count actually scales with panelRadius (D18). Also adds
tests/mutation_tests_shield_panel_cut1.py, which mutates the D18 fix, the
D4 fix, and the shaderPass deletion in turn and asserts the probe goes red
each time, restoring the source after each case (own file per the
convention mutation_tests_addressables_cut.py established, so as not to
touch item-provenance's or the addressables cut's harnesses).
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-18 06:08:04 +02:00
|
|
|
[Tooltip("Surface compression at full charge. This is the shield's hit budget.")]
|
|
|
|
|
public float temperInit = 0.55f;
|
Cut 5: hold the rim, clamp v, fix fracture's clock, tune to spec
R7 (operator ruling): the panel's emitters hold its edge, so the rim is no
longer a free surface. Deletes the openW ghost term end to end -- its only
consumer in ShieldSim.compute's Laplacian, and the accumulation loop in
TilingBuilder that built it -- rather than zeroing it at runtime. A broken
cell's own faces are untouched (KWaveStep's per-edge `solid` check already
treats a broken/ungrown neighbour as free), so the dicing cascade survives.
D9: v was unclamped while u was, so a hard hit drove v far past what one
substep's u-clamp could absorb, pinning u in compression indefinitely (the
measured collapse above energy ~60). Added `_VelClamp` and clamp v to it
every substep in KWaveStep, exposed as ShieldPanel.velocityClamp (100).
D10: KFracture dispatched once per frame but read `_Dt` still holding the
substep dt from the wave loop, so damage and temper erosion accrued at
1/substeps of the intended rate. Fracture's clock is the frame it settles
once per, not the substep, so it now gets the frame dt -- one line, not the
five-extra-dispatches-per-panel alternative (dispatch count is the measured
per-panel cost at up to 12 live panels).
D15: zero-init BuildLine's output struct so the compute compiler's dataflow
analysis stops flagging every branch as a potential non-initializer.
Tuning (ShieldPanel.cs Material header): tensileStrength 0.9->0.2,
temperErosion 2.2->12, damageGain 6->100, breakAt 0.75->0.08,
storedEnergyGain 0.8->1 -- makes a solid hit dice a local cluster around
impact, a weak hit leave nothing, and repeated weak hits erode temper
substantially without breaking anything outright.
Adds ShieldPanelCut5Verify (batchmode probe, same pattern as Cuts 1-3):
pins monotonic peak tension against energy (no D9-style collapse), solid
vs. weak hit outcomes, temper erosion under repeated sub-critical hits,
breaks clustering at the impact rather than the rim (R7), and a broken
cell's neighbours breaking more often than distant cells (the cascade).
D9 and D10 mutations were manually reverted and confirmed to fail their
respective checks; R7's reintroduced-rim mutation did not reliably fail
the cluster check within this probe's energy/step budget -- noted as a
known gap rather than papered over.
The map's own operator-level check ("repeated hits on the same spot break
through on a later hit than the first") is left to play: a closing hit
landed after several prior weak hits on one live panel instance measured
a materially lower peak tension than the same energy against a fresh
panel, a reproducible multi-hit interaction this cut did not chase to
ground.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-18 20:04:05 +02:00
|
|
|
public float temperErosion = 12f;
|
Land the six shield-panel sources, ported to built-in and compiling
Cut 1 of docs/shield-panel-cut.md: land ShieldPanel.cs, ShieldPanel.shader,
ShieldSim.compute, ShieldCommon.hlsl, TilingBuilder.cs and Tilings.cs under
Assets/Shaders/Compute/ShieldField/, alongside Lightning/ and Slime/.
Port (URP -> built-in, three substitutions per the map's §2.1 probe):
- drop the UniversalPipeline SubShader tag, add IgnoreProjector
- swap the URP Core.hlsl include for UnityCG.cginc
- TransformWorldToHClip -> UnityWorldToClipPos (3 call sites)
Drawing collapse (D2/D5): RenderParams has no shaderPass member in
6000.3.24f1, and a multi-pass material draws every SubShader pass from one
indirect call regardless, so the three per-pass args buffers and draw calls
collapse to one args buffer sized to the widest pass (Outline) and one
Graphics.RenderPrimitivesIndirect call. Deleted: argsOutline, argsConduit,
drawFill/drawOutline/drawConduits and their branches.
Also deleted: ShieldPanelTester (the click harness the operator ruled
against; it also can't run — activeInputHandler is New Input System only)
and [ExecuteAlways] (incompatible with a pool per D13; Rebuild stays on the
context menu).
Defects fixed:
- D1: TilingBuilder.cs's inner `dist` shadowed the outer one (CS0136);
renamed to nbrDist.
- D3: CellStatic was 15 floats against a declared Stride of 16; added pad2
on both the HLSL and C# sides.
- D4: `centroid` is reserved in the compute compiler; renamed the
ShieldCommon.hlsl field to `center` (kept the C# field name as-is; it's
not reserved there and the two sides don't need matching names, only
matching layout).
- D6: fixed the comment on _BreakBudget — it's two elements (count + cap),
not the "single element" the old comment claimed.
- D12: the two-element uint[] Update() allocated every frame is now a
cached field, cheaper still under a pool of many live panels.
- D18 (R5 — operator ruling, not deferred to a follow-up): Penrose ignored
panelRadius entirely — a fixed authored iteration count meant the tiling
was just uniformly rescaled, not resampled, so cell count never grew with
the panel. Every other tiling here uses cellSize to hold local cell size
constant while radius extends coverage; Penrose now derives its iteration
count the same way (each substitution divides edge length by phi, solved
for the count that brings a unit edge to ~cellSize once scaled to
radius). This retires the now-meaningless `penroseIterations` field and
the `subdivisions` parameter it threaded through Tilings.Generate.
- Also removed a dead `idx` counter in Tilings.Voronoi (D8's mention of
dead code; the Vector2.zero sentinel ambiguity itself is unfixed, same as
D7/D19 — Voronoi and TriHex stay recorded defects, not fixed, per R5).
Not fixed here, per the map: D7 (Voronoi is unusably slow) and D19 (TriHex
has zero dual edges) are recorded, not fixed. D9/D10 (fracture tuning) are
Cut 5. D17 (growth gate blocks the spawning hit) and D20 (_PanelSpawnTime
hardcoded to 0) are Cut 3.
Repo-specific fix not in the map: Assets/Scripts/UI/ContextMenu.cs defines
a global `ContextMenu` MonoBehaviour that shadows UnityEngine.ContextMenu,
so `[ContextMenu("Rebuild")]` doesn't compile here (CS0616) even though it
did in the map's throwaway probe project. Used the fully-qualified
`[UnityEngine.ContextMenu(...)]` instead.
Adds Assets/Scripts/Editor/ShieldPanelCut1Verify.cs, a batchmode probe (no
NUnit path exists per the map's Q7 — Tests.asmdef can't see
Assembly-CSharp): asserts zero shader/compute compile errors and that
Penrose cell count actually scales with panelRadius (D18). Also adds
tests/mutation_tests_shield_panel_cut1.py, which mutates the D18 fix, the
D4 fix, and the shaderPass deletion in turn and asserts the probe goes red
each time, restoring the source after each case (own file per the
convention mutation_tests_addressables_cut.py established, so as not to
touch item-provenance's or the addressables cut's harnesses).
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-18 06:08:04 +02:00
|
|
|
[Tooltip("How much stored core energy a break dumps back into the field.")]
|
Cut 5: hold the rim, clamp v, fix fracture's clock, tune to spec
R7 (operator ruling): the panel's emitters hold its edge, so the rim is no
longer a free surface. Deletes the openW ghost term end to end -- its only
consumer in ShieldSim.compute's Laplacian, and the accumulation loop in
TilingBuilder that built it -- rather than zeroing it at runtime. A broken
cell's own faces are untouched (KWaveStep's per-edge `solid` check already
treats a broken/ungrown neighbour as free), so the dicing cascade survives.
D9: v was unclamped while u was, so a hard hit drove v far past what one
substep's u-clamp could absorb, pinning u in compression indefinitely (the
measured collapse above energy ~60). Added `_VelClamp` and clamp v to it
every substep in KWaveStep, exposed as ShieldPanel.velocityClamp (100).
D10: KFracture dispatched once per frame but read `_Dt` still holding the
substep dt from the wave loop, so damage and temper erosion accrued at
1/substeps of the intended rate. Fracture's clock is the frame it settles
once per, not the substep, so it now gets the frame dt -- one line, not the
five-extra-dispatches-per-panel alternative (dispatch count is the measured
per-panel cost at up to 12 live panels).
D15: zero-init BuildLine's output struct so the compute compiler's dataflow
analysis stops flagging every branch as a potential non-initializer.
Tuning (ShieldPanel.cs Material header): tensileStrength 0.9->0.2,
temperErosion 2.2->12, damageGain 6->100, breakAt 0.75->0.08,
storedEnergyGain 0.8->1 -- makes a solid hit dice a local cluster around
impact, a weak hit leave nothing, and repeated weak hits erode temper
substantially without breaking anything outright.
Adds ShieldPanelCut5Verify (batchmode probe, same pattern as Cuts 1-3):
pins monotonic peak tension against energy (no D9-style collapse), solid
vs. weak hit outcomes, temper erosion under repeated sub-critical hits,
breaks clustering at the impact rather than the rim (R7), and a broken
cell's neighbours breaking more often than distant cells (the cascade).
D9 and D10 mutations were manually reverted and confirmed to fail their
respective checks; R7's reintroduced-rim mutation did not reliably fail
the cluster check within this probe's energy/step budget -- noted as a
known gap rather than papered over.
The map's own operator-level check ("repeated hits on the same spot break
through on a later hit than the first") is left to play: a closing hit
landed after several prior weak hits on one live panel instance measured
a materially lower peak tension than the same energy against a fresh
panel, a reproducible multi-hit interaction this cut did not chase to
ground.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-18 20:04:05 +02:00
|
|
|
public float storedEnergyGain = 1f;
|
|
|
|
|
public float damageGain = 100f;
|
|
|
|
|
[Range(0.05f, 1f)] public float breakAt = 0.08f;
|
Land the six shield-panel sources, ported to built-in and compiling
Cut 1 of docs/shield-panel-cut.md: land ShieldPanel.cs, ShieldPanel.shader,
ShieldSim.compute, ShieldCommon.hlsl, TilingBuilder.cs and Tilings.cs under
Assets/Shaders/Compute/ShieldField/, alongside Lightning/ and Slime/.
Port (URP -> built-in, three substitutions per the map's §2.1 probe):
- drop the UniversalPipeline SubShader tag, add IgnoreProjector
- swap the URP Core.hlsl include for UnityCG.cginc
- TransformWorldToHClip -> UnityWorldToClipPos (3 call sites)
Drawing collapse (D2/D5): RenderParams has no shaderPass member in
6000.3.24f1, and a multi-pass material draws every SubShader pass from one
indirect call regardless, so the three per-pass args buffers and draw calls
collapse to one args buffer sized to the widest pass (Outline) and one
Graphics.RenderPrimitivesIndirect call. Deleted: argsOutline, argsConduit,
drawFill/drawOutline/drawConduits and their branches.
Also deleted: ShieldPanelTester (the click harness the operator ruled
against; it also can't run — activeInputHandler is New Input System only)
and [ExecuteAlways] (incompatible with a pool per D13; Rebuild stays on the
context menu).
Defects fixed:
- D1: TilingBuilder.cs's inner `dist` shadowed the outer one (CS0136);
renamed to nbrDist.
- D3: CellStatic was 15 floats against a declared Stride of 16; added pad2
on both the HLSL and C# sides.
- D4: `centroid` is reserved in the compute compiler; renamed the
ShieldCommon.hlsl field to `center` (kept the C# field name as-is; it's
not reserved there and the two sides don't need matching names, only
matching layout).
- D6: fixed the comment on _BreakBudget — it's two elements (count + cap),
not the "single element" the old comment claimed.
- D12: the two-element uint[] Update() allocated every frame is now a
cached field, cheaper still under a pool of many live panels.
- D18 (R5 — operator ruling, not deferred to a follow-up): Penrose ignored
panelRadius entirely — a fixed authored iteration count meant the tiling
was just uniformly rescaled, not resampled, so cell count never grew with
the panel. Every other tiling here uses cellSize to hold local cell size
constant while radius extends coverage; Penrose now derives its iteration
count the same way (each substitution divides edge length by phi, solved
for the count that brings a unit edge to ~cellSize once scaled to
radius). This retires the now-meaningless `penroseIterations` field and
the `subdivisions` parameter it threaded through Tilings.Generate.
- Also removed a dead `idx` counter in Tilings.Voronoi (D8's mention of
dead code; the Vector2.zero sentinel ambiguity itself is unfixed, same as
D7/D19 — Voronoi and TriHex stay recorded defects, not fixed, per R5).
Not fixed here, per the map: D7 (Voronoi is unusably slow) and D19 (TriHex
has zero dual edges) are recorded, not fixed. D9/D10 (fracture tuning) are
Cut 5. D17 (growth gate blocks the spawning hit) and D20 (_PanelSpawnTime
hardcoded to 0) are Cut 3.
Repo-specific fix not in the map: Assets/Scripts/UI/ContextMenu.cs defines
a global `ContextMenu` MonoBehaviour that shadows UnityEngine.ContextMenu,
so `[ContextMenu("Rebuild")]` doesn't compile here (CS0616) even though it
did in the map's throwaway probe project. Used the fully-qualified
`[UnityEngine.ContextMenu(...)]` instead.
Adds Assets/Scripts/Editor/ShieldPanelCut1Verify.cs, a batchmode probe (no
NUnit path exists per the map's Q7 — Tests.asmdef can't see
Assembly-CSharp): asserts zero shader/compute compile errors and that
Penrose cell count actually scales with panelRadius (D18). Also adds
tests/mutation_tests_shield_panel_cut1.py, which mutates the D18 fix, the
D4 fix, and the shaderPass deletion in turn and asserts the probe goes red
each time, restoring the source after each case (own file per the
convention mutation_tests_addressables_cut.py established, so as not to
touch item-provenance's or the addressables cut's harnesses).
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-18 06:08:04 +02:00
|
|
|
[Range(0f, 0.9f)] public float crackStart = 0.18f;
|
|
|
|
|
[Tooltip("Breaks permitted per frame. Without this the cascade finishes in one frame and you see nothing.")]
|
|
|
|
|
[Range(0.001f, 1f)] public float breakBudgetFraction = 0.02f;
|
|
|
|
|
|
|
|
|
|
[Header("Shards")]
|
|
|
|
|
public float radialKick = 1.6f;
|
|
|
|
|
public float lateralKick = 2.2f;
|
|
|
|
|
public float normalKick = 1.1f;
|
|
|
|
|
public float spinKick = 7f;
|
|
|
|
|
public float shardLife = 2.5f;
|
|
|
|
|
public float shardDrag = 0.7f;
|
|
|
|
|
[Range(2, 8)] public int maxShards = 6;
|
|
|
|
|
|
|
|
|
|
[Header("Render")]
|
|
|
|
|
public float conduitGain = 2.5f;
|
|
|
|
|
|
|
|
|
|
// ---------------- runtime ----------------
|
|
|
|
|
struct PendingHit
|
|
|
|
|
{
|
|
|
|
|
public Vector2 pos, dir;
|
|
|
|
|
public float energy, radius, obliquity, spin;
|
|
|
|
|
public FracturePattern pattern;
|
|
|
|
|
public float starArms, focus, twist;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
readonly Queue<PendingHit> pending = new();
|
|
|
|
|
|
|
|
|
|
TilingData data;
|
|
|
|
|
Material mat;
|
|
|
|
|
MaterialPropertyBlock mpb;
|
|
|
|
|
|
|
|
|
|
GraphicsBuffer bCells, bVerts, bEdges, bState;
|
|
|
|
|
GraphicsBuffer bUA, bUB, bV, bEcho, bFlow, bVisible, bBudget;
|
|
|
|
|
GraphicsBuffer argsFill;
|
|
|
|
|
|
|
|
|
|
bool uUsingA = true;
|
|
|
|
|
int kInit, kInject, kWave, kEcho, kFracture, kUpdate;
|
|
|
|
|
int groups, echoSlot;
|
|
|
|
|
float simTime;
|
|
|
|
|
Bounds worldBounds;
|
|
|
|
|
uint[] budgetScratch; // cached to avoid a per-frame allocation (D12)
|
|
|
|
|
|
2026-09-18 13:51:11 +02:00
|
|
|
// D17/D20 (Cut 3, docs/shield-panel-cut.md): the panel's own arm instant, backdated by
|
|
|
|
|
// one growDuration so a cell at the strike centre (spawnDelay ~= 0) is already past the
|
|
|
|
|
// KInject/KWaveStep/KFracture growth gate the moment it is struck. See ResetSim.
|
|
|
|
|
float panelSpawnTime;
|
|
|
|
|
CellState[] growthSeed; // cached per-cell arm state; rebuilt only when the tiling is (re)built,
|
|
|
|
|
// re-uploaded (not reallocated) on every ResetSim — no interception allocation.
|
|
|
|
|
|
Land the six shield-panel sources, ported to built-in and compiling
Cut 1 of docs/shield-panel-cut.md: land ShieldPanel.cs, ShieldPanel.shader,
ShieldSim.compute, ShieldCommon.hlsl, TilingBuilder.cs and Tilings.cs under
Assets/Shaders/Compute/ShieldField/, alongside Lightning/ and Slime/.
Port (URP -> built-in, three substitutions per the map's §2.1 probe):
- drop the UniversalPipeline SubShader tag, add IgnoreProjector
- swap the URP Core.hlsl include for UnityCG.cginc
- TransformWorldToHClip -> UnityWorldToClipPos (3 call sites)
Drawing collapse (D2/D5): RenderParams has no shaderPass member in
6000.3.24f1, and a multi-pass material draws every SubShader pass from one
indirect call regardless, so the three per-pass args buffers and draw calls
collapse to one args buffer sized to the widest pass (Outline) and one
Graphics.RenderPrimitivesIndirect call. Deleted: argsOutline, argsConduit,
drawFill/drawOutline/drawConduits and their branches.
Also deleted: ShieldPanelTester (the click harness the operator ruled
against; it also can't run — activeInputHandler is New Input System only)
and [ExecuteAlways] (incompatible with a pool per D13; Rebuild stays on the
context menu).
Defects fixed:
- D1: TilingBuilder.cs's inner `dist` shadowed the outer one (CS0136);
renamed to nbrDist.
- D3: CellStatic was 15 floats against a declared Stride of 16; added pad2
on both the HLSL and C# sides.
- D4: `centroid` is reserved in the compute compiler; renamed the
ShieldCommon.hlsl field to `center` (kept the C# field name as-is; it's
not reserved there and the two sides don't need matching names, only
matching layout).
- D6: fixed the comment on _BreakBudget — it's two elements (count + cap),
not the "single element" the old comment claimed.
- D12: the two-element uint[] Update() allocated every frame is now a
cached field, cheaper still under a pool of many live panels.
- D18 (R5 — operator ruling, not deferred to a follow-up): Penrose ignored
panelRadius entirely — a fixed authored iteration count meant the tiling
was just uniformly rescaled, not resampled, so cell count never grew with
the panel. Every other tiling here uses cellSize to hold local cell size
constant while radius extends coverage; Penrose now derives its iteration
count the same way (each substitution divides edge length by phi, solved
for the count that brings a unit edge to ~cellSize once scaled to
radius). This retires the now-meaningless `penroseIterations` field and
the `subdivisions` parameter it threaded through Tilings.Generate.
- Also removed a dead `idx` counter in Tilings.Voronoi (D8's mention of
dead code; the Vector2.zero sentinel ambiguity itself is unfixed, same as
D7/D19 — Voronoi and TriHex stay recorded defects, not fixed, per R5).
Not fixed here, per the map: D7 (Voronoi is unusably slow) and D19 (TriHex
has zero dual edges) are recorded, not fixed. D9/D10 (fracture tuning) are
Cut 5. D17 (growth gate blocks the spawning hit) and D20 (_PanelSpawnTime
hardcoded to 0) are Cut 3.
Repo-specific fix not in the map: Assets/Scripts/UI/ContextMenu.cs defines
a global `ContextMenu` MonoBehaviour that shadows UnityEngine.ContextMenu,
so `[ContextMenu("Rebuild")]` doesn't compile here (CS0616) even though it
did in the map's throwaway probe project. Used the fully-qualified
`[UnityEngine.ContextMenu(...)]` instead.
Adds Assets/Scripts/Editor/ShieldPanelCut1Verify.cs, a batchmode probe (no
NUnit path exists per the map's Q7 — Tests.asmdef can't see
Assembly-CSharp): asserts zero shader/compute compile errors and that
Penrose cell count actually scales with panelRadius (D18). Also adds
tests/mutation_tests_shield_panel_cut1.py, which mutates the D18 fix, the
D4 fix, and the shaderPass deletion in turn and asserts the probe goes red
each time, restoring the source after each case (own file per the
convention mutation_tests_addressables_cut.py established, so as not to
touch item-provenance's or the addressables cut's harnesses).
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-18 06:08:04 +02:00
|
|
|
const int EchoSlots = 16;
|
|
|
|
|
static readonly int MaxV = Limits.MaxVerts;
|
|
|
|
|
|
|
|
|
|
// shader ids
|
|
|
|
|
static readonly int idCells = Shader.PropertyToID("_Cells");
|
|
|
|
|
static readonly int idVerts = Shader.PropertyToID("_Verts");
|
|
|
|
|
static readonly int idEdges = Shader.PropertyToID("_Edges");
|
|
|
|
|
static readonly int idState = Shader.PropertyToID("_State");
|
|
|
|
|
static readonly int idUIn = Shader.PropertyToID("_UIn");
|
|
|
|
|
static readonly int idUOut = Shader.PropertyToID("_UOut");
|
|
|
|
|
static readonly int idU = Shader.PropertyToID("_U");
|
|
|
|
|
static readonly int idV = Shader.PropertyToID("_V");
|
|
|
|
|
static readonly int idEcho = Shader.PropertyToID("_Echo");
|
|
|
|
|
static readonly int idFlow = Shader.PropertyToID("_EdgeFlow");
|
|
|
|
|
static readonly int idVisible = Shader.PropertyToID("_Visible");
|
|
|
|
|
static readonly int idBudget = Shader.PropertyToID("_BreakBudget");
|
|
|
|
|
|
|
|
|
|
// ==============================================================
|
2026-09-18 13:51:11 +02:00
|
|
|
// Cut 3 (docs/shield-panel-cut.md §2.4): a pooled panel is SetActive(false)/(true) every
|
|
|
|
|
// time it is returned to and taken back out of Assets/Scripts/Prototype.cs's pool, which
|
|
|
|
|
// fires OnDisable/OnEnable on every reuse, not just once at load. The pool's entire reason
|
|
|
|
|
// for existing is that a rebuild is 1.3-21 ms and a ResetSim is 8-11 us (§2.4); OnEnable
|
|
|
|
|
// unconditionally rebuilding and OnDisable unconditionally releasing would pay the
|
|
|
|
|
// expensive path on every single interception, silently defeating the pool. So the tiling
|
|
|
|
|
// and buffers are built once (guarded by `data == null`) and released only when the
|
|
|
|
|
// GameObject is actually destroyed, not merely deactivated -- ShieldInterceptor.ResetSim()
|
|
|
|
|
// is what re-arms a reused instance (see ResetSim below), not OnEnable.
|
|
|
|
|
void OnEnable() { if (data == null) Rebuild(); }
|
|
|
|
|
void OnDisable() { }
|
|
|
|
|
void OnDestroy() { ReleaseAll(); }
|
|
|
|
|
|
|
|
|
|
// Cut 3 verification surface: counts real tiling builds so the pooling fix (OnEnable only
|
|
|
|
|
// rebuilds when data == null) can be pinned against a regression that rebuilds on every
|
|
|
|
|
// pooled reuse (see ShieldPanelCut3Verify.CheckPoolReuseDoesNotRebuild).
|
|
|
|
|
public int DebugRebuildCount { get; private set; }
|
Land the six shield-panel sources, ported to built-in and compiling
Cut 1 of docs/shield-panel-cut.md: land ShieldPanel.cs, ShieldPanel.shader,
ShieldSim.compute, ShieldCommon.hlsl, TilingBuilder.cs and Tilings.cs under
Assets/Shaders/Compute/ShieldField/, alongside Lightning/ and Slime/.
Port (URP -> built-in, three substitutions per the map's §2.1 probe):
- drop the UniversalPipeline SubShader tag, add IgnoreProjector
- swap the URP Core.hlsl include for UnityCG.cginc
- TransformWorldToHClip -> UnityWorldToClipPos (3 call sites)
Drawing collapse (D2/D5): RenderParams has no shaderPass member in
6000.3.24f1, and a multi-pass material draws every SubShader pass from one
indirect call regardless, so the three per-pass args buffers and draw calls
collapse to one args buffer sized to the widest pass (Outline) and one
Graphics.RenderPrimitivesIndirect call. Deleted: argsOutline, argsConduit,
drawFill/drawOutline/drawConduits and their branches.
Also deleted: ShieldPanelTester (the click harness the operator ruled
against; it also can't run — activeInputHandler is New Input System only)
and [ExecuteAlways] (incompatible with a pool per D13; Rebuild stays on the
context menu).
Defects fixed:
- D1: TilingBuilder.cs's inner `dist` shadowed the outer one (CS0136);
renamed to nbrDist.
- D3: CellStatic was 15 floats against a declared Stride of 16; added pad2
on both the HLSL and C# sides.
- D4: `centroid` is reserved in the compute compiler; renamed the
ShieldCommon.hlsl field to `center` (kept the C# field name as-is; it's
not reserved there and the two sides don't need matching names, only
matching layout).
- D6: fixed the comment on _BreakBudget — it's two elements (count + cap),
not the "single element" the old comment claimed.
- D12: the two-element uint[] Update() allocated every frame is now a
cached field, cheaper still under a pool of many live panels.
- D18 (R5 — operator ruling, not deferred to a follow-up): Penrose ignored
panelRadius entirely — a fixed authored iteration count meant the tiling
was just uniformly rescaled, not resampled, so cell count never grew with
the panel. Every other tiling here uses cellSize to hold local cell size
constant while radius extends coverage; Penrose now derives its iteration
count the same way (each substitution divides edge length by phi, solved
for the count that brings a unit edge to ~cellSize once scaled to
radius). This retires the now-meaningless `penroseIterations` field and
the `subdivisions` parameter it threaded through Tilings.Generate.
- Also removed a dead `idx` counter in Tilings.Voronoi (D8's mention of
dead code; the Vector2.zero sentinel ambiguity itself is unfixed, same as
D7/D19 — Voronoi and TriHex stay recorded defects, not fixed, per R5).
Not fixed here, per the map: D7 (Voronoi is unusably slow) and D19 (TriHex
has zero dual edges) are recorded, not fixed. D9/D10 (fracture tuning) are
Cut 5. D17 (growth gate blocks the spawning hit) and D20 (_PanelSpawnTime
hardcoded to 0) are Cut 3.
Repo-specific fix not in the map: Assets/Scripts/UI/ContextMenu.cs defines
a global `ContextMenu` MonoBehaviour that shadows UnityEngine.ContextMenu,
so `[ContextMenu("Rebuild")]` doesn't compile here (CS0616) even though it
did in the map's throwaway probe project. Used the fully-qualified
`[UnityEngine.ContextMenu(...)]` instead.
Adds Assets/Scripts/Editor/ShieldPanelCut1Verify.cs, a batchmode probe (no
NUnit path exists per the map's Q7 — Tests.asmdef can't see
Assembly-CSharp): asserts zero shader/compute compile errors and that
Penrose cell count actually scales with panelRadius (D18). Also adds
tests/mutation_tests_shield_panel_cut1.py, which mutates the D18 fix, the
D4 fix, and the shaderPass deletion in turn and asserts the probe goes red
each time, restoring the source after each case (own file per the
convention mutation_tests_addressables_cut.py established, so as not to
touch item-provenance's or the addressables cut's harnesses).
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-18 06:08:04 +02:00
|
|
|
|
|
|
|
|
[UnityEngine.ContextMenu("Rebuild")]
|
|
|
|
|
public void Rebuild()
|
|
|
|
|
{
|
2026-09-18 13:51:11 +02:00
|
|
|
DebugRebuildCount++;
|
Land the six shield-panel sources, ported to built-in and compiling
Cut 1 of docs/shield-panel-cut.md: land ShieldPanel.cs, ShieldPanel.shader,
ShieldSim.compute, ShieldCommon.hlsl, TilingBuilder.cs and Tilings.cs under
Assets/Shaders/Compute/ShieldField/, alongside Lightning/ and Slime/.
Port (URP -> built-in, three substitutions per the map's §2.1 probe):
- drop the UniversalPipeline SubShader tag, add IgnoreProjector
- swap the URP Core.hlsl include for UnityCG.cginc
- TransformWorldToHClip -> UnityWorldToClipPos (3 call sites)
Drawing collapse (D2/D5): RenderParams has no shaderPass member in
6000.3.24f1, and a multi-pass material draws every SubShader pass from one
indirect call regardless, so the three per-pass args buffers and draw calls
collapse to one args buffer sized to the widest pass (Outline) and one
Graphics.RenderPrimitivesIndirect call. Deleted: argsOutline, argsConduit,
drawFill/drawOutline/drawConduits and their branches.
Also deleted: ShieldPanelTester (the click harness the operator ruled
against; it also can't run — activeInputHandler is New Input System only)
and [ExecuteAlways] (incompatible with a pool per D13; Rebuild stays on the
context menu).
Defects fixed:
- D1: TilingBuilder.cs's inner `dist` shadowed the outer one (CS0136);
renamed to nbrDist.
- D3: CellStatic was 15 floats against a declared Stride of 16; added pad2
on both the HLSL and C# sides.
- D4: `centroid` is reserved in the compute compiler; renamed the
ShieldCommon.hlsl field to `center` (kept the C# field name as-is; it's
not reserved there and the two sides don't need matching names, only
matching layout).
- D6: fixed the comment on _BreakBudget — it's two elements (count + cap),
not the "single element" the old comment claimed.
- D12: the two-element uint[] Update() allocated every frame is now a
cached field, cheaper still under a pool of many live panels.
- D18 (R5 — operator ruling, not deferred to a follow-up): Penrose ignored
panelRadius entirely — a fixed authored iteration count meant the tiling
was just uniformly rescaled, not resampled, so cell count never grew with
the panel. Every other tiling here uses cellSize to hold local cell size
constant while radius extends coverage; Penrose now derives its iteration
count the same way (each substitution divides edge length by phi, solved
for the count that brings a unit edge to ~cellSize once scaled to
radius). This retires the now-meaningless `penroseIterations` field and
the `subdivisions` parameter it threaded through Tilings.Generate.
- Also removed a dead `idx` counter in Tilings.Voronoi (D8's mention of
dead code; the Vector2.zero sentinel ambiguity itself is unfixed, same as
D7/D19 — Voronoi and TriHex stay recorded defects, not fixed, per R5).
Not fixed here, per the map: D7 (Voronoi is unusably slow) and D19 (TriHex
has zero dual edges) are recorded, not fixed. D9/D10 (fracture tuning) are
Cut 5. D17 (growth gate blocks the spawning hit) and D20 (_PanelSpawnTime
hardcoded to 0) are Cut 3.
Repo-specific fix not in the map: Assets/Scripts/UI/ContextMenu.cs defines
a global `ContextMenu` MonoBehaviour that shadows UnityEngine.ContextMenu,
so `[ContextMenu("Rebuild")]` doesn't compile here (CS0616) even though it
did in the map's throwaway probe project. Used the fully-qualified
`[UnityEngine.ContextMenu(...)]` instead.
Adds Assets/Scripts/Editor/ShieldPanelCut1Verify.cs, a batchmode probe (no
NUnit path exists per the map's Q7 — Tests.asmdef can't see
Assembly-CSharp): asserts zero shader/compute compile errors and that
Penrose cell count actually scales with panelRadius (D18). Also adds
tests/mutation_tests_shield_panel_cut1.py, which mutates the D18 fix, the
D4 fix, and the shaderPass deletion in turn and asserts the probe goes red
each time, restoring the source after each case (own file per the
convention mutation_tests_addressables_cut.py established, so as not to
touch item-provenance's or the addressables cut's harnesses).
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-18 06:08:04 +02:00
|
|
|
ReleaseAll();
|
|
|
|
|
if (sim == null || panelShader == null) return;
|
|
|
|
|
|
|
|
|
|
var soup = Tilings.Generate(tiling, panelRadius, cellSize, seed);
|
|
|
|
|
|
|
|
|
|
if (tiling == TilingKind.PenroseP3 && mergeRhombs)
|
|
|
|
|
soup = TilingBuilder.MergeRhombs(soup);
|
|
|
|
|
if (takeDual)
|
|
|
|
|
soup = TilingBuilder.TakeDual(soup);
|
|
|
|
|
|
|
|
|
|
data = TilingBuilder.Build(soup,
|
|
|
|
|
weldEps: Mathf.Max(cellSize * 0.02f, 1e-4f),
|
|
|
|
|
clipRadius: panelRadius,
|
|
|
|
|
wavefrontOrigin: Vector2.zero,
|
|
|
|
|
wavefrontSpeed: wavefrontSpeed,
|
|
|
|
|
delayJitter: delayJitter,
|
|
|
|
|
seed: seed);
|
|
|
|
|
|
|
|
|
|
AllocateBuffers();
|
|
|
|
|
CacheKernels();
|
|
|
|
|
ResetSim();
|
|
|
|
|
CheckCfl();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void AllocateBuffers()
|
|
|
|
|
{
|
|
|
|
|
int n = data.CellCount;
|
|
|
|
|
groups = Mathf.CeilToInt(n / 64f);
|
|
|
|
|
|
|
|
|
|
bCells = new GraphicsBuffer(GraphicsBuffer.Target.Structured, n, CellStatic.Stride);
|
|
|
|
|
bVerts = new GraphicsBuffer(GraphicsBuffer.Target.Structured, data.verts.Length, sizeof(float) * 2);
|
|
|
|
|
bEdges = new GraphicsBuffer(GraphicsBuffer.Target.Structured, Mathf.Max(data.EdgeCount, 1), DualEdge.Stride);
|
2026-09-18 13:51:11 +02:00
|
|
|
bState = new GraphicsBuffer(GraphicsBuffer.Target.Structured, n, CellState.Stride);
|
Land the six shield-panel sources, ported to built-in and compiling
Cut 1 of docs/shield-panel-cut.md: land ShieldPanel.cs, ShieldPanel.shader,
ShieldSim.compute, ShieldCommon.hlsl, TilingBuilder.cs and Tilings.cs under
Assets/Shaders/Compute/ShieldField/, alongside Lightning/ and Slime/.
Port (URP -> built-in, three substitutions per the map's §2.1 probe):
- drop the UniversalPipeline SubShader tag, add IgnoreProjector
- swap the URP Core.hlsl include for UnityCG.cginc
- TransformWorldToHClip -> UnityWorldToClipPos (3 call sites)
Drawing collapse (D2/D5): RenderParams has no shaderPass member in
6000.3.24f1, and a multi-pass material draws every SubShader pass from one
indirect call regardless, so the three per-pass args buffers and draw calls
collapse to one args buffer sized to the widest pass (Outline) and one
Graphics.RenderPrimitivesIndirect call. Deleted: argsOutline, argsConduit,
drawFill/drawOutline/drawConduits and their branches.
Also deleted: ShieldPanelTester (the click harness the operator ruled
against; it also can't run — activeInputHandler is New Input System only)
and [ExecuteAlways] (incompatible with a pool per D13; Rebuild stays on the
context menu).
Defects fixed:
- D1: TilingBuilder.cs's inner `dist` shadowed the outer one (CS0136);
renamed to nbrDist.
- D3: CellStatic was 15 floats against a declared Stride of 16; added pad2
on both the HLSL and C# sides.
- D4: `centroid` is reserved in the compute compiler; renamed the
ShieldCommon.hlsl field to `center` (kept the C# field name as-is; it's
not reserved there and the two sides don't need matching names, only
matching layout).
- D6: fixed the comment on _BreakBudget — it's two elements (count + cap),
not the "single element" the old comment claimed.
- D12: the two-element uint[] Update() allocated every frame is now a
cached field, cheaper still under a pool of many live panels.
- D18 (R5 — operator ruling, not deferred to a follow-up): Penrose ignored
panelRadius entirely — a fixed authored iteration count meant the tiling
was just uniformly rescaled, not resampled, so cell count never grew with
the panel. Every other tiling here uses cellSize to hold local cell size
constant while radius extends coverage; Penrose now derives its iteration
count the same way (each substitution divides edge length by phi, solved
for the count that brings a unit edge to ~cellSize once scaled to
radius). This retires the now-meaningless `penroseIterations` field and
the `subdivisions` parameter it threaded through Tilings.Generate.
- Also removed a dead `idx` counter in Tilings.Voronoi (D8's mention of
dead code; the Vector2.zero sentinel ambiguity itself is unfixed, same as
D7/D19 — Voronoi and TriHex stay recorded defects, not fixed, per R5).
Not fixed here, per the map: D7 (Voronoi is unusably slow) and D19 (TriHex
has zero dual edges) are recorded, not fixed. D9/D10 (fracture tuning) are
Cut 5. D17 (growth gate blocks the spawning hit) and D20 (_PanelSpawnTime
hardcoded to 0) are Cut 3.
Repo-specific fix not in the map: Assets/Scripts/UI/ContextMenu.cs defines
a global `ContextMenu` MonoBehaviour that shadows UnityEngine.ContextMenu,
so `[ContextMenu("Rebuild")]` doesn't compile here (CS0616) even though it
did in the map's throwaway probe project. Used the fully-qualified
`[UnityEngine.ContextMenu(...)]` instead.
Adds Assets/Scripts/Editor/ShieldPanelCut1Verify.cs, a batchmode probe (no
NUnit path exists per the map's Q7 — Tests.asmdef can't see
Assembly-CSharp): asserts zero shader/compute compile errors and that
Penrose cell count actually scales with panelRadius (D18). Also adds
tests/mutation_tests_shield_panel_cut1.py, which mutates the D18 fix, the
D4 fix, and the shaderPass deletion in turn and asserts the probe goes red
each time, restoring the source after each case (own file per the
convention mutation_tests_addressables_cut.py established, so as not to
touch item-provenance's or the addressables cut's harnesses).
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-18 06:08:04 +02:00
|
|
|
|
|
|
|
|
bUA = new GraphicsBuffer(GraphicsBuffer.Target.Structured, n, sizeof(float));
|
|
|
|
|
bUB = new GraphicsBuffer(GraphicsBuffer.Target.Structured, n, sizeof(float));
|
|
|
|
|
bV = new GraphicsBuffer(GraphicsBuffer.Target.Structured, n, sizeof(float));
|
|
|
|
|
bEcho = new GraphicsBuffer(GraphicsBuffer.Target.Structured, n * EchoSlots, sizeof(float));
|
|
|
|
|
bFlow = new GraphicsBuffer(GraphicsBuffer.Target.Structured, Mathf.Max(data.EdgeCount, 1), sizeof(float));
|
|
|
|
|
bBudget = new GraphicsBuffer(GraphicsBuffer.Target.Structured, 2, sizeof(uint));
|
|
|
|
|
|
|
|
|
|
bVisible = new GraphicsBuffer(GraphicsBuffer.Target.Append | GraphicsBuffer.Target.Structured,
|
|
|
|
|
n, sizeof(uint));
|
|
|
|
|
|
|
|
|
|
bCells.SetData(data.cells);
|
|
|
|
|
bVerts.SetData(data.verts);
|
|
|
|
|
if (data.EdgeCount > 0) bEdges.SetData(data.dualEdges);
|
|
|
|
|
|
|
|
|
|
// One args buffer, sized to the widest pass (Outline: n*2 segments * 6 verts).
|
|
|
|
|
// RenderParams exposes no per-pass selector in 6000.3.24f1 (D2), and a multi-pass material
|
|
|
|
|
// draws every pass from one indirect call anyway (D5/§2.1.4-5), so the three
|
|
|
|
|
// per-pass buffers this shipped with were dead weight, not three draw paths.
|
|
|
|
|
argsFill = MakeArgs((uint)(MaxV * 2 * 6));
|
|
|
|
|
|
|
|
|
|
mat = new Material(panelShader) { hideFlags = HideFlags.DontSave };
|
|
|
|
|
mpb = new MaterialPropertyBlock();
|
|
|
|
|
|
|
|
|
|
var b = data.bounds;
|
|
|
|
|
float pad = panelRadius * 1.5f + shardLife * 3f;
|
|
|
|
|
worldBounds = new Bounds(transform.TransformPoint(b.center),
|
|
|
|
|
b.size + Vector3.one * pad);
|
|
|
|
|
|
|
|
|
|
budgetScratch ??= new uint[2];
|
2026-09-18 13:51:11 +02:00
|
|
|
BuildGrowthSeed();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// D17/D20: precompute the arm-time CellState for every cell once per tiling build, keyed
|
|
|
|
|
// only on each cell's baked `spawnDelay` (TilingBuilder.cs, itself derived from the
|
|
|
|
|
// wavefrontOrigin the tiling was built with -- always the panel centre, per Cut 1's note
|
|
|
|
|
// that one tiling serves every panel). ResetSim re-uploads this same array on every arm/
|
|
|
|
|
// reuse; nothing here runs at interception time (§3's "no allocation at interception").
|
|
|
|
|
void BuildGrowthSeed()
|
|
|
|
|
{
|
|
|
|
|
growthSeed ??= new CellState[data.CellCount];
|
|
|
|
|
if (growthSeed.Length != data.CellCount) growthSeed = new CellState[data.CellCount];
|
|
|
|
|
|
|
|
|
|
for (int i = 0; i < growthSeed.Length; i++)
|
|
|
|
|
{
|
|
|
|
|
growthSeed[i] = default;
|
|
|
|
|
growthSeed[i].breakTime = -1f;
|
|
|
|
|
growthSeed[i].temper = temperInit;
|
|
|
|
|
// Backdate the arm instant by one growDuration (panelSpawnTime, set alongside this
|
|
|
|
|
// in ResetSim) so a cell with spawnDelay ~= 0 -- the strike centre -- reads as
|
|
|
|
|
// already past the 0.6 growth gate the instant it is struck, while a cell whose
|
|
|
|
|
// baked spawnDelay is >= growDuration starts at 0 and grows forward normally as
|
|
|
|
|
// simTime advances past panelSpawnTime. This is what fixes D17 (the growth gate
|
|
|
|
|
// otherwise swallows the hit that created the panel, because KUpdate -- the only
|
|
|
|
|
// writer of `growth` -- does not run until after this same frame's KInject) without
|
|
|
|
|
// touching the gate itself or adding a per-interception dispatch.
|
|
|
|
|
growthSeed[i].growth = Mathf.Clamp01(
|
|
|
|
|
(growDuration - data.cells[i].spawnDelay) / Mathf.Max(growDuration, 1e-3f));
|
|
|
|
|
}
|
Land the six shield-panel sources, ported to built-in and compiling
Cut 1 of docs/shield-panel-cut.md: land ShieldPanel.cs, ShieldPanel.shader,
ShieldSim.compute, ShieldCommon.hlsl, TilingBuilder.cs and Tilings.cs under
Assets/Shaders/Compute/ShieldField/, alongside Lightning/ and Slime/.
Port (URP -> built-in, three substitutions per the map's §2.1 probe):
- drop the UniversalPipeline SubShader tag, add IgnoreProjector
- swap the URP Core.hlsl include for UnityCG.cginc
- TransformWorldToHClip -> UnityWorldToClipPos (3 call sites)
Drawing collapse (D2/D5): RenderParams has no shaderPass member in
6000.3.24f1, and a multi-pass material draws every SubShader pass from one
indirect call regardless, so the three per-pass args buffers and draw calls
collapse to one args buffer sized to the widest pass (Outline) and one
Graphics.RenderPrimitivesIndirect call. Deleted: argsOutline, argsConduit,
drawFill/drawOutline/drawConduits and their branches.
Also deleted: ShieldPanelTester (the click harness the operator ruled
against; it also can't run — activeInputHandler is New Input System only)
and [ExecuteAlways] (incompatible with a pool per D13; Rebuild stays on the
context menu).
Defects fixed:
- D1: TilingBuilder.cs's inner `dist` shadowed the outer one (CS0136);
renamed to nbrDist.
- D3: CellStatic was 15 floats against a declared Stride of 16; added pad2
on both the HLSL and C# sides.
- D4: `centroid` is reserved in the compute compiler; renamed the
ShieldCommon.hlsl field to `center` (kept the C# field name as-is; it's
not reserved there and the two sides don't need matching names, only
matching layout).
- D6: fixed the comment on _BreakBudget — it's two elements (count + cap),
not the "single element" the old comment claimed.
- D12: the two-element uint[] Update() allocated every frame is now a
cached field, cheaper still under a pool of many live panels.
- D18 (R5 — operator ruling, not deferred to a follow-up): Penrose ignored
panelRadius entirely — a fixed authored iteration count meant the tiling
was just uniformly rescaled, not resampled, so cell count never grew with
the panel. Every other tiling here uses cellSize to hold local cell size
constant while radius extends coverage; Penrose now derives its iteration
count the same way (each substitution divides edge length by phi, solved
for the count that brings a unit edge to ~cellSize once scaled to
radius). This retires the now-meaningless `penroseIterations` field and
the `subdivisions` parameter it threaded through Tilings.Generate.
- Also removed a dead `idx` counter in Tilings.Voronoi (D8's mention of
dead code; the Vector2.zero sentinel ambiguity itself is unfixed, same as
D7/D19 — Voronoi and TriHex stay recorded defects, not fixed, per R5).
Not fixed here, per the map: D7 (Voronoi is unusably slow) and D19 (TriHex
has zero dual edges) are recorded, not fixed. D9/D10 (fracture tuning) are
Cut 5. D17 (growth gate blocks the spawning hit) and D20 (_PanelSpawnTime
hardcoded to 0) are Cut 3.
Repo-specific fix not in the map: Assets/Scripts/UI/ContextMenu.cs defines
a global `ContextMenu` MonoBehaviour that shadows UnityEngine.ContextMenu,
so `[ContextMenu("Rebuild")]` doesn't compile here (CS0616) even though it
did in the map's throwaway probe project. Used the fully-qualified
`[UnityEngine.ContextMenu(...)]` instead.
Adds Assets/Scripts/Editor/ShieldPanelCut1Verify.cs, a batchmode probe (no
NUnit path exists per the map's Q7 — Tests.asmdef can't see
Assembly-CSharp): asserts zero shader/compute compile errors and that
Penrose cell count actually scales with panelRadius (D18). Also adds
tests/mutation_tests_shield_panel_cut1.py, which mutates the D18 fix, the
D4 fix, and the shaderPass deletion in turn and asserts the probe goes red
each time, restoring the source after each case (own file per the
convention mutation_tests_addressables_cut.py established, so as not to
touch item-provenance's or the addressables cut's harnesses).
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-18 06:08:04 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static GraphicsBuffer MakeArgs(uint vertsPerInstance)
|
|
|
|
|
{
|
|
|
|
|
var a = new GraphicsBuffer(GraphicsBuffer.Target.IndirectArguments, 1,
|
|
|
|
|
GraphicsBuffer.IndirectDrawArgs.size);
|
|
|
|
|
a.SetData(new[] { new GraphicsBuffer.IndirectDrawArgs {
|
|
|
|
|
vertexCountPerInstance = vertsPerInstance,
|
|
|
|
|
instanceCount = 0, startVertex = 0, startInstance = 0 } });
|
|
|
|
|
return a;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void CacheKernels()
|
|
|
|
|
{
|
|
|
|
|
kInit = sim.FindKernel("KInit");
|
|
|
|
|
kInject = sim.FindKernel("KInject");
|
|
|
|
|
kWave = sim.FindKernel("KWaveStep");
|
|
|
|
|
kEcho = sim.FindKernel("KEcho");
|
|
|
|
|
kFracture = sim.FindKernel("KFracture");
|
|
|
|
|
kUpdate = sim.FindKernel("KUpdate");
|
|
|
|
|
|
|
|
|
|
foreach (int k in new[] { kInit, kInject, kWave, kEcho, kFracture, kUpdate })
|
|
|
|
|
{
|
|
|
|
|
sim.SetBuffer(k, idCells, bCells);
|
|
|
|
|
sim.SetBuffer(k, idEdges, bEdges);
|
|
|
|
|
sim.SetBuffer(k, idState, bState);
|
|
|
|
|
sim.SetBuffer(k, idV, bV);
|
|
|
|
|
sim.SetBuffer(k, idEcho, bEcho);
|
|
|
|
|
sim.SetBuffer(k, idFlow, bFlow);
|
|
|
|
|
sim.SetBuffer(k, idBudget, bBudget);
|
|
|
|
|
}
|
|
|
|
|
sim.SetBuffer(kInit, idUOut, bUA);
|
|
|
|
|
sim.SetBuffer(kWave, idFlow, bFlow);
|
|
|
|
|
sim.SetBuffer(kUpdate, idVisible, bVisible);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[UnityEngine.ContextMenu("Reset Simulation")]
|
|
|
|
|
public void ResetSim()
|
|
|
|
|
{
|
2026-09-18 13:51:11 +02:00
|
|
|
// Cut 3: a freshly-instantiated pool instance's OnEnable is not guaranteed to have run
|
|
|
|
|
// by the time the interceptor calls ResetSim() on it in the same call stack (observed in
|
|
|
|
|
// the batchmode probe: Object.Instantiate's Awake/OnEnable are not synchronous with the
|
|
|
|
|
// call that created the clone in every context this runs in). ResetSim is what every
|
|
|
|
|
// spawn and reuse already goes through, so it -- not OnEnable's timing -- is what
|
|
|
|
|
// guarantees "built before struck". This costs nothing on the pooled-reuse path that
|
|
|
|
|
// §2.4's budget is about: data is already non-null there, so this is one null check, not
|
|
|
|
|
// a second rebuild.
|
|
|
|
|
if (data == null) { Rebuild(); return; } // Rebuild() ends by calling ResetSim() itself
|
|
|
|
|
if (sim == null) return;
|
Land the six shield-panel sources, ported to built-in and compiling
Cut 1 of docs/shield-panel-cut.md: land ShieldPanel.cs, ShieldPanel.shader,
ShieldSim.compute, ShieldCommon.hlsl, TilingBuilder.cs and Tilings.cs under
Assets/Shaders/Compute/ShieldField/, alongside Lightning/ and Slime/.
Port (URP -> built-in, three substitutions per the map's §2.1 probe):
- drop the UniversalPipeline SubShader tag, add IgnoreProjector
- swap the URP Core.hlsl include for UnityCG.cginc
- TransformWorldToHClip -> UnityWorldToClipPos (3 call sites)
Drawing collapse (D2/D5): RenderParams has no shaderPass member in
6000.3.24f1, and a multi-pass material draws every SubShader pass from one
indirect call regardless, so the three per-pass args buffers and draw calls
collapse to one args buffer sized to the widest pass (Outline) and one
Graphics.RenderPrimitivesIndirect call. Deleted: argsOutline, argsConduit,
drawFill/drawOutline/drawConduits and their branches.
Also deleted: ShieldPanelTester (the click harness the operator ruled
against; it also can't run — activeInputHandler is New Input System only)
and [ExecuteAlways] (incompatible with a pool per D13; Rebuild stays on the
context menu).
Defects fixed:
- D1: TilingBuilder.cs's inner `dist` shadowed the outer one (CS0136);
renamed to nbrDist.
- D3: CellStatic was 15 floats against a declared Stride of 16; added pad2
on both the HLSL and C# sides.
- D4: `centroid` is reserved in the compute compiler; renamed the
ShieldCommon.hlsl field to `center` (kept the C# field name as-is; it's
not reserved there and the two sides don't need matching names, only
matching layout).
- D6: fixed the comment on _BreakBudget — it's two elements (count + cap),
not the "single element" the old comment claimed.
- D12: the two-element uint[] Update() allocated every frame is now a
cached field, cheaper still under a pool of many live panels.
- D18 (R5 — operator ruling, not deferred to a follow-up): Penrose ignored
panelRadius entirely — a fixed authored iteration count meant the tiling
was just uniformly rescaled, not resampled, so cell count never grew with
the panel. Every other tiling here uses cellSize to hold local cell size
constant while radius extends coverage; Penrose now derives its iteration
count the same way (each substitution divides edge length by phi, solved
for the count that brings a unit edge to ~cellSize once scaled to
radius). This retires the now-meaningless `penroseIterations` field and
the `subdivisions` parameter it threaded through Tilings.Generate.
- Also removed a dead `idx` counter in Tilings.Voronoi (D8's mention of
dead code; the Vector2.zero sentinel ambiguity itself is unfixed, same as
D7/D19 — Voronoi and TriHex stay recorded defects, not fixed, per R5).
Not fixed here, per the map: D7 (Voronoi is unusably slow) and D19 (TriHex
has zero dual edges) are recorded, not fixed. D9/D10 (fracture tuning) are
Cut 5. D17 (growth gate blocks the spawning hit) and D20 (_PanelSpawnTime
hardcoded to 0) are Cut 3.
Repo-specific fix not in the map: Assets/Scripts/UI/ContextMenu.cs defines
a global `ContextMenu` MonoBehaviour that shadows UnityEngine.ContextMenu,
so `[ContextMenu("Rebuild")]` doesn't compile here (CS0616) even though it
did in the map's throwaway probe project. Used the fully-qualified
`[UnityEngine.ContextMenu(...)]` instead.
Adds Assets/Scripts/Editor/ShieldPanelCut1Verify.cs, a batchmode probe (no
NUnit path exists per the map's Q7 — Tests.asmdef can't see
Assembly-CSharp): asserts zero shader/compute compile errors and that
Penrose cell count actually scales with panelRadius (D18). Also adds
tests/mutation_tests_shield_panel_cut1.py, which mutates the D18 fix, the
D4 fix, and the shaderPass deletion in turn and asserts the probe goes red
each time, restoring the source after each case (own file per the
convention mutation_tests_addressables_cut.py established, so as not to
touch item-provenance's or the addressables cut's harnesses).
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-18 06:08:04 +02:00
|
|
|
simTime = 0f;
|
|
|
|
|
echoSlot = 0;
|
|
|
|
|
uUsingA = true;
|
|
|
|
|
pending.Clear();
|
|
|
|
|
|
|
|
|
|
sim.SetInt("_CellCount", data.CellCount);
|
|
|
|
|
sim.SetFloat("_TemperInit", temperInit);
|
|
|
|
|
sim.SetBuffer(kInit, idUOut, bUA);
|
|
|
|
|
sim.Dispatch(kInit, groups, 1, 1);
|
|
|
|
|
|
|
|
|
|
// second clear so both halves of the ping-pong start at rest
|
|
|
|
|
sim.SetBuffer(kInit, idUOut, bUB);
|
|
|
|
|
sim.Dispatch(kInit, groups, 1, 1);
|
|
|
|
|
sim.SetBuffer(kInit, idUOut, bUA);
|
2026-09-18 13:51:11 +02:00
|
|
|
|
|
|
|
|
// D17/D20: KInit above just zeroed every cell's `growth` (and this panel's own local
|
|
|
|
|
// clock, simTime, back to 0). Overwrite with the precomputed arm-time state instead of
|
|
|
|
|
// leaving it at 0 -- see BuildGrowthSeed's comment for why waiting on KUpdate to catch
|
|
|
|
|
// up is one frame too late for a hit queued this same call. `growDuration` may have
|
|
|
|
|
// changed in the inspector since the seed was built, but recomputing it here would be a
|
|
|
|
|
// per-arm allocation-shaped cost the pool's 8-11 us budget (§2.4) has no room for;
|
|
|
|
|
// Rebuild (a manual, non-pooled action) is what re-derives the seed from authored
|
|
|
|
|
// fields, matching CheckCfl's existing must-Rebuild-to-see-it contract.
|
|
|
|
|
panelSpawnTime = -growDuration;
|
|
|
|
|
bState.SetData(growthSeed);
|
Land the six shield-panel sources, ported to built-in and compiling
Cut 1 of docs/shield-panel-cut.md: land ShieldPanel.cs, ShieldPanel.shader,
ShieldSim.compute, ShieldCommon.hlsl, TilingBuilder.cs and Tilings.cs under
Assets/Shaders/Compute/ShieldField/, alongside Lightning/ and Slime/.
Port (URP -> built-in, three substitutions per the map's §2.1 probe):
- drop the UniversalPipeline SubShader tag, add IgnoreProjector
- swap the URP Core.hlsl include for UnityCG.cginc
- TransformWorldToHClip -> UnityWorldToClipPos (3 call sites)
Drawing collapse (D2/D5): RenderParams has no shaderPass member in
6000.3.24f1, and a multi-pass material draws every SubShader pass from one
indirect call regardless, so the three per-pass args buffers and draw calls
collapse to one args buffer sized to the widest pass (Outline) and one
Graphics.RenderPrimitivesIndirect call. Deleted: argsOutline, argsConduit,
drawFill/drawOutline/drawConduits and their branches.
Also deleted: ShieldPanelTester (the click harness the operator ruled
against; it also can't run — activeInputHandler is New Input System only)
and [ExecuteAlways] (incompatible with a pool per D13; Rebuild stays on the
context menu).
Defects fixed:
- D1: TilingBuilder.cs's inner `dist` shadowed the outer one (CS0136);
renamed to nbrDist.
- D3: CellStatic was 15 floats against a declared Stride of 16; added pad2
on both the HLSL and C# sides.
- D4: `centroid` is reserved in the compute compiler; renamed the
ShieldCommon.hlsl field to `center` (kept the C# field name as-is; it's
not reserved there and the two sides don't need matching names, only
matching layout).
- D6: fixed the comment on _BreakBudget — it's two elements (count + cap),
not the "single element" the old comment claimed.
- D12: the two-element uint[] Update() allocated every frame is now a
cached field, cheaper still under a pool of many live panels.
- D18 (R5 — operator ruling, not deferred to a follow-up): Penrose ignored
panelRadius entirely — a fixed authored iteration count meant the tiling
was just uniformly rescaled, not resampled, so cell count never grew with
the panel. Every other tiling here uses cellSize to hold local cell size
constant while radius extends coverage; Penrose now derives its iteration
count the same way (each substitution divides edge length by phi, solved
for the count that brings a unit edge to ~cellSize once scaled to
radius). This retires the now-meaningless `penroseIterations` field and
the `subdivisions` parameter it threaded through Tilings.Generate.
- Also removed a dead `idx` counter in Tilings.Voronoi (D8's mention of
dead code; the Vector2.zero sentinel ambiguity itself is unfixed, same as
D7/D19 — Voronoi and TriHex stay recorded defects, not fixed, per R5).
Not fixed here, per the map: D7 (Voronoi is unusably slow) and D19 (TriHex
has zero dual edges) are recorded, not fixed. D9/D10 (fracture tuning) are
Cut 5. D17 (growth gate blocks the spawning hit) and D20 (_PanelSpawnTime
hardcoded to 0) are Cut 3.
Repo-specific fix not in the map: Assets/Scripts/UI/ContextMenu.cs defines
a global `ContextMenu` MonoBehaviour that shadows UnityEngine.ContextMenu,
so `[ContextMenu("Rebuild")]` doesn't compile here (CS0616) even though it
did in the map's throwaway probe project. Used the fully-qualified
`[UnityEngine.ContextMenu(...)]` instead.
Adds Assets/Scripts/Editor/ShieldPanelCut1Verify.cs, a batchmode probe (no
NUnit path exists per the map's Q7 — Tests.asmdef can't see
Assembly-CSharp): asserts zero shader/compute compile errors and that
Penrose cell count actually scales with panelRadius (D18). Also adds
tests/mutation_tests_shield_panel_cut1.py, which mutates the D18 fix, the
D4 fix, and the shaderPass deletion in turn and asserts the probe goes red
each time, restoring the source after each case (own file per the
convention mutation_tests_addressables_cut.py established, so as not to
touch item-provenance's or the addressables cut's harnesses).
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-18 06:08:04 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void CheckCfl()
|
|
|
|
|
{
|
|
|
|
|
float minArea = float.MaxValue;
|
|
|
|
|
int maxDeg = 0;
|
|
|
|
|
foreach (var c in data.cells)
|
|
|
|
|
{
|
|
|
|
|
minArea = Mathf.Min(minArea, c.area);
|
|
|
|
|
maxDeg = Mathf.Max(maxDeg, (int)c.nbrCount);
|
|
|
|
|
}
|
|
|
|
|
// rough stability bound for the explicit leapfrog on an irregular graph
|
|
|
|
|
float lim = waveSpeed2 * fixedSubstepDt * fixedSubstepDt * maxDeg / Mathf.Max(minArea, 1e-6f);
|
|
|
|
|
if (lim > 1.8f)
|
|
|
|
|
Debug.LogWarning($"[ShieldPanel] CFL estimate {lim:F2} exceeds ~1.8 and the wave " +
|
|
|
|
|
$"will likely diverge. Lower waveSpeed2 or fixedSubstepDt, or raise cellSize. " +
|
|
|
|
|
$"(smallest cell area {minArea:E2}, max degree {maxDeg})", this);
|
|
|
|
|
}
|
|
|
|
|
|
2026-09-18 13:51:11 +02:00
|
|
|
// ==============================================================
|
|
|
|
|
// Read-only verification surface for the Cut 3 batchmode probe (Tests.asmdef cannot see
|
|
|
|
|
// Assembly-CSharp -- the map's Q7 -- so this, like Cut 1/2's probes, is what stands in for
|
|
|
|
|
// a unit test). Nothing outside ShieldPanel may WRITE these buffers (the authority map's
|
|
|
|
|
// "nothing outside ShieldPanel touches its buffers" is about writers); GraphicsBuffer.
|
|
|
|
|
// GetData is a read.
|
|
|
|
|
public int DebugCellCount => data?.CellCount ?? 0;
|
|
|
|
|
public GraphicsBuffer DebugStateBuffer => bState;
|
|
|
|
|
public GraphicsBuffer DebugVField => bV;
|
|
|
|
|
public Vector2 DebugCellCenter(int i) => data.cells[i].centroid;
|
|
|
|
|
|
Land the six shield-panel sources, ported to built-in and compiling
Cut 1 of docs/shield-panel-cut.md: land ShieldPanel.cs, ShieldPanel.shader,
ShieldSim.compute, ShieldCommon.hlsl, TilingBuilder.cs and Tilings.cs under
Assets/Shaders/Compute/ShieldField/, alongside Lightning/ and Slime/.
Port (URP -> built-in, three substitutions per the map's §2.1 probe):
- drop the UniversalPipeline SubShader tag, add IgnoreProjector
- swap the URP Core.hlsl include for UnityCG.cginc
- TransformWorldToHClip -> UnityWorldToClipPos (3 call sites)
Drawing collapse (D2/D5): RenderParams has no shaderPass member in
6000.3.24f1, and a multi-pass material draws every SubShader pass from one
indirect call regardless, so the three per-pass args buffers and draw calls
collapse to one args buffer sized to the widest pass (Outline) and one
Graphics.RenderPrimitivesIndirect call. Deleted: argsOutline, argsConduit,
drawFill/drawOutline/drawConduits and their branches.
Also deleted: ShieldPanelTester (the click harness the operator ruled
against; it also can't run — activeInputHandler is New Input System only)
and [ExecuteAlways] (incompatible with a pool per D13; Rebuild stays on the
context menu).
Defects fixed:
- D1: TilingBuilder.cs's inner `dist` shadowed the outer one (CS0136);
renamed to nbrDist.
- D3: CellStatic was 15 floats against a declared Stride of 16; added pad2
on both the HLSL and C# sides.
- D4: `centroid` is reserved in the compute compiler; renamed the
ShieldCommon.hlsl field to `center` (kept the C# field name as-is; it's
not reserved there and the two sides don't need matching names, only
matching layout).
- D6: fixed the comment on _BreakBudget — it's two elements (count + cap),
not the "single element" the old comment claimed.
- D12: the two-element uint[] Update() allocated every frame is now a
cached field, cheaper still under a pool of many live panels.
- D18 (R5 — operator ruling, not deferred to a follow-up): Penrose ignored
panelRadius entirely — a fixed authored iteration count meant the tiling
was just uniformly rescaled, not resampled, so cell count never grew with
the panel. Every other tiling here uses cellSize to hold local cell size
constant while radius extends coverage; Penrose now derives its iteration
count the same way (each substitution divides edge length by phi, solved
for the count that brings a unit edge to ~cellSize once scaled to
radius). This retires the now-meaningless `penroseIterations` field and
the `subdivisions` parameter it threaded through Tilings.Generate.
- Also removed a dead `idx` counter in Tilings.Voronoi (D8's mention of
dead code; the Vector2.zero sentinel ambiguity itself is unfixed, same as
D7/D19 — Voronoi and TriHex stay recorded defects, not fixed, per R5).
Not fixed here, per the map: D7 (Voronoi is unusably slow) and D19 (TriHex
has zero dual edges) are recorded, not fixed. D9/D10 (fracture tuning) are
Cut 5. D17 (growth gate blocks the spawning hit) and D20 (_PanelSpawnTime
hardcoded to 0) are Cut 3.
Repo-specific fix not in the map: Assets/Scripts/UI/ContextMenu.cs defines
a global `ContextMenu` MonoBehaviour that shadows UnityEngine.ContextMenu,
so `[ContextMenu("Rebuild")]` doesn't compile here (CS0616) even though it
did in the map's throwaway probe project. Used the fully-qualified
`[UnityEngine.ContextMenu(...)]` instead.
Adds Assets/Scripts/Editor/ShieldPanelCut1Verify.cs, a batchmode probe (no
NUnit path exists per the map's Q7 — Tests.asmdef can't see
Assembly-CSharp): asserts zero shader/compute compile errors and that
Penrose cell count actually scales with panelRadius (D18). Also adds
tests/mutation_tests_shield_panel_cut1.py, which mutates the D18 fix, the
D4 fix, and the shaderPass deletion in turn and asserts the probe goes red
each time, restoring the source after each case (own file per the
convention mutation_tests_addressables_cut.py established, so as not to
touch item-provenance's or the addressables cut's harnesses).
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-18 06:08:04 +02:00
|
|
|
// ==============================================================
|
Shield panel: gate reuse and Hit on the panel's own radius
The operator's play report on FieldShieldTest ("weak hits are very
janky, clicking outside the hex still makes it tremble and shatter,
pooling seems very rough") traced to two provable defects, not one
tuning constant:
- ShieldPanel.Hit had no bounds check at all. A point handed to it was
always enqueued and injected regardless of how far outside the
panel's own panelRadius it was.
- ShieldInterceptor.FindReusable decided reuse from a raw world-space
chord distance to the panel's stale spawn-time Position, using
PanelRadius*ReuseFraction (authored up to 2x) as the threshold. On a
non-uniformly scaled/curved envelope a chord across curvature is not
the panel's own in-plane extent, so a click clearly outside the
rendered tiling could still measure inside threshold and get routed
to Hit() -- which had nothing to reject it.
- Reusing a panel never refreshed its pooled DieAt, so a panel struck
again could still expire and get returned to the pool mid-fracture
("pooling seems very rough").
Fix: ShieldPanel.ContainsWorldPoint is now the one owner of "is this
point within my own radius" (panel-local in-plane distance vs.
panelRadius), used both as Hit's hard gate and as FindReusable's
candidacy test (with ReuseFraction, now capped at 1, narrowing but
never widening past the physical radius). Reuse now also extends
DieAt from the moment of the new hit.
Pinned in ShieldPanelCut3Verify.CheckHitOutsideRadiusDoesNotReachPanel:
a direct Hit() and a routed Absorb() outside a panel's own radius must
enqueue nothing and leave its state buffer untouched, and a genuinely
outside click opens its own panel. Deterministic (checks the pending
queue and no-op state, not temper drift) because breakBudgetFraction
paces fracture over many frames on purpose, so temper keeps drifting
from an earlier hit alone -- a drift-based check would have been
measuring that, not this rule (caught this against the check's own
first run).
All four ShieldPanelCut<N>Verify probes pass (batchmode, real GPU
device, no -nographics).
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-18 21:02:18 +02:00
|
|
|
// Cut 3 fix (operator's play report, docs/shield-panel-cut.md): the panel is the one owner
|
|
|
|
|
// of "is this point within my own radius" -- both the reuse decision (ShieldInterceptor.
|
|
|
|
|
// FindReusable) and the strike gate below (Hit) go through this so a hit outside a panel's
|
|
|
|
|
// own tiling can never reach it, however it got routed. `radiusScale` is clamped to at most
|
|
|
|
|
// 1 -- ReuseFraction's authored [0,1] range narrows the reuse catchment; it must never widen
|
|
|
|
|
// it past the panel's own physical radius, which is what Hit gates on.
|
|
|
|
|
public bool ContainsWorldPoint(Vector3 worldPoint, float radiusScale = 1f)
|
|
|
|
|
{
|
|
|
|
|
Vector3 local = transform.InverseTransformPoint(worldPoint);
|
|
|
|
|
float scale = Mathf.Clamp01(radiusScale);
|
|
|
|
|
float r = panelRadius * scale;
|
|
|
|
|
return local.x * local.x + local.y * local.y <= r * r;
|
|
|
|
|
}
|
|
|
|
|
|
Land the six shield-panel sources, ported to built-in and compiling
Cut 1 of docs/shield-panel-cut.md: land ShieldPanel.cs, ShieldPanel.shader,
ShieldSim.compute, ShieldCommon.hlsl, TilingBuilder.cs and Tilings.cs under
Assets/Shaders/Compute/ShieldField/, alongside Lightning/ and Slime/.
Port (URP -> built-in, three substitutions per the map's §2.1 probe):
- drop the UniversalPipeline SubShader tag, add IgnoreProjector
- swap the URP Core.hlsl include for UnityCG.cginc
- TransformWorldToHClip -> UnityWorldToClipPos (3 call sites)
Drawing collapse (D2/D5): RenderParams has no shaderPass member in
6000.3.24f1, and a multi-pass material draws every SubShader pass from one
indirect call regardless, so the three per-pass args buffers and draw calls
collapse to one args buffer sized to the widest pass (Outline) and one
Graphics.RenderPrimitivesIndirect call. Deleted: argsOutline, argsConduit,
drawFill/drawOutline/drawConduits and their branches.
Also deleted: ShieldPanelTester (the click harness the operator ruled
against; it also can't run — activeInputHandler is New Input System only)
and [ExecuteAlways] (incompatible with a pool per D13; Rebuild stays on the
context menu).
Defects fixed:
- D1: TilingBuilder.cs's inner `dist` shadowed the outer one (CS0136);
renamed to nbrDist.
- D3: CellStatic was 15 floats against a declared Stride of 16; added pad2
on both the HLSL and C# sides.
- D4: `centroid` is reserved in the compute compiler; renamed the
ShieldCommon.hlsl field to `center` (kept the C# field name as-is; it's
not reserved there and the two sides don't need matching names, only
matching layout).
- D6: fixed the comment on _BreakBudget — it's two elements (count + cap),
not the "single element" the old comment claimed.
- D12: the two-element uint[] Update() allocated every frame is now a
cached field, cheaper still under a pool of many live panels.
- D18 (R5 — operator ruling, not deferred to a follow-up): Penrose ignored
panelRadius entirely — a fixed authored iteration count meant the tiling
was just uniformly rescaled, not resampled, so cell count never grew with
the panel. Every other tiling here uses cellSize to hold local cell size
constant while radius extends coverage; Penrose now derives its iteration
count the same way (each substitution divides edge length by phi, solved
for the count that brings a unit edge to ~cellSize once scaled to
radius). This retires the now-meaningless `penroseIterations` field and
the `subdivisions` parameter it threaded through Tilings.Generate.
- Also removed a dead `idx` counter in Tilings.Voronoi (D8's mention of
dead code; the Vector2.zero sentinel ambiguity itself is unfixed, same as
D7/D19 — Voronoi and TriHex stay recorded defects, not fixed, per R5).
Not fixed here, per the map: D7 (Voronoi is unusably slow) and D19 (TriHex
has zero dual edges) are recorded, not fixed. D9/D10 (fracture tuning) are
Cut 5. D17 (growth gate blocks the spawning hit) and D20 (_PanelSpawnTime
hardcoded to 0) are Cut 3.
Repo-specific fix not in the map: Assets/Scripts/UI/ContextMenu.cs defines
a global `ContextMenu` MonoBehaviour that shadows UnityEngine.ContextMenu,
so `[ContextMenu("Rebuild")]` doesn't compile here (CS0616) even though it
did in the map's throwaway probe project. Used the fully-qualified
`[UnityEngine.ContextMenu(...)]` instead.
Adds Assets/Scripts/Editor/ShieldPanelCut1Verify.cs, a batchmode probe (no
NUnit path exists per the map's Q7 — Tests.asmdef can't see
Assembly-CSharp): asserts zero shader/compute compile errors and that
Penrose cell count actually scales with panelRadius (D18). Also adds
tests/mutation_tests_shield_panel_cut1.py, which mutates the D18 fix, the
D4 fix, and the shaderPass deletion in turn and asserts the probe goes red
each time, restoring the source after each case (own file per the
convention mutation_tests_addressables_cut.py established, so as not to
touch item-provenance's or the addressables cut's harnesses).
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-18 06:08:04 +02:00
|
|
|
/// <summary>Strike the panel. worldDir is the projectile's travel direction.</summary>
|
|
|
|
|
public void Hit(Vector3 worldPos, Vector3 worldDir, float energy,
|
|
|
|
|
FracturePattern pattern = FracturePattern.Radial,
|
|
|
|
|
float radius = -1f)
|
|
|
|
|
{
|
|
|
|
|
if (data == null) return;
|
Shield panel: gate reuse and Hit on the panel's own radius
The operator's play report on FieldShieldTest ("weak hits are very
janky, clicking outside the hex still makes it tremble and shatter,
pooling seems very rough") traced to two provable defects, not one
tuning constant:
- ShieldPanel.Hit had no bounds check at all. A point handed to it was
always enqueued and injected regardless of how far outside the
panel's own panelRadius it was.
- ShieldInterceptor.FindReusable decided reuse from a raw world-space
chord distance to the panel's stale spawn-time Position, using
PanelRadius*ReuseFraction (authored up to 2x) as the threshold. On a
non-uniformly scaled/curved envelope a chord across curvature is not
the panel's own in-plane extent, so a click clearly outside the
rendered tiling could still measure inside threshold and get routed
to Hit() -- which had nothing to reject it.
- Reusing a panel never refreshed its pooled DieAt, so a panel struck
again could still expire and get returned to the pool mid-fracture
("pooling seems very rough").
Fix: ShieldPanel.ContainsWorldPoint is now the one owner of "is this
point within my own radius" (panel-local in-plane distance vs.
panelRadius), used both as Hit's hard gate and as FindReusable's
candidacy test (with ReuseFraction, now capped at 1, narrowing but
never widening past the physical radius). Reuse now also extends
DieAt from the moment of the new hit.
Pinned in ShieldPanelCut3Verify.CheckHitOutsideRadiusDoesNotReachPanel:
a direct Hit() and a routed Absorb() outside a panel's own radius must
enqueue nothing and leave its state buffer untouched, and a genuinely
outside click opens its own panel. Deterministic (checks the pending
queue and no-op state, not temper drift) because breakBudgetFraction
paces fracture over many frames on purpose, so temper keeps drifting
from an earlier hit alone -- a drift-based check would have been
measuring that, not this rule (caught this against the check's own
first run).
All four ShieldPanelCut<N>Verify probes pass (batchmode, real GPU
device, no -nographics).
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-18 21:02:18 +02:00
|
|
|
// R (operator, playing FieldShieldTest): a hit outside the panel's own radius must not
|
|
|
|
|
// reach it at all -- no injection, no temper loss, no tremble. See ContainsWorldPoint.
|
|
|
|
|
if (!ContainsWorldPoint(worldPos)) return;
|
Land the six shield-panel sources, ported to built-in and compiling
Cut 1 of docs/shield-panel-cut.md: land ShieldPanel.cs, ShieldPanel.shader,
ShieldSim.compute, ShieldCommon.hlsl, TilingBuilder.cs and Tilings.cs under
Assets/Shaders/Compute/ShieldField/, alongside Lightning/ and Slime/.
Port (URP -> built-in, three substitutions per the map's §2.1 probe):
- drop the UniversalPipeline SubShader tag, add IgnoreProjector
- swap the URP Core.hlsl include for UnityCG.cginc
- TransformWorldToHClip -> UnityWorldToClipPos (3 call sites)
Drawing collapse (D2/D5): RenderParams has no shaderPass member in
6000.3.24f1, and a multi-pass material draws every SubShader pass from one
indirect call regardless, so the three per-pass args buffers and draw calls
collapse to one args buffer sized to the widest pass (Outline) and one
Graphics.RenderPrimitivesIndirect call. Deleted: argsOutline, argsConduit,
drawFill/drawOutline/drawConduits and their branches.
Also deleted: ShieldPanelTester (the click harness the operator ruled
against; it also can't run — activeInputHandler is New Input System only)
and [ExecuteAlways] (incompatible with a pool per D13; Rebuild stays on the
context menu).
Defects fixed:
- D1: TilingBuilder.cs's inner `dist` shadowed the outer one (CS0136);
renamed to nbrDist.
- D3: CellStatic was 15 floats against a declared Stride of 16; added pad2
on both the HLSL and C# sides.
- D4: `centroid` is reserved in the compute compiler; renamed the
ShieldCommon.hlsl field to `center` (kept the C# field name as-is; it's
not reserved there and the two sides don't need matching names, only
matching layout).
- D6: fixed the comment on _BreakBudget — it's two elements (count + cap),
not the "single element" the old comment claimed.
- D12: the two-element uint[] Update() allocated every frame is now a
cached field, cheaper still under a pool of many live panels.
- D18 (R5 — operator ruling, not deferred to a follow-up): Penrose ignored
panelRadius entirely — a fixed authored iteration count meant the tiling
was just uniformly rescaled, not resampled, so cell count never grew with
the panel. Every other tiling here uses cellSize to hold local cell size
constant while radius extends coverage; Penrose now derives its iteration
count the same way (each substitution divides edge length by phi, solved
for the count that brings a unit edge to ~cellSize once scaled to
radius). This retires the now-meaningless `penroseIterations` field and
the `subdivisions` parameter it threaded through Tilings.Generate.
- Also removed a dead `idx` counter in Tilings.Voronoi (D8's mention of
dead code; the Vector2.zero sentinel ambiguity itself is unfixed, same as
D7/D19 — Voronoi and TriHex stay recorded defects, not fixed, per R5).
Not fixed here, per the map: D7 (Voronoi is unusably slow) and D19 (TriHex
has zero dual edges) are recorded, not fixed. D9/D10 (fracture tuning) are
Cut 5. D17 (growth gate blocks the spawning hit) and D20 (_PanelSpawnTime
hardcoded to 0) are Cut 3.
Repo-specific fix not in the map: Assets/Scripts/UI/ContextMenu.cs defines
a global `ContextMenu` MonoBehaviour that shadows UnityEngine.ContextMenu,
so `[ContextMenu("Rebuild")]` doesn't compile here (CS0616) even though it
did in the map's throwaway probe project. Used the fully-qualified
`[UnityEngine.ContextMenu(...)]` instead.
Adds Assets/Scripts/Editor/ShieldPanelCut1Verify.cs, a batchmode probe (no
NUnit path exists per the map's Q7 — Tests.asmdef can't see
Assembly-CSharp): asserts zero shader/compute compile errors and that
Penrose cell count actually scales with panelRadius (D18). Also adds
tests/mutation_tests_shield_panel_cut1.py, which mutates the D18 fix, the
D4 fix, and the shaderPass deletion in turn and asserts the probe goes red
each time, restoring the source after each case (own file per the
convention mutation_tests_addressables_cut.py established, so as not to
touch item-provenance's or the addressables cut's harnesses).
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-18 06:08:04 +02:00
|
|
|
|
|
|
|
|
Vector3 local = transform.InverseTransformPoint(worldPos);
|
|
|
|
|
Vector3 ldir = transform.InverseTransformDirection(worldDir).normalized;
|
|
|
|
|
|
|
|
|
|
Vector2 flat = new Vector2(ldir.x, ldir.y);
|
|
|
|
|
float obliquity = Mathf.Clamp01(1f - Mathf.Abs(ldir.z));
|
|
|
|
|
|
|
|
|
|
pending.Enqueue(new PendingHit
|
|
|
|
|
{
|
|
|
|
|
pos = new Vector2(local.x, local.y),
|
|
|
|
|
dir = flat.sqrMagnitude > 1e-6f ? flat.normalized : Vector2.right,
|
|
|
|
|
energy = energy,
|
|
|
|
|
radius = radius > 0f ? radius : Mathf.Max(cellSize * 2.5f, panelRadius * 0.09f),
|
|
|
|
|
obliquity = obliquity,
|
|
|
|
|
pattern = pattern,
|
|
|
|
|
starArms = Random.Range(3, 9),
|
|
|
|
|
focus = Mathf.Lerp(0.15f, 0.55f, Random.value),
|
|
|
|
|
twist = Random.Range(0.4f, 1.6f),
|
|
|
|
|
spin = Random.value * Mathf.PI * 2f
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ==============================================================
|
|
|
|
|
void Update()
|
|
|
|
|
{
|
|
|
|
|
if (sim == null || data == null || mat == null) return;
|
|
|
|
|
|
|
|
|
|
float dt = Application.isPlaying ? Time.deltaTime : 1f / 60f;
|
|
|
|
|
simTime += dt;
|
|
|
|
|
|
|
|
|
|
sim.SetInt("_CellCount", data.CellCount);
|
|
|
|
|
sim.SetFloat("_Time", simTime);
|
|
|
|
|
PushMaterialUniforms();
|
|
|
|
|
|
|
|
|
|
// ---- break budget resets every frame; this is what paces the cascade
|
|
|
|
|
uint cap = (uint)Mathf.Max(1, Mathf.RoundToInt(data.CellCount * breakBudgetFraction));
|
|
|
|
|
budgetScratch[0] = 0u;
|
|
|
|
|
budgetScratch[1] = cap;
|
|
|
|
|
bBudget.SetData(budgetScratch);
|
|
|
|
|
|
|
|
|
|
// ---- inject queued hits
|
|
|
|
|
sim.SetFloat("_Dt", fixedSubstepDt);
|
|
|
|
|
while (pending.Count > 0)
|
|
|
|
|
{
|
|
|
|
|
var h = pending.Dequeue();
|
|
|
|
|
sim.SetVector("_HitPos", h.pos);
|
|
|
|
|
sim.SetVector("_HitDir", h.dir);
|
|
|
|
|
sim.SetFloat("_HitEnergy", h.energy);
|
|
|
|
|
sim.SetFloat("_HitRadius", h.radius);
|
|
|
|
|
sim.SetFloat("_HitObliquity", h.obliquity);
|
|
|
|
|
sim.SetInt("_Pattern", (int)h.pattern);
|
|
|
|
|
sim.SetFloat("_StarArms", h.starArms);
|
|
|
|
|
sim.SetFloat("_PatternFocus", h.focus);
|
|
|
|
|
sim.SetFloat("_SpiralTwist", h.twist);
|
|
|
|
|
sim.SetFloat("_HitSpin", h.spin);
|
|
|
|
|
sim.SetInt("_EchoSlot", echoSlot);
|
|
|
|
|
sim.SetBuffer(kInject, idV, bV);
|
|
|
|
|
sim.Dispatch(kInject, groups, 1, 1);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ---- wave substeps (fixed dt: the CFL limit does not care about frame rate)
|
|
|
|
|
sim.SetFloat("_WaveSpeed2", waveSpeed2);
|
|
|
|
|
sim.SetFloat("_Damping", damping);
|
|
|
|
|
sim.SetFloat("_StressClamp", stressClamp);
|
Cut 5: hold the rim, clamp v, fix fracture's clock, tune to spec
R7 (operator ruling): the panel's emitters hold its edge, so the rim is no
longer a free surface. Deletes the openW ghost term end to end -- its only
consumer in ShieldSim.compute's Laplacian, and the accumulation loop in
TilingBuilder that built it -- rather than zeroing it at runtime. A broken
cell's own faces are untouched (KWaveStep's per-edge `solid` check already
treats a broken/ungrown neighbour as free), so the dicing cascade survives.
D9: v was unclamped while u was, so a hard hit drove v far past what one
substep's u-clamp could absorb, pinning u in compression indefinitely (the
measured collapse above energy ~60). Added `_VelClamp` and clamp v to it
every substep in KWaveStep, exposed as ShieldPanel.velocityClamp (100).
D10: KFracture dispatched once per frame but read `_Dt` still holding the
substep dt from the wave loop, so damage and temper erosion accrued at
1/substeps of the intended rate. Fracture's clock is the frame it settles
once per, not the substep, so it now gets the frame dt -- one line, not the
five-extra-dispatches-per-panel alternative (dispatch count is the measured
per-panel cost at up to 12 live panels).
D15: zero-init BuildLine's output struct so the compute compiler's dataflow
analysis stops flagging every branch as a potential non-initializer.
Tuning (ShieldPanel.cs Material header): tensileStrength 0.9->0.2,
temperErosion 2.2->12, damageGain 6->100, breakAt 0.75->0.08,
storedEnergyGain 0.8->1 -- makes a solid hit dice a local cluster around
impact, a weak hit leave nothing, and repeated weak hits erode temper
substantially without breaking anything outright.
Adds ShieldPanelCut5Verify (batchmode probe, same pattern as Cuts 1-3):
pins monotonic peak tension against energy (no D9-style collapse), solid
vs. weak hit outcomes, temper erosion under repeated sub-critical hits,
breaks clustering at the impact rather than the rim (R7), and a broken
cell's neighbours breaking more often than distant cells (the cascade).
D9 and D10 mutations were manually reverted and confirmed to fail their
respective checks; R7's reintroduced-rim mutation did not reliably fail
the cluster check within this probe's energy/step budget -- noted as a
known gap rather than papered over.
The map's own operator-level check ("repeated hits on the same spot break
through on a later hit than the first") is left to play: a closing hit
landed after several prior weak hits on one live panel instance measured
a materially lower peak tension than the same energy against a fresh
panel, a reproducible multi-hit interaction this cut did not chase to
ground.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-18 20:04:05 +02:00
|
|
|
sim.SetFloat("_VelClamp", velocityClamp);
|
Land the six shield-panel sources, ported to built-in and compiling
Cut 1 of docs/shield-panel-cut.md: land ShieldPanel.cs, ShieldPanel.shader,
ShieldSim.compute, ShieldCommon.hlsl, TilingBuilder.cs and Tilings.cs under
Assets/Shaders/Compute/ShieldField/, alongside Lightning/ and Slime/.
Port (URP -> built-in, three substitutions per the map's §2.1 probe):
- drop the UniversalPipeline SubShader tag, add IgnoreProjector
- swap the URP Core.hlsl include for UnityCG.cginc
- TransformWorldToHClip -> UnityWorldToClipPos (3 call sites)
Drawing collapse (D2/D5): RenderParams has no shaderPass member in
6000.3.24f1, and a multi-pass material draws every SubShader pass from one
indirect call regardless, so the three per-pass args buffers and draw calls
collapse to one args buffer sized to the widest pass (Outline) and one
Graphics.RenderPrimitivesIndirect call. Deleted: argsOutline, argsConduit,
drawFill/drawOutline/drawConduits and their branches.
Also deleted: ShieldPanelTester (the click harness the operator ruled
against; it also can't run — activeInputHandler is New Input System only)
and [ExecuteAlways] (incompatible with a pool per D13; Rebuild stays on the
context menu).
Defects fixed:
- D1: TilingBuilder.cs's inner `dist` shadowed the outer one (CS0136);
renamed to nbrDist.
- D3: CellStatic was 15 floats against a declared Stride of 16; added pad2
on both the HLSL and C# sides.
- D4: `centroid` is reserved in the compute compiler; renamed the
ShieldCommon.hlsl field to `center` (kept the C# field name as-is; it's
not reserved there and the two sides don't need matching names, only
matching layout).
- D6: fixed the comment on _BreakBudget — it's two elements (count + cap),
not the "single element" the old comment claimed.
- D12: the two-element uint[] Update() allocated every frame is now a
cached field, cheaper still under a pool of many live panels.
- D18 (R5 — operator ruling, not deferred to a follow-up): Penrose ignored
panelRadius entirely — a fixed authored iteration count meant the tiling
was just uniformly rescaled, not resampled, so cell count never grew with
the panel. Every other tiling here uses cellSize to hold local cell size
constant while radius extends coverage; Penrose now derives its iteration
count the same way (each substitution divides edge length by phi, solved
for the count that brings a unit edge to ~cellSize once scaled to
radius). This retires the now-meaningless `penroseIterations` field and
the `subdivisions` parameter it threaded through Tilings.Generate.
- Also removed a dead `idx` counter in Tilings.Voronoi (D8's mention of
dead code; the Vector2.zero sentinel ambiguity itself is unfixed, same as
D7/D19 — Voronoi and TriHex stay recorded defects, not fixed, per R5).
Not fixed here, per the map: D7 (Voronoi is unusably slow) and D19 (TriHex
has zero dual edges) are recorded, not fixed. D9/D10 (fracture tuning) are
Cut 5. D17 (growth gate blocks the spawning hit) and D20 (_PanelSpawnTime
hardcoded to 0) are Cut 3.
Repo-specific fix not in the map: Assets/Scripts/UI/ContextMenu.cs defines
a global `ContextMenu` MonoBehaviour that shadows UnityEngine.ContextMenu,
so `[ContextMenu("Rebuild")]` doesn't compile here (CS0616) even though it
did in the map's throwaway probe project. Used the fully-qualified
`[UnityEngine.ContextMenu(...)]` instead.
Adds Assets/Scripts/Editor/ShieldPanelCut1Verify.cs, a batchmode probe (no
NUnit path exists per the map's Q7 — Tests.asmdef can't see
Assembly-CSharp): asserts zero shader/compute compile errors and that
Penrose cell count actually scales with panelRadius (D18). Also adds
tests/mutation_tests_shield_panel_cut1.py, which mutates the D18 fix, the
D4 fix, and the shaderPass deletion in turn and asserts the probe goes red
each time, restoring the source after each case (own file per the
convention mutation_tests_addressables_cut.py established, so as not to
touch item-provenance's or the addressables cut's harnesses).
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-18 06:08:04 +02:00
|
|
|
sim.SetInt("_EchoDelay", echoDelaySteps);
|
|
|
|
|
sim.SetFloat("_EchoReflect", echoReflect);
|
|
|
|
|
|
|
|
|
|
for (int s = 0; s < substeps; s++)
|
|
|
|
|
{
|
|
|
|
|
var src = uUsingA ? bUA : bUB;
|
|
|
|
|
var dst = uUsingA ? bUB : bUA;
|
|
|
|
|
|
|
|
|
|
sim.SetBuffer(kWave, idUIn, src);
|
|
|
|
|
sim.SetBuffer(kWave, idUOut, dst);
|
|
|
|
|
sim.Dispatch(kWave, groups, 1, 1);
|
|
|
|
|
uUsingA = !uUsingA;
|
|
|
|
|
|
|
|
|
|
sim.SetInt("_EchoSlot", echoSlot);
|
|
|
|
|
sim.Dispatch(kEcho, groups, 1, 1);
|
|
|
|
|
echoSlot = (echoSlot + 1) % EchoSlots;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var cur = uUsingA ? bUA : bUB;
|
|
|
|
|
|
|
|
|
|
// ---- fracture
|
Cut 5: hold the rim, clamp v, fix fracture's clock, tune to spec
R7 (operator ruling): the panel's emitters hold its edge, so the rim is no
longer a free surface. Deletes the openW ghost term end to end -- its only
consumer in ShieldSim.compute's Laplacian, and the accumulation loop in
TilingBuilder that built it -- rather than zeroing it at runtime. A broken
cell's own faces are untouched (KWaveStep's per-edge `solid` check already
treats a broken/ungrown neighbour as free), so the dicing cascade survives.
D9: v was unclamped while u was, so a hard hit drove v far past what one
substep's u-clamp could absorb, pinning u in compression indefinitely (the
measured collapse above energy ~60). Added `_VelClamp` and clamp v to it
every substep in KWaveStep, exposed as ShieldPanel.velocityClamp (100).
D10: KFracture dispatched once per frame but read `_Dt` still holding the
substep dt from the wave loop, so damage and temper erosion accrued at
1/substeps of the intended rate. Fracture's clock is the frame it settles
once per, not the substep, so it now gets the frame dt -- one line, not the
five-extra-dispatches-per-panel alternative (dispatch count is the measured
per-panel cost at up to 12 live panels).
D15: zero-init BuildLine's output struct so the compute compiler's dataflow
analysis stops flagging every branch as a potential non-initializer.
Tuning (ShieldPanel.cs Material header): tensileStrength 0.9->0.2,
temperErosion 2.2->12, damageGain 6->100, breakAt 0.75->0.08,
storedEnergyGain 0.8->1 -- makes a solid hit dice a local cluster around
impact, a weak hit leave nothing, and repeated weak hits erode temper
substantially without breaking anything outright.
Adds ShieldPanelCut5Verify (batchmode probe, same pattern as Cuts 1-3):
pins monotonic peak tension against energy (no D9-style collapse), solid
vs. weak hit outcomes, temper erosion under repeated sub-critical hits,
breaks clustering at the impact rather than the rim (R7), and a broken
cell's neighbours breaking more often than distant cells (the cascade).
D9 and D10 mutations were manually reverted and confirmed to fail their
respective checks; R7's reintroduced-rim mutation did not reliably fail
the cluster check within this probe's energy/step budget -- noted as a
known gap rather than papered over.
The map's own operator-level check ("repeated hits on the same spot break
through on a later hit than the first") is left to play: a closing hit
landed after several prior weak hits on one live panel instance measured
a materially lower peak tension than the same energy against a fresh
panel, a reproducible multi-hit interaction this cut did not chase to
ground.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-18 20:04:05 +02:00
|
|
|
// D10: KFracture dispatches once per frame while KWaveStep just integrated
|
|
|
|
|
// `substeps` times at fixedSubstepDt. _Dt was still holding that substep dt from
|
|
|
|
|
// the injection/wave setup above, so damage and temper erosion accrued at 1/substeps
|
|
|
|
|
// of the intended rate. Fracture's clock is the frame, not the substep — it reads
|
|
|
|
|
// one settled `u` per frame regardless of how many substeps produced it — so hand it
|
|
|
|
|
// the frame dt here. (Dispatching KFracture inside the substep loop instead would be
|
|
|
|
|
// the "physically honest" alternative the map names, but at up to 12 live panels
|
|
|
|
|
// dispatch count is the measured cost (§2.3), and fracture only needs to see the
|
|
|
|
|
// post-wave state once a frame, not mid-integration.)
|
|
|
|
|
sim.SetFloat("_Dt", dt);
|
Land the six shield-panel sources, ported to built-in and compiling
Cut 1 of docs/shield-panel-cut.md: land ShieldPanel.cs, ShieldPanel.shader,
ShieldSim.compute, ShieldCommon.hlsl, TilingBuilder.cs and Tilings.cs under
Assets/Shaders/Compute/ShieldField/, alongside Lightning/ and Slime/.
Port (URP -> built-in, three substitutions per the map's §2.1 probe):
- drop the UniversalPipeline SubShader tag, add IgnoreProjector
- swap the URP Core.hlsl include for UnityCG.cginc
- TransformWorldToHClip -> UnityWorldToClipPos (3 call sites)
Drawing collapse (D2/D5): RenderParams has no shaderPass member in
6000.3.24f1, and a multi-pass material draws every SubShader pass from one
indirect call regardless, so the three per-pass args buffers and draw calls
collapse to one args buffer sized to the widest pass (Outline) and one
Graphics.RenderPrimitivesIndirect call. Deleted: argsOutline, argsConduit,
drawFill/drawOutline/drawConduits and their branches.
Also deleted: ShieldPanelTester (the click harness the operator ruled
against; it also can't run — activeInputHandler is New Input System only)
and [ExecuteAlways] (incompatible with a pool per D13; Rebuild stays on the
context menu).
Defects fixed:
- D1: TilingBuilder.cs's inner `dist` shadowed the outer one (CS0136);
renamed to nbrDist.
- D3: CellStatic was 15 floats against a declared Stride of 16; added pad2
on both the HLSL and C# sides.
- D4: `centroid` is reserved in the compute compiler; renamed the
ShieldCommon.hlsl field to `center` (kept the C# field name as-is; it's
not reserved there and the two sides don't need matching names, only
matching layout).
- D6: fixed the comment on _BreakBudget — it's two elements (count + cap),
not the "single element" the old comment claimed.
- D12: the two-element uint[] Update() allocated every frame is now a
cached field, cheaper still under a pool of many live panels.
- D18 (R5 — operator ruling, not deferred to a follow-up): Penrose ignored
panelRadius entirely — a fixed authored iteration count meant the tiling
was just uniformly rescaled, not resampled, so cell count never grew with
the panel. Every other tiling here uses cellSize to hold local cell size
constant while radius extends coverage; Penrose now derives its iteration
count the same way (each substitution divides edge length by phi, solved
for the count that brings a unit edge to ~cellSize once scaled to
radius). This retires the now-meaningless `penroseIterations` field and
the `subdivisions` parameter it threaded through Tilings.Generate.
- Also removed a dead `idx` counter in Tilings.Voronoi (D8's mention of
dead code; the Vector2.zero sentinel ambiguity itself is unfixed, same as
D7/D19 — Voronoi and TriHex stay recorded defects, not fixed, per R5).
Not fixed here, per the map: D7 (Voronoi is unusably slow) and D19 (TriHex
has zero dual edges) are recorded, not fixed. D9/D10 (fracture tuning) are
Cut 5. D17 (growth gate blocks the spawning hit) and D20 (_PanelSpawnTime
hardcoded to 0) are Cut 3.
Repo-specific fix not in the map: Assets/Scripts/UI/ContextMenu.cs defines
a global `ContextMenu` MonoBehaviour that shadows UnityEngine.ContextMenu,
so `[ContextMenu("Rebuild")]` doesn't compile here (CS0616) even though it
did in the map's throwaway probe project. Used the fully-qualified
`[UnityEngine.ContextMenu(...)]` instead.
Adds Assets/Scripts/Editor/ShieldPanelCut1Verify.cs, a batchmode probe (no
NUnit path exists per the map's Q7 — Tests.asmdef can't see
Assembly-CSharp): asserts zero shader/compute compile errors and that
Penrose cell count actually scales with panelRadius (D18). Also adds
tests/mutation_tests_shield_panel_cut1.py, which mutates the D18 fix, the
D4 fix, and the shaderPass deletion in turn and asserts the probe goes red
each time, restoring the source after each case (own file per the
convention mutation_tests_addressables_cut.py established, so as not to
touch item-provenance's or the addressables cut's harnesses).
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-18 06:08:04 +02:00
|
|
|
sim.SetFloat("_TensileStrength", tensileStrength);
|
|
|
|
|
sim.SetFloat("_TemperErosion", temperErosion);
|
|
|
|
|
sim.SetFloat("_StoredEnergyGain", storedEnergyGain);
|
|
|
|
|
sim.SetFloat("_DamageGain", damageGain);
|
|
|
|
|
sim.SetFloat("_BreakAt", breakAt);
|
|
|
|
|
sim.SetFloat("_CrackStart", crackStart);
|
|
|
|
|
sim.SetFloat("_RadialKick", radialKick);
|
|
|
|
|
sim.SetFloat("_LateralKick", lateralKick);
|
|
|
|
|
sim.SetFloat("_NormalKick", normalKick);
|
|
|
|
|
sim.SetFloat("_SpinKick", spinKick);
|
|
|
|
|
sim.SetBuffer(kFracture, idUIn, cur);
|
|
|
|
|
sim.Dispatch(kFracture, groups, 1, 1);
|
|
|
|
|
|
|
|
|
|
// ---- per-frame state advance + visibility
|
|
|
|
|
sim.SetFloat("_Dt", dt);
|
2026-09-18 13:51:11 +02:00
|
|
|
sim.SetFloat("_PanelSpawnTime", panelSpawnTime);
|
Land the six shield-panel sources, ported to built-in and compiling
Cut 1 of docs/shield-panel-cut.md: land ShieldPanel.cs, ShieldPanel.shader,
ShieldSim.compute, ShieldCommon.hlsl, TilingBuilder.cs and Tilings.cs under
Assets/Shaders/Compute/ShieldField/, alongside Lightning/ and Slime/.
Port (URP -> built-in, three substitutions per the map's §2.1 probe):
- drop the UniversalPipeline SubShader tag, add IgnoreProjector
- swap the URP Core.hlsl include for UnityCG.cginc
- TransformWorldToHClip -> UnityWorldToClipPos (3 call sites)
Drawing collapse (D2/D5): RenderParams has no shaderPass member in
6000.3.24f1, and a multi-pass material draws every SubShader pass from one
indirect call regardless, so the three per-pass args buffers and draw calls
collapse to one args buffer sized to the widest pass (Outline) and one
Graphics.RenderPrimitivesIndirect call. Deleted: argsOutline, argsConduit,
drawFill/drawOutline/drawConduits and their branches.
Also deleted: ShieldPanelTester (the click harness the operator ruled
against; it also can't run — activeInputHandler is New Input System only)
and [ExecuteAlways] (incompatible with a pool per D13; Rebuild stays on the
context menu).
Defects fixed:
- D1: TilingBuilder.cs's inner `dist` shadowed the outer one (CS0136);
renamed to nbrDist.
- D3: CellStatic was 15 floats against a declared Stride of 16; added pad2
on both the HLSL and C# sides.
- D4: `centroid` is reserved in the compute compiler; renamed the
ShieldCommon.hlsl field to `center` (kept the C# field name as-is; it's
not reserved there and the two sides don't need matching names, only
matching layout).
- D6: fixed the comment on _BreakBudget — it's two elements (count + cap),
not the "single element" the old comment claimed.
- D12: the two-element uint[] Update() allocated every frame is now a
cached field, cheaper still under a pool of many live panels.
- D18 (R5 — operator ruling, not deferred to a follow-up): Penrose ignored
panelRadius entirely — a fixed authored iteration count meant the tiling
was just uniformly rescaled, not resampled, so cell count never grew with
the panel. Every other tiling here uses cellSize to hold local cell size
constant while radius extends coverage; Penrose now derives its iteration
count the same way (each substitution divides edge length by phi, solved
for the count that brings a unit edge to ~cellSize once scaled to
radius). This retires the now-meaningless `penroseIterations` field and
the `subdivisions` parameter it threaded through Tilings.Generate.
- Also removed a dead `idx` counter in Tilings.Voronoi (D8's mention of
dead code; the Vector2.zero sentinel ambiguity itself is unfixed, same as
D7/D19 — Voronoi and TriHex stay recorded defects, not fixed, per R5).
Not fixed here, per the map: D7 (Voronoi is unusably slow) and D19 (TriHex
has zero dual edges) are recorded, not fixed. D9/D10 (fracture tuning) are
Cut 5. D17 (growth gate blocks the spawning hit) and D20 (_PanelSpawnTime
hardcoded to 0) are Cut 3.
Repo-specific fix not in the map: Assets/Scripts/UI/ContextMenu.cs defines
a global `ContextMenu` MonoBehaviour that shadows UnityEngine.ContextMenu,
so `[ContextMenu("Rebuild")]` doesn't compile here (CS0616) even though it
did in the map's throwaway probe project. Used the fully-qualified
`[UnityEngine.ContextMenu(...)]` instead.
Adds Assets/Scripts/Editor/ShieldPanelCut1Verify.cs, a batchmode probe (no
NUnit path exists per the map's Q7 — Tests.asmdef can't see
Assembly-CSharp): asserts zero shader/compute compile errors and that
Penrose cell count actually scales with panelRadius (D18). Also adds
tests/mutation_tests_shield_panel_cut1.py, which mutates the D18 fix, the
D4 fix, and the shaderPass deletion in turn and asserts the probe goes red
each time, restoring the source after each case (own file per the
convention mutation_tests_addressables_cut.py established, so as not to
touch item-provenance's or the addressables cut's harnesses).
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-18 06:08:04 +02:00
|
|
|
sim.SetFloat("_GrowDuration", growDuration);
|
|
|
|
|
sim.SetFloat("_GlowDecay", 3.2f);
|
|
|
|
|
sim.SetFloat("_ShardLife", shardLife);
|
|
|
|
|
sim.SetFloat("_ShardDrag", shardDrag);
|
|
|
|
|
|
|
|
|
|
bVisible.SetCounterValue(0);
|
|
|
|
|
sim.Dispatch(kUpdate, groups, 1, 1);
|
|
|
|
|
|
|
|
|
|
GraphicsBuffer.CopyCount(bVisible, argsFill, sizeof(uint));
|
|
|
|
|
|
|
|
|
|
Draw(cur);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void PushMaterialUniforms()
|
|
|
|
|
{
|
|
|
|
|
mat.SetFloat("_CrackStart", crackStart);
|
|
|
|
|
mat.SetFloat("_ShardLife", shardLife);
|
|
|
|
|
mat.SetInt("_MaxShards", maxShards);
|
|
|
|
|
mat.SetFloat("_ConduitGain", conduitGain);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void Draw(GraphicsBuffer curU)
|
|
|
|
|
{
|
|
|
|
|
mpb.Clear();
|
|
|
|
|
mpb.SetBuffer(idCells, bCells);
|
|
|
|
|
mpb.SetBuffer(idVerts, bVerts);
|
|
|
|
|
mpb.SetBuffer(idEdges, bEdges);
|
|
|
|
|
mpb.SetBuffer(idState, bState);
|
|
|
|
|
mpb.SetBuffer(idU, curU);
|
|
|
|
|
mpb.SetBuffer(idFlow, bFlow);
|
|
|
|
|
mpb.SetBuffer(idVisible, bVisible);
|
|
|
|
|
mpb.SetMatrix("_PanelToWorld", transform.localToWorldMatrix);
|
|
|
|
|
mpb.SetFloat("_Time01", simTime);
|
|
|
|
|
|
|
|
|
|
worldBounds.center = transform.position;
|
|
|
|
|
|
|
|
|
|
var rp = new RenderParams(mat)
|
|
|
|
|
{
|
|
|
|
|
worldBounds = worldBounds,
|
|
|
|
|
matProps = mpb,
|
|
|
|
|
shadowCastingMode = ShadowCastingMode.Off,
|
|
|
|
|
receiveShadows = false,
|
|
|
|
|
layer = gameObject.layer
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// RenderParams exposes no per-pass selector in 6000.3.24f1 (D2), and a multi-pass
|
|
|
|
|
// material draws every SubShader pass from one indirect call regardless
|
|
|
|
|
// (D5) — Fill, Outline and Conduit all run here, pixel-identical to the
|
|
|
|
|
// old three-call version (§2.1.5), off one args buffer sized to the
|
|
|
|
|
// widest pass (Outline).
|
|
|
|
|
Graphics.RenderPrimitivesIndirect(rp, MeshTopology.Triangles, argsFill, 1, 0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ==============================================================
|
|
|
|
|
void ReleaseAll()
|
|
|
|
|
{
|
|
|
|
|
void R(ref GraphicsBuffer b) { b?.Release(); b = null; }
|
|
|
|
|
R(ref bCells); R(ref bVerts); R(ref bEdges); R(ref bState);
|
|
|
|
|
R(ref bUA); R(ref bUB); R(ref bV); R(ref bEcho); R(ref bFlow);
|
|
|
|
|
R(ref bVisible); R(ref bBudget);
|
|
|
|
|
R(ref argsFill);
|
|
|
|
|
|
|
|
|
|
if (mat != null)
|
|
|
|
|
{
|
|
|
|
|
if (Application.isPlaying) Destroy(mat); else DestroyImmediate(mat);
|
|
|
|
|
mat = null;
|
|
|
|
|
}
|
|
|
|
|
data = null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void OnDrawGizmosSelected()
|
|
|
|
|
{
|
|
|
|
|
Gizmos.color = new Color(0.4f, 0.8f, 1f, 0.5f);
|
|
|
|
|
Gizmos.matrix = transform.localToWorldMatrix;
|
|
|
|
|
Gizmos.DrawWireSphere(Vector3.zero, panelRadius);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|