Add painted-height HUD sliders for AO shift and relief intensity

Wire three material-backed look sliders (occlusion AO shift, relief normal
intensity, parallax intensity) through the paint HUD, its layout/state
persistence, the .paint slot format (bumped to v4), owner-authoritative
network sync and the runtime reset, mirroring the existing camo-knob path so
they save, restore, replicate and reset alongside the brush values.

Change the occlusion AO mapping from a division threshold to a linear shift:
AO fades from 1 down to (1 - shift) at the deepest painted hole, so a 0 shift
no longer leaves black spots. Enable _NORMALMAP alongside _PARALLAXMAP so URP
transforms the painted relief normal into world space (otherwise it discards
normalTS and the relief is invisible), and pin the added keyword in the build
keep-alive material.

Stop the pipette from picking the occlusion/height value and turn its toggle
off after a pick. Default the knobs to 0.2 shift / 1.4 normal / 1.0 parallax
across the shader, painter, network and HUD fallbacks.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
adminandClaude Opus 4.8 committed 2026-07-01 07:07:39 +02:00
1 parent fdcb4f8d8a
commit 59939b3496
11 files changed
+216 -49

No files matched your search

+27 -4
View File
@@ -16,7 +16,7 @@ public partial class MeshPainter
// map's canonical Color32 bytes. Raw (not PNG) avoids any sRGB/alpha-premultiply ambiguity and
// round-trips the exact canonical bytes ReadCanonical produces.
private const uint PaintFileMagic = 0x504D4843; // 'CHMP' little-endian
private const int PaintFileVersion = 3; // v3: added the per-player material scalars (emission + camo)
private const int PaintFileVersion = 4; // v3: emission + camo scalars; v4: + painted-height knobs
private const string PaintFileExt = ".paint";
/// <summary>
@@ -26,9 +26,11 @@ public partial class MeshPainter
/// </summary>
private struct PaintScalars
{
public bool has; // false = legacy v2 slot with no scalars -> leave the painter's values untouched
public bool has; // false = legacy v2 slot with no scalars -> leave the painter's values untouched
public bool hasHeight; // v4+ painted-height knobs present (occlusion threshold + normal/parallax intensity)
public float emissionIntensity;
public float camoHalfLambert, camoHalfLambertBrightness, camoSpecularDampen;
public float occlusionThreshold, heightNormalIntensity, parallaxIntensity;
}
private static string PaintLibraryDir =>
@@ -104,6 +106,11 @@ public partial class MeshPainter
w.Write(CamoHalfLambertBrightness);
w.Write(CamoSpecularDampen);
// v4: the painted-height knobs (same material-instance source of truth as the camo knobs).
w.Write(OcclusionThreshold);
w.Write(HeightNormalIntensity);
w.Write(ParallaxIntensity);
var bytes = new byte[n * 4];
for (int mi = 0; mi < MAP_COUNT; mi++)
{
@@ -160,6 +167,13 @@ public partial class MeshPainter
CamoHalfLambertBrightness = scalars.camoHalfLambertBrightness;
CamoSpecularDampen = scalars.camoSpecularDampen;
}
// v4 slots also carry the painted-height knobs; older slots (v2/v3) leave them at the current value.
if (scalars.hasHeight)
{
OcclusionThreshold = scalars.occlusionThreshold;
HeightNormalIntensity = scalars.heightNormalIntensity;
ParallaxIntensity = scalars.parallaxIntensity;
}
return true;
}
@@ -190,7 +204,7 @@ public partial class MeshPainter
{
if (r.ReadUInt32() != PaintFileMagic) return null;
int ver = r.ReadInt32();
if (ver != 2 && ver != PaintFileVersion) return null; // list legacy v2 slots + current
if (ver < 2 || ver > PaintFileVersion) return null; // list legacy v2..v3 slots + current
return r.ReadString();
}
}
@@ -216,7 +230,7 @@ public partial class MeshPainter
{
if (r.ReadUInt32() != PaintFileMagic) return null;
int ver = r.ReadInt32();
if (ver != 2 && ver != PaintFileVersion) return null; // read legacy v2 + current
if (ver < 2 || ver > PaintFileVersion) return null; // read legacy v2..v3 + current
r.ReadString(); // display name (header only; not needed for the pixel load)
int w = r.ReadInt32();
int h = r.ReadInt32();
@@ -232,6 +246,15 @@ public partial class MeshPainter
scalars.camoSpecularDampen = r.ReadSingle();
}
// v4+: the painted-height knobs follow the camo scalars.
if (ver >= 4)
{
scalars.hasHeight = true;
scalars.occlusionThreshold = r.ReadSingle();
scalars.heightNormalIntensity = r.ReadSingle();
scalars.parallaxIntensity = r.ReadSingle();
}
// Resolution change -> ignore rather than smear mismatched data; the brush re-seeds
// from the material instead.
if (w != textureSize || h != textureSize)