diff --git a/Assets/Scripts/MeshPainter.Pick.cs b/Assets/Scripts/MeshPainter.Pick.cs index 88d0550..31b37d3 100644 --- a/Assets/Scripts/MeshPainter.Pick.cs +++ b/Assets/Scripts/MeshPainter.Pick.cs @@ -7,9 +7,10 @@ public partial class MeshPainter // // A sampling MODE rather than a paint channel: while active (Shift or Ctrl held, or the HUD toggle), a // 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 - with every - // opacity/alpha forced to 1 so re-painting reproduces the picked look at full strength. Occlusion is - // excluded: it drives painted height now, so the pipette neither samples it nor enables its toggle. + // 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. // // 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,8 +23,8 @@ 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 (all alphas set to 1) 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.")] + "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.")] public bool pipette = false; [Tooltip("On a foreign-object pick, where albedo comes from. OFF (default): albedo is read " + @@ -46,8 +47,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, forcing the sampled opacities/alphas - /// to 1 and enabling those paint channels. Occlusion/height is excluded (see FinalizePick). Reads + /// (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 /// regardless of which channels are currently toggled. /// Pick paths, by what was hit: /// - our paint mesh -> texel-exact from the painted maps (full PBR); @@ -85,16 +86,15 @@ public partial class MeshPainter } /// - /// Common tail of every successful pick: forces the sampled opacities/alphas to 1, enables the - /// sampled paint channels, leaves pipette mode (one-shot) and notifies listeners (the in-game HUD) to - /// re-sync their widgets. 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. + /// 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. /// private void FinalizePick() { - metallicSmoothnessOpacity = 1f; - paintAlbedo = true; paintMetallicSmoothness = true; paintOcclusion = false; @@ -138,12 +138,13 @@ public partial class MeshPainter Color32 m = mso[idx]; Color32 e = em[idx]; - // sRGB bytes map straight back into the gamma-space brush colours (round-trips exactly). + // 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, 1f); + brushColor = new Color(a.r / 255f, a.g / 255f, a.b / 255f, brushColor.a); metallic = m.r / 255f; smoothness = m.a / 255f; - emissionColor = new Color(e.r / 255f, e.g / 255f, e.b / 255f, 1f); + emissionColor = new Color(e.r / 255f, e.g / 255f, e.b / 255f, emissionColor.a); return true; } @@ -163,7 +164,7 @@ public partial class MeshPainter // Keep the material's texel-exact PBR; optionally replace albedo with the lit on-screen // colour. No camera (headless) -> keep the material's raw albedo rather than failing. if (pipetteAlbedoFromScreen && TrySampleScreenColor(screenPos, out Color lit)) - brushColor = new Color(Mathf.Clamp01(lit.r), Mathf.Clamp01(lit.g), Mathf.Clamp01(lit.b), 1f); + brushColor = new Color(Mathf.Clamp01(lit.r), Mathf.Clamp01(lit.g), Mathf.Clamp01(lit.b), brushColor.a); return true; } @@ -224,7 +225,7 @@ public partial class MeshPainter Color albedo = baseCol; if (hasUv && baseTex) albedo = SampleTexel(baseTex, ApplyST(mat, "_BaseMap", "_MainTex", uv), true) * baseCol; - brushColor = new Color(Mathf.Clamp01(albedo.r), Mathf.Clamp01(albedo.g), Mathf.Clamp01(albedo.b), 1f); + brushColor = new Color(Mathf.Clamp01(albedo.r), Mathf.Clamp01(albedo.g), Mathf.Clamp01(albedo.b), brushColor.a); // --- Metallic / Smoothness: MetallicGloss map (linear) --- // URP/Lit: metallic = map.r ; smoothness = map.a * _Smoothness (or _Metallic/_Glossiness scalars). @@ -248,7 +249,7 @@ public partial class MeshPainter Color emCol = emColor; if (hasUv && emTex) emCol = SampleTexel(emTex, ApplyST(mat, "_EmissionMap", null, uv), true) * emColor; - emissionColor = new Color(Mathf.Clamp01(emCol.r), Mathf.Clamp01(emCol.g), Mathf.Clamp01(emCol.b), 1f); + emissionColor = new Color(Mathf.Clamp01(emCol.r), Mathf.Clamp01(emCol.g), Mathf.Clamp01(emCol.b), emissionColor.a); return true; } @@ -327,10 +328,10 @@ public partial class MeshPainter if (weightSum <= 1e-4f) return false; float inv = 1f / weightSum; // alphamap weights normally sum to 1, but guard anyway - brushColor = new Color(Mathf.Clamp01(albedo.r * inv), Mathf.Clamp01(albedo.g * inv), Mathf.Clamp01(albedo.b * inv), 1f); + brushColor = new Color(Mathf.Clamp01(albedo.r * inv), Mathf.Clamp01(albedo.g * inv), Mathf.Clamp01(albedo.b * inv), brushColor.a); metallic = Mathf.Clamp01(metallicVal * inv); smoothness = Mathf.Clamp01(smoothVal * inv); - emissionColor = new Color(0f, 0f, 0f, 1f); // occlusion left untouched (now painted height) + emissionColor = new Color(0f, 0f, 0f, emissionColor.a); // occlusion left untouched (now painted height) return true; } @@ -364,10 +365,10 @@ public partial class MeshPainter { if (!TrySampleScreenColor(screenPos, out Color c)) return false; - brushColor = new Color(Mathf.Clamp01(c.r), Mathf.Clamp01(c.g), Mathf.Clamp01(c.b), 1f); + brushColor = new Color(Mathf.Clamp01(c.r), Mathf.Clamp01(c.g), Mathf.Clamp01(c.b), brushColor.a); metallic = 0f; smoothness = 0f; - emissionColor = new Color(0f, 0f, 0f, 1f); // occlusion left untouched (now painted height) + emissionColor = new Color(0f, 0f, 0f, emissionColor.a); // occlusion left untouched (now painted height) return true; }