Add spray brush, paint interval, and per-channel paint opacity

Spray brush mode: bakes a procedural scattered-dot mask per stamp (random
position, size, alpha within the brush disc) and paints it through the
existing single-stamp pipeline; dot centers clamped to (0.5 - dotR) so the
spray footprint matches the solid brush radius.

Paint interval: float on MeshPainter; 0 keeps the continuous interpolated
stroke, >0 emits a single timed dab every x.xx seconds.

Color picker alpha: horizontal alpha bar on both HSV pickers, runtime-built
in Rebind (no prefab re-bake), passed through to brushColor.a / emissionColor.a.

Metallic/smoothness + occlusion paint opacity: new opacity fields applied in
both CPU and GPU backends (GPU MSO draw split into MS and occlusion sub-stamps
for independent opacity), with cloned alpha sliders wired into the HUD.

Also includes the prefab layout tweaks and scene adjustments.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
adminandClaude Opus 4.8 committed 2026-06-24 19:07:28 +02:00
1 parent af3aa34d66
commit b07d9a22d2
9 files changed
+1227 -86

No files matched your search

+11 -7
View File
@@ -112,6 +112,8 @@ public partial class MeshPainter
float msoR = Mathf.Clamp01(metallic) * 255f; // metallic
float msoG = Mathf.Clamp01(occlusion) * 255f; // occlusion
float msoA = Mathf.Clamp01(smoothness) * 255f; // smoothness
float msOpMul = Mathf.Clamp01(metallicSmoothnessOpacity); // metallic+smoothness paint opacity
float aoOpMul = Mathf.Clamp01(occlusionOpacity); // occlusion paint opacity
CpuMap mso = _cpu[MAP_MSO];
bool aMSO = aMS || aAO;
@@ -210,14 +212,16 @@ public partial class MeshPainter
if (aMSO)
{
// Mask map blend (op = mask, no extra alpha multiplier). Metallic(R) +
// smoothness(A) only when painting MS; occlusion(G) only when painting AO.
// Untouched channels keep their previous value; B stays 0.
// Mask map blend. Metallic(R) + smoothness(A) blend with the MS opacity,
// occlusion(G) with its own opacity - each = mask * the channel's opacity
// multiplier (the albedo brushColor.a equivalent). Untouched channels keep
// their previous value; B stays 0.
Color32 dst = mso.pixels[idx];
float it = 1f - maskOp;
byte mr = aMS ? (byte)(dst.r * it + msoR * maskOp) : dst.r;
byte mg = aAO ? (byte)(dst.g * it + msoG * maskOp) : dst.g;
byte ma = aMS ? (byte)(dst.a * it + msoA * maskOp) : dst.a;
float msOp = maskOp * msOpMul;
float aoOp = maskOp * aoOpMul;
byte mr = aMS ? (byte)(dst.r * (1f - msOp) + msoR * msOp) : dst.r;
byte mg = aAO ? (byte)(dst.g * (1f - aoOp) + msoG * aoOp) : dst.g;
byte ma = aMS ? (byte)(dst.a * (1f - msOp) + msoA * msOp) : dst.a;
mso.pixels[idx] = new Color32(mr, mg, 0, ma);
mso.painted[idx] = true;
ExpandDirty(mso, x, y);