199 lines
		
	
	
		
			7.1 KiB
		
	
	
	
		
			C#
		
	
	
	
	
	
			
		
		
	
	
			199 lines
		
	
	
		
			7.1 KiB
		
	
	
	
		
			C#
		
	
	
	
	
	
| using System;
 | |
| using System.Collections;
 | |
| using System.Collections.Generic;
 | |
| using UnityEngine;
 | |
| 
 | |
| namespace EzySlice {
 | |
| 
 | |
|     /**
 | |
|      * Contains static functionality for performing Triangulation on arbitrary vertices.
 | |
|      * Read the individual function descriptions for specific details.
 | |
|      */
 | |
|     public sealed class Triangulator {
 | |
| 
 | |
|         /**
 | |
|          * Represents a 3D Vertex which has been mapped onto a 2D surface
 | |
|          * and is mainly used in MonotoneChain to triangulate a set of vertices
 | |
|          * against a flat plane.
 | |
|          */
 | |
|         internal struct Mapped2D {
 | |
|             private readonly Vector3 original;
 | |
|             private readonly Vector2 mapped;
 | |
| 
 | |
|             public Mapped2D(Vector3 newOriginal, Vector3 u, Vector3 v) {
 | |
|                 this.original = newOriginal;
 | |
|                 this.mapped = new Vector2(Vector3.Dot(newOriginal, u), Vector3.Dot(newOriginal, v));
 | |
|             }
 | |
| 
 | |
|             public Vector2 mappedValue {
 | |
|                 get { return this.mapped; }
 | |
|             }
 | |
| 
 | |
|             public Vector3 originalValue {
 | |
|                 get { return this.original; }
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         /**
 | |
|          * Overloaded variant of MonotoneChain which will calculate UV coordinates of the Triangles
 | |
|          * between 0.0 and 1.0 (default).
 | |
|          * 
 | |
|          * See MonotoneChain(vertices, normal, tri, TextureRegion) for full explanation
 | |
|          */
 | |
|         public static bool MonotoneChain(List<Vector3> vertices, Vector3 normal, out List<Triangle> tri) {
 | |
|             // default texture region is in coordinates 0,0 to 1,1
 | |
|             return MonotoneChain(vertices, normal, out tri, new TextureRegion(0.0f, 0.0f, 1.0f, 1.0f));
 | |
|         }
 | |
| 
 | |
|         /**
 | |
|          * O(n log n) Convex Hull Algorithm. 
 | |
|          * Accepts a list of vertices as Vector3 and triangulates them according to a projection
 | |
|          * plane defined as planeNormal. Algorithm will output vertices, indices and UV coordinates
 | |
|          * as arrays
 | |
|          */
 | |
|         public static bool MonotoneChain(List<Vector3> vertices, Vector3 normal, out List<Triangle> tri, TextureRegion texRegion) {
 | |
|             int count = vertices.Count;
 | |
| 
 | |
|             // we cannot triangulate less than 3 points. Use minimum of 3 points
 | |
|             if (count < 3) {
 | |
|                 tri = null;
 | |
|                 return false;
 | |
|             }
 | |
| 
 | |
|             // first, we map from 3D points into a 2D plane represented by the provided normal
 | |
|             Vector3 u = Vector3.Normalize(Vector3.Cross(normal, Vector3.up));
 | |
|             if (Vector3.zero == u) {
 | |
|                 u = Vector3.Normalize(Vector3.Cross(normal, Vector3.forward));
 | |
|             }
 | |
|             Vector3 v = Vector3.Cross(u, normal);
 | |
| 
 | |
|             // generate an array of mapped values
 | |
|             Mapped2D[] mapped = new Mapped2D[count];
 | |
| 
 | |
|             // these values will be used to generate new UV coordinates later on
 | |
|             float maxDivX = float.MinValue;
 | |
|             float maxDivY = float.MinValue;
 | |
|             float minDivX = float.MaxValue;
 | |
|             float minDivY = float.MaxValue;
 | |
| 
 | |
|             // map the 3D vertices into the 2D mapped values
 | |
|             for (int i = 0; i < count; i++) {
 | |
|                 Vector3 vertToAdd = vertices[i];
 | |
| 
 | |
|                 Mapped2D newMappedValue = new Mapped2D(vertToAdd, u, v);
 | |
|                 Vector2 mapVal = newMappedValue.mappedValue;
 | |
| 
 | |
|                 // grab our maximal values so we can map UV's in a proper range
 | |
|                 maxDivX = Mathf.Max(maxDivX, mapVal.x);
 | |
|                 maxDivY = Mathf.Max(maxDivY, mapVal.y);
 | |
|                 minDivX = Mathf.Min(minDivX, mapVal.x);
 | |
|                 minDivY = Mathf.Min(minDivY, mapVal.y);
 | |
| 
 | |
|                 mapped[i] = newMappedValue;
 | |
|             }
 | |
| 
 | |
|             // sort our newly generated array values
 | |
|             Array.Sort<Mapped2D>(mapped, (a, b) => {
 | |
|                 Vector2 x = a.mappedValue;
 | |
|                 Vector2 p = b.mappedValue;
 | |
| 
 | |
|                 return (x.x < p.x || (x.x == p.x && x.y < p.y)) ? -1 : 1;
 | |
|             });
 | |
| 
 | |
|             // our final hull mappings will end up in here
 | |
|             Mapped2D[] hulls = new Mapped2D[count + 1];
 | |
| 
 | |
|             int k = 0;
 | |
| 
 | |
|             // build the lower hull of the chain
 | |
|             for (int i = 0; i < count; i++) {
 | |
|                 while (k >= 2) {
 | |
|                     Vector2 mA = hulls[k - 2].mappedValue;
 | |
|                     Vector2 mB = hulls[k - 1].mappedValue;
 | |
|                     Vector2 mC = mapped[i].mappedValue;
 | |
| 
 | |
|                     if (Intersector.TriArea2D(mA.x, mA.y, mB.x, mB.y, mC.x, mC.y) > 0.0f) {
 | |
|                         break;
 | |
|                     }
 | |
| 
 | |
|                     k--;
 | |
|                 }
 | |
| 
 | |
|                 hulls[k++] = mapped[i];
 | |
|             }
 | |
| 
 | |
|             // build the upper hull of the chain
 | |
|             for (int i = count - 2, t = k + 1; i >= 0; i--) {
 | |
|                 while (k >= t) {
 | |
|                     Vector2 mA = hulls[k - 2].mappedValue;
 | |
|                     Vector2 mB = hulls[k - 1].mappedValue;
 | |
|                     Vector2 mC = mapped[i].mappedValue;
 | |
| 
 | |
|                     if (Intersector.TriArea2D(mA.x, mA.y, mB.x, mB.y, mC.x, mC.y) > 0.0f) {
 | |
|                         break;
 | |
|                     }
 | |
| 
 | |
|                     k--;
 | |
|                 }
 | |
| 
 | |
|                 hulls[k++] = mapped[i];
 | |
|             }
 | |
| 
 | |
|             // finally we can build our mesh, generate all the variables
 | |
|             // and fill them up
 | |
|             int vertCount = k - 1;
 | |
|             int triCount = (vertCount - 2) * 3;
 | |
| 
 | |
|             // this should not happen, but here just in case
 | |
|             if (vertCount < 3) {
 | |
|                 tri = null;
 | |
|                 return false;
 | |
|             }
 | |
| 
 | |
|             // ensure List does not dynamically grow, performing copy ops each time!
 | |
|             tri = new List<Triangle>(triCount / 3);
 | |
| 
 | |
|             float width = maxDivX - minDivX;
 | |
|             float height = maxDivY - minDivY;
 | |
| 
 | |
|             int indexCount = 1;
 | |
| 
 | |
|             // generate both the vertices and uv's in this loop
 | |
|             for (int i = 0; i < triCount; i += 3) {
 | |
|                 // the Vertices in our triangle
 | |
|                 Mapped2D posA = hulls[0];
 | |
|                 Mapped2D posB = hulls[indexCount];
 | |
|                 Mapped2D posC = hulls[indexCount + 1];
 | |
| 
 | |
|                 // generate UV Maps
 | |
|                 Vector2 uvA = posA.mappedValue;
 | |
|                 Vector2 uvB = posB.mappedValue;
 | |
|                 Vector2 uvC = posC.mappedValue;
 | |
| 
 | |
|                 uvA.x = (uvA.x - minDivX) / width;
 | |
|                 uvA.y = (uvA.y - minDivY) / height;
 | |
| 
 | |
|                 uvB.x = (uvB.x - minDivX) / width;
 | |
|                 uvB.y = (uvB.y - minDivY) / height;
 | |
| 
 | |
|                 uvC.x = (uvC.x - minDivX) / width;
 | |
|                 uvC.y = (uvC.y - minDivY) / height;
 | |
| 
 | |
|                 Triangle newTriangle = new Triangle(posA.originalValue, posB.originalValue, posC.originalValue);
 | |
| 
 | |
|                 // ensure our UV coordinates are mapped into the requested TextureRegion
 | |
|                 newTriangle.SetUV(texRegion.Map(uvA), texRegion.Map(uvB), texRegion.Map(uvC));
 | |
| 
 | |
|                 // the normals is the same for all vertices since the final mesh is completly flat
 | |
|                 newTriangle.SetNormal(normal, normal, normal);
 | |
|                 newTriangle.ComputeTangents();
 | |
| 
 | |
|                 tri.Add(newTriangle);
 | |
| 
 | |
|                 indexCount++;
 | |
|             }
 | |
| 
 | |
|             return true;
 | |
|         }
 | |
|     }
 | |
| } | 
