using System.Collections.Generic; using UnityEngine; namespace Mtree { public class Node { public List 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(); creator = crea; growth = new GrowthInformation(0f, 0f, 1f); this.type = type; distanceFromOrigin = distancToOrigin; } public Queue 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 queue = new Queue(); 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> ToSplines() { Stack> stack = new Stack>(); stack.Push(new Queue()); ToSplinesRec(stack, Vector3.up); return stack; } private void ToSplinesRec(Stack> 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()); children[i].ToSplinesRec(points, direction, parentRadius2); } } public void GetSelection(Queue 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 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 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 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(); 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 list = new List(); list.Add(children[0]); List 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; } } }