aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/OpenSim/Region/Physics/BulletSPlugin/BSShapeCollection.cs
diff options
context:
space:
mode:
Diffstat (limited to '')
-rwxr-xr-xOpenSim/Region/Physics/BulletSPlugin/BSShapeCollection.cs479
1 files changed, 275 insertions, 204 deletions
diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSShapeCollection.cs b/OpenSim/Region/Physics/BulletSPlugin/BSShapeCollection.cs
index 399a133..d3ba273 100755
--- a/OpenSim/Region/Physics/BulletSPlugin/BSShapeCollection.cs
+++ b/OpenSim/Region/Physics/BulletSPlugin/BSShapeCollection.cs
@@ -51,7 +51,7 @@ public class BSShapeCollection : IDisposable
51 } 51 }
52 52
53 // Description of a hull. 53 // Description of a hull.
54 // Meshes and hulls have the same shape hash key but we only need hulls for efficient physical objects 54 // Meshes and hulls have the same shape hash key but we only need hulls for efficient collision calculations.
55 private struct HullDesc 55 private struct HullDesc
56 { 56 {
57 public IntPtr ptr; 57 public IntPtr ptr;
@@ -59,17 +59,9 @@ public class BSShapeCollection : IDisposable
59 public DateTime lastReferenced; 59 public DateTime lastReferenced;
60 } 60 }
61 61
62 private struct BodyDesc 62 // The sharable set of meshes and hulls. Indexed by their shape hash.
63 { 63 private Dictionary<System.UInt64, MeshDesc> Meshes = new Dictionary<System.UInt64, MeshDesc>();
64 public IntPtr ptr; 64 private Dictionary<System.UInt64, HullDesc> Hulls = new Dictionary<System.UInt64, HullDesc>();
65 // Bodies are only used once so reference count is always either one or zero
66 public int referenceCount;
67 public DateTime lastReferenced;
68 }
69
70 private Dictionary<ulong, MeshDesc> Meshes = new Dictionary<ulong, MeshDesc>();
71 private Dictionary<ulong, HullDesc> Hulls = new Dictionary<ulong, HullDesc>();
72 private Dictionary<uint, BodyDesc> Bodies = new Dictionary<uint, BodyDesc>();
73 65
74 public BSShapeCollection(BSScene physScene) 66 public BSShapeCollection(BSScene physScene)
75 { 67 {
@@ -92,8 +84,12 @@ public class BSShapeCollection : IDisposable
92 // First checks the shape and updates that if necessary then makes 84 // First checks the shape and updates that if necessary then makes
93 // sure the body is of the right type. 85 // sure the body is of the right type.
94 // Return 'true' if either the body or the shape changed. 86 // Return 'true' if either the body or the shape changed.
87 // 'shapeCallback' and 'bodyCallback' are, if non-null, functions called just before
88 // the current shape or body is destroyed. This allows the caller to remove any
89 // higher level dependencies on the shape or body. Mostly used for LinkSets to
90 // remove the physical constraints before the body is destroyed.
95 // Called at taint-time!! 91 // Called at taint-time!!
96 public bool GetBodyAndShape(bool forceRebuild, BulletSim sim, BSPrim prim, 92 public bool GetBodyAndShape(bool forceRebuild, BulletSim sim, BSPhysObject prim,
97 ShapeData shapeData, PrimitiveBaseShape pbs, 93 ShapeData shapeData, PrimitiveBaseShape pbs,
98 ShapeDestructionCallback shapeCallback, BodyDestructionCallback bodyCallback) 94 ShapeDestructionCallback shapeCallback, BodyDestructionCallback bodyCallback)
99 { 95 {
@@ -103,7 +99,8 @@ public class BSShapeCollection : IDisposable
103 lock (m_collectionActivityLock) 99 lock (m_collectionActivityLock)
104 { 100 {
105 // Do we have the correct geometry for this type of object? 101 // Do we have the correct geometry for this type of object?
106 // Updates prim.BSShape with information/pointers to requested shape 102 // Updates prim.BSShape with information/pointers to shape.
103 // CreateGeom returns 'true' of BSShape as changed to a new shape.
107 bool newGeom = CreateGeom(forceRebuild, prim, shapeData, pbs, shapeCallback); 104 bool newGeom = CreateGeom(forceRebuild, prim, shapeData, pbs, shapeCallback);
108 // If we had to select a new shape geometry for the object, 105 // If we had to select a new shape geometry for the object,
109 // rebuild the body around it. 106 // rebuild the body around it.
@@ -120,26 +117,24 @@ public class BSShapeCollection : IDisposable
120 117
121 // Track another user of a body 118 // Track another user of a body
122 // We presume the caller has allocated the body. 119 // We presume the caller has allocated the body.
123 // Bodies only have one user so the reference count is either 1 or 0. 120 // Bodies only have one user so the body is just put into the world if not already there.
124 public void ReferenceBody(BulletBody body, bool atTaintTime) 121 public void ReferenceBody(BulletBody body, bool inTaintTime)
125 { 122 {
126 lock (m_collectionActivityLock) 123 lock (m_collectionActivityLock)
127 { 124 {
128 BodyDesc bodyDesc; 125 DetailLog("{0},BSShapeCollection.ReferenceBody,newBody", body.ID, body);
129 if (Bodies.TryGetValue(body.ID, out bodyDesc)) 126 BSScene.TaintCallback createOperation = delegate()
130 { 127 {
131 bodyDesc.referenceCount++; 128 if (!BulletSimAPI.IsInWorld2(body.ptr))
132 DetailLog("{0},BSShapeCollection.ReferenceBody,existingBody,body={1},ref={2}", body.ID, body, bodyDesc.referenceCount); 129 {
133 } 130 BulletSimAPI.AddObjectToWorld2(PhysicsScene.World.ptr, body.ptr);
131 DetailLog("{0},BSShapeCollection.ReferenceBody,addedToWorld,ref={1}", body.ID, body);
132 }
133 };
134 if (inTaintTime)
135 createOperation();
134 else 136 else
135 { 137 PhysicsScene.TaintedObject("BSShapeCollection.ReferenceBody", createOperation);
136 // New entry
137 bodyDesc.ptr = body.ptr;
138 bodyDesc.referenceCount = 1;
139 DetailLog("{0},BSShapeCollection.ReferenceBody,newBody,ref={1}", body.ID, body, bodyDesc.referenceCount);
140 }
141 bodyDesc.lastReferenced = System.DateTime.Now;
142 Bodies[body.ID] = bodyDesc;
143 } 138 }
144 } 139 }
145 140
@@ -152,42 +147,25 @@ public class BSShapeCollection : IDisposable
152 147
153 lock (m_collectionActivityLock) 148 lock (m_collectionActivityLock)
154 { 149 {
155 BodyDesc bodyDesc; 150 BSScene.TaintCallback removeOperation = delegate()
156 if (Bodies.TryGetValue(body.ID, out bodyDesc))
157 { 151 {
158 bodyDesc.referenceCount--; 152 DetailLog("{0},BSShapeCollection.DereferenceBody,DestroyingBody. ptr={1}, inTaintTime={2}",
159 bodyDesc.lastReferenced = System.DateTime.Now; 153 body.ID, body.ptr.ToString("X"), inTaintTime);
160 Bodies[body.ID] = bodyDesc; 154 // If the caller needs to know the old body is going away, pass the event up.
161 DetailLog("{0},BSShapeCollection.DereferenceBody,ref={1}", body.ID, bodyDesc.referenceCount); 155 if (bodyCallback != null) bodyCallback(body);
162 156
163 // If body is no longer being used, free it -- bodies are never shared. 157 // It may have already been removed from the world in which case the next is a NOOP.
164 if (bodyDesc.referenceCount == 0) 158 BulletSimAPI.RemoveObjectFromWorld2(PhysicsScene.World.ptr, body.ptr);
165 { 159
166 Bodies.Remove(body.ID); 160 // Zero any reference to the shape so it is not freed when the body is deleted.
167 BSScene.TaintCallback removeOperation = delegate() 161 BulletSimAPI.SetCollisionShape2(PhysicsScene.World.ptr, body.ptr, IntPtr.Zero);
168 { 162 BulletSimAPI.DestroyObject2(PhysicsScene.World.ptr, body.ptr);
169 DetailLog("{0},BSShapeCollection.DereferenceBody,DestroyingBody. ptr={1}", 163 };
170 body.ID, body.ptr.ToString("X")); 164 // If already in taint-time, do the operations now. Otherwise queue for later.
171 // If the caller needs to know the old body is going away, pass the event up. 165 if (inTaintTime)
172 if (bodyCallback != null) bodyCallback(body); 166 removeOperation();
173
174 // Zero any reference to the shape so it is not freed when the body is deleted.
175 BulletSimAPI.SetCollisionShape2(PhysicsScene.World.ptr, body.ptr, IntPtr.Zero);
176 // It may have already been removed from the world in which case the next is a NOOP.
177 BulletSimAPI.RemoveObjectFromWorld2(PhysicsScene.World.ptr, body.ptr);
178 BulletSimAPI.DestroyObject2(PhysicsScene.World.ptr, body.ptr);
179 };
180 // If already in taint-time, do the operations now. Otherwise queue for later.
181 if (inTaintTime)
182 removeOperation();
183 else
184 PhysicsScene.TaintedObject("BSShapeCollection.DereferenceBody", removeOperation);
185 }
186 }
187 else 167 else
188 { 168 PhysicsScene.TaintedObject("BSShapeCollection.DereferenceBody", removeOperation);
189 DetailLog("{0},BSShapeCollection.DereferenceBody,DID NOT FIND BODY", body.ID, bodyDesc.referenceCount);
190 }
191 } 169 }
192 } 170 }
193 171
@@ -208,7 +186,7 @@ public class BSShapeCollection : IDisposable
208 { 186 {
209 // There is an existing instance of this mesh. 187 // There is an existing instance of this mesh.
210 meshDesc.referenceCount++; 188 meshDesc.referenceCount++;
211 DetailLog("{0},BSShapeColliction.ReferenceShape,existingMesh,key={1},cnt={2}", 189 DetailLog("{0},BSShapeCollection.ReferenceShape,existingMesh,key={1},cnt={2}",
212 BSScene.DetailLogZero, shape.shapeKey.ToString("X"), meshDesc.referenceCount); 190 BSScene.DetailLogZero, shape.shapeKey.ToString("X"), meshDesc.referenceCount);
213 } 191 }
214 else 192 else
@@ -217,7 +195,7 @@ public class BSShapeCollection : IDisposable
217 meshDesc.ptr = shape.ptr; 195 meshDesc.ptr = shape.ptr;
218 // We keep a reference to the underlying IMesh data so a hull can be built 196 // We keep a reference to the underlying IMesh data so a hull can be built
219 meshDesc.referenceCount = 1; 197 meshDesc.referenceCount = 1;
220 DetailLog("{0},BSShapeColliction.ReferenceShape,newMesh,key={1},cnt={2}", 198 DetailLog("{0},BSShapeCollection.ReferenceShape,newMesh,key={1},cnt={2}",
221 BSScene.DetailLogZero, shape.shapeKey.ToString("X"), meshDesc.referenceCount); 199 BSScene.DetailLogZero, shape.shapeKey.ToString("X"), meshDesc.referenceCount);
222 ret = true; 200 ret = true;
223 } 201 }
@@ -230,7 +208,7 @@ public class BSShapeCollection : IDisposable
230 { 208 {
231 // There is an existing instance of this hull. 209 // There is an existing instance of this hull.
232 hullDesc.referenceCount++; 210 hullDesc.referenceCount++;
233 DetailLog("{0},BSShapeColliction.ReferenceShape,existingHull,key={1},cnt={2}", 211 DetailLog("{0},BSShapeCollection.ReferenceShape,existingHull,key={1},cnt={2}",
234 BSScene.DetailLogZero, shape.shapeKey.ToString("X"), hullDesc.referenceCount); 212 BSScene.DetailLogZero, shape.shapeKey.ToString("X"), hullDesc.referenceCount);
235 } 213 }
236 else 214 else
@@ -238,7 +216,7 @@ public class BSShapeCollection : IDisposable
238 // This is a new reference to a hull 216 // This is a new reference to a hull
239 hullDesc.ptr = shape.ptr; 217 hullDesc.ptr = shape.ptr;
240 hullDesc.referenceCount = 1; 218 hullDesc.referenceCount = 1;
241 DetailLog("{0},BSShapeColliction.ReferenceShape,newHull,key={1},cnt={2}", 219 DetailLog("{0},BSShapeCollection.ReferenceShape,newHull,key={1},cnt={2}",
242 BSScene.DetailLogZero, shape.shapeKey.ToString("X"), hullDesc.referenceCount); 220 BSScene.DetailLogZero, shape.shapeKey.ToString("X"), hullDesc.referenceCount);
243 ret = true; 221 ret = true;
244 222
@@ -256,37 +234,42 @@ public class BSShapeCollection : IDisposable
256 } 234 }
257 235
258 // Release the usage of a shape. 236 // Release the usage of a shape.
259 // The collisionObject is released since it is a copy of the real collision shape. 237 public void DereferenceShape(BulletShape shape, bool inTaintTime, ShapeDestructionCallback shapeCallback)
260 public void DereferenceShape(BulletShape shape, bool atTaintTime, ShapeDestructionCallback shapeCallback)
261 { 238 {
262 if (shape.ptr == IntPtr.Zero) 239 if (shape.ptr == IntPtr.Zero)
263 return; 240 return;
264 241
265 BSScene.TaintCallback dereferenceOperation = delegate() 242 BSScene.TaintCallback dereferenceOperation = delegate()
266 { 243 {
267 switch (shape.type) 244 if (shape.ptr != IntPtr.Zero)
268 { 245 {
269 case ShapeData.PhysicsShapeType.SHAPE_HULL: 246 if (shape.isNativeShape)
270 DereferenceHull(shape, shapeCallback); 247 {
271 break;
272 case ShapeData.PhysicsShapeType.SHAPE_MESH:
273 DereferenceMesh(shape, shapeCallback);
274 break;
275 case ShapeData.PhysicsShapeType.SHAPE_UNKNOWN:
276 break;
277 default:
278 // Native shapes are not tracked and are released immediately 248 // Native shapes are not tracked and are released immediately
279 if (shape.ptr != IntPtr.Zero & shape.isNativeShape) 249 DetailLog("{0},BSShapeCollection.DereferenceShape,deleteNativeShape,ptr={1},taintTime={2}",
250 BSScene.DetailLogZero, shape.ptr.ToString("X"), inTaintTime);
251 if (shapeCallback != null) shapeCallback(shape);
252 BulletSimAPI.DeleteCollisionShape2(PhysicsScene.World.ptr, shape.ptr);
253 }
254 else
255 {
256 switch (shape.type)
280 { 257 {
281 DetailLog("{0},BSShapeCollection.DereferenceShape,deleteNativeShape,ptr={1},taintTime={2}", 258 case ShapeData.PhysicsShapeType.SHAPE_HULL:
282 BSScene.DetailLogZero, shape.ptr.ToString("X"), atTaintTime); 259 DereferenceHull(shape, shapeCallback);
283 if (shapeCallback != null) shapeCallback(shape); 260 break;
284 BulletSimAPI.DeleteCollisionShape2(PhysicsScene.World.ptr, shape.ptr); 261 case ShapeData.PhysicsShapeType.SHAPE_MESH:
262 DereferenceMesh(shape, shapeCallback);
263 break;
264 case ShapeData.PhysicsShapeType.SHAPE_UNKNOWN:
265 break;
266 default:
267 break;
285 } 268 }
286 break; 269 }
287 } 270 }
288 }; 271 };
289 if (atTaintTime) 272 if (inTaintTime)
290 { 273 {
291 lock (m_collectionActivityLock) 274 lock (m_collectionActivityLock)
292 { 275 {
@@ -336,19 +319,31 @@ public class BSShapeCollection : IDisposable
336 319
337 // Create the geometry information in Bullet for later use. 320 // Create the geometry information in Bullet for later use.
338 // The objects needs a hull if it's physical otherwise a mesh is enough. 321 // The objects needs a hull if it's physical otherwise a mesh is enough.
339 // No locking here because this is done when we know physics is not simulating. 322 // if 'forceRebuild' is true, the geometry is unconditionally rebuilt. For meshes and hulls,
340 // if 'forceRebuild' is true, the geometry is rebuilt. Otherwise a previously built version is used. 323 // shared geometries will be used. If the parameters of the existing shape are the same
324 // as this request, the shape is not rebuilt.
325 // Info in prim.BSShape is updated to the new shape.
341 // Returns 'true' if the geometry was rebuilt. 326 // Returns 'true' if the geometry was rebuilt.
342 // Called at taint-time! 327 // Called at taint-time!
343 private bool CreateGeom(bool forceRebuild, BSPrim prim, ShapeData shapeData, 328 private bool CreateGeom(bool forceRebuild, BSPhysObject prim, ShapeData shapeData,
344 PrimitiveBaseShape pbs, ShapeDestructionCallback shapeCallback) 329 PrimitiveBaseShape pbs, ShapeDestructionCallback shapeCallback)
345 { 330 {
346 bool ret = false; 331 bool ret = false;
347 bool haveShape = false; 332 bool haveShape = false;
348 bool nativeShapePossible = true; 333 bool nativeShapePossible = true;
349 334
335 if (shapeData.Type == ShapeData.PhysicsShapeType.SHAPE_AVATAR)
336 {
337 // an avatar capsule is close to a native shape (it is not shared)
338 ret = GetReferenceToNativeShape(prim, shapeData, ShapeData.PhysicsShapeType.SHAPE_AVATAR,
339 ShapeData.FixedShapeKey.KEY_CAPSULE, shapeCallback);
340 DetailLog("{0},BSShapeCollection.CreateGeom,avatarCapsule,shape={1}", prim.LocalID, prim.BSShape);
341 haveShape = true;
342 }
350 // If the prim attributes are simple, this could be a simple Bullet native shape 343 // If the prim attributes are simple, this could be a simple Bullet native shape
351 if (nativeShapePossible 344 if (!haveShape
345 && pbs != null
346 && nativeShapePossible
352 && ((pbs.SculptEntry && !PhysicsScene.ShouldMeshSculptedPrim) 347 && ((pbs.SculptEntry && !PhysicsScene.ShouldMeshSculptedPrim)
353 || (pbs.ProfileBegin == 0 && pbs.ProfileEnd == 0 348 || (pbs.ProfileBegin == 0 && pbs.ProfileEnd == 0
354 && pbs.ProfileHollow == 0 349 && pbs.ProfileHollow == 0
@@ -358,7 +353,8 @@ public class BSShapeCollection : IDisposable
358 && pbs.PathScaleX == 100 && pbs.PathScaleY == 100 353 && pbs.PathScaleX == 100 && pbs.PathScaleY == 100
359 && pbs.PathShearX == 0 && pbs.PathShearY == 0) ) ) 354 && pbs.PathShearX == 0 && pbs.PathShearY == 0) ) )
360 { 355 {
361 if (pbs.ProfileShape == ProfileShape.HalfCircle && pbs.PathCurve == (byte)Extrusion.Curve1) 356 if ((pbs.ProfileShape == ProfileShape.HalfCircle && pbs.PathCurve == (byte)Extrusion.Curve1)
357 && pbs.Scale.X == pbs.Scale.Y && pbs.Scale.Y == pbs.Scale.Z)
362 { 358 {
363 haveShape = true; 359 haveShape = true;
364 if (forceRebuild 360 if (forceRebuild
@@ -372,7 +368,7 @@ public class BSShapeCollection : IDisposable
372 prim.LocalID, forceRebuild, prim.BSShape); 368 prim.LocalID, forceRebuild, prim.BSShape);
373 } 369 }
374 } 370 }
375 else 371 if (pbs.ProfileShape == ProfileShape.Square && pbs.PathCurve == (byte)Extrusion.Straight)
376 { 372 {
377 haveShape = true; 373 haveShape = true;
378 if (forceRebuild 374 if (forceRebuild
@@ -390,9 +386,9 @@ public class BSShapeCollection : IDisposable
390 // If a simple shape is not happening, create a mesh and possibly a hull. 386 // If a simple shape is not happening, create a mesh and possibly a hull.
391 // Note that if it's a native shape, the check for physical/non-physical is not 387 // Note that if it's a native shape, the check for physical/non-physical is not
392 // made. Native shapes are best used in either case. 388 // made. Native shapes are best used in either case.
393 if (!haveShape) 389 if (!haveShape && pbs != null)
394 { 390 {
395 if (prim.IsPhysical) 391 if (prim.IsPhysical && PhysicsScene.ShouldUseHullsForPhysicalObjects)
396 { 392 {
397 // Update prim.BSShape to reference a hull of this shape. 393 // Update prim.BSShape to reference a hull of this shape.
398 ret = GetReferenceToHull(prim, shapeData, pbs, shapeCallback); 394 ret = GetReferenceToHull(prim, shapeData, pbs, shapeCallback);
@@ -409,12 +405,12 @@ public class BSShapeCollection : IDisposable
409 return ret; 405 return ret;
410 } 406 }
411 407
412 // Creates a native shape and assignes it to prim.BSShape 408 // Creates a native shape and assignes it to prim.BSShape.
413 private bool GetReferenceToNativeShape( BSPrim prim, ShapeData shapeData, 409 // "Native" shapes are never shared. they are created here and destroyed in DereferenceShape().
410 private bool GetReferenceToNativeShape(BSPhysObject prim, ShapeData shapeData,
414 ShapeData.PhysicsShapeType shapeType, ShapeData.FixedShapeKey shapeKey, 411 ShapeData.PhysicsShapeType shapeType, ShapeData.FixedShapeKey shapeKey,
415 ShapeDestructionCallback shapeCallback) 412 ShapeDestructionCallback shapeCallback)
416 { 413 {
417 BulletShape newShape;
418 414
419 shapeData.Type = shapeType; 415 shapeData.Type = shapeType;
420 // Bullet native objects are scaled by the Bullet engine so pass the size in 416 // Bullet native objects are scaled by the Bullet engine so pass the size in
@@ -424,29 +420,48 @@ public class BSShapeCollection : IDisposable
424 // release any previous shape 420 // release any previous shape
425 DereferenceShape(prim.BSShape, true, shapeCallback); 421 DereferenceShape(prim.BSShape, true, shapeCallback);
426 422
427 // Native shapes are always built independently. 423 BulletShape newShape = BuildPhysicalNativeShape(shapeType, shapeData, shapeKey);
428 newShape = new BulletShape(BulletSimAPI.BuildNativeShape2(PhysicsScene.World.ptr, shapeData), shapeType);
429 newShape.shapeKey = (ulong)shapeKey;
430 newShape.isNativeShape = true;
431 424
432 // Don't need to do a 'ReferenceShape()' here because native shapes are not tracked. 425 // Don't need to do a 'ReferenceShape()' here because native shapes are not shared.
433 // DetailLog("{0},BSShapeCollection.AddNativeShapeToPrim,create,newshape={1}", shapeData.ID, newShape); 426 DetailLog("{0},BSShapeCollection.AddNativeShapeToPrim,create,newshape={1},scale={2}",
427 shapeData.ID, newShape, shapeData.Scale);
434 428
435 prim.BSShape = newShape; 429 prim.BSShape = newShape;
436 return true; 430 return true;
437 } 431 }
438 432
433 private BulletShape BuildPhysicalNativeShape(ShapeData.PhysicsShapeType shapeType,
434 ShapeData shapeData, ShapeData.FixedShapeKey shapeKey)
435 {
436 BulletShape newShape;
437
438 if (shapeType == ShapeData.PhysicsShapeType.SHAPE_AVATAR)
439 {
440 newShape = new BulletShape(
441 BulletSimAPI.BuildCapsuleShape2(PhysicsScene.World.ptr, 1.0f, 1.0f, shapeData.Scale),
442 shapeType);
443 }
444 else
445 {
446 newShape = new BulletShape(BulletSimAPI.BuildNativeShape2(PhysicsScene.World.ptr, shapeData), shapeType);
447 }
448 newShape.shapeKey = (System.UInt64)shapeKey;
449 newShape.isNativeShape = true;
450
451 return newShape;
452 }
453
439 // Builds a mesh shape in the physical world and updates prim.BSShape. 454 // Builds a mesh shape in the physical world and updates prim.BSShape.
440 // Dereferences previous shape in BSShape and adds a reference for this new shape. 455 // Dereferences previous shape in BSShape and adds a reference for this new shape.
441 // Returns 'true' of a mesh was actually built. Otherwise . 456 // Returns 'true' of a mesh was actually built. Otherwise .
442 // Called at taint-time! 457 // Called at taint-time!
443 private bool GetReferenceToMesh(BSPrim prim, ShapeData shapeData, PrimitiveBaseShape pbs, 458 private bool GetReferenceToMesh(BSPhysObject prim, ShapeData shapeData, PrimitiveBaseShape pbs,
444 ShapeDestructionCallback shapeCallback) 459 ShapeDestructionCallback shapeCallback)
445 { 460 {
446 BulletShape newShape = new BulletShape(IntPtr.Zero); 461 BulletShape newShape = new BulletShape(IntPtr.Zero);
447 462
448 float lod; 463 float lod;
449 ulong newMeshKey = ComputeShapeKey(shapeData, pbs, out lod); 464 System.UInt64 newMeshKey = ComputeShapeKey(shapeData, pbs, out lod);
450 465
451 // if this new shape is the same as last time, don't recreate the mesh 466 // if this new shape is the same as last time, don't recreate the mesh
452 if (newMeshKey == prim.BSShape.shapeKey && prim.BSShape.type == ShapeData.PhysicsShapeType.SHAPE_MESH) 467 if (newMeshKey == prim.BSShape.shapeKey && prim.BSShape.type == ShapeData.PhysicsShapeType.SHAPE_MESH)
@@ -459,6 +474,8 @@ public class BSShapeCollection : IDisposable
459 DereferenceShape(prim.BSShape, true, shapeCallback); 474 DereferenceShape(prim.BSShape, true, shapeCallback);
460 475
461 newShape = CreatePhysicalMesh(prim.PhysObjectName, newMeshKey, pbs, shapeData.Size, lod); 476 newShape = CreatePhysicalMesh(prim.PhysObjectName, newMeshKey, pbs, shapeData.Size, lod);
477 // Take evasive action if the mesh was not constructed.
478 newShape = VerifyMeshCreated(newShape, prim, shapeData, pbs);
462 479
463 ReferenceShape(newShape); 480 ReferenceShape(newShape);
464 481
@@ -469,10 +486,10 @@ public class BSShapeCollection : IDisposable
469 return true; // 'true' means a new shape has been added to this prim 486 return true; // 'true' means a new shape has been added to this prim
470 } 487 }
471 488
472 private BulletShape CreatePhysicalMesh(string objName, ulong newMeshKey, PrimitiveBaseShape pbs, OMV.Vector3 size, float lod) 489 private BulletShape CreatePhysicalMesh(string objName, System.UInt64 newMeshKey, PrimitiveBaseShape pbs, OMV.Vector3 size, float lod)
473 { 490 {
474 IMesh meshData = null; 491 IMesh meshData = null;
475 IntPtr meshPtr; 492 IntPtr meshPtr = IntPtr.Zero;
476 MeshDesc meshDesc; 493 MeshDesc meshDesc;
477 if (Meshes.TryGetValue(newMeshKey, out meshDesc)) 494 if (Meshes.TryGetValue(newMeshKey, out meshDesc))
478 { 495 {
@@ -484,23 +501,26 @@ public class BSShapeCollection : IDisposable
484 // Pass false for physicalness as this creates some sort of bounding box which we don't need 501 // Pass false for physicalness as this creates some sort of bounding box which we don't need
485 meshData = PhysicsScene.mesher.CreateMesh(objName, pbs, size, lod, false); 502 meshData = PhysicsScene.mesher.CreateMesh(objName, pbs, size, lod, false);
486 503
487 int[] indices = meshData.getIndexListAsInt(); 504 if (meshData != null)
488 List<OMV.Vector3> vertices = meshData.getVertexList();
489
490 float[] verticesAsFloats = new float[vertices.Count * 3];
491 int vi = 0;
492 foreach (OMV.Vector3 vv in vertices)
493 { 505 {
494 verticesAsFloats[vi++] = vv.X; 506 int[] indices = meshData.getIndexListAsInt();
495 verticesAsFloats[vi++] = vv.Y; 507 List<OMV.Vector3> vertices = meshData.getVertexList();
496 verticesAsFloats[vi++] = vv.Z; 508
497 } 509 float[] verticesAsFloats = new float[vertices.Count * 3];
510 int vi = 0;
511 foreach (OMV.Vector3 vv in vertices)
512 {
513 verticesAsFloats[vi++] = vv.X;
514 verticesAsFloats[vi++] = vv.Y;
515 verticesAsFloats[vi++] = vv.Z;
516 }
498 517
499 // m_log.DebugFormat("{0}: CreateGeomMesh: calling CreateMesh. lid={1}, key={2}, indices={3}, vertices={4}", 518 // m_log.DebugFormat("{0}: CreateGeomMesh: calling CreateMesh. lid={1}, key={2}, indices={3}, vertices={4}",
500 // LogHeader, prim.LocalID, newMeshKey, indices.Length, vertices.Count); 519 // LogHeader, prim.LocalID, newMeshKey, indices.Length, vertices.Count);
501 520
502 meshPtr = BulletSimAPI.CreateMeshShape2(PhysicsScene.World.ptr, 521 meshPtr = BulletSimAPI.CreateMeshShape2(PhysicsScene.World.ptr,
503 indices.GetLength(0), indices, vertices.Count, verticesAsFloats); 522 indices.GetLength(0), indices, vertices.Count, verticesAsFloats);
523 }
504 } 524 }
505 BulletShape newShape = new BulletShape(meshPtr, ShapeData.PhysicsShapeType.SHAPE_MESH); 525 BulletShape newShape = new BulletShape(meshPtr, ShapeData.PhysicsShapeType.SHAPE_MESH);
506 newShape.shapeKey = newMeshKey; 526 newShape.shapeKey = newMeshKey;
@@ -510,13 +530,13 @@ public class BSShapeCollection : IDisposable
510 530
511 // See that hull shape exists in the physical world and update prim.BSShape. 531 // See that hull shape exists in the physical world and update prim.BSShape.
512 // We could be creating the hull because scale changed or whatever. 532 // We could be creating the hull because scale changed or whatever.
513 private bool GetReferenceToHull(BSPrim prim, ShapeData shapeData, PrimitiveBaseShape pbs, 533 private bool GetReferenceToHull(BSPhysObject prim, ShapeData shapeData, PrimitiveBaseShape pbs,
514 ShapeDestructionCallback shapeCallback) 534 ShapeDestructionCallback shapeCallback)
515 { 535 {
516 BulletShape newShape; 536 BulletShape newShape;
517 537
518 float lod; 538 float lod;
519 ulong newHullKey = ComputeShapeKey(shapeData, pbs, out lod); 539 System.UInt64 newHullKey = ComputeShapeKey(shapeData, pbs, out lod);
520 540
521 // if the hull hasn't changed, don't rebuild it 541 // if the hull hasn't changed, don't rebuild it
522 if (newHullKey == prim.BSShape.shapeKey && prim.BSShape.type == ShapeData.PhysicsShapeType.SHAPE_HULL) 542 if (newHullKey == prim.BSShape.shapeKey && prim.BSShape.type == ShapeData.PhysicsShapeType.SHAPE_HULL)
@@ -525,10 +545,11 @@ public class BSShapeCollection : IDisposable
525 DetailLog("{0},BSShapeCollection.CreateGeomHull,create,oldKey={1},newKey={2}", 545 DetailLog("{0},BSShapeCollection.CreateGeomHull,create,oldKey={1},newKey={2}",
526 prim.LocalID, prim.BSShape.shapeKey.ToString("X"), newHullKey.ToString("X")); 546 prim.LocalID, prim.BSShape.shapeKey.ToString("X"), newHullKey.ToString("X"));
527 547
528 // Remove usage of the previous shape. Also removes reference to underlying mesh if it is a hull. 548 // Remove usage of the previous shape.
529 DereferenceShape(prim.BSShape, true, shapeCallback); 549 DereferenceShape(prim.BSShape, true, shapeCallback);
530 550
531 newShape = CreatePhysicalHull(prim.PhysObjectName, newHullKey, pbs, shapeData.Size, lod); 551 newShape = CreatePhysicalHull(prim.PhysObjectName, newHullKey, pbs, shapeData.Size, lod);
552 newShape = VerifyMeshCreated(newShape, prim, shapeData, pbs);
532 553
533 ReferenceShape(newShape); 554 ReferenceShape(newShape);
534 555
@@ -539,10 +560,10 @@ public class BSShapeCollection : IDisposable
539 } 560 }
540 561
541 List<ConvexResult> m_hulls; 562 List<ConvexResult> m_hulls;
542 private BulletShape CreatePhysicalHull(string objName, ulong newHullKey, PrimitiveBaseShape pbs, OMV.Vector3 size, float lod) 563 private BulletShape CreatePhysicalHull(string objName, System.UInt64 newHullKey, PrimitiveBaseShape pbs, OMV.Vector3 size, float lod)
543 { 564 {
544 565
545 IntPtr hullPtr; 566 IntPtr hullPtr = IntPtr.Zero;
546 HullDesc hullDesc; 567 HullDesc hullDesc;
547 if (Hulls.TryGetValue(newHullKey, out hullDesc)) 568 if (Hulls.TryGetValue(newHullKey, out hullDesc))
548 { 569 {
@@ -554,86 +575,89 @@ public class BSShapeCollection : IDisposable
554 // Build a new hull in the physical world 575 // Build a new hull in the physical world
555 // Pass false for physicalness as this creates some sort of bounding box which we don't need 576 // Pass false for physicalness as this creates some sort of bounding box which we don't need
556 IMesh meshData = PhysicsScene.mesher.CreateMesh(objName, pbs, size, lod, false); 577 IMesh meshData = PhysicsScene.mesher.CreateMesh(objName, pbs, size, lod, false);
557 578 if (meshData != null)
558 int[] indices = meshData.getIndexListAsInt();
559 List<OMV.Vector3> vertices = meshData.getVertexList();
560
561 //format conversion from IMesh format to DecompDesc format
562 List<int> convIndices = new List<int>();
563 List<float3> convVertices = new List<float3>();
564 for (int ii = 0; ii < indices.GetLength(0); ii++)
565 { 579 {
566 convIndices.Add(indices[ii]);
567 }
568 foreach (OMV.Vector3 vv in vertices)
569 {
570 convVertices.Add(new float3(vv.X, vv.Y, vv.Z));
571 }
572 580
573 // setup and do convex hull conversion 581 int[] indices = meshData.getIndexListAsInt();
574 m_hulls = new List<ConvexResult>(); 582 List<OMV.Vector3> vertices = meshData.getVertexList();
575 DecompDesc dcomp = new DecompDesc();
576 dcomp.mIndices = convIndices;
577 dcomp.mVertices = convVertices;
578 ConvexBuilder convexBuilder = new ConvexBuilder(HullReturn);
579 // create the hull into the _hulls variable
580 convexBuilder.process(dcomp);
581
582 // Convert the vertices and indices for passing to unmanaged.
583 // The hull information is passed as a large floating point array.
584 // The format is:
585 // convHulls[0] = number of hulls
586 // convHulls[1] = number of vertices in first hull
587 // convHulls[2] = hull centroid X coordinate
588 // convHulls[3] = hull centroid Y coordinate
589 // convHulls[4] = hull centroid Z coordinate
590 // convHulls[5] = first hull vertex X
591 // convHulls[6] = first hull vertex Y
592 // convHulls[7] = first hull vertex Z
593 // convHulls[8] = second hull vertex X
594 // ...
595 // convHulls[n] = number of vertices in second hull
596 // convHulls[n+1] = second hull centroid X coordinate
597 // ...
598 //
599 // TODO: is is very inefficient. Someday change the convex hull generator to return
600 // data structures that do not need to be converted in order to pass to Bullet.
601 // And maybe put the values directly into pinned memory rather than marshaling.
602 int hullCount = m_hulls.Count;
603 int totalVertices = 1; // include one for the count of the hulls
604 foreach (ConvexResult cr in m_hulls)
605 {
606 totalVertices += 4; // add four for the vertex count and centroid
607 totalVertices += cr.HullIndices.Count * 3; // we pass just triangles
608 }
609 float[] convHulls = new float[totalVertices];
610 583
611 convHulls[0] = (float)hullCount; 584 //format conversion from IMesh format to DecompDesc format
612 int jj = 1; 585 List<int> convIndices = new List<int>();
613 foreach (ConvexResult cr in m_hulls) 586 List<float3> convVertices = new List<float3>();
614 { 587 for (int ii = 0; ii < indices.GetLength(0); ii++)
615 // copy vertices for index access 588 {
616 float3[] verts = new float3[cr.HullVertices.Count]; 589 convIndices.Add(indices[ii]);
617 int kk = 0; 590 }
618 foreach (float3 ff in cr.HullVertices) 591 foreach (OMV.Vector3 vv in vertices)
592 {
593 convVertices.Add(new float3(vv.X, vv.Y, vv.Z));
594 }
595
596 // setup and do convex hull conversion
597 m_hulls = new List<ConvexResult>();
598 DecompDesc dcomp = new DecompDesc();
599 dcomp.mIndices = convIndices;
600 dcomp.mVertices = convVertices;
601 ConvexBuilder convexBuilder = new ConvexBuilder(HullReturn);
602 // create the hull into the _hulls variable
603 convexBuilder.process(dcomp);
604
605 // Convert the vertices and indices for passing to unmanaged.
606 // The hull information is passed as a large floating point array.
607 // The format is:
608 // convHulls[0] = number of hulls
609 // convHulls[1] = number of vertices in first hull
610 // convHulls[2] = hull centroid X coordinate
611 // convHulls[3] = hull centroid Y coordinate
612 // convHulls[4] = hull centroid Z coordinate
613 // convHulls[5] = first hull vertex X
614 // convHulls[6] = first hull vertex Y
615 // convHulls[7] = first hull vertex Z
616 // convHulls[8] = second hull vertex X
617 // ...
618 // convHulls[n] = number of vertices in second hull
619 // convHulls[n+1] = second hull centroid X coordinate
620 // ...
621 //
622 // TODO: is is very inefficient. Someday change the convex hull generator to return
623 // data structures that do not need to be converted in order to pass to Bullet.
624 // And maybe put the values directly into pinned memory rather than marshaling.
625 int hullCount = m_hulls.Count;
626 int totalVertices = 1; // include one for the count of the hulls
627 foreach (ConvexResult cr in m_hulls)
619 { 628 {
620 verts[kk++] = ff; 629 totalVertices += 4; // add four for the vertex count and centroid
630 totalVertices += cr.HullIndices.Count * 3; // we pass just triangles
621 } 631 }
632 float[] convHulls = new float[totalVertices];
622 633
623 // add to the array one hull's worth of data 634 convHulls[0] = (float)hullCount;
624 convHulls[jj++] = cr.HullIndices.Count; 635 int jj = 1;
625 convHulls[jj++] = 0f; // centroid x,y,z 636 foreach (ConvexResult cr in m_hulls)
626 convHulls[jj++] = 0f;
627 convHulls[jj++] = 0f;
628 foreach (int ind in cr.HullIndices)
629 { 637 {
630 convHulls[jj++] = verts[ind].x; 638 // copy vertices for index access
631 convHulls[jj++] = verts[ind].y; 639 float3[] verts = new float3[cr.HullVertices.Count];
632 convHulls[jj++] = verts[ind].z; 640 int kk = 0;
641 foreach (float3 ff in cr.HullVertices)
642 {
643 verts[kk++] = ff;
644 }
645
646 // add to the array one hull's worth of data
647 convHulls[jj++] = cr.HullIndices.Count;
648 convHulls[jj++] = 0f; // centroid x,y,z
649 convHulls[jj++] = 0f;
650 convHulls[jj++] = 0f;
651 foreach (int ind in cr.HullIndices)
652 {
653 convHulls[jj++] = verts[ind].x;
654 convHulls[jj++] = verts[ind].y;
655 convHulls[jj++] = verts[ind].z;
656 }
633 } 657 }
658 // create the hull data structure in Bullet
659 hullPtr = BulletSimAPI.CreateHullShape2(PhysicsScene.World.ptr, hullCount, convHulls);
634 } 660 }
635 // create the hull data structure in Bullet
636 hullPtr = BulletSimAPI.CreateHullShape2(PhysicsScene.World.ptr, hullCount, convHulls);
637 } 661 }
638 662
639 BulletShape newShape = new BulletShape(hullPtr, ShapeData.PhysicsShapeType.SHAPE_HULL); 663 BulletShape newShape = new BulletShape(hullPtr, ShapeData.PhysicsShapeType.SHAPE_HULL);
@@ -652,32 +676,77 @@ public class BSShapeCollection : IDisposable
652 676
653 // Create a hash of all the shape parameters to be used as a key 677 // Create a hash of all the shape parameters to be used as a key
654 // for this particular shape. 678 // for this particular shape.
655 private ulong ComputeShapeKey(ShapeData shapeData, PrimitiveBaseShape pbs, out float retLod) 679 private System.UInt64 ComputeShapeKey(ShapeData shapeData, PrimitiveBaseShape pbs, out float retLod)
656 { 680 {
657 // level of detail based on size and type of the object 681 // level of detail based on size and type of the object
658 float lod = PhysicsScene.MeshLOD; 682 float lod = PhysicsScene.MeshLOD;
659 if (pbs.SculptEntry) 683 if (pbs.SculptEntry)
660 lod = PhysicsScene.SculptLOD; 684 lod = PhysicsScene.SculptLOD;
661 685
686 // Mega prims usually get more detail because one can interact with shape approximations at this size.
662 float maxAxis = Math.Max(shapeData.Size.X, Math.Max(shapeData.Size.Y, shapeData.Size.Z)); 687 float maxAxis = Math.Max(shapeData.Size.X, Math.Max(shapeData.Size.Y, shapeData.Size.Z));
663 if (maxAxis > PhysicsScene.MeshMegaPrimThreshold) 688 if (maxAxis > PhysicsScene.MeshMegaPrimThreshold)
664 lod = PhysicsScene.MeshMegaPrimLOD; 689 lod = PhysicsScene.MeshMegaPrimLOD;
665 690
666 retLod = lod; 691 retLod = lod;
667 return (ulong)pbs.GetMeshKey(shapeData.Size, lod); 692 return pbs.GetMeshKey(shapeData.Size, lod);
668 } 693 }
669 // For those who don't want the LOD 694 // For those who don't want the LOD
670 private ulong ComputeShapeKey(ShapeData shapeData, PrimitiveBaseShape pbs) 695 private System.UInt64 ComputeShapeKey(ShapeData shapeData, PrimitiveBaseShape pbs)
671 { 696 {
672 float lod; 697 float lod;
673 return ComputeShapeKey(shapeData, pbs, out lod); 698 return ComputeShapeKey(shapeData, pbs, out lod);
674 } 699 }
675 700
701 private BulletShape VerifyMeshCreated(BulletShape newShape, BSPhysObject prim, ShapeData shapeData, PrimitiveBaseShape pbs)
702 {
703 // If the shape was successfully created, nothing more to do
704 if (newShape.ptr != IntPtr.Zero)
705 return newShape;
706
707 // The most common reason for failure is that an underlying asset is not available
708
709 // If this mesh has an underlying asset and we have not failed getting it before, fetch the asset
710 if (pbs.SculptEntry && !prim.LastAssetBuildFailed && pbs.SculptTexture != OMV.UUID.Zero)
711 {
712 prim.LastAssetBuildFailed = true;
713 BSPhysObject xprim = prim;
714 Util.FireAndForget(delegate
715 {
716 RequestAssetDelegate assetProvider = PhysicsScene.RequestAssetMethod;
717 if (assetProvider != null)
718 {
719 BSPhysObject yprim = xprim; // probably not necessary, but, just in case.
720 assetProvider(yprim.BaseShape.SculptTexture, delegate(AssetBase asset)
721 {
722 if (!yprim.BaseShape.SculptEntry)
723 return;
724 if (yprim.BaseShape.SculptTexture.ToString() != asset.ID)
725 return;
726
727 yprim.BaseShape.SculptData = new byte[asset.Data.Length];
728 asset.Data.CopyTo(yprim.BaseShape.SculptData, 0);
729 // This will cause the prim to see that the filler shape is not the right
730 // one and try again to build the object.
731 yprim.ForceBodyShapeRebuild(false);
732
733 });
734 }
735 });
736 }
737
738 // While we figure out the real problem, stick a simple native shape on the object.
739 BulletShape fillinShape =
740 BuildPhysicalNativeShape(ShapeData.PhysicsShapeType.SHAPE_SPHERE, shapeData, ShapeData.FixedShapeKey.KEY_SPHERE);
741
742 return fillinShape;
743 }
744
676 // Create a body object in Bullet. 745 // Create a body object in Bullet.
677 // Updates prim.BSBody with the information about the new body if one is created. 746 // Updates prim.BSBody with the information about the new body if one is created.
678 // Returns 'true' if an object was actually created. 747 // Returns 'true' if an object was actually created.
679 // Called at taint-time. 748 // Called at taint-time.
680 private bool CreateBody(bool forceRebuild, BSPrim prim, BulletSim sim, BulletShape shape, 749 private bool CreateBody(bool forceRebuild, BSPhysObject prim, BulletSim sim, BulletShape shape,
681 ShapeData shapeData, BodyDestructionCallback bodyCallback) 750 ShapeData shapeData, BodyDestructionCallback bodyCallback)
682 { 751 {
683 bool ret = false; 752 bool ret = false;
@@ -701,6 +770,7 @@ public class BSShapeCollection : IDisposable
701 770
702 if (mustRebuild || forceRebuild) 771 if (mustRebuild || forceRebuild)
703 { 772 {
773 // Free any old body
704 DereferenceBody(prim.BSBody, true, bodyCallback); 774 DereferenceBody(prim.BSBody, true, bodyCallback);
705 775
706 BulletBody aBody; 776 BulletBody aBody;
@@ -709,13 +779,13 @@ public class BSShapeCollection : IDisposable
709 { 779 {
710 bodyPtr = BulletSimAPI.CreateBodyFromShape2(sim.ptr, shape.ptr, 780 bodyPtr = BulletSimAPI.CreateBodyFromShape2(sim.ptr, shape.ptr,
711 shapeData.ID, shapeData.Position, shapeData.Rotation); 781 shapeData.ID, shapeData.Position, shapeData.Rotation);
712 // DetailLog("{0},BSShapeCollection.CreateBody,mesh,ptr={1}", prim.LocalID, bodyPtr.ToString("X")); 782 DetailLog("{0},BSShapeCollection.CreateBody,mesh,ptr={1}", prim.LocalID, bodyPtr.ToString("X"));
713 } 783 }
714 else 784 else
715 { 785 {
716 bodyPtr = BulletSimAPI.CreateGhostFromShape2(sim.ptr, shape.ptr, 786 bodyPtr = BulletSimAPI.CreateGhostFromShape2(sim.ptr, shape.ptr,
717 shapeData.ID, shapeData.Position, shapeData.Rotation); 787 shapeData.ID, shapeData.Position, shapeData.Rotation);
718 // DetailLog("{0},BSShapeCollection.CreateBody,ghost,ptr={1}", prim.LocalID, bodyPtr.ToString("X")); 788 DetailLog("{0},BSShapeCollection.CreateBody,ghost,ptr={1}", prim.LocalID, bodyPtr.ToString("X"));
719 } 789 }
720 aBody = new BulletBody(shapeData.ID, bodyPtr); 790 aBody = new BulletBody(shapeData.ID, bodyPtr);
721 791
@@ -731,7 +801,8 @@ public class BSShapeCollection : IDisposable
731 801
732 private void DetailLog(string msg, params Object[] args) 802 private void DetailLog(string msg, params Object[] args)
733 { 803 {
734 PhysicsScene.PhysicsLogging.Write(msg, args); 804 if (PhysicsScene.PhysicsLogging.Enabled)
805 PhysicsScene.DetailLog(msg, args);
735 } 806 }
736} 807}
737} 808}