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