using System; using Godot; using System.Collections.Generic; using System.Linq; public partial class Plant : MeshInstance2D { public Dictionary Segments; public Vector2 Origin; private int _segmentIdCounter = 0; public PlantSegmentNode RootNode; public Rect2 ClientRectangle = new Rect2(Vector2.Zero, new Vector2(-80, 80)); public override void _Ready() { Start(); } public void Start() { Segments = new Dictionary(); PlantSegmentNode root = new PlantSegmentNode(null, maxChildren:1); root.Direction = Vector2.Up; root.NodeType = SegmentNodeTypes.Trunk; RootNode = root; Segments.Add(_segmentIdCounter, root); _segmentIdCounter++; GrowDemo(4096); this.Mesh = CreateMesh(); } public Mesh CreateMesh() { ArrayMesh arrMesh = new ArrayMesh(); var arrays = new Godot.Collections.Array(); arrays.Resize((int)Mesh.ArrayType.Max); //Vector2[] vertices = CalculateVertices(); Vector2[] vertices = CalculateLineVertices(); Color[] colors = Helper.MakeColorArray(vertices.Length, 3); arrays[(int)Mesh.ArrayType.Vertex] = vertices; arrays[(int)Mesh.ArrayType.Color] = colors; //arrMesh.AddSurfaceFromArrays(Mesh.PrimitiveType.Triangles, arrays); arrMesh.AddSurfaceFromArrays(Mesh.PrimitiveType.Lines, arrays); return arrMesh; } // Called every frame. 'delta' is the elapsed time since the previous frame. public override void _Process(double delta) { } public void GrowDemo(int nodes) { for (int i = 0; i < nodes -1; i++) { GrowNode(); } } public void GrowNewNode(PlantNodeParams p) { //var randAngle = (float)GD.RandRange(-p.AllowedParentDiv/2, p.AllowedParentDiv/2); var randAngleZeroDiv = (float)GD.RandRange(20, 45); float randF = (0.5f - GD.Randf()); int randI = GD.RandRange(0, 10); int maxChildren = p.MaxChildren; int maxDescentants = 4096; var parent = GetFreeSlotSegment(false); PlantSegmentNode seg; Vector2 dir = parent.Direction; var freeindex = parent.FreeNodeIndex(); if (freeindex == -1) throw new System.Exception("Now free index, trotz free slot"); switch (freeindex) { case 0: // Center dir = dir.Rotated(randF); maxChildren = 2; maxDescentants = 4096; break; case 1: // left dir = dir.Rotated(-randAngleZeroDiv); break; case 2: dir = dir.Rotated(+randAngleZeroDiv); break; // 3D... } seg = new PlantSegmentNode(parent, p.MaxChildren); if (parent.AddChild(seg, freeindex)) { Segments.Add(_segmentIdCounter, seg); _segmentIdCounter++; } else { GD.PrintErr("Error bei Add"); } } public void GrowNode() { // Config float minAngleDiv =Mathf.DegToRad(23); float maxAngleDiv = Mathf.DegToRad(48); int maxChildren = 2; int maxDescentants = 128; float maxRootDeviationAngle = Mathf.DegToRad(180); // Params var randAngle = (float)GD.RandRange(minAngleDiv, maxAngleDiv); float randF = (0.5f - GD.Randf()); int randI = GD.RandRange(0, 10); // Get lowest free node var parent = GetFreeSlotSegment(false); PlantSegmentNode seg; Vector2 dir = parent.Direction; var freeindex = parent.FreeNodeIndex(); if (freeindex == -1) throw new System.Exception("Now free index, trotz free slot"); switch (freeindex) { case 0: // Center dir = dir.Rotated(randF); maxChildren = 2; maxDescentants = 4096; break; case 1: // left dir = dir.Rotated(-randAngle); break; case 2: dir = dir.Rotated(+randAngle); break; // 3D... } if (randI < 6) maxChildren = 1; if (RootNode.Direction.Dot(dir) < 0) // ..<0 = 180 grad maxRootDeviationAngle) maxChildren = 0; // wenn überhang -> branach ende seg = new PlantSegmentNode(parent, maxChildren); seg.Direction = dir; if (freeindex == 0) // center node { seg.Size = parent.Size * 0.95f; } else { seg.Size = parent.Size * (0.88f + randF/10); if (randI < 6) maxChildren = 2; } // if (CheckShootCollided(seg)) // { // //GD.Print("Collision ", seg); // // Add stump dummy - so slot is ignored // seg.NodeType = SegmentNodeTypes.Stump; // seg.MaxChildren = seg.ChildrenCount; // seg.Size = Vector2.Zero;//seg.Size * 0.4f; // } if (parent.AddChild(seg, freeindex)) { Segments.Add(_segmentIdCounter, seg); _segmentIdCounter++; } else { GD.PrintErr("Error bei Add"); } } public Vector2[] CalculateLineVertices() { int vertexCount = 2 * Segments.Count; Vector2[] vertices = new Vector2[vertexCount]; for (int i = 0; i < _segmentIdCounter; i++) { PlantSegmentNode segment = Segments[i]; Vector2 pos = segment.VecToRoot; Vector2 par = segment.PositionRelativeParent; Vector2 dir = segment.Direction; Vector2 line = segment.Line; Vector2 size = segment.Size; Vector2 start = pos; Vector2 end = pos + line; int j = i * 2; vertices[j] = start; vertices[j+1] = end; } return vertices; } public Vector2[] CalculateVertices() { Vector2[] vertices = new Vector2[6 * _segmentIdCounter]; for (int i = 0; i < Segments.Count; i++) { PlantSegmentNode segment = Segments[i]; Vector2 pos = segment.VecToRoot; Vector2 par = segment.PositionRelativeParent; Vector2 dir = segment.Direction; Vector2 line = segment.Line; Vector2 size = segment.Size; float width = size.X; Vector2 a = pos; //Vector2 b = new Vector2(a.X, a.Y + size.Y); //Vector2 c = new Vector2(b.X + width, b.Y); Vector2 b = a + line; Vector2 c = new Vector2(b.X + width, b.Y); Vector2 d = new Vector2(a.X + width, a.Y); int j = i * 6; vertices[j] = a; vertices[j+1] = b; vertices[j+2] = c; vertices[j+3] = a; vertices[j+4] = c; vertices[j+5] = d; } return vertices; } public bool CheckCollision(PlantSegmentNode node) { return false; } private PlantSegmentNode GetFreeSlotSegment(bool descending = false) { Dictionary elements = Segments; //List list = elements.OrderBy(x => x.Key).ToList(); if (descending) elements = elements.OrderByDescending(x => x.Key).ToDictionary(x => x.Key, x => x.Value); else { elements = elements.OrderBy(x => x.Key).ToDictionary(x => x.Key, x => x.Value); } foreach (var item in elements) { if (item.Value.HasFreeChildSlot) return item.Value; } return null; } } public struct PlantNodeParams { // Angle to grow - calculated from 0°(front) localspace public float AllowedParentDiv = Mathf.DegToRad(80); public float AllowedRootDiv = Mathf.DegToRad(180); //public float MinAngleParentDiv = 0; //public float MaxAngleParentDiv = 1f; //public float MaxRootDeviationAngle = 3.14f; public int MaxChildren = 3; public Vector2 Size = Vector2.One; public Vector2 Direction = Vector2.Up; public string ConfigName = "default"; public SegmentNodeTypes NodeType = SegmentNodeTypes.Trunk; public PlantNodeParams() { } }