From 5149544afea9b7279e2671b8d038b2a6b8ccb1ee Mon Sep 17 00:00:00 2001 From: MelekTaus Moribundus Date: Wed, 1 Jul 2026 21:52:37 +0200 Subject: [PATCH] Pick height/occlusion channel when sampling own player The pipette now reads the height (occlusion) value from the own paint mesh and enables the height toggle; picking anything else disables the toggle while leaving value and alpha untouched. Co-Authored-By: Claude Opus 4.8 --- Assets/Scripts/MeshPainter.Pick.cs | 39 ++++++++++++++++++++---------- 1 file changed, 26 insertions(+), 13 deletions(-) diff --git a/Assets/Scripts/MeshPainter.Pick.cs b/Assets/Scripts/MeshPainter.Pick.cs index 31b37d3..4a2512f 100644 --- a/Assets/Scripts/MeshPainter.Pick.cs +++ b/Assets/Scripts/MeshPainter.Pick.cs @@ -9,8 +9,9 @@ public partial class MeshPainter // left-click raycasts onto ANY surface (not just the paint mesh) and reads the channel values at // the hit point (albedo, metallic, smoothness, emission) into the brush properties. The brush // opacities/alphas (albedo alpha, metallic/smoothness opacity, emission alpha) are left UNTOUCHED, - // so a pick changes only WHAT is painted, not how strongly. Occlusion is excluded: it drives painted - // height now, so the pipette neither samples it nor enables its toggle. + // so a pick changes only WHAT is painted, not how strongly. Height (the occlusion channel) is picked + // and its toggle enabled ONLY when our own paint mesh is hit; any other surface leaves the height + // value untouched and disables its toggle. The height opacity (occlusionOpacity) is untouched either way. // // On a FOREIGN object the data channels (metallic/smoothness/emission) come texel-exact // from the material (raw, UNLIT). Albedo follows the 'pipetteAlbedoFromScreen' toggle: OFF (default) @@ -22,9 +23,11 @@ public partial class MeshPainter [Header("Brush - pipette (eyedropper)")] [Tooltip("Pipette mode: a click samples the channel values at the hit point on ANY surface " + - "(albedo, metallic, smoothness, emission - NOT occlusion/height) and adopts them into the " + - "brush (opacities/alphas left untouched) instead of painting. On a foreign object the data " + - "channels are read texel-exact from the material; albedo follows 'Pipette Albedo From Screen'. Held button picks once.")] + "(albedo, metallic, smoothness, emission) and adopts them into the brush (opacities/alphas " + + "left untouched) instead of painting. Height (occlusion) is adopted and its toggle enabled " + + "only when our own paint mesh is picked; any other surface disables the height toggle. On a " + + "foreign object the data channels are read texel-exact from the material; albedo follows " + + "'Pipette Albedo From Screen'. Held button picks once.")] public bool pipette = false; [Tooltip("On a foreign-object pick, where albedo comes from. OFF (default): albedo is read " + @@ -48,7 +51,8 @@ public partial class MeshPainter /// /// Raycasts at a screen position against ANY collider, samples the channel values at the hit /// (texel-exact where possible) and copies them into the brush, leaving the brush opacities/alphas - /// untouched and enabling those paint channels. Occlusion/height is excluded (see FinalizePick). Reads + /// untouched and enabling those paint channels. Height (occlusion) is adopted and its toggle enabled + /// only when our own paint mesh is hit; any other surface disables it (see FinalizePick). Reads /// regardless of which channels are currently toggled. /// Pick paths, by what was hit: /// - our paint mesh -> texel-exact from the painted maps (full PBR); @@ -61,10 +65,16 @@ public partial class MeshPainter private void PickAtScreen(Vector2 screenPos) { bool ok; + // Height (occlusion) is only meaningful off our OWN painted maps, so a hit on our paint mesh is + // the sole pick that enables the height channel - every other surface leaves it off (FinalizePick). + bool pickedHeight = false; if (TryRaycastAny(screenPos, out RaycastHit hit)) { if (hit.collider == _collider) + { ok = PickFromPaintTextures(hit); + pickedHeight = ok; + } else { Terrain terrain = hit.collider is TerrainCollider ? hit.collider.GetComponent() : null; @@ -82,22 +92,23 @@ public partial class MeshPainter } if (!ok) return; - FinalizePick(); + FinalizePick(pickedHeight); } /// /// Common tail of every successful pick: enables the sampled paint channels, leaves pipette mode /// (one-shot) and notifies listeners (the in-game HUD) to re-sync their widgets. The brush /// opacities/alphas (albedo alpha, metallic/smoothness opacity, emission alpha) are left UNTOUCHED, - /// so a pick changes only WHAT is painted, not how strongly. Occlusion is deliberately EXCLUDED: - /// that channel now drives painted height, not a surface property to sample, so the pipette neither - /// reads it (see the pick paths) nor enables its toggle - the deliberate height brush value is left untouched. + /// so a pick changes only WHAT is painted, not how strongly. Height (the occlusion channel) is a + /// special case: is true only when our own paint mesh was picked - + /// then the height toggle is enabled with the adopted value; any other surface disables the toggle + /// and keeps the deliberate height value. The height opacity (occlusionOpacity) is untouched either way. /// - private void FinalizePick() + private void FinalizePick(bool pickedHeight) { paintAlbedo = true; paintMetallicSmoothness = true; - paintOcclusion = false; + paintOcclusion = pickedHeight; paintEmission = true; pipette = false; @@ -140,10 +151,12 @@ public partial class MeshPainter // sRGB bytes map straight back into the gamma-space brush colours (round-trips exactly). The // alpha (paint opacity) is deliberately preserved, not overwritten - a pick changes only the colour. - // Occlusion (m.g) is NOT picked: it now drives painted height, kept as the deliberate brush value. brushColor = new Color(a.r / 255f, a.g / 255f, a.b / 255f, brushColor.a); metallic = m.r / 255f; smoothness = m.a / 255f; + // Occlusion (m.g) is the painted HEIGHT: adopted only off our own paint mesh, which is the sole + // pick that enables the height toggle (FinalizePick). Its opacity (occlusionOpacity) is left as-is. + occlusion = m.g / 255f; emissionColor = new Color(e.r / 255f, e.g / 255f, e.b / 255f, emissionColor.a); return true; }