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:
1 parent
d62eddc518
commit
c14525a2c8
15 files changed
+1005
-14
No files matched your search
@@ -72,6 +72,67 @@ public partial class MeshPainter
|
||||
if (sumAreaUv > 1e-9) _avgUvToLocal = (float)System.Math.Sqrt(sumArea3D / sumAreaUv);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Rasterizes the mesh UV islands into a per-texel coverage marker (true = the texel lies
|
||||
/// inside a UV triangle). This is the dilation SOURCE / overwrite guard: island texels are
|
||||
/// real content the edge-padding pass must never overwrite, while the gutter (false) stays
|
||||
/// paddable. Deriving the marker from the STATIC UV layout - instead of from transient
|
||||
/// per-stroke coverage - makes every seed (fresh start, loaded maps, backend switch) mark
|
||||
/// the same real content as painted, so the first stroke after a load/switch no longer
|
||||
/// bleeds over the existing paint. Built once and cached (UV layout + textureSize are fixed).
|
||||
/// </summary>
|
||||
private bool[] GetIslandMask()
|
||||
{
|
||||
int n = textureSize * textureSize;
|
||||
if (_islandMask != null && _islandMask.Length == n) return _islandMask;
|
||||
|
||||
var mask = new bool[n];
|
||||
if (_tris == null || _uvs == null) { _islandMask = mask; return mask; }
|
||||
|
||||
float ts = textureSize;
|
||||
const float eps = -1e-4f; // matches PaintStamp3D: tolerate 1px cracks between triangles
|
||||
int triCount = _tris.Length / 3;
|
||||
|
||||
for (int t = 0; t < triCount; t++)
|
||||
{
|
||||
int i0 = _tris[t * 3], i1 = _tris[t * 3 + 1], i2 = _tris[t * 3 + 2];
|
||||
Vector2 uv0 = _uvs[i0], uv1 = _uvs[i1], uv2 = _uvs[i2];
|
||||
|
||||
float p0x = uv0.x * ts, p0y = uv0.y * ts;
|
||||
float p1x = uv1.x * ts, p1y = uv1.y * ts;
|
||||
float p2x = uv2.x * ts, p2y = uv2.y * ts;
|
||||
|
||||
int minX = Mathf.Clamp(Mathf.FloorToInt(Mathf.Min(p0x, Mathf.Min(p1x, p2x))), 0, textureSize - 1);
|
||||
int maxX = Mathf.Clamp(Mathf.CeilToInt(Mathf.Max(p0x, Mathf.Max(p1x, p2x))), 0, textureSize - 1);
|
||||
int minY = Mathf.Clamp(Mathf.FloorToInt(Mathf.Min(p0y, Mathf.Min(p1y, p2y))), 0, textureSize - 1);
|
||||
int maxY = Mathf.Clamp(Mathf.CeilToInt(Mathf.Max(p0y, Mathf.Max(p1y, p2y))), 0, textureSize - 1);
|
||||
if (maxX < minX || maxY < minY) continue;
|
||||
|
||||
float d = (p1y - p2y) * (p0x - p2x) + (p2x - p1x) * (p0y - p2y);
|
||||
if (Mathf.Abs(d) < 1e-7f) continue;
|
||||
float invD = 1f / d;
|
||||
|
||||
for (int y = minY; y <= maxY; y++)
|
||||
{
|
||||
float py = y + 0.5f;
|
||||
int row = y * textureSize;
|
||||
for (int x = minX; x <= maxX; x++)
|
||||
{
|
||||
float px = x + 0.5f;
|
||||
float a = ((p1y - p2y) * (px - p2x) + (p2x - p1x) * (py - p2y)) * invD;
|
||||
if (a < eps) continue;
|
||||
float b = ((p2y - p0y) * (px - p2x) + (p0x - p2x) * (py - p2y)) * invD;
|
||||
if (b < eps) continue;
|
||||
if (1f - a - b < eps) continue;
|
||||
mask[row + x] = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_islandMask = mask;
|
||||
return mask;
|
||||
}
|
||||
|
||||
/// <summary>Builds the per-map seed colors (in canonical/material layout) from the existing material.</summary>
|
||||
private void BuildSeedColors()
|
||||
{
|
||||
|
||||
Reference in new issue
Block a user