using System; using System.Collections.Generic; using UnityEngine; namespace Mtree { public class Splines { private Stack> splines; public Queue verts; public Queue normals; public Queue triangles; public Queue uvs; public Queue colors; public Splines(Stack> points) { splines = points; verts = new Queue(); uvs = new Queue(); normals = new Queue(); triangles = new Queue(); colors = new Queue(); } public void GenerateMeshData(float resolutionMultiplier, int minResolution, AnimationCurve rootShape, float radiusMultiplier, float rootRadius, float rootInnerRadius, float rootHeight, float RootResolutionMultiplier, int flaresNumber, float displacmentStrength, float displacmentSize, float spinAmount, float VColBarkModifier) { Queue queue = new Queue(); Queue queue2 = new Queue(); int[] trunkTriangles = null; Vector3[] trunkVertices = null; Queue projectedVertices = new Queue(); bool flag = true; while (splines.Count > 0) { SimplexNoiseGenerator noiseGenerator = new SimplexNoiseGenerator(new int[8] { 22, 56, 50, 44, 13, 19, 7, 42 }); Queue queue3 = splines.Pop(); int num = -1; float num2 = 0f; while (queue3.Count > 0) { TreePoint point = queue3.Dequeue(); point.radius = Mathf.Max(point.radius, 0.001f); int resolution = getResolution(point, minResolution, rootRadius, rootHeight, resolutionMultiplier); int count = verts.Count; Vector3[] vertices; if (num == -1 && point.parentRadius != 0f && point.type == NodeType.FromTrunk) { vertices = AddCircleWrapped(point.position, point.direction, point.radius, resolution, point.parentRadius, point.parentDirection, displacmentStrength); ProjectBranchesOnTrunk(trunkTriangles, trunkVertices, ref vertices, projectedVertices, count); } else { vertices = AddCircle(point.position, point.direction, point.radius, resolution, num2 * spinAmount * point.radius); } queue2.Enqueue(new Vector2Int(count, count + resolution - 1)); BridgeLoops(vertices, point, num, num2, count, getFillingGapRate(num, resolution), flaresNumber, rootRadius, rootInnerRadius, rootHeight, radiusMultiplier, rootShape, noiseGenerator, displacmentSize, displacmentStrength, queue, VColBarkModifier); if (queue3.Count > 0) { float num3 = point.radius; if (point.type == NodeType.Flare) { num3 += rootRadius * rootShape.Evaluate(point.position.y / rootHeight) * radiusMultiplier; } num2 += (point.position - queue3.Peek().position).magnitude / num3; } num = resolution; } if (flag) { trunkTriangles = triangles.ToArray(); trunkVertices = verts.ToArray(); flag = false; } } RecalculateNormals(queue2, queue, projectedVertices); } private void BridgeLoops(Vector3[] loop, TreePoint point, int lastResolution, float uvHeight, int n, int fillingGapRate, int flaresNumber, float rootRadius, float rootInnerRadius, float rootHeight, float radiusMultiplier, AnimationCurve rootShape, SimplexNoiseGenerator noiseGenerator, float displacementSize, float displacementStrength, Queue trianglesWithBadNormals, float VColBarkModifier) { int num = loop.Length; int num2 = lastResolution - num; for (int i = 0; i < num; i++) { Vector3 vector = loop[i]; Vector3 normalized = (vector - point.position).normalized; Vector3 normalized2 = (vector - point.position).normalized; if (lastResolution == -1) { Vector3 normalized3 = Vector3.ProjectOnPlane(point.direction, point.parentDirection).normalized; Vector3 vector2 = (loop[0] + loop[num / 2]) / 2f - normalized3 * point.parentRadius; Vector3 vector3 = Vector3.Project(vector - vector2, point.parentDirection) + vector2; normalized2 = (vector - vector3).normalized; } bool flag = false; if (point.type == NodeType.Trunk || point.type == NodeType.Flare) { vector += noiseGenerator.noiseGradient(vector * displacementSize / radiusMultiplier, true) / 5f * point.radius * displacementStrength; flag = true; } if (point.type == NodeType.Flare) { float num3 = (float)i * 1f / (float)(num - 1) * 2f * (float)Math.PI; float num4 = Mathf.Lerp(Mathf.Abs(Mathf.Sin(num3 * (float)flaresNumber / 2f)), 1f, rootInnerRadius) * rootRadius * rootShape.Evaluate(point.position.y / rootHeight) * radiusMultiplier; vector += normalized * num4; } verts.Enqueue(vector); normals.Enqueue(normalized2); uvs.Enqueue(new Vector2((float)i * 1f / (float)(num - 1), uvHeight / 3.2f) * -1f); if (i > 0 && lastResolution > 0) { if (flag) { trianglesWithBadNormals.Enqueue(triangles.Count); trianglesWithBadNormals.Enqueue(triangles.Count + 3); } AddTriangle(n - lastResolution + i - 1, n + i - 1, n - lastResolution + i); AddTriangle(n + i - 1, n + i, n - lastResolution + i); if (i % fillingGapRate == 0 && num2 > 0) { if (flag) { trianglesWithBadNormals.Enqueue(triangles.Count); } AddTriangle(n - lastResolution + i, n + i, n - lastResolution + i + 1); num2--; lastResolution--; } } Color item = new Color(point.distanceFromOrigin / 10f * VColBarkModifier, 0f, 0f); colors.Enqueue(item); } if (num2 > 0) { for (int j = 0; j < num2; j++) { AddTriangle(n - lastResolution + num + j - 1, n + num - 1, n - lastResolution + num + j); } } } private int getFillingGapRate(int lastResolution, int resolution) { int num = lastResolution - resolution; int result = int.MaxValue; if (num > resolution) { resolution = num; } if (num > 0) { result = resolution / num; } return result; } private int getResolution(TreePoint point, int minResolution, float rootRadius, float rootHeight, float resolutionMultiplier) { int num = (int)(point.radius * resolutionMultiplier * 7f); if (point.type == NodeType.Flare) { num += (int)(rootRadius * Mathf.Pow(1f - Mathf.Max(0f, point.position.y / rootHeight), 2f) * resolutionMultiplier * 3f); } if (num < minResolution) { num = minResolution; } return num + 1; } public void RecalculateNormals(Queue duplicatedVerts, Queue selectedTriangles, Queue projectedVertices) { Vector3[] array = normals.ToArray(); HashSet hashSet = new HashSet(); Vector3[] array2 = verts.ToArray(); int[] array3 = triangles.ToArray(); int num = array3.Length; foreach (int selectedTriangle in selectedTriangles) { int num2 = array3[selectedTriangle]; int num3 = array3[selectedTriangle + 1]; int num4 = array3[selectedTriangle + 2]; if (!hashSet.Contains(num2)) { array[num2] = Vector3.zero; hashSet.Add(num2); } if (!hashSet.Contains(num3)) { array[num3] = Vector3.zero; hashSet.Add(num3); } if (!hashSet.Contains(num4)) { array[num4] = Vector3.zero; hashSet.Add(num4); } Vector3 vector = Vector3.Cross(array2[num3] - array2[num2], array2[num4] - array2[num2]); array[num2] += vector; array[num3] += vector; array[num4] += vector; } foreach (int item in hashSet) { array[item].Normalize(); } foreach (Vector2Int duplicatedVert in duplicatedVerts) { int x = duplicatedVert.x; int y = duplicatedVert.y; Vector3 vector2 = (array[x] + array[y]) / 2f; array[x] = (array[y] = vector2); } foreach (BarycentricCoordinates projectedVertex in projectedVertices) { array[projectedVertex.index] = array[projectedVertex.i1] * projectedVertex.w1 + array[projectedVertex.i2] * projectedVertex.w2 + array[projectedVertex.i3] * projectedVertex.w3; } normals = new Queue(array); } public static Vector3[] AddCircle(Vector3 position, Vector3 direction, float radius, int resolution, float spinAngle) { Vector3[] array = new Vector3[resolution]; Quaternion quaternion = Quaternion.FromToRotation(Vector3.up, direction); for (int i = 0; i < resolution; i++) { float f = (float)Math.PI * 2f * ((float)i / (float)(resolution - 1)) + spinAngle; Vector3 vector = new Vector3(Mathf.Cos(f) * radius, 0f, Mathf.Sin(f) * radius); array[i] = quaternion * vector + position; } return array; } public static Vector3[] AddCircleWrapped(Vector3 position, Vector3 direction, float radius, int resolution, float parentRadius, Vector3 parentDirection, float displcementStrength) { Vector3[] array = new Vector3[resolution]; Vector3 normalized = Vector3.ProjectOnPlane(direction, parentDirection).normalized; Quaternion quaternion = Quaternion.FromToRotation(Vector3.up, normalized); Vector3 rhs = Vector3.Cross(parentDirection, normalized); float a = Mathf.Sqrt(Mathf.Pow(Mathf.Tan(90f - Vector3.Angle(direction, parentDirection) * (float)Math.PI / 180f), 2f) + 1f); a = Mathf.Min(a, 4f) / 4f; Matrix4x4 identity = Matrix4x4.identity; identity[0, 0] += parentDirection.x * a; identity[1, 1] += parentDirection.y * a; identity[2, 2] += parentDirection.z * a; for (int i = 0; i < resolution; i++) { float f = (float)Math.PI * 2f * ((float)i / (float)(resolution - 1)); Vector3 vector = new Vector3(Mathf.Cos(f) * radius, 0f, Mathf.Sin(f) * radius); vector = quaternion * vector; float num = Mathf.Sin(Mathf.Acos(Mathf.Clamp01(Mathf.Abs(Vector3.Dot(vector, rhs)) / parentRadius))) * parentRadius; vector = identity.MultiplyPoint(vector); vector += normalized * num; vector += position + parentDirection * Vector3.Dot(direction, parentDirection) * parentRadius; array[i] = vector; } return array; } public void AddTriangle(int a, int b, int c) { triangles.Enqueue(a); triangles.Enqueue(b); triangles.Enqueue(c); } public void ProjectBranchesOnTrunk(int[] trunkTriangles, Vector3[] trunkVertices, ref Vector3[] vertices, Queue projectedVertices, int verticesCount) { Vector3[] array = new Vector3[trunkTriangles.Length / 3]; for (int i = 0; i < array.Length; i++) { array[i] = (trunkVertices[trunkTriangles[i * 3]] + trunkVertices[trunkTriangles[i * 3 + 1]] + trunkVertices[trunkTriangles[i * 3 + 2]]) / 3f; } for (int j = 0; j < vertices.Length; j++) { Vector3 vector = vertices[j]; int num = 0; float num2 = float.PositiveInfinity; for (int k = 0; k < array.Length; k++) { float num3 = Vector3.SqrMagnitude(array[k] - vector); if (num3 < num2) { num2 = num3; num = k * 3; } } Vector3 vector2 = ClosestPointOnTriangle(trunkVertices[trunkTriangles[num]], trunkVertices[trunkTriangles[num + 1]], trunkVertices[trunkTriangles[num + 2]], vertices[j]); Vector3 coordinates = Barycentric(vector2, trunkVertices[trunkTriangles[num]], trunkVertices[trunkTriangles[num + 1]], trunkVertices[trunkTriangles[num + 2]]); projectedVertices.Enqueue(new BarycentricCoordinates(verticesCount + j, trunkTriangles[num], trunkTriangles[num], trunkTriangles[num], coordinates)); vertices[j] = vector2; } } public Vector3 ClosestPointOnTriangle(Vector3 v1, Vector3 v2, Vector3 v3, Vector3 point) { Vector3 planeNormal = Vector3.Cross(v1 - v2, v1 - v3); return Vector3.ProjectOnPlane(point - v1, planeNormal) + v1; } private Vector3 Barycentric(Vector3 p, Vector3 a, Vector3 b, Vector3 c) { Vector3 vector = b - a; Vector3 vector2 = c - a; Vector3 lhs = p - a; float num = Vector3.Dot(vector, vector); float num2 = Vector3.Dot(vector, vector2); float num3 = Vector3.Dot(vector2, vector2); float num4 = Vector3.Dot(lhs, vector); float num5 = Vector3.Dot(lhs, vector2); float num6 = num * num3 - num2 * num2; Vector3 zero = Vector3.zero; zero.y = (num3 * num4 - num2 * num5) / num6; zero.z = (num * num5 - num2 * num4) / num6; zero.x = 1f - zero.y - zero.z; return zero; } } }