474 lines
17 KiB
C#
474 lines
17 KiB
C#
using System.Collections.Generic;
|
|
using UnityEngine;
|
|
|
|
namespace Mtree
|
|
{
|
|
public class MTree
|
|
{
|
|
public List<Node> stems;
|
|
|
|
public Vector3[] verts;
|
|
|
|
public Vector3[] normals;
|
|
|
|
public Vector2[] uvs;
|
|
|
|
public int[] triangles;
|
|
|
|
public int[] leafTriangles;
|
|
|
|
public Color[] colors;
|
|
|
|
public Transform treeTransform;
|
|
|
|
public float colorMultiplier;
|
|
|
|
private Queue<LeafPoint> leafPoints;
|
|
|
|
public MTree(Transform transform)
|
|
{
|
|
stems = new List<Node>();
|
|
leafPoints = new Queue<LeafPoint>();
|
|
treeTransform = transform;
|
|
}
|
|
|
|
public void AddTrunk(Vector3 position, Vector3 direction, float length, AnimationCurve radius, float radiusMultiplier, float resolution, float randomness, int creator, AnimationCurve rootShape, float rootRadius, float rootHeight, float RootResolutionMultiplier, float originAttraction)
|
|
{
|
|
MtreeBezier component = treeTransform.GetComponent<MtreeBezier>();
|
|
float num = length;
|
|
Node node = new Node(position, radius.Evaluate(0f) * radiusMultiplier, direction, creator, NodeType.Flare);
|
|
stems.Add(node);
|
|
if (!component || ((bool)component && !component.MTreeDoBezier))
|
|
{
|
|
while (num > 0f)
|
|
{
|
|
float num2 = resolution;
|
|
NodeType type = NodeType.Trunk;
|
|
Vector3 vector = Vector3.Cross(direction, Random.onUnitSphere);
|
|
if (length - num < rootHeight)
|
|
{
|
|
vector /= RootResolutionMultiplier * 2f;
|
|
num2 *= RootResolutionMultiplier;
|
|
type = NodeType.Flare;
|
|
}
|
|
float num3 = 1f / num2;
|
|
Vector3 vector2 = randomness * vector + node.direction * (1f - randomness);
|
|
Vector3 vector3 = (position - node.position) * originAttraction;
|
|
vector3.y = 0f;
|
|
vector2 += vector3;
|
|
vector2.Normalize();
|
|
if (num <= num3)
|
|
{
|
|
num3 = num;
|
|
}
|
|
num -= num3;
|
|
Vector3 pos = node.position + vector2 * num3;
|
|
float rad = radius.Evaluate(1f - num / length) * radiusMultiplier;
|
|
Node node2 = new Node(pos, rad, vector2, creator, type, length - num);
|
|
node.children.Add(node2);
|
|
node = node2;
|
|
}
|
|
}
|
|
if (!component || !component.MTreeDoBezier)
|
|
{
|
|
return;
|
|
}
|
|
length = component.GetLength();
|
|
num = length;
|
|
while (num > 1f / resolution)
|
|
{
|
|
float t = 1f - num / length;
|
|
Vector3 direction2 = component.GetDirection(t);
|
|
Vector3 vector4 = component.GetPoint(t) - treeTransform.position;
|
|
float num4 = resolution;
|
|
NodeType type2 = NodeType.Trunk;
|
|
Vector3 vector5 = Vector3.Cross(direction, Random.onUnitSphere);
|
|
if (length - num < rootHeight)
|
|
{
|
|
vector5 /= RootResolutionMultiplier * 2f;
|
|
num4 *= RootResolutionMultiplier;
|
|
type2 = NodeType.Flare;
|
|
}
|
|
float num5 = 1f / num4;
|
|
Vector3 vector6 = randomness * vector5 + direction2 * (1f - randomness);
|
|
Vector3 vector7 = (position - vector4) * originAttraction;
|
|
vector7.y = 0f;
|
|
vector6 += vector7;
|
|
vector6.Normalize();
|
|
if (num <= num5)
|
|
{
|
|
num5 = num;
|
|
}
|
|
num -= num5;
|
|
Vector3 pos2 = vector4 + vector6 * num5;
|
|
float rad2 = radius.Evaluate(1f - num / length) * radiusMultiplier;
|
|
Node node3 = new Node(pos2, rad2, vector6, creator, type2, length - num);
|
|
node.children.Add(node3);
|
|
node = node3;
|
|
}
|
|
}
|
|
|
|
public void Grow(float length, AnimationCurve lengthCurve, float resolution, float splitProba, AnimationCurve probaCurve, float splitAngle, int maxSplits, int selection, int creator, float randomness, AnimationCurve radius, float splitRadius, float upAttraction, float gravityStrength, float flatten, float minRadius)
|
|
{
|
|
float maxHeight = 0f;
|
|
Queue<Node> queue = GetSelection(selection, true, ref maxHeight);
|
|
int count = queue.Count;
|
|
for (int i = 0; i < count; i++)
|
|
{
|
|
Node node = queue.Dequeue();
|
|
float growthLength = length * lengthCurve.Evaluate(node.position.y / maxHeight);
|
|
node.AddGrowth(growthLength);
|
|
queue.Enqueue(node);
|
|
}
|
|
float num = 1f / resolution;
|
|
while (queue.Count > 0)
|
|
{
|
|
Queue<Node> queue2 = new Queue<Node>();
|
|
while (queue.Count > 0)
|
|
{
|
|
Node node2 = queue.Dequeue();
|
|
float num2 = radius.Evaluate(node2.growth.GrowthPercentage(num)) * node2.growth.initialRadius;
|
|
if (num2 < minRadius)
|
|
{
|
|
num2 = minRadius;
|
|
}
|
|
float splitProba2 = probaCurve.Evaluate(node2.growth.GrowthPercentage()) * splitProba;
|
|
Queue<Node> queue3 = node2.Grow(num, splitProba2, maxSplits, num2, splitRadius, splitAngle, creator, randomness, upAttraction, treeTransform, gravityStrength, flatten);
|
|
if (num2 == minRadius)
|
|
{
|
|
continue;
|
|
}
|
|
foreach (Node item in queue3)
|
|
{
|
|
queue2.Enqueue(item);
|
|
}
|
|
}
|
|
queue = queue2;
|
|
}
|
|
}
|
|
|
|
public void Split(int selection, int expectedNumber, float splitAngle, int creator, float splitRadius, float startLength, float spread, float flatten)
|
|
{
|
|
Queue<Node> splitCandidates = GetSplitCandidates(selection, startLength);
|
|
MtreeBezier component = treeTransform.GetComponent<MtreeBezier>();
|
|
Vector3 vector = Vector3.zero;
|
|
if ((bool)component && component.MTreeLeafDirection)
|
|
{
|
|
vector = component.s_branchdirection;
|
|
}
|
|
float num = 0f;
|
|
foreach (Node item in splitCandidates)
|
|
{
|
|
num += (item.position - item.children[0].position).magnitude;
|
|
}
|
|
float num2 = (float)expectedNumber / num;
|
|
float num3 = 0f;
|
|
foreach (Node item2 in splitCandidates)
|
|
{
|
|
float magnitude = (item2.position - item2.children[0].position).magnitude;
|
|
float num4 = magnitude * num2 + num3;
|
|
int num5 = (int)num4;
|
|
num3 = num4 - (float)num5;
|
|
Vector3 vector2 = Random.onUnitSphere;
|
|
if (flatten > 0f)
|
|
{
|
|
vector2 = Vector3.Lerp(vector2, Vector3.up, flatten).normalized * (Random.Range(0, 1) * 2 - 1);
|
|
}
|
|
Vector3 vector3 = Vector3.Cross(vector2, item2.direction);
|
|
Quaternion quaternion = Quaternion.AngleAxis(360f / (float)num5, item2.direction);
|
|
Vector3 direction = item2.children[0].direction;
|
|
for (int i = 0; i < num5; i++)
|
|
{
|
|
float num6 = Random.value * spread;
|
|
float num7 = Mathf.Lerp(item2.radius * splitRadius, item2.children[0].radius * splitRadius, num6);
|
|
Vector3 vector4 = item2.position + direction * num6 * magnitude;
|
|
Vector3 vector5 = Vector3.LerpUnclamped(item2.direction, vector3, splitAngle * (1f - item2.positionInBranch / 2f)).normalized + vector;
|
|
float num8 = Mathf.Clamp01(num7);
|
|
Vector3 pos = vector4;
|
|
float rad = item2.radius * num8 + num7 * (1f - num8);
|
|
Vector3 dir = vector5;
|
|
int crea = creator;
|
|
float distancToOrigin = item2.distanceFromOrigin + num6 * magnitude;
|
|
Node node = new Node(pos, rad, dir, crea, NodeType.Branch, distancToOrigin);
|
|
node.branchVariation = item2.branchVariation;
|
|
if (item2.type == NodeType.Trunk || item2.type == NodeType.Flare)
|
|
{
|
|
node.type = NodeType.FromTrunk;
|
|
}
|
|
dir = vector4 + vector5 * item2.radius / Mathf.Max(0.3f, Vector3.Dot(vector5, vector3)) * 1f;
|
|
distancToOrigin = num7;
|
|
pos = vector5;
|
|
crea = creator;
|
|
rad = item2.distanceFromOrigin + num6 * magnitude;
|
|
Node node2 = new Node(dir, distancToOrigin, pos, crea, NodeType.Branch, rad);
|
|
node2.branchVariation = item2.branchVariation;
|
|
node.children.Add(node2);
|
|
node.growth.canBeSplit = false;
|
|
item2.children.Add(node);
|
|
vector3 = quaternion * vector3;
|
|
}
|
|
}
|
|
}
|
|
|
|
public void TwigSplit(int expectedNumber, float splitAngle, float splitRadius, float startLength)
|
|
{
|
|
Queue<Node> splitCandidates = GetSplitCandidates(0, startLength);
|
|
float num = 0f;
|
|
foreach (Node item in splitCandidates)
|
|
{
|
|
num += (item.position - item.children[0].position).magnitude;
|
|
}
|
|
float num2 = (float)expectedNumber / num;
|
|
float num3 = 0f;
|
|
int num4 = 1;
|
|
foreach (Node item2 in splitCandidates)
|
|
{
|
|
float magnitude = (item2.position - item2.children[0].position).magnitude;
|
|
float num5 = magnitude * num2 + num3;
|
|
int num6 = (int)num5;
|
|
num3 = num5 - (float)num6;
|
|
Vector3 direction = item2.children[0].direction;
|
|
for (int i = 0; i < num6; i++)
|
|
{
|
|
float num7 = (float)i * 1f / (float)num6;
|
|
float num8 = Mathf.Lerp(item2.radius * splitRadius, item2.children[0].radius * splitRadius, num7);
|
|
Vector3 vector = item2.position + direction * magnitude * num7;
|
|
Quaternion quaternion = Quaternion.AngleAxis(splitAngle * (float)num4, Vector3.up);
|
|
Vector3 vector2 = quaternion * item2.direction;
|
|
float num9 = Mathf.Clamp01(num8);
|
|
Vector3 pos = vector;
|
|
float rad = item2.radius * num9 + num8 * (1f - num9);
|
|
Vector3 dir = vector2;
|
|
int crea = 1;
|
|
float distancToOrigin = item2.distanceFromOrigin + num7 * magnitude;
|
|
Node node = new Node(pos, rad, dir, crea, NodeType.Branch, distancToOrigin);
|
|
node.branchVariation = item2.branchVariation;
|
|
if (item2.type == NodeType.Trunk || item2.type == NodeType.Flare)
|
|
{
|
|
node.type = NodeType.FromTrunk;
|
|
}
|
|
node.growth.canBeSplit = true;
|
|
item2.children.Add(node);
|
|
num4 *= -1;
|
|
}
|
|
}
|
|
}
|
|
|
|
public void AddBranches(int selection, float length, AnimationCurve lengthCurve, float resolution, int number, float splitProba, AnimationCurve splitProbaCurve, float angle, float randomness, AnimationCurve shape, float radius, float upAttraction, int creator, float start, float gravityStrength, float flatten, float minRadius)
|
|
{
|
|
Split(selection, number, angle, creator, radius, start, 1f, flatten);
|
|
Grow(length, lengthCurve, resolution, splitProba, splitProbaCurve, 0.3f, 2, creator, creator, randomness, shape, 0.9f, upAttraction, gravityStrength, flatten, minRadius);
|
|
}
|
|
|
|
public void AddLeafs(float maxRadius, int number, Mesh[] mesh, float size, bool overrideNormals, float minWeight, float maxWeight, int selection, float leafAngle, bool procedural, float length, float resolution, int uLoops, float gravityStrength)
|
|
{
|
|
Queue<LeafCandidate> queue = new Queue<LeafCandidate>();
|
|
float totalLength = 0f;
|
|
foreach (Node stem in stems)
|
|
{
|
|
stem.GetLeafCandidates(queue, maxRadius * stem.radius, ref totalLength, selection);
|
|
}
|
|
if (number < 1)
|
|
{
|
|
number = 2;
|
|
}
|
|
if (number > queue.Count)
|
|
{
|
|
ExtendLeafCandidates(queue, number);
|
|
}
|
|
LeafCandidate[] array = Sample(queue.ToArray(), number);
|
|
LeafCandidate[] array2 = array;
|
|
for (int i = 0; i < array2.Length; i++)
|
|
{
|
|
LeafCandidate leafCandidate = array2[i];
|
|
Vector3 direction = leafCandidate.direction;
|
|
direction.y /= 3f;
|
|
Vector3 vector = Vector3.LerpUnclamped(direction, Vector3.down, Random.Range(minWeight, maxWeight));
|
|
Quaternion quaternion = Quaternion.Euler(0f, Random.Range(0f - leafAngle, leafAngle), 0f);
|
|
vector = quaternion * vector;
|
|
leafPoints.Enqueue(new LeafPoint(leafCandidate.position, vector, leafCandidate.size * size, mesh, overrideNormals, leafCandidate.distanceFromOrigin, length, uLoops, resolution, procedural, gravityStrength));
|
|
}
|
|
}
|
|
|
|
private void ExtendLeafCandidates(Queue<LeafCandidate> candidates, int number)
|
|
{
|
|
LeafCandidate[] array = candidates.ToArray();
|
|
int num = 0;
|
|
int num2 = 0;
|
|
LeafCandidate[] array2 = array;
|
|
for (int i = 0; i < array2.Length; i++)
|
|
{
|
|
LeafCandidate leafCandidate = array2[i];
|
|
if (!leafCandidate.isEnd)
|
|
{
|
|
num2++;
|
|
}
|
|
}
|
|
num = ((num2 != 0) ? (number / num2) : 0);
|
|
LeafCandidate[] array3 = array;
|
|
for (int j = 0; j < array3.Length; j++)
|
|
{
|
|
LeafCandidate leafCandidate2 = array3[j];
|
|
for (int k = 0; k < num; k++)
|
|
{
|
|
Vector3 pos = leafCandidate2.position + leafCandidate2.direction * leafCandidate2.parentBranchLength * (k + 1) / (num + 1);
|
|
candidates.Enqueue(new LeafCandidate(pos, leafCandidate2.direction, leafCandidate2.size, leafCandidate2.parentBranchLength, leafCandidate2.distanceFromOrigin, leafCandidate2.isEnd));
|
|
}
|
|
}
|
|
}
|
|
|
|
private LeafCandidate[] Sample(LeafCandidate[] candidates, int number)
|
|
{
|
|
number = Mathf.Min(candidates.Length, number);
|
|
LeafCandidate[] array = new LeafCandidate[number];
|
|
for (int i = 0; i < number; i++)
|
|
{
|
|
int num = Random.Range(i, candidates.Length - 1);
|
|
array[i] = candidates[num];
|
|
candidates[num] = candidates[i];
|
|
}
|
|
return array;
|
|
}
|
|
|
|
private Queue<Node> GetSelection(int selection, bool extremitiesOnly, ref float maxHeight)
|
|
{
|
|
Queue<Node> queue = new Queue<Node>();
|
|
foreach (Node stem in stems)
|
|
{
|
|
stem.GetSelection(queue, selection, extremitiesOnly, ref maxHeight);
|
|
}
|
|
return queue;
|
|
}
|
|
|
|
private Queue<Node> GetSplitCandidates(int selection, float start)
|
|
{
|
|
Queue<Node> queue = new Queue<Node>();
|
|
foreach (Node stem in stems)
|
|
{
|
|
stem.branchVariation = Random.Range(0f, 1f);
|
|
stem.UpdatePositionInBranch();
|
|
stem.GetSplitCandidates(queue, selection, start, start);
|
|
}
|
|
return queue;
|
|
}
|
|
|
|
public void Simplify(float angleThreshold, float radiusTreshold)
|
|
{
|
|
foreach (Node stem in stems)
|
|
{
|
|
stem.Simplify(null, angleThreshold, radiusTreshold);
|
|
}
|
|
}
|
|
|
|
public void GenerateMeshData(TreeFunction trunk, float simplifyLeafs, float radialResolution, float VColBarkModifier, float VColLeafModifier)
|
|
{
|
|
Stack<Queue<TreePoint>> stack = new Stack<Queue<TreePoint>>();
|
|
foreach (Node stem in stems)
|
|
{
|
|
Stack<Queue<TreePoint>> stack2 = stem.ToSplines();
|
|
while (stack2.Count > 0)
|
|
{
|
|
stack.Push(stack2.Pop());
|
|
}
|
|
}
|
|
Splines splines = new Splines(stack);
|
|
splines.GenerateMeshData(7f * radialResolution, 3, trunk.TrootShape, trunk.TradiusMultiplier, trunk.TrootRadius, trunk.TrootInnerRadius, trunk.TrootHeight, trunk.TrootResolution, trunk.TflareNumber, trunk.TdisplacementStrength, trunk.TdisplacementSize, trunk.TspinAmount, VColBarkModifier);
|
|
Queue<int> queue = new Queue<int>();
|
|
GenerateLeafData(splines.verts, splines.normals, splines.uvs, queue, splines.verts.Count, splines.colors, simplifyLeafs, VColBarkModifier, VColLeafModifier);
|
|
verts = splines.verts.ToArray();
|
|
normals = splines.normals.ToArray();
|
|
uvs = splines.uvs.ToArray();
|
|
triangles = splines.triangles.ToArray();
|
|
colors = splines.colors.ToArray();
|
|
leafTriangles = queue.ToArray();
|
|
}
|
|
|
|
public void GenerateMeshData(TrunkFunction trunk, float simplifyLeafs, float radialResolution, float VColBarkModifier, float VColLeafModifier)
|
|
{
|
|
Stack<Queue<TreePoint>> stack = new Stack<Queue<TreePoint>>();
|
|
foreach (Node stem in stems)
|
|
{
|
|
Stack<Queue<TreePoint>> stack2 = stem.ToSplines();
|
|
while (stack2.Count > 0)
|
|
{
|
|
stack.Push(stack2.Pop());
|
|
}
|
|
}
|
|
Splines splines = new Splines(stack);
|
|
splines.GenerateMeshData(7f * radialResolution, 3, trunk.rootShape, trunk.radiusMultiplier, trunk.rootRadius, trunk.rootInnerRadius, trunk.rootHeight, trunk.rootResolution, trunk.flareNumber, trunk.displacementStrength, trunk.displacementSize, trunk.spinAmount, VColBarkModifier);
|
|
Queue<int> queue = new Queue<int>();
|
|
GenerateLeafData(splines.verts, splines.normals, splines.uvs, queue, splines.verts.Count, splines.colors, simplifyLeafs, VColBarkModifier, VColLeafModifier);
|
|
verts = splines.verts.ToArray();
|
|
normals = splines.normals.ToArray();
|
|
uvs = splines.uvs.ToArray();
|
|
triangles = splines.triangles.ToArray();
|
|
colors = splines.colors.ToArray();
|
|
leafTriangles = queue.ToArray();
|
|
}
|
|
|
|
public void GenerateLeafData(Queue<Vector3> leafVerts, Queue<Vector3> leafNormals, Queue<Vector2> leafUvs, Queue<int> leafTriangles, int vertexOffset, Queue<Color> colors, float simplify, float VColBarkModifier, float VColLeafModifier)
|
|
{
|
|
foreach (LeafPoint leafPoint in leafPoints)
|
|
{
|
|
Mesh mesh = leafPoint.GetMesh((simplify > 0.1f) ? 1 : 0);
|
|
if (mesh == null || Random.value < simplify)
|
|
{
|
|
continue;
|
|
}
|
|
int vertexCount = mesh.vertexCount;
|
|
Quaternion quaternion = Quaternion.FromToRotation(Vector3.forward, leafPoint.direction);
|
|
quaternion = Quaternion.LookRotation(leafPoint.direction);
|
|
float value = Random.value;
|
|
float a = 0f;
|
|
float num = float.PositiveInfinity;
|
|
float[] array = new float[vertexCount];
|
|
for (int i = 0; i < vertexCount; i++)
|
|
{
|
|
Vector3 vector = mesh.vertices[i];
|
|
Vector3 vector2 = mesh.normals[i];
|
|
if (!leafPoint.procedural)
|
|
{
|
|
vector2 = quaternion * vector2;
|
|
vector = quaternion * vector * leafPoint.size * (1f + simplify * 0.5f) + leafPoint.position;
|
|
}
|
|
leafVerts.Enqueue(vector);
|
|
if (leafPoint.overrideNormals)
|
|
{
|
|
leafNormals.Enqueue(leafPoint.position.normalized);
|
|
}
|
|
else
|
|
{
|
|
leafNormals.Enqueue(vector2);
|
|
}
|
|
leafUvs.Enqueue(mesh.uv[i]);
|
|
float num2 = Vector3.Distance(leafPoint.position, vector);
|
|
a = Mathf.Max(a, num2);
|
|
num = Mathf.Min(num, num2);
|
|
array[i] = num2;
|
|
}
|
|
for (int j = 0; j < vertexCount; j++)
|
|
{
|
|
float num3 = array[j] - num;
|
|
colors.Enqueue(new Color(leafPoint.distanceFromOrigin / 10f * VColBarkModifier, value, num3 * VColLeafModifier));
|
|
}
|
|
int num4 = mesh.triangles.Length;
|
|
for (int k = 0; k < num4; k++)
|
|
{
|
|
leafTriangles.Enqueue(mesh.triangles[k] + vertexOffset);
|
|
}
|
|
vertexOffset += vertexCount;
|
|
}
|
|
}
|
|
|
|
public List<Vector3> DebugPositions()
|
|
{
|
|
List<Vector3> list = new List<Vector3>();
|
|
foreach (Node stem in stems)
|
|
{
|
|
stem.DebugPosRec(list);
|
|
}
|
|
return list;
|
|
}
|
|
}
|
|
}
|