Files
2026-02-21 16:45:37 +08:00

325 lines
12 KiB
C#

using System;
using System.Collections.Generic;
using UnityEngine;
namespace Mtree
{
public class Splines
{
private Stack<Queue<TreePoint>> splines;
public Queue<Vector3> verts;
public Queue<Vector3> normals;
public Queue<int> triangles;
public Queue<Vector2> uvs;
public Queue<Color> colors;
public Splines(Stack<Queue<TreePoint>> points)
{
splines = points;
verts = new Queue<Vector3>();
uvs = new Queue<Vector2>();
normals = new Queue<Vector3>();
triangles = new Queue<int>();
colors = new Queue<Color>();
}
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<int> queue = new Queue<int>();
Queue<Vector2Int> queue2 = new Queue<Vector2Int>();
int[] trunkTriangles = null;
Vector3[] trunkVertices = null;
Queue<BarycentricCoordinates> projectedVertices = new Queue<BarycentricCoordinates>();
bool flag = true;
while (splines.Count > 0)
{
SimplexNoiseGenerator noiseGenerator = new SimplexNoiseGenerator(new int[8] { 22, 56, 50, 44, 13, 19, 7, 42 });
Queue<TreePoint> 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<int> 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<Vector2Int> duplicatedVerts, Queue<int> selectedTriangles, Queue<BarycentricCoordinates> projectedVertices)
{
Vector3[] array = normals.ToArray();
HashSet<int> hashSet = new HashSet<int>();
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<Vector3>(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<BarycentricCoordinates> 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;
}
}
}