Hunter shot: replicated laser beam + X-ray hit flash; snapshot apply in-place reseed

- Laser.shader + HunterLaserBeam: additive HDR beam, fade in/out over opacity
  and thickness, code-driven LineRenderer, tunable emission multiplier.
- FirstPersonMode: TryShoot computes world endpoint, fires beam with lateral
  muzzle offset (right) so it isn't dead-center from hunter view.
- NetworkPlayerSetup: shot relay RPCs (owner->server->all) + hit-flash RPC
  (server->all), flash OR-branch in ComputeXRay.
- MatchManager: hit reveal broadcasts X-ray flash first, delays hider removal
  by hitRevealDelay so the glow is visible before the body vanishes.
- MeshPainter reseed: in-place fast path avoids full GPU backend teardown on
  snapshot apply (no ~70MB RT churn), fixes spawn/paint-load hitch.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
adminandClaude Opus 4.8 committed 2026-07-04 14:18:55 +02:00
1 parent 5b95cb7f56
commit 9290ef650b
10 files changed
+403 -23

No files matched your search

@@ -32,11 +32,27 @@ public partial class MeshPainter
}
/// <summary>
/// Tears down the active backend and rebuilds it from externally supplied canonical maps (load /
/// late-join snapshot / undo). Runs the island/dilation seeding path so edge padding is rebuilt for
/// the new content. The input arrays are never mutated (SetupBackend clones its seed).
/// Re-seeds the backend from externally supplied canonical maps (load / late-join snapshot / undo),
/// rebuilding the island/dilation edge padding for the new content. The input arrays are never mutated
/// (both seed paths clone before touching a map).
///
/// Fast path: when the backend is already built on this same type + size - the common runtime reseed
/// (remote snapshot apply, carry/library load, undo/redo) - the existing RTs/textures are re-filled in
/// place instead of a full teardown+rebuild. At 1024² a rebuild frees and re-creates nine ARGBHalf RTs
/// (~70 MB of GPU allocation) every call, which stalls the frame and shows up as a hitch on every
/// observer when a (freshly painted) avatar spawns. Re-seeding reuses the RTs (identical size/format)
/// for a bit-identical result. A pending GPU<->CPU switch or an un-built backend falls through to the
/// full rebuild.
/// </summary>
private void ReseedCanonical(Color32[][] canonical) => RebuildBackend(useGpu, canonical);
private void ReseedCanonical(Color32[][] canonical)
{
if (_setupValid)
{
if (useGpu && _gpuActive && _gpu != null) { ReseedGpuInPlace(canonical); _hasLastScreen = false; return; }
if (!useGpu && !_gpuActive && _cpu != null) { ReseedCpuInPlace(canonical); _hasLastScreen = false; return; }
}
RebuildBackend(useGpu, canonical);
}
/// <summary>Tears down the active backend and sets up the requested one on the given maps.</summary>
private void RebuildBackend(bool gpu, Color32[][] canonical)
@@ -43,6 +43,27 @@ public partial class MeshPainter
return px;
}
/// <summary>
/// Re-seeds the ALREADY-built CPU maps from canonical data without recreating the Texture2D mirrors:
/// each map's pixel buffer + island guard are re-seeded and re-uploaded into the existing texture,
/// mirroring SetupCpu but reusing the textures. The GPU counterpart is ReseedGpuInPlace. Callers ensure
/// !_gpuActive && _cpu != null.
/// </summary>
private void ReseedCpuInPlace(Color32[][] canonical)
{
for (int mi = 0; mi < MAP_COUNT; mi++)
{
var cm = _cpu[mi];
cm.pixels = SeedCpuPixels(mi, canonical);
cm.painted = (bool[])GetIslandMask().Clone();
if (cm.fillStamp != null) System.Array.Clear(cm.fillStamp, 0, cm.fillStamp.Length);
cm.dilateStamp = 0;
cm.dx0 = textureSize; cm.dy0 = textureSize; cm.dx1 = -1; cm.dy1 = -1; // no pending dirty region
cm.tex.SetPixels32(cm.pixels);
cm.tex.Apply(false);
}
}
private void TeardownCpu()
{
if (_cpu == null) return;
@@ -100,6 +100,23 @@ public partial class MeshPainter
}
}
/// <summary>
/// Re-seeds the ALREADY-built GPU maps from canonical data without recreating the RenderTextures or
/// materials: each map's display RT is re-uploaded + re-stamped (MSO re-packed) exactly as SetupGpu
/// does, but the existing RTs (same size/format) are reused, so the fur/albedo RTs a sibling
/// FurShellRenderer samples keep their references. Avoids the per-apply ~70 MB RT alloc/free churn a
/// full RebuildBackend incurs at 1024². Callers ensure _gpuActive && _gpu != null.
/// </summary>
private void ReseedGpuInPlace(Color32[][] canonical)
{
for (int mi = 0; mi < MAP_COUNT; mi++)
{
SeedGpuMap(mi, canonical);
var gm = _gpu[mi];
AssignMapTexture(mi, gm.final != null ? gm.final : gm.display);
}
}
/// <summary>
/// Stamps the dilation coverage guard into a map's alpha: rasterizes the mesh with the brush
/// shader's coverage pass (pass 1), writing alpha = 1 on exactly the texels the GPU brush can