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