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