-
Notifications
You must be signed in to change notification settings - Fork 29
/
CollisionHandlerAdvanced.cs
374 lines (328 loc) · 15.9 KB
/
CollisionHandlerAdvanced.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
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
using ParallaxGrass;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using UnityEngine;
using UnityEngine.Profiling;
namespace Grass
{
//Store all objects from all scatters on a quad in a list of positions
//Query craft position, craft bound and obtain a position
//If craft bound + mesh bound is in range, enable the gameobject
//Create component on GameObject which checks against a bool "enabled"
//OnEnable, add the GameObject to an event
//Run the octree query and set "enabled" (and enable) all GameObjects that are in range
//After the query has run on the octree, run event which checks all active GameObjects to see if it still has "enabled" set to true
//If it's false, disable itself
//REMEMBER: THE QUAD MOVES and so do all the objects on it. Store the quad's original position, use its new position to calculate the vector which
//will transform the craft's current position to where it would be without shader offset changes.
//Basically query tree with VesselPosition + (oldQuadPosition - newQuadPosition)
public static class QuadColliderData
{
public static Dictionary<PQ, List<Position>> data = new Dictionary<PQ, List<Position>>(); //So that the Octree can retrieve position data
public delegate void QuadDataCreated(PQ quad, List<Position> data);
public static event QuadDataCreated onQuadColliderDataCreated;
public delegate void QuadDataDestroyed(PQ quad);
public static event QuadDataDestroyed onQuadColliderDataDestroyed;
public static void DataCreated(PQ quad, List<Position> data)
{
if (onQuadColliderDataCreated != null) { onQuadColliderDataCreated(quad, data); }
}
public static void DataRemoved(PQ quad)
{
if (onQuadColliderDataDestroyed != null) { onQuadColliderDataDestroyed(quad); }
}
}
public class AutoDisabler : MonoBehaviour
{
public int lifeTime = 3;
public bool alwaysOn = false;
public bool active = false;
void OnEnable()
{
lifeTime = 3;
active = true;
}
void FixedUpdate()
{
if (active == false) { return; }
if (alwaysOn) { return; }
lifeTime--;
if (lifeTime <= 0 && !alwaysOn)
{
active = false;
ObjectPool.ReturnObject(this.gameObject);
gameObject.SetActive(false);
}
}
}
public class Position
{
public float bound; //Max mesh size in one dimension
public GameObject collider; //The actual collider gameObject, but this is null when the craft is not in range
public AutoDisabler autoDisabler; //The component which turns off the collider if it has not been in range recently
public Scatter scatter; //The scatter this position belongs to
public Vector3 worldPos; //Only assigned on GO creation - In order to use this position use (worldPos + (quadOriginalPosition - quad.gameObject.transform.position))
public Quaternion rot; //GameObject rotation
public Vector3 scale; //GameObject scale
public Mesh collisionMesh;
public Vector3 quadOriginalPosition;
public PQ quad;
bool active = false;
public Position(Vector3 worldPos, Quaternion rot, Vector3 scale, float bound, Scatter scatter, Mesh collisionMesh, Vector3 quadOriginalPosition, PQ quad)
{
this.worldPos = worldPos;
this.rot = rot;
this.scale = scale;
this.bound = bound;
this.scatter = scatter;
this.collisionMesh = collisionMesh;
this.quadOriginalPosition = quadOriginalPosition;
this.quad = quad;
}
public void CreateGameObject() //Called by the octree
{
if (autoDisabler != null)
{
if (autoDisabler.active)
{
autoDisabler.lifeTime = 3;
autoDisabler.alwaysOn = scatter.alwaysCollideable;
return;
}
//Otherwise it's inactive, which means the GO is in the pool so we need to fetch a new one
}
collider = ObjectPool.FetchObject();
autoDisabler = collider.GetComponent<AutoDisabler>();
autoDisabler.alwaysOn = scatter.alwaysCollideable;
Vector3 actualWorldPos = worldPos + (quad.gameObject.transform.position - quadOriginalPosition); //Transform from old quad position to new quad position, parent and forget
collider.transform.position = actualWorldPos;
collider.transform.rotation = rot;
collider.transform.localScale = scale;
collider.GetComponent<MeshCollider>().sharedMesh = collisionMesh;
//collider.GetComponent<MeshFilter>().sharedMesh = collisionMesh;
collider.layer = quad.gameObject.layer;
collider.tag = quad.gameObject.tag;
collider.transform.parent = quad.transform;
collider.SetActive(true);
active = true;
}
public void DestroyGameObject()
{
if (active)
{
active = false;
if (collider != null && autoDisabler != null && autoDisabler.active)
{
collider.SetActive(false);
autoDisabler.alwaysOn = false;
ObjectPool.ReturnObject(collider);
}
}
}
}
public class CollisionHandlerAdvanced //Per quad
{
PQ quad;
List<Position> positions = new List<Position>();
List<GameObject> alwaysActiveObjects = new List<GameObject>();
Dictionary<Scatter, PositionData[]> scatterData = new Dictionary<Scatter, PositionData[]>();
OctTree tree;
int timesDataAdded = 0;
public List<Vector3> nearbyPoints = new List<Vector3>();
float rangeLimit; //Range at which quads will attempt to generate colliders - Physics Range Extender use this
public int maxDataCount = 0;
public bool cleaned = false;
public bool initialized = false;
public bool allDataPresent = false;
public Bounds vesselBoundingBox;
public OctBoundingBox vesselSearchBox;
private float maxBounds;
private Vector3 quadOriginalPosition; //Original position of quad in world space when the octree was created
public CollisionHandlerAdvanced(ref PQ quad, float rangeLimit)
{
this.quad = quad;
this.rangeLimit = rangeLimit;
ScatterManagerPlus.OnQuadPhysicsCheck += RangeCheck;
}
public void AddData(Scatter scatter, PositionData[] data, Matrix4x4 inverseQTW, Vector3 oldShaderOffset) //Add data in quad LOCAL SPACE
{
if (scatterData.ContainsKey(scatter))
{
scatterData.Remove(scatter);
}
scatterData.Add(scatter, data);
for (int i = 0; i < data.Length; i++)
{
Vector3 pos = data[i].mat.GetColumn(3);
pos -= oldShaderOffset;
pos = inverseQTW.MultiplyPoint(pos); //Now in local space relative to quad
scatterData[scatter][i].mat.SetColumn(3, pos);
}
timesDataAdded++;
if (timesDataAdded == maxDataCount) //All data from the quad is now present
{
allDataPresent = true;
RangeCheck();
}
}
float craftDist = 0;
float minDist = 0;
float maxMeshBounds = 0;
public void CreateData(Scatter scatter, PositionData[] data)
{
GameObject model = scatter.collisionMesh != null ? GameDatabase.Instance.GetModel(scatter.collisionMesh) : GameDatabase.Instance.GetModel(scatter.model);
Mesh mesh = GameObject.Instantiate(model.GetComponent<MeshFilter>().mesh);
UnityEngine.Object.Destroy(model);
Matrix4x4 quadToWorld = quad.gameObject.transform.localToWorldMatrix;
for (int i = 0; i < data.Length; i++)
{
Vector3 pos = data[i].mat.GetColumn(3);
pos = quadToWorld.MultiplyPoint(pos); //"old quad" location -> new quad location
Quaternion rotation = Quaternion.LookRotation(data[i].mat.GetColumn(2), data[i].mat.GetColumn(1));
Vector3 scale = new Vector3(data[i].mat.GetColumn(0).magnitude, data[i].mat.GetColumn(1).magnitude, data[i].mat.GetColumn(2).magnitude);
float meshBound = Mathf.Max((mesh.bounds.min - mesh.bounds.max).magnitude * Mathf.Max(scale.x, scale.y, scale.z), 10.0f); //Length of mesh in world space
if (meshBound > maxMeshBounds && !scatter.alwaysCollideable) { maxMeshBounds = meshBound; }
if (!scatter.alwaysCollideable)
{
Position thisObject = new Position(pos, rotation, scale, meshBound, scatter, mesh, quadOriginalPosition, quad); //Object has correct world space AT THS TIME THIS METHOD WAS CALLED. Transform craft search position to the quad's position AT THIS TIME
positions.Add(thisObject);
}
if (scatter.alwaysCollideable)
{
GameObject go = CreateAlwaysActiveObject(pos, rotation, scale, mesh, quadOriginalPosition, quad);
alwaysActiveObjects.Add(go);
}
}
}
public GameObject CreateAlwaysActiveObject(Vector3 pos, Quaternion rot, Vector3 scale, Mesh mesh, Vector3 quadOriginalPosition, PQ quad)
{
GameObject collider = new GameObject();
MeshCollider mc = collider.AddComponent<MeshCollider>();
Vector3 actualWorldPos = pos + (quad.gameObject.transform.position - quadOriginalPosition); //Transform from old quad position to new quad position, parent and forget
collider.transform.position = actualWorldPos;
collider.transform.rotation = rot;
collider.transform.localScale = scale;
mc.sharedMesh = mesh;
mc.sharedMaterial = ObjectPool.material;
collider.layer = quad.gameObject.layer;
collider.tag = quad.gameObject.tag;
collider.transform.parent = quad.transform;
collider.SetActive(true);
return collider;
}
public void GetNearestRangeToALoadedCraft()
{
minDist = 100000;
for (int i = 0; i < FlightGlobals.VesselsLoaded.Count; i++)
{
if (ScatterGlobalSettings.onlyQueryControllable && !FlightGlobals.VesselsLoaded[i].isCommandable) { continue; }
float heightFromSurface = Mathf.Min(FlightGlobals.VesselsLoaded[i].heightFromTerrain, (float)FlightGlobals.VesselsLoaded[i].altitude);
if (heightFromSurface > 100 || FlightGlobals.VesselsLoaded[i].speed > 250) { continue; }
craftDist = Vector3.Distance(FlightGlobals.VesselsLoaded[i].transform.position, quad.gameObject.transform.position);
if (craftDist < minDist) { minDist = craftDist; }
}
}
public void CreateOctree() //tree.Insert() creates a lot of garbage, idk why, but worth looking into because it causes a 15ms stutter
{
OctBoundingBox box = OctTreeUtils.GetBounds(quad.gameObject.GetComponent<Renderer>().bounds); //This position is different when querying
tree = new OctTree(box, quad);
for (int i = 0; i < positions.Count; i++)
{
tree.Insert(ref positions[i].worldPos, i); //We can use the index when querying to return a position and get the GO, bounds and worldpos easily
}
}
public void RangeCheck()
{
GetNearestRangeToALoadedCraft();
if (minDist < rangeLimit)
{
if (FlightGlobals.VesselsLoaded.Count == 0) { return; } //Don't generate colliders when the craft is going mega speed
if (!initialized && allDataPresent)
{
Scatter[] keys = scatterData.Keys.ToArray();
quadOriginalPosition = quad.gameObject.transform.position;
for (int i = 0; i < keys.Length; i++)
{
CreateData(keys[i], scatterData[keys[i]]);
}
QuadColliderData.data.Add(quad, positions);
QuadColliderData.DataCreated(quad, positions);
CreateOctree();
initialized = true;
}
if (initialized)
{
nearbyPoints.Clear();
for (int i = 0; i < FlightGlobals.VesselsLoaded.Count; i++)
{
if (ScatterGlobalSettings.onlyQueryControllable && !FlightGlobals.VesselsLoaded[i].isCommandable) { continue; }
maxBounds = Mathf.Max(FlightGlobals.VesselsLoaded[i].vesselSize.x, FlightGlobals.VesselsLoaded[i].vesselSize.y, FlightGlobals.VesselsLoaded[i].vesselSize.z);
maxBounds = Mathf.Max(maxMeshBounds, maxBounds); //If the craft is too small, use bounds of the largest, non-always-active mesh to prevent pop-in
vesselBoundingBox = new Bounds(FlightGlobals.VesselsLoaded[i].transform.position + (quadOriginalPosition - quad.gameObject.transform.position), new Vector3(maxBounds, maxBounds, maxBounds));
vesselSearchBox = OctTreeUtils.GetBounds(vesselBoundingBox);
Profiler.BeginSample("Octree Query");
tree.QueryRange(ref vesselSearchBox, ref nearbyPoints);
Profiler.EndSample();
}
}
}
else
{
if (initialized)
{
DestroyColliders();
return;
}
}
}
public void DestroyColliders()
{
for (int i = 0; i < positions.Count; i++)
{
positions[i].DestroyGameObject();
}
for (int i = 0; i < alwaysActiveObjects.Count; i++)
{
UnityEngine.Object.Destroy(alwaysActiveObjects[i]);
}
alwaysActiveObjects.Clear();
positions.Clear();
if (QuadColliderData.data.ContainsKey(quad))
{
QuadColliderData.data[quad].Clear();
QuadColliderData.data.Remove(quad);
QuadColliderData.DataRemoved(quad);
}
tree = null;
initialized = false;
}
public void Cleanup()
{
ScatterManagerPlus.OnQuadPhysicsCheck -= RangeCheck;
for (int i = 0; i < positions.Count; i++)
{
positions[i].DestroyGameObject();
}
for (int i = 0; i < alwaysActiveObjects.Count; i++)
{
UnityEngine.Object.Destroy(alwaysActiveObjects[i]);
}
alwaysActiveObjects.Clear();
positions.Clear();
scatterData.Clear();
if (QuadColliderData.data.ContainsKey(quad))
{
QuadColliderData.data[quad].Clear();
QuadColliderData.data.Remove(quad);
}
tree = null;
initialized = false;
cleaned = true;
allDataPresent = false;
timesDataAdded = 0;
}
}
}