326 lines
8.7 KiB
C#
326 lines
8.7 KiB
C#
using System.Collections.Generic;
|
|
using UnityEngine;
|
|
|
|
namespace Mtree
|
|
{
|
|
public class Node
|
|
{
|
|
public List<Node> children;
|
|
|
|
public Vector3 position;
|
|
|
|
public Vector3 direction;
|
|
|
|
public float radius;
|
|
|
|
public int creator;
|
|
|
|
public float distanceFromOrigin;
|
|
|
|
public NodeType type;
|
|
|
|
public GrowthInformation growth;
|
|
|
|
public float positionInBranch;
|
|
|
|
public float branchVariation;
|
|
|
|
public Node(Vector3 pos, float rad, Vector3 dir, int crea, NodeType type = NodeType.Branch, float distancToOrigin = 0f)
|
|
{
|
|
position = pos;
|
|
radius = rad;
|
|
direction = dir;
|
|
children = new List<Node>();
|
|
creator = crea;
|
|
growth = new GrowthInformation(0f, 0f, 1f);
|
|
this.type = type;
|
|
distanceFromOrigin = distancToOrigin;
|
|
}
|
|
|
|
public Queue<Node> Grow(float length, float splitProba, int maxNumber, float rad, float secondaryBranchRadius, float angle, int creator, float randomness, float upAttraction, Transform treeTransform, float gravityStrength, float flatten)
|
|
{
|
|
int num = (int)(splitProba / Random.value) + 1;
|
|
if (num > maxNumber)
|
|
{
|
|
num = maxNumber;
|
|
}
|
|
if (!growth.canBeSplit)
|
|
{
|
|
num = 1;
|
|
}
|
|
Queue<Node> queue = new Queue<Node>();
|
|
bool flag = length > growth.remainingGrowth;
|
|
if (flag)
|
|
{
|
|
length = growth.remainingGrowth;
|
|
}
|
|
Vector3 vector = Vector3.zero;
|
|
for (int i = 0; i < num; i++)
|
|
{
|
|
Vector3 vector2 = Random.onUnitSphere;
|
|
if (flatten >= 0f && num > 1)
|
|
{
|
|
vector2 = Vector3.Lerp(vector2, Vector3.up * (Random.Range(0, 1) * 2 - 1), flatten);
|
|
}
|
|
Vector3 vector3 = Vector3.Cross(vector2, direction);
|
|
if (num == 2)
|
|
{
|
|
if (i == 0)
|
|
{
|
|
vector = vector3;
|
|
}
|
|
if (i == 1)
|
|
{
|
|
vector3 = -vector;
|
|
}
|
|
randomness = secondaryBranchRadius * angle;
|
|
}
|
|
if (vector3.y < 0f)
|
|
{
|
|
vector3.y -= vector3.y * upAttraction;
|
|
}
|
|
Vector3 vector4 = Vector3.down * gravityStrength * 0.1f * length;
|
|
if (i == 0)
|
|
{
|
|
Vector3 dir = Vector3.Lerp(direction, vector3, randomness).normalized;
|
|
if (!ObstacleAvoidance(ref dir, position, treeTransform, 10f))
|
|
{
|
|
dir += vector4;
|
|
if (gravityStrength != 0f)
|
|
{
|
|
dir.Normalize();
|
|
}
|
|
Vector3 vector5 = position + dir * length;
|
|
Vector3 pos = vector5;
|
|
float rad2 = rad;
|
|
Vector3 dir2 = dir;
|
|
int crea = creator;
|
|
float distancToOrigin = distanceFromOrigin + length / (0.1f + radius);
|
|
Node node = new Node(pos, rad2, dir2, crea, NodeType.Branch, distancToOrigin);
|
|
node.growth = new GrowthInformation(growth.growthGoal, growth.remainingGrowth - length, growth.initialRadius);
|
|
if (!flag)
|
|
{
|
|
queue.Enqueue(node);
|
|
}
|
|
children.Add(node);
|
|
}
|
|
continue;
|
|
}
|
|
Vector3 dir3 = (direction * (1f - angle) + vector3 * angle).normalized;
|
|
if (!ObstacleAvoidance(ref dir3, position, treeTransform, 10f))
|
|
{
|
|
dir3 += vector4;
|
|
if (gravityStrength != 0f)
|
|
{
|
|
dir3.Normalize();
|
|
}
|
|
Vector3 vector6 = position + dir3 * length;
|
|
Vector3 pos = position;
|
|
float distancToOrigin = rad * 0.9f;
|
|
Vector3 dir4 = dir3;
|
|
int crea = creator;
|
|
float rad2 = distanceFromOrigin;
|
|
Node node2 = new Node(pos, distancToOrigin, dir4, crea, NodeType.Branch, rad2);
|
|
node2.growth.canBeSplit = false;
|
|
dir4 = vector6;
|
|
rad2 = rad * secondaryBranchRadius;
|
|
pos = dir3;
|
|
crea = creator;
|
|
distancToOrigin = distanceFromOrigin + length;
|
|
Node node3 = new Node(dir4, rad2, pos, crea, NodeType.Branch, distancToOrigin);
|
|
node2.children.Add(node3);
|
|
children.Add(node2);
|
|
node3.growth = new GrowthInformation(growth.growthGoal, growth.remainingGrowth - length, growth.initialRadius * secondaryBranchRadius);
|
|
if (!flag)
|
|
{
|
|
queue.Enqueue(node3);
|
|
}
|
|
}
|
|
}
|
|
return queue;
|
|
}
|
|
|
|
public Stack<Queue<TreePoint>> ToSplines()
|
|
{
|
|
Stack<Queue<TreePoint>> stack = new Stack<Queue<TreePoint>>();
|
|
stack.Push(new Queue<TreePoint>());
|
|
ToSplinesRec(stack, Vector3.up);
|
|
return stack;
|
|
}
|
|
|
|
private void ToSplinesRec(Stack<Queue<TreePoint>> points, Vector3 parentDirection, float parentRadius = 0f)
|
|
{
|
|
int count = children.Count;
|
|
float parentRadius2 = radius;
|
|
if (type == NodeType.Trunk && count > 0)
|
|
{
|
|
parentRadius2 = children[0].radius;
|
|
direction = (children[0].position - position).normalized;
|
|
}
|
|
points.Peek().Enqueue(new TreePoint(position, direction, radius, type, parentDirection, distanceFromOrigin, parentRadius));
|
|
if (count > 0)
|
|
{
|
|
children[0].ToSplinesRec(points, direction);
|
|
}
|
|
for (int i = 1; i < count; i++)
|
|
{
|
|
points.Push(new Queue<TreePoint>());
|
|
children[i].ToSplinesRec(points, direction, parentRadius2);
|
|
}
|
|
}
|
|
|
|
public void GetSelection(Queue<Node> selected, int selection, bool extremitiesOnly, ref float maxHeight)
|
|
{
|
|
if (selection == creator && (!extremitiesOnly || children.Count <= 0))
|
|
{
|
|
selected.Enqueue(this);
|
|
if (position.y > maxHeight)
|
|
{
|
|
maxHeight = position.y;
|
|
}
|
|
}
|
|
foreach (Node child in children)
|
|
{
|
|
child.GetSelection(selected, selection, extremitiesOnly, ref maxHeight);
|
|
}
|
|
}
|
|
|
|
public void GetSplitCandidates(Queue<Node> selected, int selection, float start, float remainingLength)
|
|
{
|
|
if (start <= positionInBranch && selection == creator && children.Count == 1)
|
|
{
|
|
positionInBranch = (positionInBranch - start) * (1f - start);
|
|
selected.Enqueue(this);
|
|
}
|
|
for (int i = 0; i < children.Count; i++)
|
|
{
|
|
if (i == 0)
|
|
{
|
|
float magnitude = (children[i].position - position).magnitude;
|
|
children[i].GetSplitCandidates(selected, selection, start, remainingLength - magnitude);
|
|
}
|
|
else
|
|
{
|
|
children[i].GetSplitCandidates(selected, selection, start, start);
|
|
}
|
|
}
|
|
}
|
|
|
|
public void DebugPosRec(List<Vector3> positions)
|
|
{
|
|
positions.Add(position);
|
|
foreach (Node child in children)
|
|
{
|
|
child.DebugPosRec(positions);
|
|
}
|
|
}
|
|
|
|
public void AddGrowth(float growthLength)
|
|
{
|
|
growth.remainingGrowth = growthLength;
|
|
growth.growthGoal = growthLength;
|
|
growth.initialRadius = radius;
|
|
}
|
|
|
|
private bool ObstacleAvoidance(ref Vector3 dir, Vector3 position, Transform transform, float distance)
|
|
{
|
|
RaycastHit hitInfo;
|
|
if (Physics.Raycast(transform.TransformPoint(position), transform.TransformDirection(dir), out hitInfo, distance))
|
|
{
|
|
Vector3 vector = transform.InverseTransformDirection(hitInfo.normal) * Mathf.Exp((0f - hitInfo.distance) * 0.3f) * 1f;
|
|
dir += vector;
|
|
dir.Normalize();
|
|
return vector.magnitude > 0.5f;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
public void GetLeafCandidates(Queue<LeafCandidate> candidates, float maxBranchRadius, ref float totalLength, int selection)
|
|
{
|
|
if (radius < maxBranchRadius && creator == selection)
|
|
{
|
|
float num = 0f;
|
|
if (children.Count > 0)
|
|
{
|
|
num = (children[0].position - position).magnitude;
|
|
}
|
|
totalLength += num;
|
|
candidates.Enqueue(new LeafCandidate(position, direction, Mathf.Pow(radius, 0.2f), num, distanceFromOrigin, children.Count > 0));
|
|
}
|
|
foreach (Node child in children)
|
|
{
|
|
child.GetLeafCandidates(candidates, maxBranchRadius, ref totalLength, selection);
|
|
}
|
|
}
|
|
|
|
public void Simplify(Node parent, float angleThreshold, float radiusThreshold)
|
|
{
|
|
if (radius < radiusThreshold)
|
|
{
|
|
children = new List<Node>();
|
|
return;
|
|
}
|
|
Node parent2 = this;
|
|
int count;
|
|
if (children.Count > 0 && parent != null && parent.radius / children[0].radius < 2f)
|
|
{
|
|
Vector3 vector = position - parent.position;
|
|
Vector3 to = children[0].position - position;
|
|
if (Vector3.Angle(vector, to) < angleThreshold && type != NodeType.Flare)
|
|
{
|
|
List<Node> list = new List<Node>();
|
|
list.Add(children[0]);
|
|
List<Node> list2 = list;
|
|
count = parent.children.Count;
|
|
for (int i = 1; i < count; i++)
|
|
{
|
|
list2.Add(parent.children[i]);
|
|
}
|
|
count = children.Count;
|
|
for (int j = 1; j < count; j++)
|
|
{
|
|
list2.Add(children[j]);
|
|
}
|
|
parent.children = list2;
|
|
parent2 = parent;
|
|
}
|
|
}
|
|
count = 0;
|
|
foreach (Node child in children)
|
|
{
|
|
if (count == 0)
|
|
{
|
|
child.Simplify(parent2, angleThreshold, radiusThreshold);
|
|
}
|
|
else
|
|
{
|
|
child.Simplify(null, angleThreshold, radiusThreshold);
|
|
}
|
|
count++;
|
|
}
|
|
}
|
|
|
|
public float UpdatePositionInBranch(float distanceFromBranchOrigin = 0f, Node parent = null)
|
|
{
|
|
float num = ((parent != null) ? Vector3.Distance(parent.position, position) : 0f);
|
|
num += distanceFromBranchOrigin;
|
|
float num2;
|
|
if (children.Count == 0)
|
|
{
|
|
positionInBranch = 1f;
|
|
num2 = num;
|
|
}
|
|
else
|
|
{
|
|
num2 = children[0].UpdatePositionInBranch(num, this);
|
|
positionInBranch = num / num2;
|
|
for (int i = 1; i < children.Count; i++)
|
|
{
|
|
children[i].UpdatePositionInBranch();
|
|
}
|
|
}
|
|
return num2;
|
|
}
|
|
}
|
|
}
|