Add HUD brush mask selector and fix dilation seam artifacts

- Add mask selector with previews to the brush card (data-driven from
  painter.brushMasks); persist activeBrushIndex with HUD layout.
- Fix alpha/coverage artifacts when overpainting after load or backend
  switch: derive the dilation source marker from a deterministic UV island
  mask instead of resetting it to "unpainted" on every seed.
- Import the two final brush mask textures (Brushes/).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
adminandClaude Opus 4.8 committed 2026-06-25 06:23:08 +02:00
1 parent d62eddc518
commit c14525a2c8
15 files changed
+1005 -14

No files matched your search

+11 -8
View File
@@ -68,7 +68,7 @@ public partial class MeshPainter
return true;
}
/// <summary>Seeds a GPU map's display RT from canonical data (alpha = 0 = unpainted), then packs MS to final.</summary>
/// <summary>Seeds a GPU map's display RT from canonical data (coverage alpha = UV islands), then packs MS to final.</summary>
private void SeedGpuMap(int mi, Color32[][] canonical)
{
var src = (canonical != null && canonical[mi] != null && canonical[mi].Length == textureSize * textureSize)
@@ -78,15 +78,15 @@ public partial class MeshPainter
if (mi == MAP_MSO)
{
// Canonical (R=metallic, G=occlusion, A=smoothness) -> work layout
// (R=metallic, G=smoothness, B=occlusion); alpha = coverage stays 0 (unpainted).
// (R=metallic, G=smoothness, B=occlusion); UploadColorsToRT fills A = island coverage.
var work = new Color32[src.Length];
for (int i = 0; i < src.Length; i++) work[i] = new Color32(src[i].r, src[i].a, src[i].g, 0);
UploadColorsToRT(work, gm.display, paintedAlpha: false, linear: true);
UploadColorsToRT(work, gm.display, linear: true);
PackMs(gm); // build the final mask RT once so the material shows the seeded values
}
else
{
UploadColorsToRT((Color32[])src.Clone(), gm.display, paintedAlpha: false, linear: gm.sRGB == false);
UploadColorsToRT((Color32[])src.Clone(), gm.display, linear: gm.sRGB == false);
}
}
@@ -125,12 +125,15 @@ public partial class MeshPainter
}
/// <summary>Writes Color32 data into a RenderTexture via a temporary texture.</summary>
private void UploadColorsToRT(Color32[] colors, RenderTexture rt, bool paintedAlpha, bool linear)
private void UploadColorsToRT(Color32[] colors, RenderTexture rt, bool linear)
{
var tmp = new Texture2D(textureSize, textureSize, TextureFormat.RGBA32, false, linear);
// Alpha marks painted (255) vs unpainted (0) for the dilation pass.
byte a = paintedAlpha ? (byte)255 : (byte)0;
for (int i = 0; i < colors.Length; i++) colors[i].a = a;
// Alpha is the dilation coverage marker: island texels (real content) = 255 so the
// dilate pass keeps them; the gutter = 0 so it stays paddable. Driving this from the UV
// islands (not a flat "unpainted") protects seeded content from the first stroke's bleed.
var island = GetIslandMask();
bool useMask = island.Length == colors.Length;
for (int i = 0; i < colors.Length; i++) colors[i].a = (useMask && island[i]) ? (byte)255 : (byte)0;
tmp.SetPixels32(colors);
tmp.Apply(false);