Persist material scalars, reset the HUD, and split alpha from the picker

Save and restore the emission intensity and the three camo knobs
(half-Lambert, half-Lambert brightness, specular dampen) with a named
painting. The .paint format moves to v3 with a small scalar block
between the header and the pixel maps; v2 paintings still load with
their painter values left untouched. Loading re-syncs the emission and
camo sliders, and peers pick the values up for free through the
existing owner-authoritative paint NetworkVariables.

Add a HUD reset (Lib_ResetHud button plus context-menu fallback) that
behaves as if the saved hud-layout file were deleted: it removes the
file, restores panels to their authored prefab positions, and resets
the brush values to the painter/material defaults captured before any
carry/restore runs.

Remove the alpha bar from HsvColorPicker so alpha is driven by
standalone Slider_AlbedoAlpha / Slider_EmissionAlpha bindings; the
picker now writes RGB only and the sliders write alpha only, with no
feedback between them. Add a generic SliderCurve component that reshapes
a slider's handle position into a non-linear value (with an inverse for
setting values back), and make the slider wiring curve-aware.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
adminandClaude Opus 4.8 committed 2026-06-30 18:20:13 +02:00
1 parent fe8f12b8bb
commit 057c99fa1d
7 files changed
+313 -128

No files matched your search

+95
View File
@@ -60,6 +60,16 @@ public partial class PaintHud
private bool _hasPendingCamo;
private bool _camoEverReady;
// Authored defaults for the runtime reset (= "as if the saved layout file had been deleted"): the
// prefab's panel positions, captured before any saved layout is restored over them, and the painter's
// prefab/material default brush values, captured before carry/restore mutate the live painter. Camo is
// material-backed (readable only once IsReady), so it is filled in the LateUpdate ready-gate instead.
private readonly Dictionary<string, Vector2> _authoredPanelPos = new Dictionary<string, Vector2>();
private bool _hasAuthoredLayout;
private readonly HudValues _defaults = new HudValues();
private bool _hasDefaults; // non-camo defaults captured (first bind, before restore/carry)
private bool _hasDefaultCamo; // camo defaults captured (first ready-gate, before any pending camo apply)
private static string HudSaveDir => Path.Combine(Application.persistentDataPath, "MeshPaint");
private string HudSavePath()
@@ -211,6 +221,17 @@ public partial class PaintHud
// save file) the material keeps its authored camo and the sliders just reflect it.
if (!_camoSynced && painter.IsReady)
{
// Capture the material's authored camo as the reset default BEFORE any pending (restored or
// carried) camo is applied over it. Once per HUD: a later session/respawn keeps the very
// first authored value, so a reset always returns to the genuine material default.
if (!_hasDefaultCamo)
{
_defaults.camoHalfLambert = painter.CamoHalfLambert;
_defaults.camoHalfLambertBrightness = painter.CamoHalfLambertBrightness;
_defaults.camoSpecularDampen = painter.CamoSpecularDampen;
_hasDefaultCamo = true;
}
if (_hasPendingCamo)
{
painter.CamoHalfLambert = _pendingCamo.x;
@@ -402,6 +423,80 @@ public partial class PaintHud
}
}
// --- Reset to authored defaults (drives the HUD's reset button) ---
/// <summary>
/// Snapshots the authored panel positions from the freshly instantiated layout, before any saved
/// layout is restored over them. The runtime reset restores exactly these. Called once from Start
/// (right after the layout prefab is instantiated); idempotent.
/// </summary>
public void CaptureAuthoredLayout()
{
if (_hasAuthoredLayout || !_panel) return;
var root = _panel.transform;
foreach (var name in DraggablePanels)
if (FindDeep(root, name) is RectTransform rt)
_authoredPanelPos[name] = rt.anchoredPosition;
_hasAuthoredLayout = true;
}
/// <summary>
/// Snapshots the painter's prefab-default brush values on the first bind, before carry/restore mutate
/// the live painter. Non-camo only here (a freshly spawned painter holds its prefab defaults); the
/// material-backed camo is not readable yet and is captured later in the LateUpdate ready-gate.
/// </summary>
private void CaptureDefaults()
{
if (_hasDefaults || !painter) return;
CopyValues(painter, _defaults); // CopyValues skips camo until IsReady -> camo stays for the ready-gate
_hasDefaults = true;
}
/// <summary>
/// Resets the HUD to its authored defaults, exactly as if the saved layout file had been deleted: the
/// panels jump back to their prefab positions and every brush value returns to the painter's prefab /
/// material defaults. The save file is deleted too, so the reset also survives the next start.
/// </summary>
[ContextMenu("Reset HUD Layout And Values")]
public void ResetHudToDefaults()
{
// Delete the saved layout so a restart opens at the authored defaults as well (the literal intent).
ClearSavedHudState();
// Panels -> authored prefab positions.
if (_panel && _hasAuthoredLayout)
{
var root = _panel.transform;
foreach (var kv in _authoredPanelPos)
if (FindDeep(root, kv.Key) is RectTransform rt)
rt.anchoredPosition = kv.Value;
}
// Brush values -> painter prefab/material defaults, then push them to every widget.
if (painter && _hasDefaults)
{
// Reset clicked before the ready-gate captured the camo defaults? keep the live camo rather than
// slamming it to the not-yet-filled 0 (which would flatten the material's lighting).
if (!_hasDefaultCamo && painter.IsReady)
{
_defaults.camoHalfLambert = painter.CamoHalfLambert;
_defaults.camoHalfLambertBrightness = painter.CamoHalfLambertBrightness;
_defaults.camoSpecularDampen = painter.CamoSpecularDampen;
}
ApplyValues(painter, _defaults);
SyncFromPainter();
SyncCamoSliders();
// ApplyValues wrote the camo straight to the material (painter is ready at a runtime reset), so
// drop any still-pending restored camo the ready-gate would otherwise re-apply over the default.
_hasPendingCamo = false;
CaptureCarry(); // a later respawn now carries the defaults, not the pre-reset values
SnapshotSavedBaseline(); // values match the (now deleted) save -> the reset itself triggers no auto-save
}
}
/// <summary>Deletes the saved HUD layout (next start opens at the authored layout / defaults). Editor convenience.</summary>
[ContextMenu("Clear Saved HUD Layout")]
public void ClearSavedHudState()