325 lines
12 KiB
C#
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;
|
|
}
|
|
}
|
|
}
|