// ╔════════════════════════════════════════════════════════════════╗ // ║ Copyright © 2025 NWH Coding d.o.o. All rights reserved. ║ // ║ Licensed under Unity Asset Store Terms of Service: ║ // ║ https://unity.com/legal/as-terms ║ // ║ Use permitted only in compliance with the License. ║ // ║ Distributed "AS IS", without warranty of any kind. ║ // ╚════════════════════════════════════════════════════════════════╝ #region using System.Collections.Generic; using UnityEngine; #endregion namespace NWH.DWP2.MeshDecimation { /// /// Represents a triangle in the mesh decimation algorithm. /// Stores triangle vertices, normals, UVs, and adjacency information. /// public class Tri { /// /// Original mesh vertex index for vertex 0. /// public int defaultIndex0; /// /// Original mesh vertex index for vertex 1. /// public int defaultIndex1; /// /// Original mesh vertex index for vertex 2. /// public int defaultIndex2; /// /// Whether this triangle has been deleted. /// public bool deleted; /// /// Unique identifier for this triangle. /// public int id; /// /// Face normal vector. /// public Vector3 normal; /// /// Texture coordinate for vertex 0. /// public Vector2 uv0; /// /// Texture coordinate for vertex 1. /// public Vector2 uv1; /// /// Texture coordinate for vertex 2. /// public Vector2 uv2; /// /// First vertex of the triangle. /// public Vert v0; /// /// Second vertex of the triangle. /// public Vert v1; /// /// Third vertex of the triangle. /// public Vert v2; /// /// Vertex normal for vertex 0. /// public Vector3 vn0; /// /// Vertex normal for vertex 1. /// public Vector3 vn1; /// /// Vertex normal for vertex 2. /// public Vector3 vn2; /// /// Initializes a new triangle with vertices, texture coordinates, and establishes vertex relationships. /// /// Unique identifier for this triangle. /// First vertex. /// Second vertex. /// Third vertex. /// Texture coordinate for first vertex. /// Texture coordinate for second vertex. /// Texture coordinate for third vertex. public Tri(int id, Vert v0, Vert v1, Vert v2, Vector2 uv0, Vector2 uv1, Vector2 uv2) { this.id = id; this.v0 = v0; this.v1 = v1; this.v2 = v2; this.uv0 = uv0; this.uv1 = uv1; this.uv2 = uv2; RecalculateNormal(); v0.AddFace(this); v1.AddFace(this); v2.AddFace(this); v0.AddNeighbor(v1); v0.AddNeighbor(v2); v1.AddNeighbor(v0); v1.AddNeighbor(v2); v2.AddNeighbor(v0); v2.AddNeighbor(v1); } /// /// Sets the original mesh vertex indices for this triangle. /// /// Original index for vertex 0. /// Original index for vertex 1. /// Original index for vertex 2. public void SetDefaultIndices(int n0, int n1, int n2) { defaultIndex0 = n0; defaultIndex1 = n1; defaultIndex2 = n2; } /// /// Removes this triangle and updates vertex relationships. /// Records the removal in history for potential undo operations. /// /// History object to record the removal. public void RemoveTriangle(History his) { v0.RemoveFace(this); v1.RemoveFace(this); v2.RemoveFace(this); v0.RemoveIfNonNeighbor(v1); v0.RemoveIfNonNeighbor(v2); v1.RemoveIfNonNeighbor(v0); v1.RemoveIfNonNeighbor(v2); v2.RemoveIfNonNeighbor(v1); v2.RemoveIfNonNeighbor(v0); deleted = true; his.RemovedTriangle(id); } /// /// Gets the texture coordinate for a given vertex. /// /// Vertex to query. /// Texture coordinate for the vertex, or zero vector if not found. public Vector2 uvAt(Vert v) { Vector3 vec = v.position; if (vec == v0.position) { return uv0; } if (vec == v1.position) { return uv1; } if (vec == v2.position) { return uv2; } return new Vector2(); } /// /// Gets the vertex normal for a given vertex. /// /// Vertex to query. /// Vertex normal for the vertex, or zero vector if not found. public Vector3 normalAt(Vert v) { Vector3 vec = v.position; if (vec == v0.position) { return vn0; } if (vec == v1.position) { return vn1; } if (vec == v2.position) { return vn2; } return new Vector3(); } /// /// Sets the texture coordinate for a given vertex. /// /// Vertex to update. /// New texture coordinate. public void setUV(Vert v, Vector2 newuv) { Vector3 vec = v.position; if (vec == v0.position) { uv0 = newuv; } else if (vec == v1.position) { uv1 = newuv; } else if (vec == v2.position) { uv2 = newuv; } } /// /// Sets the vertex normal for a given vertex. /// /// Vertex to update. /// New vertex normal. public void setVN(Vert v, Vector3 newNormal) { Vector3 vec = v.position; if (vec == v0.position) { vn0 = newNormal; } else if (vec == v1.position) { vn1 = newNormal; } else if (vec == v2.position) { vn2 = newNormal; } } /// /// Checks if this triangle contains a given vertex. /// /// Vertex to check. /// True if the triangle contains the vertex, false otherwise. public bool HasVertex(Vert v) { Vector3 vec = v.position; return vec == v0.position || vec == v1.position || vec == v2.position; } /// /// Recalculates the face normal using cross product of edge vectors. /// public void RecalculateNormal() { Vector3 v1pos = v1.position; normal = Vector3.Cross(v1pos - v0.position, v2.position - v1pos); if (normal.magnitude == 0) { return; } normal.Normalize(); } /// /// Recalculates averaged vertex normals based on adjacent face normals. /// Only called when normal recalculation is enabled. Smooths normals even at UV seams. /// /// Dot product threshold for normal smoothing. public void RecalculateAvgNormals(float smoothAngleDot) { int i; List flist = new(); List slist = new(); int n = flist.Count; Tri f; Vector3 fn; flist = v0.face; slist.Clear(); for (i = 0; i < n; ++i) { f = flist[i]; fn = f.normal; if (fn.x * normal.x + fn.y * normal.y + fn.z * normal.z > smoothAngleDot) { vn0 += fn; slist.Add(f); } } vn0.Normalize(); n = slist.Count; for (i = 0; i < n; ++i) { f = slist[i]; f.setVN(v0, vn0); } flist = v1.face; n = flist.Count; slist.Clear(); for (i = 0; i < n; ++i) { f = flist[i]; fn = f.normal; if (fn.x * normal.x + fn.y * normal.y + fn.z * normal.z > smoothAngleDot) { vn1 += fn; slist.Add(f); } } vn1.Normalize(); n = slist.Count; for (i = 0; i < n; ++i) { f = slist[i]; f.setVN(v1, vn1); } flist = v2.face; n = flist.Count; slist.Clear(); for (i = 0; i < n; ++i) { f = flist[i]; fn = f.normal; if (fn.x * normal.x + fn.y * normal.y + fn.z * normal.z > smoothAngleDot) { vn2 += fn; slist.Add(f); } } vn2.Normalize(); n = slist.Count; for (i = 0; i < n; ++i) { f = slist[i]; f.setVN(v2, vn2); } } /// /// Replaces a vertex in this triangle with a new vertex and updates all relationships. /// Records the replacement in history for potential undo operations. /// /// Old vertex to be replaced. /// New vertex to replace with. /// New texture coordinate. /// New vertex normal. /// History object to record the replacement. public void ReplaceVertex(Vert vo, Vert vnew, Vector2 newUV, Vector3 newVN, History his) { Vector3 vec = vo.position; Vert changedVertex = v2; int changedVertexId = 2; Vector3 changedNormal = vn2; Vector2 changedUV = uv2; if (vec == v0.position) { changedVertex = v0; changedVertexId = 0; changedNormal = vn0; changedUV = uv0; v0 = vnew; vn0 = newVN; uv0 = newUV; } else if (vec == v1.position) { changedVertex = v1; changedVertexId = 1; changedNormal = vn1; changedUV = uv1; v1 = vnew; vn1 = newVN; uv1 = newUV; } else { v2 = vnew; vn2 = newVN; uv2 = newUV; } vo.RemoveFace(this); vnew.AddFace(this); vo.RemoveIfNonNeighbor(v0); v0.RemoveIfNonNeighbor(vo); vo.RemoveIfNonNeighbor(v1); v1.RemoveIfNonNeighbor(vo); vo.RemoveIfNonNeighbor(v2); v2.RemoveIfNonNeighbor(vo); v0.AddNeighbor(v1); v0.AddNeighbor(v2); v1.AddNeighbor(v0); v1.AddNeighbor(v2); v2.AddNeighbor(v0); v2.AddNeighbor(v1); RecalculateNormal(); his.ReplaceVertex(id, changedVertexId, changedVertex.id, changedNormal, changedUV, vnew.id, newVN, newUV); } } }