From 67e36963396a94a780a12d16b90220dabc1ef4a1 Mon Sep 17 00:00:00 2001 From: MelekTaus Moribundus Date: Mon, 29 Jun 2026 20:12:05 +0200 Subject: [PATCH] Add a toggle for picking foreign albedo from screen or material The hybrid foreign-object pipette previously always overwrote albedo with the lit on-screen pixel. Gate that behind a new serialized pipetteAlbedoFromScreen flag (default off): off reads albedo texel-exact from the material like the other channels, on takes it from the rendered, fully-lit pixel. The data channels still always come from the material. Co-Authored-By: Claude Opus 4.8 --- Assets/Scripts/MeshPainter.Pick.cs | 43 ++++++++++++++++++------------ 1 file changed, 26 insertions(+), 17 deletions(-) diff --git a/Assets/Scripts/MeshPainter.Pick.cs b/Assets/Scripts/MeshPainter.Pick.cs index c963f17..b0ae17e 100644 --- a/Assets/Scripts/MeshPainter.Pick.cs +++ b/Assets/Scripts/MeshPainter.Pick.cs @@ -10,20 +10,27 @@ public partial class MeshPainter // the hit point (albedo, metallic, smoothness, occlusion, emission) into the brush properties - // with every opacity/alpha forced to 1 so re-painting reproduces the picked look at full strength. // - // On a FOREIGN object the pick is a HYBRID: the data channels (metallic/smoothness/occlusion/ - // emission) come texel-exact from the material (raw, UNLIT), while albedo is taken from the - // rendered, fully-lit on-screen pixel ("what you see" - honours baked lightmaps/ambient/SSAO). - // This combines the PBR fidelity of the material read with the lit look of the screen. When no - // texel-exact material read is possible (no readable UV / headless) it falls back to a pure screen - // pick (lit albedo, data channels at defaults). Our own paint mesh and terrain are read texel-exact. + // On a FOREIGN object the data channels (metallic/smoothness/occlusion/emission) come texel-exact + // from the material (raw, UNLIT). Albedo follows the 'pipetteAlbedoFromScreen' toggle: OFF (default) + // reads it texel-exact from the material too; ON takes it from the rendered, fully-lit on-screen + // pixel ("what you see" - honours baked lightmaps/ambient/SSAO), combining material PBR fidelity + // with the lit look. When no texel-exact material read is possible (no readable UV / headless) it + // falls back to a pure screen pick (lit albedo, data channels at defaults). Our own paint mesh and + // terrain are read texel-exact. [Header("Brush - pipette (eyedropper)")] [Tooltip("Pipette mode: a click samples ALL channel values at the hit point on ANY surface " + "(albedo, metallic, smoothness, occlusion, emission) 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 while albedo comes from the lit on-screen pixel. Held button picks once.")] + "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 " + + "texel-exact from the material like the other channels (raw, unlit). ON: albedo is taken " + + "from the lit on-screen pixel ('what you see' - honours baked lightmaps/ambient/SSAO). " + + "The data channels (metallic/smoothness/occlusion/emission) always come from the material.")] + [SerializeField] private bool pipetteAlbedoFromScreen = false; + /// /// Raised after a successful pick, once the brush has adopted the sampled values and all four /// paint-channel flags have been enabled. The in-game HUD listens to re-sync its widgets. @@ -44,8 +51,8 @@ public partial class MeshPainter /// - our paint mesh -> texel-exact from the painted maps (full PBR); /// - terrain ground -> blend of the terrain layers by their splat weights (full PBR); /// - terrain tree / detail -> the rendered on-screen colour (no per-instance renderer/UV); - /// - any foreign object -> hybrid: data channels texel-exact from the material, albedo from - /// the rendered, fully-lit on-screen pixel (baked lightmaps/ambient/SSAO); + /// - any foreign object -> data channels texel-exact from the material; albedo from the + /// material or the lit on-screen pixel per pipetteAlbedoFromScreen; /// - anything else / sky -> the rendered on-screen colour. /// private void PickAtScreen(Vector2 screenPos) @@ -138,19 +145,21 @@ public partial class MeshPainter } /// - /// Hybrid foreign-object pick: the data channels (metallic/smoothness/occlusion/emission) come - /// texel-exact from the material via (raw, unlit), while - /// albedo is overwritten with the rendered, fully-lit on-screen pixel ("what you see" - honours - /// baked lightmaps/ambient/SSAO). When no texel-exact material read is possible (no readable UV / - /// headless) it falls back to a pure screen pick (lit albedo, data channels at defaults). + /// Foreign-object pick: the data channels (metallic/smoothness/occlusion/emission) always come + /// texel-exact from the material via (raw, unlit). Albedo + /// follows : when set, it is overwritten with the rendered, + /// fully-lit on-screen pixel ("what you see" - honours baked lightmaps/ambient/SSAO); otherwise + /// (default) it stays the material's texel-exact value. When no texel-exact material read is + /// possible (no readable UV / headless) it falls back to a pure screen pick (lit albedo, data + /// channels at defaults). /// private bool PickFromForeignHybrid(RaycastHit hit, Vector2 screenPos) { if (PickFromForeignMaterial(hit)) { - // Keep the material's texel-exact PBR; replace only albedo with the lit on-screen colour. - // No camera (headless) -> keep the material's raw albedo rather than failing the pick. - if (TrySampleScreenColor(screenPos, out Color lit)) + // 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); return true; }