Skip to content

Commit 3935dd3

Browse files
committed
added changelog, and added unit tests
1 parent 0ea66c5 commit 3935dd3

File tree

4 files changed

+129
-4
lines changed

4 files changed

+129
-4
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
1111

1212
- [PBLD-242] Fixed a bug where edges were being incorrectly selected when one or both vertices were behind the camera's near plane, causing flipped lines and inconsistent selection behavior.
1313
- [PBLD-226] Fixed a bug where ProBuilder faces could not selected when obscured by another GameObject
14+
- [PBLD-164] Fixed a bug with UV autostitching where the position offset would not take into account the face rotation center offset.
1415

1516
## [6.0.6] - 2025-07-01
1617

Runtime/MeshOperations/UV/TextureStitching.cs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,11 @@ static partial class UVEditing
88
/// Provided two faces, this method will attempt to project @f2 and align its size, rotation, and position to match
99
/// the shared edge on f1. Returns true on success, false otherwise.
1010
/// </summary>
11-
/// <param name="mesh"></param>
12-
/// <param name="f1"></param>
13-
/// <param name="f2"></param>
11+
/// <param name="mesh">The mesh containing the faces</param>
12+
/// <param name="f1">The anchor face</param>
13+
/// <param name="f2">The face to align to the anchor</param>
1414
/// <param name="channel"></param>
15-
/// <returns></returns>
15+
/// <returns>true if the autostitching succeeded, else fase</returns>
1616
public static bool AutoStitch(ProBuilderMesh mesh, Face f1, Face f2, int channel)
1717
{
1818
var wings = WingedEdge.GetWingedEdges(mesh, new [] { f1, f2 });
Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
using System.Linq;
2+
using NUnit.Framework;
3+
using UnityEngine;
4+
using UnityEngine.ProBuilder;
5+
using UnityEngine.ProBuilder.MeshOperations;
6+
using UnityEngine.ProBuilder.Shapes;
7+
8+
[TestFixture]
9+
public class AutoStitchTests
10+
{
11+
const float k_Tolerance = 0.0001f;
12+
[Test]
13+
public void AutoStitch_AlignsEdgesCorrectly()
14+
{
15+
// Step 1: Create a cube and deform one face
16+
var cube = ShapeFactory.Instantiate<Cube>();
17+
Assume.That(cube, Is.Not.Null);
18+
19+
var f0 = cube.faces[0]; // idx : 0,1,2,3
20+
var f1 = cube.faces[1]; // idx : 4,5,6,7
21+
var vertices = cube.positionsInternal;
22+
23+
// let's modify the non adjacent edge
24+
vertices[5] += new Vector3(0, -0.5f, 0);
25+
vertices[8] += new Vector3(0, -0.5f, 0);
26+
vertices[7] += new Vector3(0, 0.5f, 0);
27+
vertices[10] += new Vector3(0, 0.5f, 0);
28+
cube.positionsInternal = vertices;
29+
30+
cube.ToMesh();
31+
cube.Refresh();
32+
33+
// Step 2: Perform AutoStitch
34+
bool succeeded = UVEditing.AutoStitch(cube, f0, f1, 0);
35+
Assert.IsTrue(succeeded, "AutoStitch operation failed.");
36+
37+
// Step 3: Verify that the edge of one face UV is the same as the other face UV
38+
var uvs = cube.texturesInternal;
39+
40+
// Get the shared edge between f0 and f1
41+
var sharedEdge = WingedEdge.GetWingedEdges(cube, new[] { f0, f1 })
42+
.FirstOrDefault(x => x.face == f0 && x.opposite != null && x.opposite.face == f1);
43+
44+
Assume.That(sharedEdge, Is.Not.Null, "No shared edge found between the two faces.");
45+
46+
// Check if UVs on the shared edge are aligned
47+
var f0Edge = sharedEdge.opposite.edge.common;
48+
var f1Edge = sharedEdge.opposite.edge.local;
49+
50+
// Compare UVs for each vertex in the shared edge
51+
AssertUVsAlmostEqual(uvs[f0Edge.a], uvs[f1Edge.a], k_Tolerance, $"UV mismatch at vertex {f0Edge.a}.");
52+
AssertUVsAlmostEqual(uvs[f0Edge.b], uvs[f1Edge.b], k_Tolerance, $"UV mismatch at vertex {f0Edge.b}.");
53+
54+
// Cleanup
55+
Object.DestroyImmediate(cube.gameObject);
56+
}
57+
58+
[Test]
59+
public void AutoStitch_AlignsEdgesCorrectly_WhenRotated()
60+
{
61+
// Step 1: Create a cube
62+
var cube = ShapeFactory.Instantiate<Cube>();
63+
Assume.That(cube, Is.Not.Null);
64+
65+
// Step 2: Rotate all faces of the cube
66+
var rotation = Quaternion.Euler(45, 45, 45);
67+
var vertices = cube.positionsInternal;
68+
69+
for (int i = 0; i < vertices.Length; i++)
70+
{
71+
vertices[i] = rotation * vertices[i];
72+
}
73+
74+
cube.positionsInternal = vertices;
75+
cube.ToMesh();
76+
cube.Refresh();
77+
78+
// Deform one face slightly to ensure edge misalignment
79+
var f0 = cube.faces[0]; // idx : 0,1,2,3
80+
var f1 = cube.faces[1]; // idx : 4,5,6,7
81+
82+
// let's modify the non adjacent edge
83+
vertices[5] += new Vector3(0, -0.5f, 0);
84+
vertices[8] += new Vector3(0, -0.5f, 0);
85+
vertices[7] += new Vector3(0, 0.5f, 0);
86+
vertices[10] += new Vector3(0, 0.5f, 0);
87+
cube.positionsInternal = vertices;
88+
89+
cube.ToMesh();
90+
cube.Refresh();
91+
92+
// Step 3: Perform AutoStitch
93+
bool succeeded = UVEditing.AutoStitch(cube, f0, f1, 0);
94+
Assert.IsTrue(succeeded, "AutoStitch operation failed.");
95+
96+
// Step 4: Verify that the edge of one face UV is the same as the other face UV
97+
var uvs = cube.texturesInternal;
98+
99+
// Get the shared edge between f0 and f1
100+
var sharedEdge = WingedEdge.GetWingedEdges(cube, new[] { f0, f1 })
101+
.FirstOrDefault(x => x.face == f0 && x.opposite != null && x.opposite.face == f1);
102+
103+
Assume.That(sharedEdge, Is.Not.Null, "No shared edge found between the two faces.");
104+
105+
// Check if UVs on the shared edge are aligned
106+
var f0Edge = sharedEdge.opposite.edge.common;
107+
var f1Edge = sharedEdge.opposite.edge.local;
108+
109+
// Compare UVs for each vertex in the shared edge
110+
AssertUVsAlmostEqual(uvs[f0Edge.a], uvs[f1Edge.a], k_Tolerance, $"UV mismatch at vertex {f0Edge.a}.");
111+
AssertUVsAlmostEqual(uvs[f0Edge.b], uvs[f1Edge.b], k_Tolerance, $"UV mismatch at vertex {f0Edge.b}.");
112+
113+
// Cleanup
114+
Object.DestroyImmediate(cube.gameObject);
115+
}
116+
117+
private void AssertUVsAlmostEqual(Vector2 uv1, Vector2 uv2, float tolerance, string message)
118+
{
119+
Assert.That(uv1.x, Is.EqualTo(uv2.x).Within(tolerance), $"{message} on X coordinate.");
120+
Assert.That(uv1.y, Is.EqualTo(uv2.y).Within(tolerance), $"{message} on Y coordinate.");
121+
}
122+
}

Tests/Editor/UV/AutoStitchingTests.cs.meta

Lines changed: 2 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)