Files
chameleon/Assets/Scripts/Components/MeshPainter/MeshPainter.Backend.cs
T
adminandClaude Opus 4.8 e23fae0c50 Reorganize scripts into subfolders by responsibility
Move scripts under Assets/Scripts/ into Components, GameMode, Helper,
Network, Enum, Struct and ScriptableObject folders. Pure file moves
(plus folder .meta files); no code changes.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-02 15:02:15 +02:00

110 lines
4.1 KiB
C#

using UnityEngine;
public partial class MeshPainter
{
// --- Backend selection / switching ---
private void SetupBackend(bool gpu, Color32[][] canonical)
{
// SetupGpu returns false if the shaders are missing and it fell back to CPU; honor
// the real outcome so _gpuActive never claims GPU while _gpu is null (would NRE on
// the next SwitchBackend / paint).
if (gpu)
{
_gpuActive = SetupGpu(canonical);
}
else
{
SetupCpu(canonical);
_gpuActive = false;
}
}
/// <summary>Switches the backend at runtime, carrying over all painted maps.</summary>
private void SwitchBackend(bool toGpu)
{
var canonical = new Color32[MAP_COUNT][];
for (int mi = 0; mi < MAP_COUNT; mi++) canonical[mi] = ReadCanonical(mi);
RebuildBackend(toGpu, canonical);
Debug.Log($"[MeshPainter] Backend switched -> {(toGpu ? "GPU" : "CPU")} painting.", this);
}
/// <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).
/// </summary>
private void ReseedCanonical(Color32[][] canonical) => 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)
{
if (_gpuActive) TeardownGpu();
else TeardownCpu();
SetupBackend(gpu, canonical);
_hasLastScreen = false;
}
/// <summary>Seed source for a map: the supplied canonical data when valid, else the material seed.</summary>
private Color32[] ResolveSeed(int mi, Color32[][] canonical)
{
return (canonical != null && canonical[mi] != null && canonical[mi].Length == textureSize * textureSize)
? canonical[mi] : _seedCanonical[mi];
}
// Backend-agnostic stroke dispatch (GPU vs CPU), shared by local painting and replayed net stamps.
private void BeginBackendStroke()
{
if (_gpuActive) BeginGpuStroke();
else ResetCpuDirty();
}
private void DrawBackendStamp(Vector3 pLocal, float radiusLocal, Vector3 u, Vector3 v, Vector3 n)
{
if (_gpuActive) DrawBrushGpu(pLocal, radiusLocal, u, v, n);
else PaintStamp3D(pLocal, radiusLocal, u, v, n);
}
private void EndBackendStroke()
{
if (_gpuActive) EndGpuStroke();
else EndCpuStroke();
}
/// <summary>Reads a map from the active backend into canonical (material) layout.</summary>
private Color32[] ReadCanonical(int mi)
{
if (_gpuActive)
{
var gm = _gpu[mi];
// MSO: read the packed final mask RT (R=metallic, G=occlusion, A=smoothness).
// Others: the display RT. The mask map is linear data; color maps read sRGB.
if (mi == MAP_MSO) return ReadRtPixels(gm.final, linear: true);
return ReadRtPixels(gm.display, linear: MapIsLinear(mi));
}
return (Color32[])_cpu[mi].pixels.Clone();
}
private static bool MapIsLinear(int mi) => mi == MAP_MSO;
// Material slots per map, as pre-hashed IDs (this runs per stroke end - no per-call allocations).
// MSO: one mask map drives three URP slots: _MetallicGlossMap (R/A) + _OcclusionMap (G) +
// _ParallaxMap (G = occlusion reused as a 0.5-centred height for parallax/relief).
private static readonly int[][] MapPropIds =
{
new[] { BaseMapId, MainTexId }, // MAP_ALBEDO
new[] { MetallicGlossMapId, OcclusionMapId, ParallaxMapId }, // MAP_MSO
new[] { EmissionMapId }, // MAP_EM
};
private void AssignMapTexture(int mi, Texture tex)
{
if (!_materialInstance) return;
foreach (int id in MapPropIds[mi])
if (_materialInstance.HasProperty(id)) _materialInstance.SetTexture(id, tex);
}
}