Rasagar/Library/PackageCache/com.unity.probuilder/Runtime/MeshOperations/UV/TextureStitching.cs
2024-08-26 23:07:20 +03:00

140 lines
5.4 KiB
C#

using System.Linq;
namespace UnityEngine.ProBuilder.MeshOperations
{
static partial class UVEditing
{
/// <summary>
/// Provided two faces, this method will attempt to project @f2 and align its size, rotation, and position to match
/// the shared edge on f1. Returns true on success, false otherwise.
/// </summary>
/// <param name="mesh"></param>
/// <param name="f1"></param>
/// <param name="f2"></param>
/// <param name="channel"></param>
/// <returns></returns>
public static bool AutoStitch(ProBuilderMesh mesh, Face f1, Face f2, int channel)
{
var wings = WingedEdge.GetWingedEdges(mesh, new [] { f1, f2 });
var sharedEdge = wings.FirstOrDefault(x => x.face == f1 && x.opposite != null && x.opposite.face == f2);
if (sharedEdge == null)
return false;
if (f1.manualUV)
f2.manualUV = true;
f1.textureGroup = -1;
f2.textureGroup = -1;
Projection.PlanarProject(mesh, f2);
if (AlignEdges(mesh, f2, sharedEdge.edge.local, sharedEdge.opposite.edge.local, channel))
{
if (!f2.manualUV)
UvUnwrapping.SetAutoAndAlignUnwrapParamsToUVs(mesh, new [] { f2 });
return true;
}
return false;
}
/// <summary>
/// move the UVs to where the edges passed meet
/// </summary>
/// <param name="mesh"></param>
/// <param name="faceToMove"></param>
/// <param name="edgeToAlignTo"></param>
/// <param name="edgeToBeAligned"></param>
/// <param name="channel"></param>
/// <returns></returns>
static bool AlignEdges(ProBuilderMesh mesh, Face faceToMove, Edge edgeToAlignTo, Edge edgeToBeAligned, int channel)
{
Vector2[] uvs = GetUVs(mesh, channel);
SharedVertex[] sharedIndexes = mesh.sharedVerticesInternal;
// Match each edge vertex to the other
int[] matchX = new int[2] { edgeToAlignTo.a, -1 };
int[] matchY = new int[2] { edgeToAlignTo.b, -1 };
int siIndex = mesh.GetSharedVertexHandle(edgeToAlignTo.a);
if (siIndex < 0)
return false;
if (sharedIndexes[siIndex].Contains(edgeToBeAligned.a))
{
matchX[1] = edgeToBeAligned.a;
matchY[1] = edgeToBeAligned.b;
}
else
{
matchX[1] = edgeToBeAligned.b;
matchY[1] = edgeToBeAligned.a;
}
// scale face 2 to match the edge size of f1
float dist_e1 = Vector2.Distance(uvs[edgeToAlignTo.a], uvs[edgeToAlignTo.b]);
float dist_e2 = Vector2.Distance(uvs[edgeToBeAligned.a], uvs[edgeToBeAligned.b]);
float scale = dist_e1 / dist_e2;
// doesn't matter what point we scale around because we'll move it in the next step anyways
foreach (int i in faceToMove.distinctIndexesInternal)
uvs[i] = uvs[i].ScaleAroundPoint(Vector2.zero, Vector2.one * scale);
// Figure out where the center of each edge is so that we can move the f2 edge to match f1's origin
Vector2 f1_center = (uvs[edgeToAlignTo.a] + uvs[edgeToAlignTo.b]) / 2f;
Vector2 f2_center = (uvs[edgeToBeAligned.a] + uvs[edgeToBeAligned.b]) / 2f;
Vector2 diff = f1_center - f2_center;
// Move f2 face to where it's matching edge center is on top of f1's center
foreach (int i in faceToMove.distinctIndexesInternal)
uvs[i] += diff;
// Now that the edge's centers are matching, rotate f2 to match f1's angle
Vector2 angle1 = uvs[matchY[0]] - uvs[matchX[0]];
Vector2 angle2 = uvs[matchY[1]] - uvs[matchX[1]];
float angle = Vector2.Angle(angle1, angle2);
if (Vector3.Cross(angle1, angle2).z < 0)
angle = 360f - angle;
foreach (int i in faceToMove.distinctIndexesInternal)
uvs[i] = Math.RotateAroundPoint(uvs[i], f1_center, angle);
float error = Mathf.Abs(Vector2.Distance(uvs[matchX[0]], uvs[matchX[1]])) + Mathf.Abs(Vector2.Distance(uvs[matchY[0]], uvs[matchY[1]]));
// now check that the matched UVs are on top of one another if the error allowance is greater than some small value
if (error > .02f)
{
// first try rotating 180 degrees
foreach (int i in faceToMove.distinctIndexesInternal)
uvs[i] = Math.RotateAroundPoint(uvs[i], f1_center, 180f);
float e2 = Mathf.Abs(Vector2.Distance(uvs[matchX[0]], uvs[matchX[1]])) + Mathf.Abs(Vector2.Distance(uvs[matchY[0]], uvs[matchY[1]]));
if (e2 < error)
error = e2;
else
{
// flip 'em back around
foreach (int i in faceToMove.distinctIndexesInternal)
uvs[i] = Math.RotateAroundPoint(uvs[i], f1_center, 180f);
}
}
// If successfully aligned, merge the sharedIndexesUV
SplitUVs(mesh, faceToMove.distinctIndexesInternal);
mesh.SetTexturesCoincident(matchX);
mesh.SetTexturesCoincident(matchY);
ApplyUVs(mesh, uvs, channel);
return true;
}
}
}