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.cs1000
1 files changed, 1000 insertions, 0 deletions
diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSShapeCollection.cs b/OpenSim/Region/Physics/BulletSPlugin/BSShapeCollection.cs
new file mode 100755
index 0000000..29a23c0
--- /dev/null
+++ b/OpenSim/Region/Physics/BulletSPlugin/BSShapeCollection.cs
@@ -0,0 +1,1000 @@
1/*
2 * Copyright (c) Contributors, http://opensimulator.org/
3 * See CONTRIBUTORS.TXT for a full list of copyright holders.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 * * Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * * Redistributions in binary form must reproduce the above copyrightD
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 * * Neither the name of the OpenSimulator Project nor the
13 * names of its contributors may be used to endorse or promote products
14 * derived from this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
17 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19 * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
20 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27using System;
28using System.Collections.Generic;
29using System.Text;
30using OMV = OpenMetaverse;
31using OpenSim.Framework;
32using OpenSim.Region.Physics.Manager;
33using OpenSim.Region.Physics.ConvexDecompositionDotNet;
34
35namespace OpenSim.Region.Physics.BulletSPlugin
36{
37public sealed class BSShapeCollection : IDisposable
38{
39 private static string LogHeader = "[BULLETSIM SHAPE COLLECTION]";
40
41 private BSScene PhysicsScene { get; set; }
42
43 private Object m_collectionActivityLock = new Object();
44
45 // Description of a Mesh
46 private struct MeshDesc
47 {
48 public IntPtr ptr;
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 IntPtr ptr;
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 public BSShapeCollection(BSScene physScene)
69 {
70 PhysicsScene = physScene;
71 }
72
73 public void Dispose()
74 {
75 // TODO!!!!!!!!!
76 }
77
78 // Callbacks called just before either the body or shape is destroyed.
79 // Mostly used for changing bodies out from under Linksets.
80 // Useful for other cases where parameters need saving.
81 // Passing 'null' says no callback.
82 public delegate void ShapeDestructionCallback(BulletShape shape);
83 public delegate void BodyDestructionCallback(BulletBody body);
84
85 // Called to update/change the body and shape for an object.
86 // First checks the shape and updates that if necessary then makes
87 // sure the body is of the right type.
88 // Return 'true' if either the body or the shape changed.
89 // 'shapeCallback' and 'bodyCallback' are, if non-null, functions called just before
90 // the current shape or body is destroyed. This allows the caller to remove any
91 // higher level dependencies on the shape or body. Mostly used for LinkSets to
92 // remove the physical constraints before the body is destroyed.
93 // Called at taint-time!!
94 public bool GetBodyAndShape(bool forceRebuild, BulletSim sim, BSPhysObject prim,
95 ShapeDestructionCallback shapeCallback, BodyDestructionCallback bodyCallback)
96 {
97 PhysicsScene.AssertInTaintTime("BSShapeCollection.GetBodyAndShape");
98
99 bool ret = false;
100
101 // This lock could probably be pushed down lower but building shouldn't take long
102 lock (m_collectionActivityLock)
103 {
104 // Do we have the correct geometry for this type of object?
105 // Updates prim.BSShape with information/pointers to shape.
106 // Returns 'true' of BSShape is changed to a new shape.
107 bool newGeom = CreateGeom(forceRebuild, prim, shapeCallback);
108 // If we had to select a new shape geometry for the object,
109 // rebuild the body around it.
110 // Updates prim.BSBody with information/pointers to requested body
111 // Returns 'true' if BSBody was changed.
112 bool newBody = CreateBody((newGeom || forceRebuild), prim, PhysicsScene.World,
113 prim.PhysShape, bodyCallback);
114 ret = newGeom || newBody;
115 }
116 DetailLog("{0},BSShapeCollection.GetBodyAndShape,taintExit,force={1},ret={2},body={3},shape={4}",
117 prim.LocalID, forceRebuild, ret, prim.PhysBody, prim.PhysShape);
118
119 return ret;
120 }
121
122 // Track another user of a body.
123 // We presume the caller has allocated the body.
124 // Bodies only have one user so the body is just put into the world if not already there.
125 public void ReferenceBody(BulletBody body, bool inTaintTime)
126 {
127 lock (m_collectionActivityLock)
128 {
129 DetailLog("{0},BSShapeCollection.ReferenceBody,newBody,body={1}", body.ID, body);
130 PhysicsScene.TaintedObject(inTaintTime, "BSShapeCollection.ReferenceBody", delegate()
131 {
132 if (!BulletSimAPI.IsInWorld2(body.ptr))
133 {
134 BulletSimAPI.AddObjectToWorld2(PhysicsScene.World.ptr, body.ptr);
135 DetailLog("{0},BSShapeCollection.ReferenceBody,addedToWorld,ref={1}", body.ID, body);
136 }
137 });
138 }
139 }
140
141 // Release the usage of a body.
142 // Called when releasing use of a BSBody. BSShape is handled separately.
143 public void DereferenceBody(BulletBody body, bool inTaintTime, BodyDestructionCallback bodyCallback )
144 {
145 if (body.ptr == IntPtr.Zero)
146 return;
147
148 lock (m_collectionActivityLock)
149 {
150 PhysicsScene.TaintedObject(inTaintTime, "BSShapeCollection.DereferenceBody", delegate()
151 {
152 DetailLog("{0},BSShapeCollection.DereferenceBody,DestroyingBody,body={1},inTaintTime={2}",
153 body.ID, body, inTaintTime);
154 // If the caller needs to know the old body is going away, pass the event up.
155 if (bodyCallback != null) bodyCallback(body);
156
157 if (BulletSimAPI.IsInWorld2(body.ptr))
158 {
159 BulletSimAPI.RemoveObjectFromWorld2(PhysicsScene.World.ptr, body.ptr);
160 DetailLog("{0},BSShapeCollection.DereferenceBody,removingFromWorld. Body={1}", body.ID, body);
161 }
162
163 // Zero any reference to the shape so it is not freed when the body is deleted.
164 BulletSimAPI.SetCollisionShape2(PhysicsScene.World.ptr, body.ptr, IntPtr.Zero);
165 BulletSimAPI.DestroyObject2(PhysicsScene.World.ptr, body.ptr);
166 });
167 }
168 }
169
170 // Track the datastructures and use count for a shape.
171 // When creating a hull, this is called first to reference the mesh
172 // and then again to reference the hull.
173 // Meshes and hulls for the same shape have the same hash key.
174 // NOTE that native shapes are not added to the mesh list or removed.
175 // Returns 'true' if this is the initial reference to the shape. Otherwise reused.
176 public bool ReferenceShape(BulletShape shape)
177 {
178 bool ret = false;
179 switch (shape.type)
180 {
181 case ShapeData.PhysicsShapeType.SHAPE_MESH:
182 MeshDesc meshDesc;
183 if (Meshes.TryGetValue(shape.shapeKey, out meshDesc))
184 {
185 // There is an existing instance of this mesh.
186 meshDesc.referenceCount++;
187 DetailLog("{0},BSShapeCollection.ReferenceShape,existingMesh,key={1},cnt={2}",
188 BSScene.DetailLogZero, shape.shapeKey.ToString("X"), meshDesc.referenceCount);
189 }
190 else
191 {
192 // This is a new reference to a mesh
193 meshDesc.ptr = shape.ptr;
194 meshDesc.shapeKey = shape.shapeKey;
195 // We keep a reference to the underlying IMesh data so a hull can be built
196 meshDesc.referenceCount = 1;
197 DetailLog("{0},BSShapeCollection.ReferenceShape,newMesh,key={1},cnt={2}",
198 BSScene.DetailLogZero, shape.shapeKey.ToString("X"), meshDesc.referenceCount);
199 ret = true;
200 }
201 meshDesc.lastReferenced = System.DateTime.Now;
202 Meshes[shape.shapeKey] = meshDesc;
203 break;
204 case ShapeData.PhysicsShapeType.SHAPE_HULL:
205 HullDesc hullDesc;
206 if (Hulls.TryGetValue(shape.shapeKey, out hullDesc))
207 {
208 // There is an existing instance of this hull.
209 hullDesc.referenceCount++;
210 DetailLog("{0},BSShapeCollection.ReferenceShape,existingHull,key={1},cnt={2}",
211 BSScene.DetailLogZero, shape.shapeKey.ToString("X"), hullDesc.referenceCount);
212 }
213 else
214 {
215 // This is a new reference to a hull
216 hullDesc.ptr = shape.ptr;
217 hullDesc.shapeKey = shape.shapeKey;
218 hullDesc.referenceCount = 1;
219 DetailLog("{0},BSShapeCollection.ReferenceShape,newHull,key={1},cnt={2}",
220 BSScene.DetailLogZero, shape.shapeKey.ToString("X"), hullDesc.referenceCount);
221 ret = true;
222
223 }
224 hullDesc.lastReferenced = System.DateTime.Now;
225 Hulls[shape.shapeKey] = hullDesc;
226 break;
227 case ShapeData.PhysicsShapeType.SHAPE_UNKNOWN:
228 break;
229 default:
230 // Native shapes are not tracked and they don't go into any list
231 break;
232 }
233 return ret;
234 }
235
236 // Release the usage of a shape.
237 public void DereferenceShape(BulletShape shape, bool inTaintTime, ShapeDestructionCallback shapeCallback)
238 {
239 if (shape.ptr == IntPtr.Zero)
240 return;
241
242 PhysicsScene.TaintedObject(inTaintTime, "BSShapeCollection.DereferenceShape", delegate()
243 {
244 if (shape.ptr != IntPtr.Zero)
245 {
246 if (shape.isNativeShape)
247 {
248 // Native shapes are not tracked and are released immediately
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)
257 {
258 case ShapeData.PhysicsShapeType.SHAPE_HULL:
259 DereferenceHull(shape, shapeCallback);
260 break;
261 case ShapeData.PhysicsShapeType.SHAPE_MESH:
262 DereferenceMesh(shape, shapeCallback);
263 break;
264 case ShapeData.PhysicsShapeType.SHAPE_COMPOUND:
265 DereferenceCompound(shape, shapeCallback);
266 break;
267 case ShapeData.PhysicsShapeType.SHAPE_UNKNOWN:
268 break;
269 default:
270 break;
271 }
272 }
273 }
274 });
275 }
276
277 // Count down the reference count for a mesh shape
278 // Called at taint-time.
279 private void DereferenceMesh(BulletShape shape, ShapeDestructionCallback shapeCallback)
280 {
281 MeshDesc meshDesc;
282 if (Meshes.TryGetValue(shape.shapeKey, out meshDesc))
283 {
284 meshDesc.referenceCount--;
285 // TODO: release the Bullet storage
286 if (shapeCallback != null) shapeCallback(shape);
287 meshDesc.lastReferenced = System.DateTime.Now;
288 Meshes[shape.shapeKey] = meshDesc;
289 DetailLog("{0},BSShapeCollection.DereferenceMesh,shape={1},refCnt={2}",
290 BSScene.DetailLogZero, shape, meshDesc.referenceCount);
291
292 }
293 }
294
295 // Count down the reference count for a hull shape
296 // Called at taint-time.
297 private void DereferenceHull(BulletShape shape, ShapeDestructionCallback shapeCallback)
298 {
299 HullDesc hullDesc;
300 if (Hulls.TryGetValue(shape.shapeKey, out hullDesc))
301 {
302 hullDesc.referenceCount--;
303 // TODO: release the Bullet storage (aging old entries?)
304
305 // Tell upper layers that, if they have dependencies on this shape, this link is going away
306 if (shapeCallback != null) shapeCallback(shape);
307
308 hullDesc.lastReferenced = System.DateTime.Now;
309 Hulls[shape.shapeKey] = hullDesc;
310 DetailLog("{0},BSShapeCollection.DereferenceHull,shape={1},refCnt={2}",
311 BSScene.DetailLogZero, shape, hullDesc.referenceCount);
312 }
313 }
314
315 // Remove a reference to a compound shape.
316 // Taking a compound shape apart is a little tricky because if you just delete the
317 // physical shape, it will free all the underlying children. We can't do that because
318 // they could be shared. So, this removes each of the children from the compound and
319 // dereferences them separately before destroying the compound collision object itself.
320 // Called at taint-time.
321 private void DereferenceCompound(BulletShape shape, ShapeDestructionCallback shapeCallback)
322 {
323 if (!BulletSimAPI.IsCompound2(shape.ptr))
324 {
325 // Failed the sanity check!!
326 PhysicsScene.Logger.ErrorFormat("{0} Attempt to free a compound shape that is not compound!! type={1}, ptr={2}",
327 LogHeader, shape.type, shape.ptr.ToString("X"));
328 DetailLog("{0},BSShapeCollection.DereferenceCompound,notACompoundShape,type={1},ptr={2}",
329 BSScene.DetailLogZero, shape.type, shape.ptr.ToString("X"));
330 return;
331 }
332
333 int numChildren = BulletSimAPI.GetNumberOfCompoundChildren2(shape.ptr);
334 DetailLog("{0},BSShapeCollection.DereferenceCompound,shape={1},children={2}", BSScene.DetailLogZero, shape, numChildren);
335
336 for (int ii = numChildren - 1; ii >= 0; ii--)
337 {
338 IntPtr childShape = BulletSimAPI.RemoveChildShapeFromCompoundShapeIndex2(shape.ptr, ii);
339 DereferenceAnonCollisionShape(childShape);
340 }
341 BulletSimAPI.DeleteCollisionShape2(PhysicsScene.World.ptr, shape.ptr);
342 }
343
344 // Sometimes we have a pointer to a collision shape but don't know what type it is.
345 // Figure out type and call the correct dereference routine.
346 // Called at taint-time.
347 private void DereferenceAnonCollisionShape(IntPtr cShape)
348 {
349 MeshDesc meshDesc;
350 HullDesc hullDesc;
351
352 BulletShape shapeInfo = new BulletShape(cShape);
353 if (TryGetMeshByPtr(cShape, out meshDesc))
354 {
355 shapeInfo.type = ShapeData.PhysicsShapeType.SHAPE_MESH;
356 shapeInfo.shapeKey = meshDesc.shapeKey;
357 }
358 else
359 {
360 if (TryGetHullByPtr(cShape, out hullDesc))
361 {
362 shapeInfo.type = ShapeData.PhysicsShapeType.SHAPE_HULL;
363 shapeInfo.shapeKey = hullDesc.shapeKey;
364 }
365 else
366 {
367 if (BulletSimAPI.IsCompound2(cShape))
368 {
369 shapeInfo.type = ShapeData.PhysicsShapeType.SHAPE_COMPOUND;
370 }
371 else
372 {
373 if (BulletSimAPI.IsNativeShape2(cShape))
374 {
375 shapeInfo.isNativeShape = true;
376 shapeInfo.type = ShapeData.PhysicsShapeType.SHAPE_BOX; // (technically, type doesn't matter)
377 }
378 }
379 }
380 }
381
382 DetailLog("{0},BSShapeCollection.DereferenceAnonCollisionShape,shape={1}", BSScene.DetailLogZero, shapeInfo);
383
384 if (shapeInfo.type != ShapeData.PhysicsShapeType.SHAPE_UNKNOWN)
385 {
386 DereferenceShape(shapeInfo, true, null);
387 }
388 else
389 {
390 PhysicsScene.Logger.ErrorFormat("{0} Could not decypher shape type. Region={1}, addr={2}",
391 LogHeader, PhysicsScene.RegionName, cShape.ToString("X"));
392 }
393 }
394
395 // Create the geometry information in Bullet for later use.
396 // The objects needs a hull if it's physical otherwise a mesh is enough.
397 // if 'forceRebuild' is true, the geometry is unconditionally rebuilt. For meshes and hulls,
398 // shared geometries will be used. If the parameters of the existing shape are the same
399 // as this request, the shape is not rebuilt.
400 // Info in prim.BSShape is updated to the new shape.
401 // Returns 'true' if the geometry was rebuilt.
402 // Called at taint-time!
403 private bool CreateGeom(bool forceRebuild, BSPhysObject prim, ShapeDestructionCallback shapeCallback)
404 {
405 bool ret = false;
406 bool haveShape = false;
407
408 if (!haveShape && prim.PreferredPhysicalShape == ShapeData.PhysicsShapeType.SHAPE_AVATAR)
409 {
410 // an avatar capsule is close to a native shape (it is not shared)
411 ret = GetReferenceToNativeShape(prim, ShapeData.PhysicsShapeType.SHAPE_AVATAR,
412 ShapeData.FixedShapeKey.KEY_CAPSULE, shapeCallback);
413 DetailLog("{0},BSShapeCollection.CreateGeom,avatarCapsule,shape={1}", prim.LocalID, prim.PhysShape);
414 ret = true;
415 haveShape = true;
416 }
417
418 // Compound shapes are handled special as they are rebuilt from scratch.
419 // This isn't too great a hardship since most of the child shapes will already been created.
420 if (!haveShape && prim.PreferredPhysicalShape == ShapeData.PhysicsShapeType.SHAPE_COMPOUND)
421 {
422 ret = GetReferenceToCompoundShape(prim, shapeCallback);
423 DetailLog("{0},BSShapeCollection.CreateGeom,compoundShape,shape={1}", prim.LocalID, prim.PhysShape);
424 haveShape = true;
425 }
426
427 if (!haveShape)
428 {
429 ret = CreateGeomNonSpecial(forceRebuild, prim, shapeCallback);
430 }
431
432 return ret;
433 }
434
435 // Create a mesh/hull shape or a native shape if 'nativeShapePossible' is 'true'.
436 private bool CreateGeomNonSpecial(bool forceRebuild, BSPhysObject prim, ShapeDestructionCallback shapeCallback)
437 {
438 bool ret = false;
439 bool haveShape = false;
440 bool nativeShapePossible = true;
441 PrimitiveBaseShape pbs = prim.BaseShape;
442
443 // If the prim attributes are simple, this could be a simple Bullet native shape
444 if (!haveShape
445 && pbs != null
446 && nativeShapePossible
447 && ((pbs.SculptEntry && !PhysicsScene.ShouldMeshSculptedPrim)
448 || (pbs.ProfileBegin == 0 && pbs.ProfileEnd == 0
449 && pbs.ProfileHollow == 0
450 && pbs.PathTwist == 0 && pbs.PathTwistBegin == 0
451 && pbs.PathBegin == 0 && pbs.PathEnd == 0
452 && pbs.PathTaperX == 0 && pbs.PathTaperY == 0
453 && pbs.PathScaleX == 100 && pbs.PathScaleY == 100
454 && pbs.PathShearX == 0 && pbs.PathShearY == 0) ) )
455 {
456 // It doesn't look like Bullet scales spheres so make sure the scales are all equal
457 if ((pbs.ProfileShape == ProfileShape.HalfCircle && pbs.PathCurve == (byte)Extrusion.Curve1)
458 && pbs.Scale.X == pbs.Scale.Y && pbs.Scale.Y == pbs.Scale.Z)
459 {
460 haveShape = true;
461 if (forceRebuild
462 || prim.Scale != prim.Size
463 || prim.PhysShape.type != ShapeData.PhysicsShapeType.SHAPE_SPHERE
464 )
465 {
466 ret = GetReferenceToNativeShape(prim, ShapeData.PhysicsShapeType.SHAPE_SPHERE,
467 ShapeData.FixedShapeKey.KEY_SPHERE, shapeCallback);
468 DetailLog("{0},BSShapeCollection.CreateGeom,sphere,force={1},shape={2}",
469 prim.LocalID, forceRebuild, prim.PhysShape);
470 }
471 }
472 if (!haveShape && pbs.ProfileShape == ProfileShape.Square && pbs.PathCurve == (byte)Extrusion.Straight)
473 {
474 haveShape = true;
475 if (forceRebuild
476 || prim.Scale != prim.Size
477 || prim.PhysShape.type != ShapeData.PhysicsShapeType.SHAPE_BOX
478 )
479 {
480 ret = GetReferenceToNativeShape( prim, ShapeData.PhysicsShapeType.SHAPE_BOX,
481 ShapeData.FixedShapeKey.KEY_BOX, shapeCallback);
482 DetailLog("{0},BSShapeCollection.CreateGeom,box,force={1},shape={2}",
483 prim.LocalID, forceRebuild, prim.PhysShape);
484 }
485 }
486 }
487
488 // If a simple shape is not happening, create a mesh and possibly a hull.
489 if (!haveShape && pbs != null)
490 {
491 ret = CreateGeomMeshOrHull(prim, shapeCallback);
492 }
493
494 return ret;
495 }
496
497 public bool CreateGeomMeshOrHull(BSPhysObject prim, ShapeDestructionCallback shapeCallback)
498 {
499
500 bool ret = false;
501 // Note that if it's a native shape, the check for physical/non-physical is not
502 // made. Native shapes work in either case.
503 if (prim.IsPhysical && PhysicsScene.ShouldUseHullsForPhysicalObjects)
504 {
505 // Update prim.BSShape to reference a hull of this shape.
506 ret = GetReferenceToHull(prim,shapeCallback);
507 DetailLog("{0},BSShapeCollection.CreateGeom,hull,shape={1},key={2}",
508 prim.LocalID, prim.PhysShape, prim.PhysShape.shapeKey.ToString("X"));
509 }
510 else
511 {
512 ret = GetReferenceToMesh(prim, shapeCallback);
513 DetailLog("{0},BSShapeCollection.CreateGeom,mesh,shape={1},key={2}",
514 prim.LocalID, prim.PhysShape, prim.PhysShape.shapeKey.ToString("X"));
515 }
516 return ret;
517 }
518
519 // Creates a native shape and assignes it to prim.BSShape.
520 // "Native" shapes are never shared. they are created here and destroyed in DereferenceShape().
521 private bool GetReferenceToNativeShape(BSPhysObject prim,
522 ShapeData.PhysicsShapeType shapeType, ShapeData.FixedShapeKey shapeKey,
523 ShapeDestructionCallback shapeCallback)
524 {
525 // release any previous shape
526 DereferenceShape(prim.PhysShape, true, shapeCallback);
527
528 // Bullet native objects are scaled by the Bullet engine so pass the size in
529 prim.Scale = prim.Size;
530
531 BulletShape newShape = BuildPhysicalNativeShape(prim, shapeType, shapeKey);
532
533 // Don't need to do a 'ReferenceShape()' here because native shapes are not shared.
534 DetailLog("{0},BSShapeCollection.AddNativeShapeToPrim,create,newshape={1},scale={2}",
535 prim.LocalID, newShape, prim.Scale);
536
537 prim.PhysShape = newShape;
538 return true;
539 }
540
541 private BulletShape BuildPhysicalNativeShape(BSPhysObject prim, ShapeData.PhysicsShapeType shapeType,
542 ShapeData.FixedShapeKey shapeKey)
543 {
544 BulletShape newShape;
545 // Need to make sure the passed shape information is for the native type.
546 ShapeData nativeShapeData = new ShapeData();
547 nativeShapeData.Type = shapeType;
548 nativeShapeData.ID = prim.LocalID;
549 nativeShapeData.Scale = prim.Scale;
550 nativeShapeData.Size = prim.Scale;
551 nativeShapeData.MeshKey = (ulong)shapeKey;
552 nativeShapeData.HullKey = (ulong)shapeKey;
553
554 if (shapeType == ShapeData.PhysicsShapeType.SHAPE_AVATAR)
555 {
556 newShape = new BulletShape(
557 BulletSimAPI.BuildCapsuleShape2(PhysicsScene.World.ptr, 1f, 1f, prim.Scale)
558 , shapeType);
559 DetailLog("{0},BSShapeCollection.BuiletPhysicalNativeShape,capsule,scale={1}", prim.LocalID, prim.Scale);
560 }
561 else
562 {
563 newShape = new BulletShape(BulletSimAPI.BuildNativeShape2(PhysicsScene.World.ptr, nativeShapeData), shapeType);
564 }
565 if (newShape.ptr == IntPtr.Zero)
566 {
567 PhysicsScene.Logger.ErrorFormat("{0} BuildPhysicalNativeShape failed. ID={1}, shape={2}",
568 LogHeader, prim.LocalID, shapeType);
569 }
570 newShape.shapeKey = (System.UInt64)shapeKey;
571 newShape.isNativeShape = true;
572
573 return newShape;
574 }
575
576 // Builds a mesh shape in the physical world and updates prim.BSShape.
577 // Dereferences previous shape in BSShape and adds a reference for this new shape.
578 // Returns 'true' of a mesh was actually built. Otherwise .
579 // Called at taint-time!
580 private bool GetReferenceToMesh(BSPhysObject prim, ShapeDestructionCallback shapeCallback)
581 {
582 BulletShape newShape = new BulletShape(IntPtr.Zero);
583
584 float lod;
585 System.UInt64 newMeshKey = ComputeShapeKey(prim.Size, prim.BaseShape, out lod);
586
587 // if this new shape is the same as last time, don't recreate the mesh
588 if (newMeshKey == prim.PhysShape.shapeKey && prim.PhysShape.type == ShapeData.PhysicsShapeType.SHAPE_MESH)
589 return false;
590
591 DetailLog("{0},BSShapeCollection.GetReferenceToMesh,create,oldKey={1},newKey={2}",
592 prim.LocalID, prim.PhysShape.shapeKey.ToString("X"), newMeshKey.ToString("X"));
593
594 // Since we're recreating new, get rid of the reference to the previous shape
595 DereferenceShape(prim.PhysShape, true, shapeCallback);
596
597 newShape = CreatePhysicalMesh(prim.PhysObjectName, newMeshKey, prim.BaseShape, prim.Size, lod);
598 // Take evasive action if the mesh was not constructed.
599 newShape = VerifyMeshCreated(newShape, prim);
600
601 ReferenceShape(newShape);
602
603 // meshes are already scaled by the meshmerizer
604 prim.Scale = new OMV.Vector3(1f, 1f, 1f);
605 prim.PhysShape = newShape;
606
607 return true; // 'true' means a new shape has been added to this prim
608 }
609
610 private BulletShape CreatePhysicalMesh(string objName, System.UInt64 newMeshKey, PrimitiveBaseShape pbs, OMV.Vector3 size, float lod)
611 {
612 IMesh meshData = null;
613 IntPtr meshPtr = IntPtr.Zero;
614 MeshDesc meshDesc;
615 if (Meshes.TryGetValue(newMeshKey, out meshDesc))
616 {
617 // If the mesh has already been built just use it.
618 meshPtr = meshDesc.ptr;
619 }
620 else
621 {
622 // Pass false for physicalness as this creates some sort of bounding box which we don't need
623 meshData = PhysicsScene.mesher.CreateMesh(objName, pbs, size, lod, false);
624
625 if (meshData != null)
626 {
627 int[] indices = meshData.getIndexListAsInt();
628 List<OMV.Vector3> vertices = meshData.getVertexList();
629
630 float[] verticesAsFloats = new float[vertices.Count * 3];
631 int vi = 0;
632 foreach (OMV.Vector3 vv in vertices)
633 {
634 verticesAsFloats[vi++] = vv.X;
635 verticesAsFloats[vi++] = vv.Y;
636 verticesAsFloats[vi++] = vv.Z;
637 }
638
639 // m_log.DebugFormat("{0}: BSShapeCollection.CreatePhysicalMesh: calling CreateMesh. lid={1}, key={2}, indices={3}, vertices={4}",
640 // LogHeader, prim.LocalID, newMeshKey, indices.Length, vertices.Count);
641
642 meshPtr = BulletSimAPI.CreateMeshShape2(PhysicsScene.World.ptr,
643 indices.GetLength(0), indices, vertices.Count, verticesAsFloats);
644 }
645 }
646 BulletShape newShape = new BulletShape(meshPtr, ShapeData.PhysicsShapeType.SHAPE_MESH);
647 newShape.shapeKey = newMeshKey;
648
649 return newShape;
650 }
651
652 // See that hull shape exists in the physical world and update prim.BSShape.
653 // We could be creating the hull because scale changed or whatever.
654 private bool GetReferenceToHull(BSPhysObject prim, ShapeDestructionCallback shapeCallback)
655 {
656 BulletShape newShape;
657
658 float lod;
659 System.UInt64 newHullKey = ComputeShapeKey(prim.Size, prim.BaseShape, out lod);
660
661 // if the hull hasn't changed, don't rebuild it
662 if (newHullKey == prim.PhysShape.shapeKey && prim.PhysShape.type == ShapeData.PhysicsShapeType.SHAPE_HULL)
663 return false;
664
665 DetailLog("{0},BSShapeCollection.GetReferenceToHull,create,oldKey={1},newKey={2}",
666 prim.LocalID, prim.PhysShape.shapeKey.ToString("X"), newHullKey.ToString("X"));
667
668 // Remove usage of the previous shape.
669 DereferenceShape(prim.PhysShape, true, shapeCallback);
670
671 newShape = CreatePhysicalHull(prim.PhysObjectName, newHullKey, prim.BaseShape, prim.Size, lod);
672 newShape = VerifyMeshCreated(newShape, prim);
673
674 ReferenceShape(newShape);
675
676 // hulls are already scaled by the meshmerizer
677 prim.Scale = new OMV.Vector3(1f, 1f, 1f);
678 prim.PhysShape = newShape;
679 return true; // 'true' means a new shape has been added to this prim
680 }
681
682 List<ConvexResult> m_hulls;
683 private BulletShape CreatePhysicalHull(string objName, System.UInt64 newHullKey, PrimitiveBaseShape pbs, OMV.Vector3 size, float lod)
684 {
685
686 IntPtr hullPtr = IntPtr.Zero;
687 HullDesc hullDesc;
688 if (Hulls.TryGetValue(newHullKey, out hullDesc))
689 {
690 // If the hull shape already is created, just use it.
691 hullPtr = hullDesc.ptr;
692 }
693 else
694 {
695 // Build a new hull in the physical world
696 // Pass false for physicalness as this creates some sort of bounding box which we don't need
697 IMesh meshData = PhysicsScene.mesher.CreateMesh(objName, pbs, size, lod, false);
698 if (meshData != null)
699 {
700
701 int[] indices = meshData.getIndexListAsInt();
702 List<OMV.Vector3> vertices = meshData.getVertexList();
703
704 //format conversion from IMesh format to DecompDesc format
705 List<int> convIndices = new List<int>();
706 List<float3> convVertices = new List<float3>();
707 for (int ii = 0; ii < indices.GetLength(0); ii++)
708 {
709 convIndices.Add(indices[ii]);
710 }
711 foreach (OMV.Vector3 vv in vertices)
712 {
713 convVertices.Add(new float3(vv.X, vv.Y, vv.Z));
714 }
715
716 // setup and do convex hull conversion
717 m_hulls = new List<ConvexResult>();
718 DecompDesc dcomp = new DecompDesc();
719 dcomp.mIndices = convIndices;
720 dcomp.mVertices = convVertices;
721 ConvexBuilder convexBuilder = new ConvexBuilder(HullReturn);
722 // create the hull into the _hulls variable
723 convexBuilder.process(dcomp);
724
725 // Convert the vertices and indices for passing to unmanaged.
726 // The hull information is passed as a large floating point array.
727 // The format is:
728 // convHulls[0] = number of hulls
729 // convHulls[1] = number of vertices in first hull
730 // convHulls[2] = hull centroid X coordinate
731 // convHulls[3] = hull centroid Y coordinate
732 // convHulls[4] = hull centroid Z coordinate
733 // convHulls[5] = first hull vertex X
734 // convHulls[6] = first hull vertex Y
735 // convHulls[7] = first hull vertex Z
736 // convHulls[8] = second hull vertex X
737 // ...
738 // convHulls[n] = number of vertices in second hull
739 // convHulls[n+1] = second hull centroid X coordinate
740 // ...
741 //
742 // TODO: is is very inefficient. Someday change the convex hull generator to return
743 // data structures that do not need to be converted in order to pass to Bullet.
744 // And maybe put the values directly into pinned memory rather than marshaling.
745 int hullCount = m_hulls.Count;
746 int totalVertices = 1; // include one for the count of the hulls
747 foreach (ConvexResult cr in m_hulls)
748 {
749 totalVertices += 4; // add four for the vertex count and centroid
750 totalVertices += cr.HullIndices.Count * 3; // we pass just triangles
751 }
752 float[] convHulls = new float[totalVertices];
753
754 convHulls[0] = (float)hullCount;
755 int jj = 1;
756 foreach (ConvexResult cr in m_hulls)
757 {
758 // copy vertices for index access
759 float3[] verts = new float3[cr.HullVertices.Count];
760 int kk = 0;
761 foreach (float3 ff in cr.HullVertices)
762 {
763 verts[kk++] = ff;
764 }
765
766 // add to the array one hull's worth of data
767 convHulls[jj++] = cr.HullIndices.Count;
768 convHulls[jj++] = 0f; // centroid x,y,z
769 convHulls[jj++] = 0f;
770 convHulls[jj++] = 0f;
771 foreach (int ind in cr.HullIndices)
772 {
773 convHulls[jj++] = verts[ind].x;
774 convHulls[jj++] = verts[ind].y;
775 convHulls[jj++] = verts[ind].z;
776 }
777 }
778 // create the hull data structure in Bullet
779 hullPtr = BulletSimAPI.CreateHullShape2(PhysicsScene.World.ptr, hullCount, convHulls);
780 }
781 }
782
783 BulletShape newShape = new BulletShape(hullPtr, ShapeData.PhysicsShapeType.SHAPE_HULL);
784 newShape.shapeKey = newHullKey;
785
786 return newShape; // 'true' means a new shape has been added to this prim
787 }
788
789 // Callback from convex hull creater with a newly created hull.
790 // Just add it to our collection of hulls for this shape.
791 private void HullReturn(ConvexResult result)
792 {
793 m_hulls.Add(result);
794 return;
795 }
796
797 // Compound shapes are always built from scratch.
798 // This shouldn't be to bad since most of the parts will be meshes that had been built previously.
799 private bool GetReferenceToCompoundShape(BSPhysObject prim, ShapeDestructionCallback shapeCallback)
800 {
801 // Remove reference to the old shape
802 // Don't need to do this as the shape is freed when the new root shape is created below.
803 // DereferenceShape(prim.PhysShape, true, shapeCallback);
804
805 BulletShape cShape = new BulletShape(
806 BulletSimAPI.CreateCompoundShape2(PhysicsScene.World.ptr, false), ShapeData.PhysicsShapeType.SHAPE_COMPOUND);
807
808 // Create the shape for the root prim and add it to the compound shape. Cannot be a native shape.
809 CreateGeomMeshOrHull(prim, shapeCallback);
810 BulletSimAPI.AddChildShapeToCompoundShape2(cShape.ptr, prim.PhysShape.ptr, OMV.Vector3.Zero, OMV.Quaternion.Identity);
811 DetailLog("{0},BSShapeCollection.GetReferenceToCompoundShape,addRootPrim,compShape={1},rootShape={2}",
812 prim.LocalID, cShape, prim.PhysShape);
813
814 prim.PhysShape = cShape;
815
816 return true;
817 }
818
819 // Create a hash of all the shape parameters to be used as a key
820 // for this particular shape.
821 private System.UInt64 ComputeShapeKey(OMV.Vector3 size, PrimitiveBaseShape pbs, out float retLod)
822 {
823 // level of detail based on size and type of the object
824 float lod = PhysicsScene.MeshLOD;
825 if (pbs.SculptEntry)
826 lod = PhysicsScene.SculptLOD;
827
828 // Mega prims usually get more detail because one can interact with shape approximations at this size.
829 float maxAxis = Math.Max(size.X, Math.Max(size.Y, size.Z));
830 if (maxAxis > PhysicsScene.MeshMegaPrimThreshold)
831 lod = PhysicsScene.MeshMegaPrimLOD;
832
833 retLod = lod;
834 return pbs.GetMeshKey(size, lod);
835 }
836 // For those who don't want the LOD
837 private System.UInt64 ComputeShapeKey(OMV.Vector3 size, PrimitiveBaseShape pbs)
838 {
839 float lod;
840 return ComputeShapeKey(size, pbs, out lod);
841 }
842
843 // The creation of a mesh or hull can fail if an underlying asset is not available.
844 // There are two cases: 1) the asset is not in the cache and it needs to be fetched;
845 // and 2) the asset cannot be converted (like failed decompression of JPEG2000s).
846 // The first case causes the asset to be fetched. The second case requires
847 // us to not loop forever.
848 // Called after creating a physical mesh or hull. If the physical shape was created,
849 // just return.
850 private BulletShape VerifyMeshCreated(BulletShape newShape, BSPhysObject prim)
851 {
852 // If the shape was successfully created, nothing more to do
853 if (newShape.ptr != IntPtr.Zero)
854 return newShape;
855
856 // If this mesh has an underlying asset and we have not failed getting it before, fetch the asset
857 if (prim.BaseShape.SculptEntry && !prim.LastAssetBuildFailed && prim.BaseShape.SculptTexture != OMV.UUID.Zero)
858 {
859 prim.LastAssetBuildFailed = true;
860 BSPhysObject xprim = prim;
861 DetailLog("{0},BSShapeCollection.VerifyMeshCreated,fetchAsset,lID={1},lastFailed={2}",
862 LogHeader, prim.LocalID, prim.LastAssetBuildFailed);
863 Util.FireAndForget(delegate
864 {
865 RequestAssetDelegate assetProvider = PhysicsScene.RequestAssetMethod;
866 if (assetProvider != null)
867 {
868 BSPhysObject yprim = xprim; // probably not necessary, but, just in case.
869 assetProvider(yprim.BaseShape.SculptTexture, delegate(AssetBase asset)
870 {
871 if (!yprim.BaseShape.SculptEntry)
872 return;
873 if (yprim.BaseShape.SculptTexture.ToString() != asset.ID)
874 return;
875
876 yprim.BaseShape.SculptData = asset.Data;
877 // This will cause the prim to see that the filler shape is not the right
878 // one and try again to build the object.
879 // No race condition with the normal shape setting since the rebuild is at taint time.
880 yprim.ForceBodyShapeRebuild(false);
881
882 });
883 }
884 });
885 }
886 else
887 {
888 if (prim.LastAssetBuildFailed)
889 {
890 PhysicsScene.Logger.ErrorFormat("{0} Mesh failed to fetch asset. lID={1}, texture={2}",
891 LogHeader, prim.LocalID, prim.BaseShape.SculptTexture);
892 }
893 }
894
895 // While we figure out the real problem, stick a simple native shape on the object.
896 BulletShape fillinShape =
897 BuildPhysicalNativeShape(prim, ShapeData.PhysicsShapeType.SHAPE_BOX, ShapeData.FixedShapeKey.KEY_BOX);
898
899 return fillinShape;
900 }
901
902 // Create a body object in Bullet.
903 // Updates prim.BSBody with the information about the new body if one is created.
904 // Returns 'true' if an object was actually created.
905 // Called at taint-time.
906 private bool CreateBody(bool forceRebuild, BSPhysObject prim, BulletSim sim, BulletShape shape,
907 BodyDestructionCallback bodyCallback)
908 {
909 bool ret = false;
910
911 // the mesh, hull or native shape must have already been created in Bullet
912 bool mustRebuild = (prim.PhysBody.ptr == IntPtr.Zero);
913
914 // If there is an existing body, verify it's of an acceptable type.
915 // If not a solid object, body is a GhostObject. Otherwise a RigidBody.
916 if (!mustRebuild)
917 {
918 CollisionObjectTypes bodyType = (CollisionObjectTypes)BulletSimAPI.GetBodyType2(prim.PhysBody.ptr);
919 if (prim.IsSolid && bodyType != CollisionObjectTypes.CO_RIGID_BODY
920 || !prim.IsSolid && bodyType != CollisionObjectTypes.CO_GHOST_OBJECT)
921 {
922 // If the collisionObject is not the correct type for solidness, rebuild what's there
923 mustRebuild = true;
924 }
925 }
926
927 if (mustRebuild || forceRebuild)
928 {
929 // Free any old body
930 DereferenceBody(prim.PhysBody, true, bodyCallback);
931
932 BulletBody aBody;
933 IntPtr bodyPtr = IntPtr.Zero;
934 if (prim.IsSolid)
935 {
936 bodyPtr = BulletSimAPI.CreateBodyFromShape2(sim.ptr, shape.ptr,
937 prim.LocalID, prim.RawPosition, prim.RawOrientation);
938 DetailLog("{0},BSShapeCollection.CreateBody,mesh,ptr={1}", prim.LocalID, bodyPtr.ToString("X"));
939 }
940 else
941 {
942 bodyPtr = BulletSimAPI.CreateGhostFromShape2(sim.ptr, shape.ptr,
943 prim.LocalID, prim.ForcePosition, prim.ForceOrientation);
944 DetailLog("{0},BSShapeCollection.CreateBody,ghost,ptr={1}", prim.LocalID, bodyPtr.ToString("X"));
945 }
946 aBody = new BulletBody(prim.LocalID, bodyPtr);
947
948 ReferenceBody(aBody, true);
949
950 prim.PhysBody = aBody;
951
952 ret = true;
953 }
954
955 return ret;
956 }
957
958 private bool TryGetMeshByPtr(IntPtr addr, out MeshDesc outDesc)
959 {
960 bool ret = false;
961 MeshDesc foundDesc = new MeshDesc();
962 foreach (MeshDesc md in Meshes.Values)
963 {
964 if (md.ptr == addr)
965 {
966 foundDesc = md;
967 ret = true;
968 break;
969 }
970
971 }
972 outDesc = foundDesc;
973 return ret;
974 }
975
976 private bool TryGetHullByPtr(IntPtr addr, out HullDesc outDesc)
977 {
978 bool ret = false;
979 HullDesc foundDesc = new HullDesc();
980 foreach (HullDesc hd in Hulls.Values)
981 {
982 if (hd.ptr == addr)
983 {
984 foundDesc = hd;
985 ret = true;
986 break;
987 }
988
989 }
990 outDesc = foundDesc;
991 return ret;
992 }
993
994 private void DetailLog(string msg, params Object[] args)
995 {
996 if (PhysicsScene.PhysicsLogging.Enabled)
997 PhysicsScene.DetailLog(msg, args);
998 }
999}
1000}