diff options
Diffstat (limited to 'OpenSim/Region/Physics/BulletSPlugin/BSShapeCollection.cs')
-rwxr-xr-x | OpenSim/Region/Physics/BulletSPlugin/BSShapeCollection.cs | 998 |
1 files changed, 142 insertions, 856 deletions
diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSShapeCollection.cs b/OpenSim/Region/Physics/BulletSPlugin/BSShapeCollection.cs index 220fbbc..32bbc8f 100755 --- a/OpenSim/Region/Physics/BulletSPlugin/BSShapeCollection.cs +++ b/OpenSim/Region/Physics/BulletSPlugin/BSShapeCollection.cs | |||
@@ -38,38 +38,15 @@ public sealed class BSShapeCollection : IDisposable | |||
38 | { | 38 | { |
39 | private static string LogHeader = "[BULLETSIM SHAPE COLLECTION]"; | 39 | private static string LogHeader = "[BULLETSIM SHAPE COLLECTION]"; |
40 | 40 | ||
41 | private BSScene PhysicsScene { get; set; } | 41 | private BSScene m_physicsScene { get; set; } |
42 | 42 | ||
43 | private Object m_collectionActivityLock = new Object(); | 43 | private Object m_collectionActivityLock = new Object(); |
44 | 44 | ||
45 | // Description of a Mesh | ||
46 | private struct MeshDesc | ||
47 | { | ||
48 | public BulletShape shape; | ||
49 | public int referenceCount; | ||
50 | public DateTime lastReferenced; | ||
51 | public UInt64 shapeKey; | ||
52 | } | ||
53 | |||
54 | // Description of a hull. | ||
55 | // Meshes and hulls have the same shape hash key but we only need hulls for efficient collision calculations. | ||
56 | private struct HullDesc | ||
57 | { | ||
58 | public BulletShape shape; | ||
59 | public int referenceCount; | ||
60 | public DateTime lastReferenced; | ||
61 | public UInt64 shapeKey; | ||
62 | } | ||
63 | |||
64 | // The sharable set of meshes and hulls. Indexed by their shape hash. | ||
65 | private Dictionary<System.UInt64, MeshDesc> Meshes = new Dictionary<System.UInt64, MeshDesc>(); | ||
66 | private Dictionary<System.UInt64, HullDesc> Hulls = new Dictionary<System.UInt64, HullDesc>(); | ||
67 | |||
68 | private bool DDetail = false; | 45 | private bool DDetail = false; |
69 | 46 | ||
70 | public BSShapeCollection(BSScene physScene) | 47 | public BSShapeCollection(BSScene physScene) |
71 | { | 48 | { |
72 | PhysicsScene = physScene; | 49 | m_physicsScene = physScene; |
73 | // Set the next to 'true' for very detailed shape update detailed logging (detailed details?) | 50 | // Set the next to 'true' for very detailed shape update detailed logging (detailed details?) |
74 | // While detailed debugging is still active, this is better than commenting out all the | 51 | // While detailed debugging is still active, this is better than commenting out all the |
75 | // DetailLog statements. When debugging slows down, this and the protected logging | 52 | // DetailLog statements. When debugging slows down, this and the protected logging |
@@ -86,22 +63,18 @@ public sealed class BSShapeCollection : IDisposable | |||
86 | // Mostly used for changing bodies out from under Linksets. | 63 | // Mostly used for changing bodies out from under Linksets. |
87 | // Useful for other cases where parameters need saving. | 64 | // Useful for other cases where parameters need saving. |
88 | // Passing 'null' says no callback. | 65 | // Passing 'null' says no callback. |
89 | public delegate void ShapeDestructionCallback(BulletShape shape); | 66 | public delegate void PhysicalDestructionCallback(BulletBody pBody, BulletShape pShape); |
90 | public delegate void BodyDestructionCallback(BulletBody body); | ||
91 | 67 | ||
92 | // Called to update/change the body and shape for an object. | 68 | // Called to update/change the body and shape for an object. |
93 | // First checks the shape and updates that if necessary then makes | 69 | // The object has some shape and body on it. Here we decide if that is the correct shape |
94 | // sure the body is of the right type. | 70 | // for the current state of the object (static/dynamic/...). |
71 | // If bodyCallback is not null, it is called if either the body or the shape are changed | ||
72 | // so dependencies (like constraints) can be removed before the physical object is dereferenced. | ||
95 | // Return 'true' if either the body or the shape changed. | 73 | // Return 'true' if either the body or the shape changed. |
96 | // 'shapeCallback' and 'bodyCallback' are, if non-null, functions called just before | 74 | // Called at taint-time. |
97 | // the current shape or body is destroyed. This allows the caller to remove any | 75 | public bool GetBodyAndShape(bool forceRebuild, BulletWorld sim, BSPhysObject prim, PhysicalDestructionCallback bodyCallback) |
98 | // higher level dependencies on the shape or body. Mostly used for LinkSets to | ||
99 | // remove the physical constraints before the body is destroyed. | ||
100 | // Called at taint-time!! | ||
101 | public bool GetBodyAndShape(bool forceRebuild, BulletWorld sim, BSPhysObject prim, | ||
102 | ShapeDestructionCallback shapeCallback, BodyDestructionCallback bodyCallback) | ||
103 | { | 76 | { |
104 | PhysicsScene.AssertInTaintTime("BSShapeCollection.GetBodyAndShape"); | 77 | m_physicsScene.AssertInTaintTime("BSShapeCollection.GetBodyAndShape"); |
105 | 78 | ||
106 | bool ret = false; | 79 | bool ret = false; |
107 | 80 | ||
@@ -111,12 +84,12 @@ public sealed class BSShapeCollection : IDisposable | |||
111 | // Do we have the correct geometry for this type of object? | 84 | // Do we have the correct geometry for this type of object? |
112 | // Updates prim.BSShape with information/pointers to shape. | 85 | // Updates prim.BSShape with information/pointers to shape. |
113 | // Returns 'true' of BSShape is changed to a new shape. | 86 | // Returns 'true' of BSShape is changed to a new shape. |
114 | bool newGeom = CreateGeom(forceRebuild, prim, shapeCallback); | 87 | bool newGeom = CreateGeom(forceRebuild, prim, bodyCallback); |
115 | // If we had to select a new shape geometry for the object, | 88 | // If we had to select a new shape geometry for the object, |
116 | // rebuild the body around it. | 89 | // rebuild the body around it. |
117 | // Updates prim.BSBody with information/pointers to requested body | 90 | // Updates prim.BSBody with information/pointers to requested body |
118 | // Returns 'true' if BSBody was changed. | 91 | // Returns 'true' if BSBody was changed. |
119 | bool newBody = CreateBody((newGeom || forceRebuild), prim, PhysicsScene.World, bodyCallback); | 92 | bool newBody = CreateBody((newGeom || forceRebuild), prim, m_physicsScene.World, bodyCallback); |
120 | ret = newGeom || newBody; | 93 | ret = newGeom || newBody; |
121 | } | 94 | } |
122 | DetailLog("{0},BSShapeCollection.GetBodyAndShape,taintExit,force={1},ret={2},body={3},shape={4}", | 95 | DetailLog("{0},BSShapeCollection.GetBodyAndShape,taintExit,force={1},ret={2},body={3},shape={4}", |
@@ -127,274 +100,20 @@ public sealed class BSShapeCollection : IDisposable | |||
127 | 100 | ||
128 | public bool GetBodyAndShape(bool forceRebuild, BulletWorld sim, BSPhysObject prim) | 101 | public bool GetBodyAndShape(bool forceRebuild, BulletWorld sim, BSPhysObject prim) |
129 | { | 102 | { |
130 | return GetBodyAndShape(forceRebuild, sim, prim, null, null); | 103 | return GetBodyAndShape(forceRebuild, sim, prim, null); |
131 | } | ||
132 | |||
133 | // Track another user of a body. | ||
134 | // We presume the caller has allocated the body. | ||
135 | // Bodies only have one user so the body is just put into the world if not already there. | ||
136 | private void ReferenceBody(BulletBody body) | ||
137 | { | ||
138 | lock (m_collectionActivityLock) | ||
139 | { | ||
140 | if (DDetail) DetailLog("{0},BSShapeCollection.ReferenceBody,newBody,body={1}", body.ID, body); | ||
141 | if (!PhysicsScene.PE.IsInWorld(PhysicsScene.World, body)) | ||
142 | { | ||
143 | PhysicsScene.PE.AddObjectToWorld(PhysicsScene.World, body); | ||
144 | if (DDetail) DetailLog("{0},BSShapeCollection.ReferenceBody,addedToWorld,ref={1}", body.ID, body); | ||
145 | } | ||
146 | } | ||
147 | } | ||
148 | |||
149 | // Release the usage of a body. | ||
150 | // Called when releasing use of a BSBody. BSShape is handled separately. | ||
151 | // Called in taint time. | ||
152 | public void DereferenceBody(BulletBody body, BodyDestructionCallback bodyCallback ) | ||
153 | { | ||
154 | if (!body.HasPhysicalBody) | ||
155 | return; | ||
156 | |||
157 | PhysicsScene.AssertInTaintTime("BSShapeCollection.DereferenceBody"); | ||
158 | |||
159 | lock (m_collectionActivityLock) | ||
160 | { | ||
161 | if (DDetail) DetailLog("{0},BSShapeCollection.DereferenceBody,DestroyingBody,body={1}", body.ID, body); | ||
162 | // If the caller needs to know the old body is going away, pass the event up. | ||
163 | if (bodyCallback != null) bodyCallback(body); | ||
164 | |||
165 | if (PhysicsScene.PE.IsInWorld(PhysicsScene.World, body)) | ||
166 | { | ||
167 | PhysicsScene.PE.RemoveObjectFromWorld(PhysicsScene.World, body); | ||
168 | if (DDetail) DetailLog("{0},BSShapeCollection.DereferenceBody,removingFromWorld. Body={1}", body.ID, body); | ||
169 | } | ||
170 | |||
171 | // Zero any reference to the shape so it is not freed when the body is deleted. | ||
172 | PhysicsScene.PE.SetCollisionShape(PhysicsScene.World, body, null); | ||
173 | PhysicsScene.PE.DestroyObject(PhysicsScene.World, body); | ||
174 | } | ||
175 | } | ||
176 | |||
177 | // Track the datastructures and use count for a shape. | ||
178 | // When creating a hull, this is called first to reference the mesh | ||
179 | // and then again to reference the hull. | ||
180 | // Meshes and hulls for the same shape have the same hash key. | ||
181 | // NOTE that native shapes are not added to the mesh list or removed. | ||
182 | // Returns 'true' if this is the initial reference to the shape. Otherwise reused. | ||
183 | public bool ReferenceShape(BulletShape shape) | ||
184 | { | ||
185 | bool ret = false; | ||
186 | switch (shape.type) | ||
187 | { | ||
188 | case BSPhysicsShapeType.SHAPE_MESH: | ||
189 | MeshDesc meshDesc; | ||
190 | if (Meshes.TryGetValue(shape.shapeKey, out meshDesc)) | ||
191 | { | ||
192 | // There is an existing instance of this mesh. | ||
193 | meshDesc.referenceCount++; | ||
194 | if (DDetail) DetailLog("{0},BSShapeCollection.ReferenceShape,existingMesh,key={1},cnt={2}", | ||
195 | BSScene.DetailLogZero, shape.shapeKey.ToString("X"), meshDesc.referenceCount); | ||
196 | } | ||
197 | else | ||
198 | { | ||
199 | // This is a new reference to a mesh | ||
200 | meshDesc.shape = shape.Clone(); | ||
201 | meshDesc.shapeKey = shape.shapeKey; | ||
202 | // We keep a reference to the underlying IMesh data so a hull can be built | ||
203 | meshDesc.referenceCount = 1; | ||
204 | if (DDetail) DetailLog("{0},BSShapeCollection.ReferenceShape,newMesh,key={1},cnt={2}", | ||
205 | BSScene.DetailLogZero, shape.shapeKey.ToString("X"), meshDesc.referenceCount); | ||
206 | ret = true; | ||
207 | } | ||
208 | meshDesc.lastReferenced = System.DateTime.Now; | ||
209 | Meshes[shape.shapeKey] = meshDesc; | ||
210 | break; | ||
211 | case BSPhysicsShapeType.SHAPE_HULL: | ||
212 | HullDesc hullDesc; | ||
213 | if (Hulls.TryGetValue(shape.shapeKey, out hullDesc)) | ||
214 | { | ||
215 | // There is an existing instance of this hull. | ||
216 | hullDesc.referenceCount++; | ||
217 | if (DDetail) DetailLog("{0},BSShapeCollection.ReferenceShape,existingHull,key={1},cnt={2}", | ||
218 | BSScene.DetailLogZero, shape.shapeKey.ToString("X"), hullDesc.referenceCount); | ||
219 | } | ||
220 | else | ||
221 | { | ||
222 | // This is a new reference to a hull | ||
223 | hullDesc.shape = shape.Clone(); | ||
224 | hullDesc.shapeKey = shape.shapeKey; | ||
225 | hullDesc.referenceCount = 1; | ||
226 | if (DDetail) DetailLog("{0},BSShapeCollection.ReferenceShape,newHull,key={1},cnt={2}", | ||
227 | BSScene.DetailLogZero, shape.shapeKey.ToString("X"), hullDesc.referenceCount); | ||
228 | ret = true; | ||
229 | |||
230 | } | ||
231 | hullDesc.lastReferenced = System.DateTime.Now; | ||
232 | Hulls[shape.shapeKey] = hullDesc; | ||
233 | break; | ||
234 | case BSPhysicsShapeType.SHAPE_UNKNOWN: | ||
235 | break; | ||
236 | default: | ||
237 | // Native shapes are not tracked and they don't go into any list | ||
238 | break; | ||
239 | } | ||
240 | return ret; | ||
241 | } | 104 | } |
242 | 105 | ||
243 | // Release the usage of a shape. | 106 | // If the existing prim's shape is to be replaced, remove the tie to the existing shape |
244 | public void DereferenceShape(BulletShape shape, ShapeDestructionCallback shapeCallback) | 107 | // before replacing it. |
108 | private void DereferenceExistingShape(BSPhysObject prim, PhysicalDestructionCallback shapeCallback) | ||
245 | { | 109 | { |
246 | if (!shape.HasPhysicalShape) | 110 | if (prim.PhysShape.HasPhysicalShape) |
247 | return; | ||
248 | |||
249 | PhysicsScene.AssertInTaintTime("BSShapeCollection.DereferenceShape"); | ||
250 | |||
251 | if (shape.HasPhysicalShape) | ||
252 | { | ||
253 | if (shape.isNativeShape) | ||
254 | { | ||
255 | // Native shapes are not tracked and are released immediately | ||
256 | if (DDetail) DetailLog("{0},BSShapeCollection.DereferenceShape,deleteNativeShape,ptr={1}", | ||
257 | BSScene.DetailLogZero, shape.AddrString); | ||
258 | if (shapeCallback != null) shapeCallback(shape); | ||
259 | PhysicsScene.PE.DeleteCollisionShape(PhysicsScene.World, shape); | ||
260 | } | ||
261 | else | ||
262 | { | ||
263 | switch (shape.type) | ||
264 | { | ||
265 | case BSPhysicsShapeType.SHAPE_HULL: | ||
266 | DereferenceHull(shape, shapeCallback); | ||
267 | break; | ||
268 | case BSPhysicsShapeType.SHAPE_MESH: | ||
269 | DereferenceMesh(shape, shapeCallback); | ||
270 | break; | ||
271 | case BSPhysicsShapeType.SHAPE_COMPOUND: | ||
272 | DereferenceCompound(shape, shapeCallback); | ||
273 | break; | ||
274 | case BSPhysicsShapeType.SHAPE_UNKNOWN: | ||
275 | break; | ||
276 | default: | ||
277 | break; | ||
278 | } | ||
279 | } | ||
280 | } | ||
281 | } | ||
282 | |||
283 | // Count down the reference count for a mesh shape | ||
284 | // Called at taint-time. | ||
285 | private void DereferenceMesh(BulletShape shape, ShapeDestructionCallback shapeCallback) | ||
286 | { | ||
287 | MeshDesc meshDesc; | ||
288 | if (Meshes.TryGetValue(shape.shapeKey, out meshDesc)) | ||
289 | { | 111 | { |
290 | meshDesc.referenceCount--; | 112 | if (shapeCallback != null) |
291 | // TODO: release the Bullet storage | 113 | shapeCallback(prim.PhysBody, prim.PhysShape.physShapeInfo); |
292 | if (shapeCallback != null) shapeCallback(shape); | 114 | prim.PhysShape.Dereference(m_physicsScene); |
293 | meshDesc.lastReferenced = System.DateTime.Now; | ||
294 | Meshes[shape.shapeKey] = meshDesc; | ||
295 | if (DDetail) DetailLog("{0},BSShapeCollection.DereferenceMesh,shape={1},refCnt={2}", | ||
296 | BSScene.DetailLogZero, shape, meshDesc.referenceCount); | ||
297 | |||
298 | } | ||
299 | } | ||
300 | |||
301 | // Count down the reference count for a hull shape | ||
302 | // Called at taint-time. | ||
303 | private void DereferenceHull(BulletShape shape, ShapeDestructionCallback shapeCallback) | ||
304 | { | ||
305 | HullDesc hullDesc; | ||
306 | if (Hulls.TryGetValue(shape.shapeKey, out hullDesc)) | ||
307 | { | ||
308 | hullDesc.referenceCount--; | ||
309 | // TODO: release the Bullet storage (aging old entries?) | ||
310 | |||
311 | // Tell upper layers that, if they have dependencies on this shape, this link is going away | ||
312 | if (shapeCallback != null) shapeCallback(shape); | ||
313 | |||
314 | hullDesc.lastReferenced = System.DateTime.Now; | ||
315 | Hulls[shape.shapeKey] = hullDesc; | ||
316 | if (DDetail) DetailLog("{0},BSShapeCollection.DereferenceHull,shape={1},refCnt={2}", | ||
317 | BSScene.DetailLogZero, shape, hullDesc.referenceCount); | ||
318 | } | ||
319 | } | ||
320 | |||
321 | // Remove a reference to a compound shape. | ||
322 | // Taking a compound shape apart is a little tricky because if you just delete the | ||
323 | // physical shape, it will free all the underlying children. We can't do that because | ||
324 | // they could be shared. So, this removes each of the children from the compound and | ||
325 | // dereferences them separately before destroying the compound collision object itself. | ||
326 | // Called at taint-time. | ||
327 | private void DereferenceCompound(BulletShape shape, ShapeDestructionCallback shapeCallback) | ||
328 | { | ||
329 | if (!PhysicsScene.PE.IsCompound(shape)) | ||
330 | { | ||
331 | // Failed the sanity check!! | ||
332 | PhysicsScene.Logger.ErrorFormat("{0} Attempt to free a compound shape that is not compound!! type={1}, ptr={2}", | ||
333 | LogHeader, shape.type, shape.AddrString); | ||
334 | if (DDetail) DetailLog("{0},BSShapeCollection.DereferenceCompound,notACompoundShape,type={1},ptr={2}", | ||
335 | BSScene.DetailLogZero, shape.type, shape.AddrString); | ||
336 | return; | ||
337 | } | ||
338 | |||
339 | int numChildren = PhysicsScene.PE.GetNumberOfCompoundChildren(shape); | ||
340 | if (DDetail) DetailLog("{0},BSShapeCollection.DereferenceCompound,shape={1},children={2}", BSScene.DetailLogZero, shape, numChildren); | ||
341 | |||
342 | for (int ii = numChildren - 1; ii >= 0; ii--) | ||
343 | { | ||
344 | BulletShape childShape = PhysicsScene.PE.RemoveChildShapeFromCompoundShapeIndex(shape, ii); | ||
345 | DereferenceAnonCollisionShape(childShape); | ||
346 | } | ||
347 | PhysicsScene.PE.DeleteCollisionShape(PhysicsScene.World, shape); | ||
348 | } | ||
349 | |||
350 | // Sometimes we have a pointer to a collision shape but don't know what type it is. | ||
351 | // Figure out type and call the correct dereference routine. | ||
352 | // Called at taint-time. | ||
353 | private void DereferenceAnonCollisionShape(BulletShape shapeInfo) | ||
354 | { | ||
355 | MeshDesc meshDesc; | ||
356 | HullDesc hullDesc; | ||
357 | |||
358 | if (TryGetMeshByPtr(shapeInfo, out meshDesc)) | ||
359 | { | ||
360 | shapeInfo.type = BSPhysicsShapeType.SHAPE_MESH; | ||
361 | shapeInfo.shapeKey = meshDesc.shapeKey; | ||
362 | } | ||
363 | else | ||
364 | { | ||
365 | if (TryGetHullByPtr(shapeInfo, out hullDesc)) | ||
366 | { | ||
367 | shapeInfo.type = BSPhysicsShapeType.SHAPE_HULL; | ||
368 | shapeInfo.shapeKey = hullDesc.shapeKey; | ||
369 | } | ||
370 | else | ||
371 | { | ||
372 | if (PhysicsScene.PE.IsCompound(shapeInfo)) | ||
373 | { | ||
374 | shapeInfo.type = BSPhysicsShapeType.SHAPE_COMPOUND; | ||
375 | } | ||
376 | else | ||
377 | { | ||
378 | if (PhysicsScene.PE.IsNativeShape(shapeInfo)) | ||
379 | { | ||
380 | shapeInfo.isNativeShape = true; | ||
381 | shapeInfo.type = BSPhysicsShapeType.SHAPE_BOX; // (technically, type doesn't matter) | ||
382 | } | ||
383 | } | ||
384 | } | ||
385 | } | ||
386 | |||
387 | if (DDetail) DetailLog("{0},BSShapeCollection.DereferenceAnonCollisionShape,shape={1}", BSScene.DetailLogZero, shapeInfo); | ||
388 | |||
389 | if (shapeInfo.type != BSPhysicsShapeType.SHAPE_UNKNOWN) | ||
390 | { | ||
391 | DereferenceShape(shapeInfo, null); | ||
392 | } | ||
393 | else | ||
394 | { | ||
395 | PhysicsScene.Logger.ErrorFormat("{0} Could not decypher shape type. Region={1}, addr={2}", | ||
396 | LogHeader, PhysicsScene.RegionName, shapeInfo.AddrString); | ||
397 | } | 115 | } |
116 | prim.PhysShape = new BSShapeNull(); | ||
398 | } | 117 | } |
399 | 118 | ||
400 | // Create the geometry information in Bullet for later use. | 119 | // Create the geometry information in Bullet for later use. |
@@ -405,60 +124,41 @@ public sealed class BSShapeCollection : IDisposable | |||
405 | // Info in prim.BSShape is updated to the new shape. | 124 | // Info in prim.BSShape is updated to the new shape. |
406 | // Returns 'true' if the geometry was rebuilt. | 125 | // Returns 'true' if the geometry was rebuilt. |
407 | // Called at taint-time! | 126 | // Called at taint-time! |
408 | private bool CreateGeom(bool forceRebuild, BSPhysObject prim, ShapeDestructionCallback shapeCallback) | 127 | private bool CreateGeom(bool forceRebuild, BSPhysObject prim, PhysicalDestructionCallback shapeCallback) |
409 | { | 128 | { |
410 | bool ret = false; | 129 | bool ret = false; |
411 | bool haveShape = false; | 130 | bool haveShape = false; |
131 | bool nativeShapePossible = true; | ||
132 | PrimitiveBaseShape pbs = prim.BaseShape; | ||
412 | 133 | ||
413 | if (!haveShape && prim.PreferredPhysicalShape == BSPhysicsShapeType.SHAPE_CAPSULE) | 134 | // Kludge to create the capsule for the avatar. |
135 | // TDOD: Remove/redo this when BSShapeAvatar is working!! | ||
136 | BSCharacter theChar = prim as BSCharacter; | ||
137 | if (theChar != null) | ||
414 | { | 138 | { |
415 | // an avatar capsule is close to a native shape (it is not shared) | 139 | DereferenceExistingShape(prim, shapeCallback); |
416 | GetReferenceToNativeShape(prim, BSPhysicsShapeType.SHAPE_CAPSULE, FixedShapeKey.KEY_CAPSULE, shapeCallback); | 140 | prim.PhysShape = BSShapeNative.GetReference(m_physicsScene, prim, |
417 | if (DDetail) DetailLog("{0},BSShapeCollection.CreateGeom,avatarCapsule,shape={1}", prim.LocalID, prim.PhysShape); | 141 | BSPhysicsShapeType.SHAPE_CAPSULE, FixedShapeKey.KEY_CAPSULE); |
418 | ret = true; | 142 | ret = true; |
419 | haveShape = true; | 143 | haveShape = true; |
420 | } | 144 | } |
421 | 145 | ||
422 | // Compound shapes are handled special as they are rebuilt from scratch. | ||
423 | // This isn't too great a hardship since most of the child shapes will have already been created. | ||
424 | if (!haveShape && prim.PreferredPhysicalShape == BSPhysicsShapeType.SHAPE_COMPOUND) | ||
425 | { | ||
426 | ret = GetReferenceToCompoundShape(prim, shapeCallback); | ||
427 | if (DDetail) DetailLog("{0},BSShapeCollection.CreateGeom,compoundShape,shape={1}", prim.LocalID, prim.PhysShape); | ||
428 | haveShape = true; | ||
429 | } | ||
430 | |||
431 | if (!haveShape) | ||
432 | { | ||
433 | ret = CreateGeomNonSpecial(forceRebuild, prim, shapeCallback); | ||
434 | } | ||
435 | |||
436 | return ret; | ||
437 | } | ||
438 | |||
439 | // Create a mesh, hull or native shape. | ||
440 | // Return 'true' if the prim's shape was changed. | ||
441 | public bool CreateGeomNonSpecial(bool forceRebuild, BSPhysObject prim, ShapeDestructionCallback shapeCallback) | ||
442 | { | ||
443 | bool ret = false; | ||
444 | bool haveShape = false; | ||
445 | bool nativeShapePossible = true; | ||
446 | PrimitiveBaseShape pbs = prim.BaseShape; | ||
447 | |||
448 | // If the prim attributes are simple, this could be a simple Bullet native shape | 146 | // If the prim attributes are simple, this could be a simple Bullet native shape |
147 | // Native shapes work whether to object is static or physical. | ||
449 | if (!haveShape | 148 | if (!haveShape |
450 | && nativeShapePossible | 149 | && nativeShapePossible |
451 | && pbs != null | 150 | && pbs != null |
452 | && !pbs.SculptEntry | 151 | && PrimHasNoCuts(pbs) |
453 | && ((pbs.SculptEntry && !BSParam.ShouldMeshSculptedPrim) || PrimHasNoCuts(pbs)) ) | 152 | && ( !pbs.SculptEntry || (pbs.SculptEntry && !BSParam.ShouldMeshSculptedPrim) ) |
153 | ) | ||
454 | { | 154 | { |
455 | // Get the scale of any existing shape so we can see if the new shape is same native type and same size. | 155 | // Get the scale of any existing shape so we can see if the new shape is same native type and same size. |
456 | OMV.Vector3 scaleOfExistingShape = OMV.Vector3.Zero; | 156 | OMV.Vector3 scaleOfExistingShape = OMV.Vector3.Zero; |
457 | if (prim.PhysShape.HasPhysicalShape) | 157 | if (prim.PhysShape.HasPhysicalShape) |
458 | scaleOfExistingShape = PhysicsScene.PE.GetLocalScaling(prim.PhysShape); | 158 | scaleOfExistingShape = m_physicsScene.PE.GetLocalScaling(prim.PhysShape.physShapeInfo); |
459 | 159 | ||
460 | if (DDetail) DetailLog("{0},BSShapeCollection.CreateGeom,maybeNative,force={1},primScale={2},primSize={3},primShape={4}", | 160 | if (DDetail) DetailLog("{0},BSShapeCollection.CreateGeom,maybeNative,force={1},primScale={2},primSize={3},primShape={4}", |
461 | prim.LocalID, forceRebuild, prim.Scale, prim.Size, prim.PhysShape.type); | 161 | prim.LocalID, forceRebuild, prim.Scale, prim.Size, prim.PhysShape.physShapeInfo.shapeType); |
462 | 162 | ||
463 | // It doesn't look like Bullet scales native spheres so make sure the scales are all equal | 163 | // It doesn't look like Bullet scales native spheres so make sure the scales are all equal |
464 | if ((pbs.ProfileShape == ProfileShape.HalfCircle && pbs.PathCurve == (byte)Extrusion.Curve1) | 164 | if ((pbs.ProfileShape == ProfileShape.HalfCircle && pbs.PathCurve == (byte)Extrusion.Curve1) |
@@ -466,26 +166,30 @@ public sealed class BSShapeCollection : IDisposable | |||
466 | { | 166 | { |
467 | haveShape = true; | 167 | haveShape = true; |
468 | if (forceRebuild | 168 | if (forceRebuild |
469 | || prim.Scale != scaleOfExistingShape | 169 | || prim.PhysShape.ShapeType != BSPhysicsShapeType.SHAPE_SPHERE |
470 | || prim.PhysShape.type != BSPhysicsShapeType.SHAPE_SPHERE | 170 | ) |
471 | ) | ||
472 | { | 171 | { |
473 | ret = GetReferenceToNativeShape(prim, BSPhysicsShapeType.SHAPE_SPHERE, | 172 | DereferenceExistingShape(prim, shapeCallback); |
474 | FixedShapeKey.KEY_SPHERE, shapeCallback); | 173 | prim.PhysShape = BSShapeNative.GetReference(m_physicsScene, prim, |
174 | BSPhysicsShapeType.SHAPE_SPHERE, FixedShapeKey.KEY_SPHERE); | ||
175 | ret = true; | ||
475 | } | 176 | } |
476 | if (DDetail) DetailLog("{0},BSShapeCollection.CreateGeom,sphere,force={1},rebuilt={2},shape={3}", | 177 | if (DDetail) DetailLog("{0},BSShapeCollection.CreateGeom,sphere,force={1},rebuilt={2},shape={3}", |
477 | prim.LocalID, forceRebuild, ret, prim.PhysShape); | 178 | prim.LocalID, forceRebuild, ret, prim.PhysShape); |
478 | } | 179 | } |
180 | // If we didn't make a sphere, maybe a box will work. | ||
479 | if (!haveShape && pbs.ProfileShape == ProfileShape.Square && pbs.PathCurve == (byte)Extrusion.Straight) | 181 | if (!haveShape && pbs.ProfileShape == ProfileShape.Square && pbs.PathCurve == (byte)Extrusion.Straight) |
480 | { | 182 | { |
481 | haveShape = true; | 183 | haveShape = true; |
482 | if (forceRebuild | 184 | if (forceRebuild |
483 | || prim.Scale != scaleOfExistingShape | 185 | || prim.Scale != scaleOfExistingShape |
484 | || prim.PhysShape.type != BSPhysicsShapeType.SHAPE_BOX | 186 | || prim.PhysShape.ShapeType != BSPhysicsShapeType.SHAPE_BOX |
485 | ) | 187 | ) |
486 | { | 188 | { |
487 | ret = GetReferenceToNativeShape( prim, BSPhysicsShapeType.SHAPE_BOX, | 189 | DereferenceExistingShape(prim, shapeCallback); |
488 | FixedShapeKey.KEY_BOX, shapeCallback); | 190 | prim.PhysShape = BSShapeNative.GetReference(m_physicsScene, prim, |
191 | BSPhysicsShapeType.SHAPE_BOX, FixedShapeKey.KEY_BOX); | ||
192 | ret = true; | ||
489 | } | 193 | } |
490 | if (DDetail) DetailLog("{0},BSShapeCollection.CreateGeom,box,force={1},rebuilt={2},shape={3}", | 194 | if (DDetail) DetailLog("{0},BSShapeCollection.CreateGeom,box,force={1},rebuilt={2},shape={3}", |
491 | prim.LocalID, forceRebuild, ret, prim.PhysShape); | 195 | prim.LocalID, forceRebuild, ret, prim.PhysShape); |
@@ -502,7 +206,7 @@ public sealed class BSShapeCollection : IDisposable | |||
502 | } | 206 | } |
503 | 207 | ||
504 | // return 'true' if this shape description does not include any cutting or twisting. | 208 | // return 'true' if this shape description does not include any cutting or twisting. |
505 | private bool PrimHasNoCuts(PrimitiveBaseShape pbs) | 209 | public static bool PrimHasNoCuts(PrimitiveBaseShape pbs) |
506 | { | 210 | { |
507 | return pbs.ProfileBegin == 0 && pbs.ProfileEnd == 0 | 211 | return pbs.ProfileBegin == 0 && pbs.ProfileEnd == 0 |
508 | && pbs.ProfileHollow == 0 | 212 | && pbs.ProfileHollow == 0 |
@@ -514,7 +218,7 @@ public sealed class BSShapeCollection : IDisposable | |||
514 | } | 218 | } |
515 | 219 | ||
516 | // return 'true' if the prim's shape was changed. | 220 | // return 'true' if the prim's shape was changed. |
517 | public bool CreateGeomMeshOrHull(BSPhysObject prim, ShapeDestructionCallback shapeCallback) | 221 | private bool CreateGeomMeshOrHull(BSPhysObject prim, PhysicalDestructionCallback shapeCallback) |
518 | { | 222 | { |
519 | 223 | ||
520 | bool ret = false; | 224 | bool ret = false; |
@@ -522,503 +226,121 @@ public sealed class BSShapeCollection : IDisposable | |||
522 | // made. Native shapes work in either case. | 226 | // made. Native shapes work in either case. |
523 | if (prim.IsPhysical && BSParam.ShouldUseHullsForPhysicalObjects) | 227 | if (prim.IsPhysical && BSParam.ShouldUseHullsForPhysicalObjects) |
524 | { | 228 | { |
525 | // Update prim.BSShape to reference a hull of this shape. | 229 | // Use a simple, single mesh convex hull shape if the object is simple enough |
526 | ret = GetReferenceToHull(prim, shapeCallback); | 230 | BSShape potentialHull = null; |
527 | if (DDetail) DetailLog("{0},BSShapeCollection.CreateGeom,hull,shape={1},key={2}", | ||
528 | prim.LocalID, prim.PhysShape, prim.PhysShape.shapeKey.ToString("X")); | ||
529 | } | ||
530 | else | ||
531 | { | ||
532 | ret = GetReferenceToMesh(prim, shapeCallback); | ||
533 | if (DDetail) DetailLog("{0},BSShapeCollection.CreateGeom,mesh,shape={1},key={2}", | ||
534 | prim.LocalID, prim.PhysShape, prim.PhysShape.shapeKey.ToString("X")); | ||
535 | } | ||
536 | return ret; | ||
537 | } | ||
538 | |||
539 | // Creates a native shape and assignes it to prim.BSShape. | ||
540 | // "Native" shapes are never shared. they are created here and destroyed in DereferenceShape(). | ||
541 | private bool GetReferenceToNativeShape(BSPhysObject prim, | ||
542 | BSPhysicsShapeType shapeType, FixedShapeKey shapeKey, | ||
543 | ShapeDestructionCallback shapeCallback) | ||
544 | { | ||
545 | // release any previous shape | ||
546 | DereferenceShape(prim.PhysShape, shapeCallback); | ||
547 | |||
548 | BulletShape newShape = BuildPhysicalNativeShape(prim, shapeType, shapeKey); | ||
549 | |||
550 | // Don't need to do a 'ReferenceShape()' here because native shapes are not shared. | ||
551 | if (DDetail) DetailLog("{0},BSShapeCollection.AddNativeShapeToPrim,create,newshape={1},scale={2}", | ||
552 | prim.LocalID, newShape, prim.Scale); | ||
553 | |||
554 | // native shapes are scaled by Bullet | ||
555 | prim.PhysShape = newShape; | ||
556 | return true; | ||
557 | } | ||
558 | |||
559 | private BulletShape BuildPhysicalNativeShape(BSPhysObject prim, BSPhysicsShapeType shapeType, | ||
560 | FixedShapeKey shapeKey) | ||
561 | { | ||
562 | BulletShape newShape; | ||
563 | // Need to make sure the passed shape information is for the native type. | ||
564 | ShapeData nativeShapeData = new ShapeData(); | ||
565 | nativeShapeData.Type = shapeType; | ||
566 | nativeShapeData.ID = prim.LocalID; | ||
567 | nativeShapeData.Scale = prim.Scale; | ||
568 | nativeShapeData.Size = prim.Scale; // unneeded, I think. | ||
569 | nativeShapeData.MeshKey = (ulong)shapeKey; | ||
570 | nativeShapeData.HullKey = (ulong)shapeKey; | ||
571 | |||
572 | if (shapeType == BSPhysicsShapeType.SHAPE_CAPSULE) | ||
573 | { | ||
574 | 231 | ||
575 | newShape = PhysicsScene.PE.BuildCapsuleShape(PhysicsScene.World, 1f, 1f, prim.Scale); | 232 | PrimitiveBaseShape pbs = prim.BaseShape; |
576 | if (DDetail) DetailLog("{0},BSShapeCollection.BuildPhysicalNativeShape,capsule,scale={1}", prim.LocalID, prim.Scale); | 233 | // Use a simple, one section convex shape for prims that are probably convex (no cuts or twists) |
577 | } | 234 | if (BSParam.ShouldUseSingleConvexHullForPrims |
578 | else | 235 | && pbs != null |
579 | { | 236 | && !pbs.SculptEntry |
580 | // Native shapes are scaled in Bullet so set the scaling to the size | 237 | && PrimHasNoCuts(pbs) |
581 | newShape = PhysicsScene.PE.BuildNativeShape(PhysicsScene.World, nativeShapeData); | 238 | ) |
582 | 239 | { | |
583 | } | 240 | potentialHull = BSShapeConvexHull.GetReference(m_physicsScene, false /* forceRebuild */, prim); |
584 | if (!newShape.HasPhysicalShape) | 241 | } |
585 | { | 242 | // Use the GImpact shape if it is a prim that has some concaveness |
586 | PhysicsScene.Logger.ErrorFormat("{0} BuildPhysicalNativeShape failed. ID={1}, shape={2}", | 243 | if (potentialHull == null |
587 | LogHeader, prim.LocalID, shapeType); | 244 | && BSParam.ShouldUseGImpactShapeForPrims |
588 | } | 245 | && pbs != null |
589 | newShape.shapeKey = (System.UInt64)shapeKey; | 246 | && !pbs.SculptEntry |
590 | newShape.isNativeShape = true; | 247 | ) |
591 | 248 | { | |
592 | return newShape; | 249 | potentialHull = BSShapeGImpact.GetReference(m_physicsScene, false /* forceRebuild */, prim); |
593 | } | 250 | } |
594 | 251 | // If not any of the simple cases, just make a hull | |
595 | // Builds a mesh shape in the physical world and updates prim.BSShape. | 252 | if (potentialHull == null) |
596 | // Dereferences previous shape in BSShape and adds a reference for this new shape. | 253 | { |
597 | // Returns 'true' of a mesh was actually built. Otherwise . | 254 | potentialHull = BSShapeHull.GetReference(m_physicsScene, false /*forceRebuild*/, prim); |
598 | // Called at taint-time! | 255 | } |
599 | private bool GetReferenceToMesh(BSPhysObject prim, ShapeDestructionCallback shapeCallback) | ||
600 | { | ||
601 | BulletShape newShape = new BulletShape(); | ||
602 | |||
603 | float lod; | ||
604 | System.UInt64 newMeshKey = ComputeShapeKey(prim.Size, prim.BaseShape, out lod); | ||
605 | |||
606 | // if this new shape is the same as last time, don't recreate the mesh | ||
607 | if (newMeshKey == prim.PhysShape.shapeKey && prim.PhysShape.type == BSPhysicsShapeType.SHAPE_MESH) | ||
608 | return false; | ||
609 | |||
610 | if (DDetail) DetailLog("{0},BSShapeCollection.GetReferenceToMesh,create,oldKey={1},newKey={2},size={3},lod={4}", | ||
611 | prim.LocalID, prim.PhysShape.shapeKey.ToString("X"), newMeshKey.ToString("X"), prim.Size, lod); | ||
612 | |||
613 | // Since we're recreating new, get rid of the reference to the previous shape | ||
614 | DereferenceShape(prim.PhysShape, shapeCallback); | ||
615 | |||
616 | newShape = CreatePhysicalMesh(prim, newMeshKey, prim.BaseShape, prim.Size, lod); | ||
617 | // Take evasive action if the mesh was not constructed. | ||
618 | newShape = VerifyMeshCreated(newShape, prim); | ||
619 | |||
620 | ReferenceShape(newShape); | ||
621 | |||
622 | prim.PhysShape = newShape; | ||
623 | |||
624 | return true; // 'true' means a new shape has been added to this prim | ||
625 | } | ||
626 | |||
627 | private BulletShape CreatePhysicalMesh(BSPhysObject prim, System.UInt64 newMeshKey, PrimitiveBaseShape pbs, OMV.Vector3 size, float lod) | ||
628 | { | ||
629 | BulletShape newShape = new BulletShape(); | ||
630 | 256 | ||
631 | MeshDesc meshDesc; | 257 | // If the current shape is not what is on the prim at the moment, time to change. |
632 | if (Meshes.TryGetValue(newMeshKey, out meshDesc)) | 258 | if (!prim.PhysShape.HasPhysicalShape |
633 | { | 259 | || potentialHull.ShapeType != prim.PhysShape.ShapeType |
634 | // If the mesh has already been built just use it. | 260 | || potentialHull.physShapeInfo.shapeKey != prim.PhysShape.physShapeInfo.shapeKey) |
635 | newShape = meshDesc.shape.Clone(); | 261 | { |
262 | DereferenceExistingShape(prim, shapeCallback); | ||
263 | prim.PhysShape = potentialHull; | ||
264 | ret = true; | ||
265 | } | ||
266 | else | ||
267 | { | ||
268 | // The current shape on the prim is the correct one. We don't need the potential reference. | ||
269 | potentialHull.Dereference(m_physicsScene); | ||
270 | } | ||
271 | if (DDetail) DetailLog("{0},BSShapeCollection.CreateGeom,hull,shape={1}", prim.LocalID, prim.PhysShape); | ||
636 | } | 272 | } |
637 | else | 273 | else |
638 | { | 274 | { |
639 | IMesh meshData = PhysicsScene.mesher.CreateMesh(prim.PhysObjectName, pbs, size, lod, | 275 | // Non-physical objects should be just meshes. |
640 | true, | 276 | BSShape potentialMesh = BSShapeMesh.GetReference(m_physicsScene, false /*forceRebuild*/, prim); |
641 | false, // say it is not physical so a bounding box is not built | 277 | // If the current shape is not what is on the prim at the moment, time to change. |
642 | false, // do not cache the mesh and do not use previously built versions | 278 | if (!prim.PhysShape.HasPhysicalShape |
643 | false // It's NOT for ODE | 279 | || potentialMesh.ShapeType != prim.PhysShape.ShapeType |
644 | ); | 280 | || potentialMesh.physShapeInfo.shapeKey != prim.PhysShape.physShapeInfo.shapeKey) |
645 | |||
646 | if (meshData != null) | ||
647 | { | 281 | { |
648 | 282 | DereferenceExistingShape(prim, shapeCallback); | |
649 | int[] indices = meshData.getIndexListAsInt(); | 283 | prim.PhysShape = potentialMesh; |
650 | int realIndicesIndex = indices.Length; | 284 | ret = true; |
651 | float[] verticesAsFloats = meshData.getVertexListAsFloat(); | ||
652 | |||
653 | if (BSParam.ShouldRemoveZeroWidthTriangles) | ||
654 | { | ||
655 | // Remove degenerate triangles. These are triangles with two of the vertices | ||
656 | // are the same. This is complicated by the problem that vertices are not | ||
657 | // made unique in sculpties so we have to compare the values in the vertex. | ||
658 | realIndicesIndex = 0; | ||
659 | for (int tri = 0; tri < indices.Length; tri += 3) | ||
660 | { | ||
661 | // Compute displacements into vertex array for each vertex of the triangle | ||
662 | int v1 = indices[tri + 0] * 3; | ||
663 | int v2 = indices[tri + 1] * 3; | ||
664 | int v3 = indices[tri + 2] * 3; | ||
665 | // Check to see if any two of the vertices are the same | ||
666 | if (!( ( verticesAsFloats[v1 + 0] == verticesAsFloats[v2 + 0] | ||
667 | && verticesAsFloats[v1 + 1] == verticesAsFloats[v2 + 1] | ||
668 | && verticesAsFloats[v1 + 2] == verticesAsFloats[v2 + 2]) | ||
669 | || ( verticesAsFloats[v2 + 0] == verticesAsFloats[v3 + 0] | ||
670 | && verticesAsFloats[v2 + 1] == verticesAsFloats[v3 + 1] | ||
671 | && verticesAsFloats[v2 + 2] == verticesAsFloats[v3 + 2]) | ||
672 | || ( verticesAsFloats[v1 + 0] == verticesAsFloats[v3 + 0] | ||
673 | && verticesAsFloats[v1 + 1] == verticesAsFloats[v3 + 1] | ||
674 | && verticesAsFloats[v1 + 2] == verticesAsFloats[v3 + 2]) ) | ||
675 | ) | ||
676 | { | ||
677 | // None of the vertices of the triangles are the same. This is a good triangle; | ||
678 | indices[realIndicesIndex + 0] = indices[tri + 0]; | ||
679 | indices[realIndicesIndex + 1] = indices[tri + 1]; | ||
680 | indices[realIndicesIndex + 2] = indices[tri + 2]; | ||
681 | realIndicesIndex += 3; | ||
682 | } | ||
683 | } | ||
684 | } | ||
685 | DetailLog("{0},BSShapeCollection.CreatePhysicalMesh,origTri={1},realTri={2},numVerts={3}", | ||
686 | BSScene.DetailLogZero, indices.Length / 3, realIndicesIndex / 3, verticesAsFloats.Length / 3); | ||
687 | |||
688 | if (realIndicesIndex != 0) | ||
689 | { | ||
690 | newShape = PhysicsScene.PE.CreateMeshShape(PhysicsScene.World, | ||
691 | realIndicesIndex, indices, verticesAsFloats.Length / 3, verticesAsFloats); | ||
692 | } | ||
693 | else | ||
694 | { | ||
695 | PhysicsScene.Logger.DebugFormat("{0} All mesh triangles degenerate. Prim {1} at {2} in {3}", | ||
696 | LogHeader, prim.PhysObjectName, prim.RawPosition, PhysicsScene.Name); | ||
697 | } | ||
698 | } | 285 | } |
286 | else | ||
287 | { | ||
288 | // We don't need this reference to the mesh that is already being using. | ||
289 | potentialMesh.Dereference(m_physicsScene); | ||
290 | } | ||
291 | if (DDetail) DetailLog("{0},BSShapeCollection.CreateGeom,mesh,shape={1}", prim.LocalID, prim.PhysShape); | ||
699 | } | 292 | } |
700 | newShape.shapeKey = newMeshKey; | 293 | return ret; |
701 | |||
702 | return newShape; | ||
703 | } | ||
704 | |||
705 | // See that hull shape exists in the physical world and update prim.BSShape. | ||
706 | // We could be creating the hull because scale changed or whatever. | ||
707 | // Return 'true' if a new hull was built. Otherwise, returning a shared hull instance. | ||
708 | private bool GetReferenceToHull(BSPhysObject prim, ShapeDestructionCallback shapeCallback) | ||
709 | { | ||
710 | BulletShape newShape; | ||
711 | |||
712 | float lod; | ||
713 | System.UInt64 newHullKey = ComputeShapeKey(prim.Size, prim.BaseShape, out lod); | ||
714 | |||
715 | // if the hull hasn't changed, don't rebuild it | ||
716 | if (newHullKey == prim.PhysShape.shapeKey && prim.PhysShape.type == BSPhysicsShapeType.SHAPE_HULL) | ||
717 | return false; | ||
718 | |||
719 | if (DDetail) DetailLog("{0},BSShapeCollection.GetReferenceToHull,create,oldKey={1},newKey={2}", | ||
720 | prim.LocalID, prim.PhysShape.shapeKey.ToString("X"), newHullKey.ToString("X")); | ||
721 | |||
722 | // Remove usage of the previous shape. | ||
723 | DereferenceShape(prim.PhysShape, shapeCallback); | ||
724 | |||
725 | newShape = CreatePhysicalHull(prim.PhysObjectName, newHullKey, prim.BaseShape, prim.Size, lod); | ||
726 | // It might not have been created if we're waiting for an asset. | ||
727 | newShape = VerifyMeshCreated(newShape, prim); | ||
728 | |||
729 | ReferenceShape(newShape); | ||
730 | |||
731 | prim.PhysShape = newShape; | ||
732 | return true; // 'true' means a new shape has been added to this prim | ||
733 | } | 294 | } |
734 | 295 | ||
735 | List<ConvexResult> m_hulls; | 296 | // Track another user of a body. |
736 | private BulletShape CreatePhysicalHull(string objName, System.UInt64 newHullKey, PrimitiveBaseShape pbs, OMV.Vector3 size, float lod) | 297 | // We presume the caller has allocated the body. |
298 | // Bodies only have one user so the body is just put into the world if not already there. | ||
299 | private void ReferenceBody(BulletBody body) | ||
737 | { | 300 | { |
738 | 301 | lock (m_collectionActivityLock) | |
739 | BulletShape newShape = new BulletShape(); | ||
740 | IntPtr hullPtr = IntPtr.Zero; | ||
741 | |||
742 | HullDesc hullDesc; | ||
743 | if (Hulls.TryGetValue(newHullKey, out hullDesc)) | ||
744 | { | ||
745 | // If the hull shape already has been created, just use the one shared instance. | ||
746 | newShape = hullDesc.shape.Clone(); | ||
747 | } | ||
748 | else | ||
749 | { | 302 | { |
750 | // Build a new hull in the physical world. | 303 | if (DDetail) DetailLog("{0},BSShapeCollection.ReferenceBody,newBody,body={1}", body.ID, body); |
751 | // Pass true for physicalness as this prevents the creation of bounding box which is not needed | 304 | if (!m_physicsScene.PE.IsInWorld(m_physicsScene.World, body)) |
752 | IMesh meshData = PhysicsScene.mesher.CreateMesh(objName, pbs, size, lod, true, false, false, false); | ||
753 | if (meshData != null) | ||
754 | { | 305 | { |
755 | 306 | m_physicsScene.PE.AddObjectToWorld(m_physicsScene.World, body); | |
756 | int[] indices = meshData.getIndexListAsInt(); | 307 | if (DDetail) DetailLog("{0},BSShapeCollection.ReferenceBody,addedToWorld,ref={1}", body.ID, body); |
757 | List<OMV.Vector3> vertices = meshData.getVertexList(); | ||
758 | |||
759 | //format conversion from IMesh format to DecompDesc format | ||
760 | List<int> convIndices = new List<int>(); | ||
761 | List<float3> convVertices = new List<float3>(); | ||
762 | for (int ii = 0; ii < indices.GetLength(0); ii++) | ||
763 | { | ||
764 | convIndices.Add(indices[ii]); | ||
765 | } | ||
766 | foreach (OMV.Vector3 vv in vertices) | ||
767 | { | ||
768 | convVertices.Add(new float3(vv.X, vv.Y, vv.Z)); | ||
769 | } | ||
770 | |||
771 | uint maxDepthSplit = (uint)BSParam.CSHullMaxDepthSplit; | ||
772 | if (BSParam.CSHullMaxDepthSplit != BSParam.CSHullMaxDepthSplitForSimpleShapes) | ||
773 | { | ||
774 | // Simple primitive shapes we know are convex so they are better implemented with | ||
775 | // fewer hulls. | ||
776 | // Check for simple shape (prim without cuts) and reduce split parameter if so. | ||
777 | if (PrimHasNoCuts(pbs)) | ||
778 | { | ||
779 | maxDepthSplit = (uint)BSParam.CSHullMaxDepthSplitForSimpleShapes; | ||
780 | } | ||
781 | } | ||
782 | |||
783 | // setup and do convex hull conversion | ||
784 | m_hulls = new List<ConvexResult>(); | ||
785 | DecompDesc dcomp = new DecompDesc(); | ||
786 | dcomp.mIndices = convIndices; | ||
787 | dcomp.mVertices = convVertices; | ||
788 | dcomp.mDepth = maxDepthSplit; | ||
789 | dcomp.mCpercent = BSParam.CSHullConcavityThresholdPercent; | ||
790 | dcomp.mPpercent = BSParam.CSHullVolumeConservationThresholdPercent; | ||
791 | dcomp.mMaxVertices = (uint)BSParam.CSHullMaxVertices; | ||
792 | dcomp.mSkinWidth = BSParam.CSHullMaxSkinWidth; | ||
793 | ConvexBuilder convexBuilder = new ConvexBuilder(HullReturn); | ||
794 | // create the hull into the _hulls variable | ||
795 | convexBuilder.process(dcomp); | ||
796 | |||
797 | DetailLog("{0},BSShapeCollection.CreatePhysicalHull,key={1},inVert={2},inInd={3},split={4},hulls={5}", | ||
798 | BSScene.DetailLogZero, newHullKey, indices.GetLength(0), vertices.Count, maxDepthSplit, m_hulls.Count); | ||
799 | |||
800 | // Convert the vertices and indices for passing to unmanaged. | ||
801 | // The hull information is passed as a large floating point array. | ||
802 | // The format is: | ||
803 | // convHulls[0] = number of hulls | ||
804 | // convHulls[1] = number of vertices in first hull | ||
805 | // convHulls[2] = hull centroid X coordinate | ||
806 | // convHulls[3] = hull centroid Y coordinate | ||
807 | // convHulls[4] = hull centroid Z coordinate | ||
808 | // convHulls[5] = first hull vertex X | ||
809 | // convHulls[6] = first hull vertex Y | ||
810 | // convHulls[7] = first hull vertex Z | ||
811 | // convHulls[8] = second hull vertex X | ||
812 | // ... | ||
813 | // convHulls[n] = number of vertices in second hull | ||
814 | // convHulls[n+1] = second hull centroid X coordinate | ||
815 | // ... | ||
816 | // | ||
817 | // TODO: is is very inefficient. Someday change the convex hull generator to return | ||
818 | // data structures that do not need to be converted in order to pass to Bullet. | ||
819 | // And maybe put the values directly into pinned memory rather than marshaling. | ||
820 | int hullCount = m_hulls.Count; | ||
821 | int totalVertices = 1; // include one for the count of the hulls | ||
822 | foreach (ConvexResult cr in m_hulls) | ||
823 | { | ||
824 | totalVertices += 4; // add four for the vertex count and centroid | ||
825 | totalVertices += cr.HullIndices.Count * 3; // we pass just triangles | ||
826 | } | ||
827 | float[] convHulls = new float[totalVertices]; | ||
828 | |||
829 | convHulls[0] = (float)hullCount; | ||
830 | int jj = 1; | ||
831 | foreach (ConvexResult cr in m_hulls) | ||
832 | { | ||
833 | // copy vertices for index access | ||
834 | float3[] verts = new float3[cr.HullVertices.Count]; | ||
835 | int kk = 0; | ||
836 | foreach (float3 ff in cr.HullVertices) | ||
837 | { | ||
838 | verts[kk++] = ff; | ||
839 | } | ||
840 | |||
841 | // add to the array one hull's worth of data | ||
842 | convHulls[jj++] = cr.HullIndices.Count; | ||
843 | convHulls[jj++] = 0f; // centroid x,y,z | ||
844 | convHulls[jj++] = 0f; | ||
845 | convHulls[jj++] = 0f; | ||
846 | foreach (int ind in cr.HullIndices) | ||
847 | { | ||
848 | convHulls[jj++] = verts[ind].x; | ||
849 | convHulls[jj++] = verts[ind].y; | ||
850 | convHulls[jj++] = verts[ind].z; | ||
851 | } | ||
852 | } | ||
853 | // create the hull data structure in Bullet | ||
854 | newShape = PhysicsScene.PE.CreateHullShape(PhysicsScene.World, hullCount, convHulls); | ||
855 | } | 308 | } |
856 | } | 309 | } |
857 | |||
858 | newShape.shapeKey = newHullKey; | ||
859 | |||
860 | return newShape; | ||
861 | } | ||
862 | |||
863 | // Callback from convex hull creater with a newly created hull. | ||
864 | // Just add it to our collection of hulls for this shape. | ||
865 | private void HullReturn(ConvexResult result) | ||
866 | { | ||
867 | m_hulls.Add(result); | ||
868 | return; | ||
869 | } | 310 | } |
870 | 311 | ||
871 | // Compound shapes are always built from scratch. | 312 | // Release the usage of a body. |
872 | // This shouldn't be to bad since most of the parts will be meshes that had been built previously. | 313 | // Called when releasing use of a BSBody. BSShape is handled separately. |
873 | private bool GetReferenceToCompoundShape(BSPhysObject prim, ShapeDestructionCallback shapeCallback) | 314 | // Called in taint time. |
874 | { | 315 | public void DereferenceBody(BulletBody body, PhysicalDestructionCallback bodyCallback ) |
875 | // Remove reference to the old shape | ||
876 | // Don't need to do this as the shape is freed when the new root shape is created below. | ||
877 | // DereferenceShape(prim.PhysShape, true, shapeCallback); | ||
878 | |||
879 | BulletShape cShape = PhysicsScene.PE.CreateCompoundShape(PhysicsScene.World, false); | ||
880 | |||
881 | // Create the shape for the root prim and add it to the compound shape. Cannot be a native shape. | ||
882 | CreateGeomMeshOrHull(prim, shapeCallback); | ||
883 | PhysicsScene.PE.AddChildShapeToCompoundShape(cShape, prim.PhysShape, OMV.Vector3.Zero, OMV.Quaternion.Identity); | ||
884 | if (DDetail) DetailLog("{0},BSShapeCollection.GetReferenceToCompoundShape,addRootPrim,compShape={1},rootShape={2}", | ||
885 | prim.LocalID, cShape, prim.PhysShape); | ||
886 | |||
887 | prim.PhysShape = cShape; | ||
888 | |||
889 | return true; | ||
890 | } | ||
891 | |||
892 | // Create a hash of all the shape parameters to be used as a key | ||
893 | // for this particular shape. | ||
894 | private System.UInt64 ComputeShapeKey(OMV.Vector3 size, PrimitiveBaseShape pbs, out float retLod) | ||
895 | { | ||
896 | // level of detail based on size and type of the object | ||
897 | float lod = BSParam.MeshLOD; | ||
898 | |||
899 | // prims with curvy internal cuts need higher lod | ||
900 | if (pbs.HollowShape == HollowShape.Circle) | ||
901 | lod = BSParam.MeshCircularLOD; | ||
902 | |||
903 | if (pbs.SculptEntry) | ||
904 | lod = BSParam.SculptLOD; | ||
905 | |||
906 | // Mega prims usually get more detail because one can interact with shape approximations at this size. | ||
907 | float maxAxis = Math.Max(size.X, Math.Max(size.Y, size.Z)); | ||
908 | if (maxAxis > BSParam.MeshMegaPrimThreshold) | ||
909 | lod = BSParam.MeshMegaPrimLOD; | ||
910 | |||
911 | retLod = lod; | ||
912 | return pbs.GetMeshKey(size, lod); | ||
913 | } | ||
914 | // For those who don't want the LOD | ||
915 | private System.UInt64 ComputeShapeKey(OMV.Vector3 size, PrimitiveBaseShape pbs) | ||
916 | { | 316 | { |
917 | float lod; | 317 | if (!body.HasPhysicalBody) |
918 | return ComputeShapeKey(size, pbs, out lod); | 318 | return; |
919 | } | ||
920 | 319 | ||
921 | // The creation of a mesh or hull can fail if an underlying asset is not available. | 320 | m_physicsScene.AssertInTaintTime("BSShapeCollection.DereferenceBody"); |
922 | // There are two cases: 1) the asset is not in the cache and it needs to be fetched; | ||
923 | // and 2) the asset cannot be converted (like failed decompression of JPEG2000s). | ||
924 | // The first case causes the asset to be fetched. The second case requires | ||
925 | // us to not loop forever. | ||
926 | // Called after creating a physical mesh or hull. If the physical shape was created, | ||
927 | // just return. | ||
928 | private BulletShape VerifyMeshCreated(BulletShape newShape, BSPhysObject prim) | ||
929 | { | ||
930 | // If the shape was successfully created, nothing more to do | ||
931 | if (newShape.HasPhysicalShape) | ||
932 | return newShape; | ||
933 | 321 | ||
934 | // VerifyMeshCreated is called after trying to create the mesh. If we think the asset had been | 322 | lock (m_collectionActivityLock) |
935 | // fetched but we end up here again, the meshing of the asset must have failed. | ||
936 | // Prevent trying to keep fetching the mesh by declaring failure. | ||
937 | if (prim.PrimAssetState == BSPhysObject.PrimAssetCondition.Fetched) | ||
938 | { | ||
939 | prim.PrimAssetState = BSPhysObject.PrimAssetCondition.Failed; | ||
940 | PhysicsScene.Logger.WarnFormat("{0} Fetched asset would not mesh. {1}, texture={2}", | ||
941 | LogHeader, prim.PhysObjectName, prim.BaseShape.SculptTexture); | ||
942 | } | ||
943 | else | ||
944 | { | 323 | { |
945 | // If this mesh has an underlying asset and we have not failed getting it before, fetch the asset | 324 | if (DDetail) DetailLog("{0},BSShapeCollection.DereferenceBody,DestroyingBody,body={1}", body.ID, body); |
946 | if (prim.BaseShape.SculptEntry | 325 | // If the caller needs to know the old body is going away, pass the event up. |
947 | && prim.PrimAssetState != BSPhysObject.PrimAssetCondition.Failed | 326 | if (bodyCallback != null) |
948 | && prim.PrimAssetState != BSPhysObject.PrimAssetCondition.Waiting | 327 | bodyCallback(body, null); |
949 | && prim.BaseShape.SculptTexture != OMV.UUID.Zero | ||
950 | ) | ||
951 | { | ||
952 | DetailLog("{0},BSShapeCollection.VerifyMeshCreated,fetchAsset", prim.LocalID); | ||
953 | // Multiple requestors will know we're waiting for this asset | ||
954 | prim.PrimAssetState = BSPhysObject.PrimAssetCondition.Waiting; | ||
955 | |||
956 | BSPhysObject xprim = prim; | ||
957 | Util.FireAndForget(delegate | ||
958 | { | ||
959 | RequestAssetDelegate assetProvider = PhysicsScene.RequestAssetMethod; | ||
960 | if (assetProvider != null) | ||
961 | { | ||
962 | BSPhysObject yprim = xprim; // probably not necessary, but, just in case. | ||
963 | assetProvider(yprim.BaseShape.SculptTexture, delegate(AssetBase asset) | ||
964 | { | ||
965 | bool assetFound = false; | ||
966 | string mismatchIDs = String.Empty; // DEBUG DEBUG | ||
967 | if (asset != null && yprim.BaseShape.SculptEntry) | ||
968 | { | ||
969 | if (yprim.BaseShape.SculptTexture.ToString() == asset.ID) | ||
970 | { | ||
971 | yprim.BaseShape.SculptData = asset.Data; | ||
972 | // This will cause the prim to see that the filler shape is not the right | ||
973 | // one and try again to build the object. | ||
974 | // No race condition with the normal shape setting since the rebuild is at taint time. | ||
975 | yprim.ForceBodyShapeRebuild(false /* inTaintTime */); | ||
976 | assetFound = true; | ||
977 | } | ||
978 | else | ||
979 | { | ||
980 | mismatchIDs = yprim.BaseShape.SculptTexture.ToString() + "/" + asset.ID; | ||
981 | } | ||
982 | } | ||
983 | if (assetFound) | ||
984 | yprim.PrimAssetState = BSPhysObject.PrimAssetCondition.Fetched; | ||
985 | else | ||
986 | yprim.PrimAssetState = BSPhysObject.PrimAssetCondition.Failed; | ||
987 | DetailLog("{0},BSShapeCollection,fetchAssetCallback,found={1},isSculpt={2},ids={3}", | ||
988 | yprim.LocalID, assetFound, yprim.BaseShape.SculptEntry, mismatchIDs ); | ||
989 | 328 | ||
990 | }); | 329 | // Removing an object not in the world is a NOOP |
991 | } | 330 | m_physicsScene.PE.RemoveObjectFromWorld(m_physicsScene.World, body); |
992 | else | ||
993 | { | ||
994 | xprim.PrimAssetState = BSPhysObject.PrimAssetCondition.Failed; | ||
995 | PhysicsScene.Logger.ErrorFormat("{0} Physical object requires asset but no asset provider. Name={1}", | ||
996 | LogHeader, PhysicsScene.Name); | ||
997 | } | ||
998 | }); | ||
999 | } | ||
1000 | else | ||
1001 | { | ||
1002 | if (prim.PrimAssetState == BSPhysObject.PrimAssetCondition.Failed) | ||
1003 | { | ||
1004 | PhysicsScene.Logger.WarnFormat("{0} Mesh failed to fetch asset. obj={1}, texture={2}", | ||
1005 | LogHeader, prim.PhysObjectName, prim.BaseShape.SculptTexture); | ||
1006 | } | ||
1007 | } | ||
1008 | } | ||
1009 | 331 | ||
1010 | // While we wait for the mesh defining asset to be loaded, stick in a simple box for the object. | 332 | // Zero any reference to the shape so it is not freed when the body is deleted. |
1011 | BulletShape fillinShape = BuildPhysicalNativeShape(prim, BSPhysicsShapeType.SHAPE_BOX, FixedShapeKey.KEY_BOX); | 333 | m_physicsScene.PE.SetCollisionShape(m_physicsScene.World, body, null); |
1012 | DetailLog("{0},BSShapeCollection.VerifyMeshCreated,boxTempShape", prim.LocalID); | ||
1013 | 334 | ||
1014 | return fillinShape; | 335 | m_physicsScene.PE.DestroyObject(m_physicsScene.World, body); |
336 | } | ||
1015 | } | 337 | } |
1016 | 338 | ||
1017 | // Create a body object in Bullet. | 339 | // Create a body object in Bullet. |
1018 | // Updates prim.BSBody with the information about the new body if one is created. | 340 | // Updates prim.BSBody with the information about the new body if one is created. |
1019 | // Returns 'true' if an object was actually created. | 341 | // Returns 'true' if an object was actually created. |
1020 | // Called at taint-time. | 342 | // Called at taint-time. |
1021 | private bool CreateBody(bool forceRebuild, BSPhysObject prim, BulletWorld sim, BodyDestructionCallback bodyCallback) | 343 | private bool CreateBody(bool forceRebuild, BSPhysObject prim, BulletWorld sim, PhysicalDestructionCallback bodyCallback) |
1022 | { | 344 | { |
1023 | bool ret = false; | 345 | bool ret = false; |
1024 | 346 | ||
@@ -1029,7 +351,7 @@ public sealed class BSShapeCollection : IDisposable | |||
1029 | // If not a solid object, body is a GhostObject. Otherwise a RigidBody. | 351 | // If not a solid object, body is a GhostObject. Otherwise a RigidBody. |
1030 | if (!mustRebuild) | 352 | if (!mustRebuild) |
1031 | { | 353 | { |
1032 | CollisionObjectTypes bodyType = (CollisionObjectTypes)PhysicsScene.PE.GetBodyType(prim.PhysBody); | 354 | CollisionObjectTypes bodyType = (CollisionObjectTypes)m_physicsScene.PE.GetBodyType(prim.PhysBody); |
1033 | if (prim.IsSolid && bodyType != CollisionObjectTypes.CO_RIGID_BODY | 355 | if (prim.IsSolid && bodyType != CollisionObjectTypes.CO_RIGID_BODY |
1034 | || !prim.IsSolid && bodyType != CollisionObjectTypes.CO_GHOST_OBJECT) | 356 | || !prim.IsSolid && bodyType != CollisionObjectTypes.CO_GHOST_OBJECT) |
1035 | { | 357 | { |
@@ -1047,12 +369,12 @@ public sealed class BSShapeCollection : IDisposable | |||
1047 | BulletBody aBody; | 369 | BulletBody aBody; |
1048 | if (prim.IsSolid) | 370 | if (prim.IsSolid) |
1049 | { | 371 | { |
1050 | aBody = PhysicsScene.PE.CreateBodyFromShape(sim, prim.PhysShape, prim.LocalID, prim.RawPosition, prim.RawOrientation); | 372 | aBody = m_physicsScene.PE.CreateBodyFromShape(sim, prim.PhysShape.physShapeInfo, prim.LocalID, prim.RawPosition, prim.RawOrientation); |
1051 | if (DDetail) DetailLog("{0},BSShapeCollection.CreateBody,mesh,body={1}", prim.LocalID, aBody); | 373 | if (DDetail) DetailLog("{0},BSShapeCollection.CreateBody,rigid,body={1}", prim.LocalID, aBody); |
1052 | } | 374 | } |
1053 | else | 375 | else |
1054 | { | 376 | { |
1055 | aBody = PhysicsScene.PE.CreateGhostFromShape(sim, prim.PhysShape, prim.LocalID, prim.RawPosition, prim.RawOrientation); | 377 | aBody = m_physicsScene.PE.CreateGhostFromShape(sim, prim.PhysShape.physShapeInfo, prim.LocalID, prim.RawPosition, prim.RawOrientation); |
1056 | if (DDetail) DetailLog("{0},BSShapeCollection.CreateBody,ghost,body={1}", prim.LocalID, aBody); | 378 | if (DDetail) DetailLog("{0},BSShapeCollection.CreateBody,ghost,body={1}", prim.LocalID, aBody); |
1057 | } | 379 | } |
1058 | 380 | ||
@@ -1066,46 +388,10 @@ public sealed class BSShapeCollection : IDisposable | |||
1066 | return ret; | 388 | return ret; |
1067 | } | 389 | } |
1068 | 390 | ||
1069 | private bool TryGetMeshByPtr(BulletShape shape, out MeshDesc outDesc) | ||
1070 | { | ||
1071 | bool ret = false; | ||
1072 | MeshDesc foundDesc = new MeshDesc(); | ||
1073 | foreach (MeshDesc md in Meshes.Values) | ||
1074 | { | ||
1075 | if (md.shape.ReferenceSame(shape)) | ||
1076 | { | ||
1077 | foundDesc = md; | ||
1078 | ret = true; | ||
1079 | break; | ||
1080 | } | ||
1081 | |||
1082 | } | ||
1083 | outDesc = foundDesc; | ||
1084 | return ret; | ||
1085 | } | ||
1086 | |||
1087 | private bool TryGetHullByPtr(BulletShape shape, out HullDesc outDesc) | ||
1088 | { | ||
1089 | bool ret = false; | ||
1090 | HullDesc foundDesc = new HullDesc(); | ||
1091 | foreach (HullDesc hd in Hulls.Values) | ||
1092 | { | ||
1093 | if (hd.shape.ReferenceSame(shape)) | ||
1094 | { | ||
1095 | foundDesc = hd; | ||
1096 | ret = true; | ||
1097 | break; | ||
1098 | } | ||
1099 | |||
1100 | } | ||
1101 | outDesc = foundDesc; | ||
1102 | return ret; | ||
1103 | } | ||
1104 | |||
1105 | private void DetailLog(string msg, params Object[] args) | 391 | private void DetailLog(string msg, params Object[] args) |
1106 | { | 392 | { |
1107 | if (PhysicsScene.PhysicsLogging.Enabled) | 393 | if (m_physicsScene.PhysicsLogging.Enabled) |
1108 | PhysicsScene.DetailLog(msg, args); | 394 | m_physicsScene.DetailLog(msg, args); |
1109 | } | 395 | } |
1110 | } | 396 | } |
1111 | } | 397 | } |