-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathTreeGenerator.cs
308 lines (260 loc) · 10.7 KB
/
TreeGenerator.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
public class TreeGenerator : MonoBehaviour {
//each is a coordinate pair: [# in current, change in branch]
public int [] segments; //how many segments main trunk has, absolute change
public float [] segmentLength; //length of each segment, % change
public float [] radius; //how big is the base of the trunk, % change
public float [] upCurve; //angle range by which tree grows upward, % change
public float [] maxTurn; //maximum turn radius in degrees, probably set to < 20
public float [] branchChance; //chance of branching at a segment
public float [] branchDeviation; //amount branch-trunk angle will deviate from 90
public float maxLeafPercent; //maximum width relative to size of base that leaves will grow
public GameObject branch;
public Mesh mfMesh;
//hashses position of branch tips to direction
public Dictionary <Vector3, Vector3> points = new Dictionary<Vector3, Vector3> ();
//for synchronization, branches is a list of branches still generating
public delegate void PopTree (TreeGenerator t);
public event PopTree pEvent;
private List <TreeGenerator> branches = new List <TreeGenerator> ();
private Vector3 lastPoint = Vector3.zero;
private Mesh mesh;
private int center; //center of faces
private int [] faces; //faces of part being extruded
private float widthLossFactor; //amount to extrude it by
private static GameObject TreeContainer;
private static float waitTime = .2f;
//set the trunk to the right size
public void Init () {
gameObject.GetComponent<MeshRenderer> ().enabled = false;
MainManager.meshManager.Add(gameObject.GetComponent<MeshRenderer> ());
//add the top faces to faces and set their height
if (TreeContainer == null) {
TreeContainer = new GameObject ();
}
gameObject.transform.parent = TreeContainer.transform;
MeshFilter mf = gameObject.GetComponent <MeshFilter> () as MeshFilter;
mf.mesh = mfMesh;
mesh = gameObject.GetComponent <MeshFilter> ().mesh;
//mesh.MarkDynamic ();
int [] triangles = mesh.triangles;
Vector3 [] vertices = mesh.vertices;
List <int> facesList = new List <int> ();
for (int i = 0; i < triangles.Length; i += 3) {
int occurrences = 0;
for (int j = i; j < i + 3; j++)
if (mesh.vertices [triangles [j]].y > 0) occurrences++;
if (occurrences == 3)
for (int j = i; j < i + 3; j++) facesList.Add (triangles [j]);
}
faces = facesList.ToArray ();
//set the size of the base to radius
float currentRadius = 0;
int curr = 0;
while (currentRadius == 0) {
Vector3 v = vertices [curr];
currentRadius += Mathf.Sqrt (Mathf.Pow (v.x, 2) + Mathf.Pow (v.z, 2));
curr++;
}
Matrix4x4 resize = new Matrix4x4 ();
resize.SetTRS (Vector3.zero, Quaternion.identity,
new Vector3 (radius [0] / currentRadius, 1, radius [0] / currentRadius));
for (int i = 0; i < vertices.Length; i++) {
vertices [i] = resize.MultiplyPoint3x4 (vertices [i]);
}
//scale the first top face
widthLossFactor = radius [0] / segments [0];
float scale = (radius [0] - widthLossFactor) / radius [0];
resize.SetTRS (Vector3.zero, Quaternion.identity,
new Vector3 (scale, 1, scale));
for (int i = 0; i < vertices.Length; i++) {
if (vertices [i].y > 0)
vertices [i] = resize.MultiplyPoint3x4 (vertices [i]);
}
//change the height of top face
Vector3 heightFactor = new Vector3 (0, segmentLength [0] - vertices [faces [0]].y, 0);
for (int i = 0; i < vertices.Length; i++) {
if (vertices [i].y > 0) vertices [i] += heightFactor;
}
//find the center (any point in more than 2 triangles)
center = -1;
for (int i = 0; i < 3; i++) {
int occurrences = 0;
for (int j = 3; j < 9; j++) {
if (faces [i] == faces [j]) occurrences++;
}
if (occurrences == 2) center = faces [i];
}
mesh.vertices = vertices;
mesh.RecalculateBounds ();
mesh.RecalculateNormals ();
//stuff for leaves
/*if (maxLeafPercent > 1) {
Vector3 pos = gameObject.transform.TransformPoint (mesh.vertices [center] + .95f * Vector3.down * segmentLength [0]);
Vector3 dir = gameObject.transform.TransformVector (Vector3.up * segmentLength [0]);
points.Add (pos, dir);
}*/
if (maxLeafPercent > 1)
lastPoint = gameObject.transform.TransformPoint (mesh.vertices [center] + Vector3.down * segmentLength [0]);
branches.Add (this);
}
public IEnumerator Grow(){
Vector3 [] parameters = new Vector3 [] {new Vector3 (0, segmentLength [0], 0),
new Vector3 (widthLossFactor, 0, 0)};
float currRadius = radius [0] - widthLossFactor;
for (int i = 0; i < segments [0] - 1; i++) {
//rotate by a random amount
Vector3 rotVector = RandomNormalCurveVector (maxTurn [0]);
Quaternion rotation = Quaternion.Euler (rotVector);
parameters [0] = rotation * parameters [0];
//rotate upward
Vector3 localY = gameObject.transform.InverseTransformVector (Vector3.up);
float rad = Random.Range (0, upCurve [0]) * Mathf.PI / 180;
parameters [0] = Vector3.RotateTowards (parameters [0], localY, rad, 0);
//make note for leaves
// if (currRadius < maxLeafPercent * radius [0]) {
// Vector3 pos = gameObject.transform.TransformPoint (mesh.vertices [center]);
// Vector3 dir2 = gameObject.transform.TransformVector (parameters [0]);
// points.Add (pos, dir2);
// }
if (currRadius < maxLeafPercent * radius [0]) {
if (lastPoint == Vector3.zero) {
lastPoint = gameObject.transform.TransformPoint (mesh.vertices [center]);
} else {
Vector3 pos = gameObject.transform.TransformPoint (mesh.vertices [center]);
points.Add (pos, lastPoint - pos);
lastPoint = pos;
}
}
Extruder.Extrude (mesh, faces, false, Extruder.ExtrudeRotate, parameters, false);
currRadius -= widthLossFactor;
float willBranch = Random.Range (0f, 1f);
if (willBranch < branchChance [0] && i != segments [0] - 2 && segments [0] > 2) {;
MakeNewBranch (gameObject.transform.TransformPoint (mesh.vertices [center]),
parameters [0], .9f * currRadius, i + 1);
}
yield return new WaitForSeconds(waitTime);
}
SharpenPoint (parameters [0]);
mesh.RecalculateNormals ();
mesh.RecalculateBounds ();
yield return null;
}
private void MakeNewBranch (Vector3 pos, Vector3 dir, float rad, int segs) {
if (rad < .05f || segments [0] - segs - segments [1] <= 0) return;
//Calculate rotation
Vector3 transformedDir = gameObject.transform.TransformVector (dir.normalized);
Vector3 rand = Random.insideUnitSphere;
Vector3 branchDir = Vector3.Cross (rand, transformedDir).normalized;
float rads = Random.Range (0, branchDeviation [0]) * Mathf.PI / 180;
branchDir = Vector3.RotateTowards (branchDir, transformedDir,
rads, 0);
rads = Random.Range (0, upCurve [0]) * Mathf.PI / 180;
branchDir = Vector3.RotateTowards (branchDir, Vector3.up,
rads, 0);
//make that branch
GameObject newBranch = Instantiate (branch, pos,
Quaternion.FromToRotation (Vector3.up, branchDir)) as GameObject;
newBranch.transform.position += branchDir * .5f;
TreeGenerator tg = newBranch.GetComponent <TreeGenerator> () as TreeGenerator;
tg.segments = new int [] {segments [0] - segs - segments [1], segments [1]};
tg.radius = new float [] {rad * (1 - radius [1]), radius [1]};
tg.segmentLength = ApplyIncrements (segmentLength);
tg.upCurve = ApplyIncrements (upCurve);
tg.maxTurn = ApplyIncrements (maxTurn);
tg.branchChance = ApplyIncrements (branchChance);
tg.mfMesh = mfMesh;
tg.branchDeviation = ApplyIncrements (branchDeviation);
tg.points = points;
tg.maxLeafPercent = (maxLeafPercent * radius [0]) / tg.radius [0];
//synchronization
tg.pEvent = RemoveBranch;
branches.Add (tg);
tg.Init ();
tg.StartCoroutine("Grow");
}
//takes the top face of the trunk and replaces it with a single point (like a cone)
private void SharpenPoint (Vector3 dir) {
//get rotation
Vector3 rotVector = RandomNormalCurveVector (maxTurn [0]);
Quaternion rotation = Quaternion.Euler (rotVector);
dir = rotation * dir;
Vector3 [] vertices = mesh.vertices;
int [] triangles = mesh.triangles;
Vector3 centerCoords = vertices [center];
HashSet <Vector3> vectorSet = new HashSet <Vector3> ();
Dictionary <int, int> trianglesLookup = new Dictionary<int, int> ();
foreach (int i in faces)
if (!vectorSet.Contains (vertices [i])) vectorSet.Add (vertices [i]);
//only keep triangles with one edge touching extruded face
HashSet <int> vertsToKeep = new HashSet <int> ();
for (int i = 0; i < triangles.Length; i += 3) {
int occurrences = 0;
for (int j = i; j < i + 3; j++)
if (vectorSet.Contains (vertices [triangles [j]])) occurrences++;
if (occurrences == 1) {
for (int j = i; j < i + 3; j++)
if (vectorSet.Contains (vertices [triangles [j]])) vertsToKeep.Add (triangles [j]);
}
}
//copy the vertices into a new array without the top face
Vector3 [] newVertices = new Vector3 [vertices.Length - vectorSet.Count - faces.Length / 3 + 50];
int k = 0; //counter for new array
for (int i = 0; i < vertices.Length; i++) {
Vector3 point = vertices [i];
if (vectorSet.Contains (point)) {
if (vertsToKeep.Contains (i)) {
trianglesLookup [i] = k;
newVertices [k] = point;
k++;
}
} else {
trianglesLookup [i] = k;
newVertices [k] = point;
k++;
}
}
//reconstruct triangles array
int [] newTriangles = new int [triangles.Length - 2 * faces.Length];
k = 0;
for (int i = 0; i < triangles.Length; i += 3) {
int occurrences = 0;
for (int j = i; j < i + 3; j++)
if (!trianglesLookup.ContainsKey (triangles [j])) occurrences++;
if (occurrences == 0) {
for (int j = i; j < i + 3; j++) {
newTriangles [k] = trianglesLookup [triangles [j]];
k++;
}
}
}
//drag in the triangles to a point, and apply the new values
foreach (int i in vertsToKeep) {
newVertices [trianglesLookup [i]] = centerCoords;
}
mesh.vertices = newVertices;
mesh.triangles = newTriangles;
Vector3 pos = gameObject.transform.TransformPoint (centerCoords);
points.Add (pos, lastPoint - pos);
RemoveBranch (this);
}
private Vector3 RandomNormalCurveVector (float range) {
return new Vector3 (
Random.Range (0f, range) * Random.Range (-1f * range, range) / range,
Random.Range (0f, range) * Random.Range (-1f * range, range) / range,
Random.Range (0f, range) * Random.Range (-1f * range, range) / range
);
}
private float [] ApplyIncrements (float [] start) {
return new float [] {start [0] * (1 - start [1]), start [1]};
}
//removes branch from the branches list, indicating it's done.
private void RemoveBranch (TreeGenerator t) {
branches.Remove (t);
if (branches.Count == 0 && pEvent != null) {
pEvent (this);
}
}
}