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 <noreply@anthropic.com>
This commit is contained in:
adminandClaude Opus 4.8 committed 2026-06-29 20:12:19 +02:00
1 parent 4da2e3a70c
commit 67e3696339
1 file changed
+26 -17
+26 -17
View File
@@ -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;
/// <summary>
/// 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.
/// </summary>
private void PickAtScreen(Vector2 screenPos)
@@ -138,19 +145,21 @@ public partial class MeshPainter
}
/// <summary>
/// Hybrid foreign-object pick: the data channels (metallic/smoothness/occlusion/emission) come
/// texel-exact from the material via <see cref="PickFromForeignMaterial"/> (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 <see cref="PickFromForeignMaterial"/> (raw, unlit). Albedo
/// follows <see cref="pipetteAlbedoFromScreen"/>: 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).
/// </summary>
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;
}