- Code and resources in .unitypackage, exe in Bin folder: My Drive
Use, share and modify the code as you want. Enjoy!
(PS: write if the links are gone, I reupload the files)
private IEnumerator GrowBranch(Vector3 branchPivot, Vector3 growDirection, float width, int currentLayer, AnimLine root) { if (currentLayer < maxLevels) { Transform rootTransform = null; if (root != null) rootTransform = root.Line.transform; AnimLine mainBranch = new AnimLine(rootTransform, branchPivot, tessellation, treeMaterial); Vector3 norm = new Vector3(growDirection.y, -growDirection.x, growDirection.z).normalized; Vector3 middle = growDirection / 2.0f; middle += norm * Random.Range(-growDirection.magnitude / 10.0f, growDirection.magnitude / 10.0f); List<Vector3> controlPoints = new List<Vector3>() { Vector3.zero, middle, growDirection }; mainBranch.CreateSpline(controlPoints, width, width * widthDecrease); if (root != null) { mainBranch.Line.transform.parent = rootTransform; mainBranch.Line.RootType = LineMesh.RootPoint.RootLine; mainBranch.Line.RootLine = root.Line; } var growEnum = StartCoroutine(mainBranch.UpdateLine(LayerGrowTime)); lines.Add(mainBranch); int noBranches = (int) Mathf.Lerp(Random.Range(minBrachPerLayer, maxBrachPerLayer), minBrachPerLayer, (float) (currentLayer) / maxLevels); Vector3[] newBranches = new Vector3[noBranches]; if (lines.Count+newBranches.Length > maxBranches) yield break; for (int i = 0; i < newBranches.Length; i++) { Vector3 angles = new Vector3 { z = (((i & 1) * 2) - 1) * Random.Range(minAngle, maxAngle) }; newBranches[i] = RotatePointAroundPivot(growDirection, Vector3.zero, angles) * Random.Range(minHeightMult, maxHeightMult); } yield return growEnum; foreach (var branch in newBranches) { StartCoroutine(GrowBranch(growDirection, branch, width * widthDecrease, currentLayer + 1, mainBranch)); } } }
I implemented an AnimLine class, which can create an animated line, weld lines and transform them. It uses meshes and only updates the necessary parts, so it's pretty fast. It also looks good to have the wind blowing the branches. Since in the AnimLine class I can parent lines and set their pivots, the wind function uses local angles to move the branches, and some randomness.
private IEnumerator UpdateBranches() { while (true) { if (maxWindStrength==0.0f) { yield return null; continue; } var time = Time.time; for (int i = 1; i < lines.Count; i++) { if (lines[i].Line.PointCount < 2) continue; float localStrength = 1.0f; if (maxWindHeight != 0.0f) localStrength = Mathf.Clamp01(lines[i].Line.GetPoint(0).y / maxWindHeight); lines[i].Line.transform.localEulerAngles = new Vector3(0, 0, maxWindStrength * localStrength * Mathf.Sin(time * windSpeed - i)); lines[i].Line.UpdateLine(0, 1); } yield return new WaitForSeconds(0.03f); } }
Now this is what I currently have. However, there are still problems with it. The infamous Z-fighting. Since I use meshes on the same plane, and because I use perspective camera and cannot offset the meshes too much, also deciding about the offsets is comlex. The weld points of different lines have wrong uv coordinates, so the texture clamps there. I will add a better work-sharing model too, so it will be more effective to update branches. Now when it updates a specified number in a frame, the update returns and continues it in the next frame, so it keeps the desired fps even with tens of thousands of branches, altough it looks terrible to have a low update batch size.