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.cs756
1 files changed, 756 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..d189f1d
--- /dev/null
+++ b/OpenSim/Region/Physics/BulletSPlugin/BSShapeCollection.cs
@@ -0,0 +1,756 @@
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 class BSShapeCollection : IDisposable
38{
39 // private static string LogHeader = "[BULLETSIM SHAPE COLLECTION]";
40
41 protected 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 }
52
53 // Description of a hull.
54 // Meshes and hulls have the same shape hash key but we only need hulls for efficient physical objects
55 private struct HullDesc
56 {
57 public IntPtr ptr;
58 public int referenceCount;
59 public DateTime lastReferenced;
60 }
61
62 private struct BodyDesc
63 {
64 public IntPtr ptr;
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<System.UInt64, MeshDesc> Meshes = new Dictionary<System.UInt64, MeshDesc>();
71 private Dictionary<System.UInt64, HullDesc> Hulls = new Dictionary<System.UInt64, HullDesc>();
72 private Dictionary<uint, BodyDesc> Bodies = new Dictionary<uint, BodyDesc>();
73
74 public BSShapeCollection(BSScene physScene)
75 {
76 PhysicsScene = physScene;
77 }
78
79 public void Dispose()
80 {
81 // TODO!!!!!!!!!
82 }
83
84 // Callbacks called just before either the body or shape is destroyed.
85 // Mostly used for changing bodies out from under Linksets.
86 // Useful for other cases where parameters need saving.
87 // Passing 'null' says no callback.
88 public delegate void ShapeDestructionCallback(BulletShape shape);
89 public delegate void BodyDestructionCallback(BulletBody body);
90
91 // Called to update/change the body and shape for an object.
92 // First checks the shape and updates that if necessary then makes
93 // sure the body is of the right type.
94 // Return 'true' if either the body or the shape changed.
95 // Called at taint-time!!
96 public bool GetBodyAndShape(bool forceRebuild, BulletSim sim, BSPrim prim,
97 ShapeData shapeData, PrimitiveBaseShape pbs,
98 ShapeDestructionCallback shapeCallback, BodyDestructionCallback bodyCallback)
99 {
100 bool ret = false;
101
102 // This lock could probably be pushed down lower but building shouldn't take long
103 lock (m_collectionActivityLock)
104 {
105 // Do we have the correct geometry for this type of object?
106 // Updates prim.BSShape with information/pointers to requested shape
107 bool newGeom = CreateGeom(forceRebuild, prim, shapeData, pbs, 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 bool newBody = CreateBody((newGeom || forceRebuild), prim, PhysicsScene.World,
112 prim.BSShape, shapeData, bodyCallback);
113 ret = newGeom || newBody;
114 }
115 DetailLog("{0},BSShapeCollection.GetBodyAndShape,force={1},ret={2},body={3},shape={4}",
116 prim.LocalID, forceRebuild, ret, prim.BSBody, prim.BSShape);
117
118 return ret;
119 }
120
121 // Track another user of a body
122 // We presume the caller has allocated the body.
123 // Bodies only have one user so the reference count is either 1 or 0.
124 public void ReferenceBody(BulletBody body, bool inTaintTime)
125 {
126 lock (m_collectionActivityLock)
127 {
128 BodyDesc bodyDesc;
129 if (Bodies.TryGetValue(body.ID, out bodyDesc))
130 {
131 bodyDesc.referenceCount++;
132 DetailLog("{0},BSShapeCollection.ReferenceBody,existingBody,body={1},ref={2}", body.ID, body, bodyDesc.referenceCount);
133 }
134 else
135 {
136 // New entry
137 bodyDesc.ptr = body.ptr;
138 bodyDesc.referenceCount = 1;
139 DetailLog("{0},BSShapeCollection.ReferenceBody,newBody,ref={2}",
140 body.ID, body, bodyDesc.referenceCount);
141 BSScene.TaintCallback createOperation = delegate()
142 {
143 if (!BulletSimAPI.IsInWorld2(body.ptr))
144 {
145 BulletSimAPI.AddObjectToWorld2(PhysicsScene.World.ptr, body.ptr);
146 DetailLog("{0},BSShapeCollection.ReferenceBody,addedToWorld,ref={1}",
147 body.ID, body);
148 }
149 };
150 if (inTaintTime)
151 createOperation();
152 else
153 PhysicsScene.TaintedObject("BSShapeCollection.ReferenceBody", createOperation);
154 }
155 bodyDesc.lastReferenced = System.DateTime.Now;
156 Bodies[body.ID] = bodyDesc;
157 }
158 }
159
160 // Release the usage of a body.
161 // Called when releasing use of a BSBody. BSShape is handled separately.
162 public void DereferenceBody(BulletBody body, bool inTaintTime, BodyDestructionCallback bodyCallback )
163 {
164 if (body.ptr == IntPtr.Zero)
165 return;
166
167 lock (m_collectionActivityLock)
168 {
169 BodyDesc bodyDesc;
170 if (Bodies.TryGetValue(body.ID, out bodyDesc))
171 {
172 bodyDesc.referenceCount--;
173 bodyDesc.lastReferenced = System.DateTime.Now;
174 Bodies[body.ID] = bodyDesc;
175 DetailLog("{0},BSShapeCollection.DereferenceBody,ref={1}", body.ID, bodyDesc.referenceCount);
176
177 // If body is no longer being used, free it -- bodies can never be shared.
178 if (bodyDesc.referenceCount == 0)
179 {
180 Bodies.Remove(body.ID);
181 BSScene.TaintCallback removeOperation = delegate()
182 {
183 DetailLog("{0},BSShapeCollection.DereferenceBody,DestroyingBody. ptr={1}, inTaintTime={2}",
184 body.ID, body.ptr.ToString("X"), inTaintTime);
185 // If the caller needs to know the old body is going away, pass the event up.
186 if (bodyCallback != null) bodyCallback(body);
187
188 // It may have already been removed from the world in which case the next is a NOOP.
189 BulletSimAPI.RemoveObjectFromWorld2(PhysicsScene.World.ptr, body.ptr);
190
191 // Zero any reference to the shape so it is not freed when the body is deleted.
192 BulletSimAPI.SetCollisionShape2(PhysicsScene.World.ptr, body.ptr, IntPtr.Zero);
193 BulletSimAPI.DestroyObject2(PhysicsScene.World.ptr, body.ptr);
194 };
195 // If already in taint-time, do the operations now. Otherwise queue for later.
196 if (inTaintTime)
197 removeOperation();
198 else
199 PhysicsScene.TaintedObject("BSShapeCollection.DereferenceBody", removeOperation);
200 }
201 }
202 else
203 {
204 DetailLog("{0},BSShapeCollection.DereferenceBody,DID NOT FIND BODY", body.ID, bodyDesc.referenceCount);
205 }
206 }
207 }
208
209 // Track the datastructures and use count for a shape.
210 // When creating a hull, this is called first to reference the mesh
211 // and then again to reference the hull.
212 // Meshes and hulls for the same shape have the same hash key.
213 // NOTE that native shapes are not added to the mesh list or removed.
214 // Returns 'true' if this is the initial reference to the shape. Otherwise reused.
215 private bool ReferenceShape(BulletShape shape)
216 {
217 bool ret = false;
218 switch (shape.type)
219 {
220 case ShapeData.PhysicsShapeType.SHAPE_MESH:
221 MeshDesc meshDesc;
222 if (Meshes.TryGetValue(shape.shapeKey, out meshDesc))
223 {
224 // There is an existing instance of this mesh.
225 meshDesc.referenceCount++;
226 DetailLog("{0},BSShapeCollection.ReferenceShape,existingMesh,key={1},cnt={2}",
227 BSScene.DetailLogZero, shape.shapeKey.ToString("X"), meshDesc.referenceCount);
228 }
229 else
230 {
231 // This is a new reference to a mesh
232 meshDesc.ptr = shape.ptr;
233 // We keep a reference to the underlying IMesh data so a hull can be built
234 meshDesc.referenceCount = 1;
235 DetailLog("{0},BSShapeCollection.ReferenceShape,newMesh,key={1},cnt={2}",
236 BSScene.DetailLogZero, shape.shapeKey.ToString("X"), meshDesc.referenceCount);
237 ret = true;
238 }
239 meshDesc.lastReferenced = System.DateTime.Now;
240 Meshes[shape.shapeKey] = meshDesc;
241 break;
242 case ShapeData.PhysicsShapeType.SHAPE_HULL:
243 HullDesc hullDesc;
244 if (Hulls.TryGetValue(shape.shapeKey, out hullDesc))
245 {
246 // There is an existing instance of this hull.
247 hullDesc.referenceCount++;
248 DetailLog("{0},BSShapeCollection.ReferenceShape,existingHull,key={1},cnt={2}",
249 BSScene.DetailLogZero, shape.shapeKey.ToString("X"), hullDesc.referenceCount);
250 }
251 else
252 {
253 // This is a new reference to a hull
254 hullDesc.ptr = shape.ptr;
255 hullDesc.referenceCount = 1;
256 DetailLog("{0},BSShapeCollection.ReferenceShape,newHull,key={1},cnt={2}",
257 BSScene.DetailLogZero, shape.shapeKey.ToString("X"), hullDesc.referenceCount);
258 ret = true;
259
260 }
261 hullDesc.lastReferenced = System.DateTime.Now;
262 Hulls[shape.shapeKey] = hullDesc;
263 break;
264 case ShapeData.PhysicsShapeType.SHAPE_UNKNOWN:
265 break;
266 default:
267 // Native shapes are not tracked and they don't go into any list
268 break;
269 }
270 return ret;
271 }
272
273 // Release the usage of a shape.
274 // The collisionObject is released since it is a copy of the real collision shape.
275 public void DereferenceShape(BulletShape shape, bool inTaintTime, ShapeDestructionCallback shapeCallback)
276 {
277 if (shape.ptr == IntPtr.Zero)
278 return;
279
280 BSScene.TaintCallback dereferenceOperation = delegate()
281 {
282 switch (shape.type)
283 {
284 case ShapeData.PhysicsShapeType.SHAPE_HULL:
285 DereferenceHull(shape, shapeCallback);
286 break;
287 case ShapeData.PhysicsShapeType.SHAPE_MESH:
288 DereferenceMesh(shape, shapeCallback);
289 break;
290 case ShapeData.PhysicsShapeType.SHAPE_UNKNOWN:
291 break;
292 default:
293 // Native shapes are not tracked and are released immediately
294 if (shape.ptr != IntPtr.Zero & shape.isNativeShape)
295 {
296 DetailLog("{0},BSShapeCollection.DereferenceShape,deleteNativeShape,ptr={1},taintTime={2}",
297 BSScene.DetailLogZero, shape.ptr.ToString("X"), inTaintTime);
298 if (shapeCallback != null) shapeCallback(shape);
299 BulletSimAPI.DeleteCollisionShape2(PhysicsScene.World.ptr, shape.ptr);
300 }
301 break;
302 }
303 };
304 if (inTaintTime)
305 {
306 lock (m_collectionActivityLock)
307 {
308 dereferenceOperation();
309 }
310 }
311 else
312 {
313 PhysicsScene.TaintedObject("BSShapeCollection.DereferenceShape", dereferenceOperation);
314 }
315 }
316
317 // Count down the reference count for a mesh shape
318 // Called at taint-time.
319 private void DereferenceMesh(BulletShape shape, ShapeDestructionCallback shapeCallback)
320 {
321 MeshDesc meshDesc;
322 if (Meshes.TryGetValue(shape.shapeKey, out meshDesc))
323 {
324 meshDesc.referenceCount--;
325 // TODO: release the Bullet storage
326 if (shapeCallback != null) shapeCallback(shape);
327 meshDesc.lastReferenced = System.DateTime.Now;
328 Meshes[shape.shapeKey] = meshDesc;
329 DetailLog("{0},BSShapeCollection.DereferenceMesh,key={1},refCnt={2}",
330 BSScene.DetailLogZero, shape.shapeKey.ToString("X"), meshDesc.referenceCount);
331
332 }
333 }
334
335 // Count down the reference count for a hull shape
336 // Called at taint-time.
337 private void DereferenceHull(BulletShape shape, ShapeDestructionCallback shapeCallback)
338 {
339 HullDesc hullDesc;
340 if (Hulls.TryGetValue(shape.shapeKey, out hullDesc))
341 {
342 hullDesc.referenceCount--;
343 // TODO: release the Bullet storage (aging old entries?)
344 if (shapeCallback != null) shapeCallback(shape);
345 hullDesc.lastReferenced = System.DateTime.Now;
346 Hulls[shape.shapeKey] = hullDesc;
347 DetailLog("{0},BSShapeCollection.DereferenceHull,key={1},refCnt={2}",
348 BSScene.DetailLogZero, shape.shapeKey.ToString("X"), hullDesc.referenceCount);
349 }
350 }
351
352 // Create the geometry information in Bullet for later use.
353 // The objects needs a hull if it's physical otherwise a mesh is enough.
354 // No locking here because this is done when we know physics is not simulating.
355 // if 'forceRebuild' is true, the geometry is rebuilt. Otherwise a previously built version is used.
356 // Returns 'true' if the geometry was rebuilt.
357 // Called at taint-time!
358 private bool CreateGeom(bool forceRebuild, BSPrim prim, ShapeData shapeData,
359 PrimitiveBaseShape pbs, ShapeDestructionCallback shapeCallback)
360 {
361 bool ret = false;
362 bool haveShape = false;
363 bool nativeShapePossible = true;
364
365 // If the prim attributes are simple, this could be a simple Bullet native shape
366 if (nativeShapePossible
367 && ((pbs.SculptEntry && !PhysicsScene.ShouldMeshSculptedPrim)
368 || (pbs.ProfileBegin == 0 && pbs.ProfileEnd == 0
369 && pbs.ProfileHollow == 0
370 && pbs.PathTwist == 0 && pbs.PathTwistBegin == 0
371 && pbs.PathBegin == 0 && pbs.PathEnd == 0
372 && pbs.PathTaperX == 0 && pbs.PathTaperY == 0
373 && pbs.PathScaleX == 100 && pbs.PathScaleY == 100
374 && pbs.PathShearX == 0 && pbs.PathShearY == 0) ) )
375 {
376 if ((pbs.ProfileShape == ProfileShape.HalfCircle && pbs.PathCurve == (byte)Extrusion.Curve1)
377 && pbs.Scale.X == pbs.Scale.Y && pbs.Scale.Y == pbs.Scale.Z)
378 {
379 haveShape = true;
380 if (forceRebuild
381 || prim.Scale != shapeData.Size
382 || prim.BSShape.type != ShapeData.PhysicsShapeType.SHAPE_SPHERE
383 )
384 {
385 ret = GetReferenceToNativeShape(prim, shapeData, ShapeData.PhysicsShapeType.SHAPE_SPHERE,
386 ShapeData.FixedShapeKey.KEY_SPHERE, shapeCallback);
387 DetailLog("{0},BSShapeCollection.CreateGeom,sphere,force={1},shape={2}",
388 prim.LocalID, forceRebuild, prim.BSShape);
389 }
390 }
391 if (pbs.ProfileShape == ProfileShape.Square && pbs.PathCurve == (byte)Extrusion.Straight)
392 {
393 haveShape = true;
394 if (forceRebuild
395 || prim.Scale != shapeData.Size
396 || prim.BSShape.type != ShapeData.PhysicsShapeType.SHAPE_BOX
397 )
398 {
399 ret = GetReferenceToNativeShape( prim, shapeData, ShapeData.PhysicsShapeType.SHAPE_BOX,
400 ShapeData.FixedShapeKey.KEY_BOX, shapeCallback);
401 DetailLog("{0},BSShapeCollection.CreateGeom,box,force={1},shape={2}",
402 prim.LocalID, forceRebuild, prim.BSShape);
403 }
404 }
405 }
406 // If a simple shape is not happening, create a mesh and possibly a hull.
407 // Note that if it's a native shape, the check for physical/non-physical is not
408 // made. Native shapes are best used in either case.
409 if (!haveShape)
410 {
411 if (prim.IsPhysical && PhysicsScene.ShouldUseHullsForPhysicalObjects)
412 {
413 // Update prim.BSShape to reference a hull of this shape.
414 ret = GetReferenceToHull(prim, shapeData, pbs, shapeCallback);
415 DetailLog("{0},BSShapeCollection.CreateGeom,hull,shape={1},key={2}",
416 shapeData.ID, prim.BSShape, prim.BSShape.shapeKey.ToString("X"));
417 }
418 else
419 {
420 ret = GetReferenceToMesh(prim, shapeData, pbs, shapeCallback);
421 DetailLog("{0},BSShapeCollection.CreateGeom,mesh,shape={1},key={2}",
422 shapeData.ID, prim.BSShape, prim.BSShape.shapeKey.ToString("X"));
423 }
424 }
425 return ret;
426 }
427
428 // Creates a native shape and assignes it to prim.BSShape
429 private bool GetReferenceToNativeShape( BSPrim prim, ShapeData shapeData,
430 ShapeData.PhysicsShapeType shapeType, ShapeData.FixedShapeKey shapeKey,
431 ShapeDestructionCallback shapeCallback)
432 {
433 BulletShape newShape;
434
435 shapeData.Type = shapeType;
436 // Bullet native objects are scaled by the Bullet engine so pass the size in
437 prim.Scale = shapeData.Size;
438 shapeData.Scale = shapeData.Size;
439
440 // release any previous shape
441 DereferenceShape(prim.BSShape, true, shapeCallback);
442
443 // Native shapes are always built independently.
444 newShape = new BulletShape(BulletSimAPI.BuildNativeShape2(PhysicsScene.World.ptr, shapeData), shapeType);
445 newShape.shapeKey = (System.UInt64)shapeKey;
446 newShape.isNativeShape = true;
447
448 // Don't need to do a 'ReferenceShape()' here because native shapes are not tracked.
449 // DetailLog("{0},BSShapeCollection.AddNativeShapeToPrim,create,newshape={1}", shapeData.ID, newShape);
450
451 prim.BSShape = newShape;
452 return true;
453 }
454
455 // Builds a mesh shape in the physical world and updates prim.BSShape.
456 // Dereferences previous shape in BSShape and adds a reference for this new shape.
457 // Returns 'true' of a mesh was actually built. Otherwise .
458 // Called at taint-time!
459 private bool GetReferenceToMesh(BSPrim prim, ShapeData shapeData, PrimitiveBaseShape pbs,
460 ShapeDestructionCallback shapeCallback)
461 {
462 BulletShape newShape = new BulletShape(IntPtr.Zero);
463
464 float lod;
465 System.UInt64 newMeshKey = ComputeShapeKey(shapeData, pbs, out lod);
466
467 // if this new shape is the same as last time, don't recreate the mesh
468 if (newMeshKey == prim.BSShape.shapeKey && prim.BSShape.type == ShapeData.PhysicsShapeType.SHAPE_MESH)
469 return false;
470
471 DetailLog("{0},BSShapeCollection.CreateGeomMesh,create,oldKey={1},newKey={2}",
472 prim.LocalID, prim.BSShape.shapeKey.ToString("X"), newMeshKey.ToString("X"));
473
474 // Since we're recreating new, get rid of the reference to the previous shape
475 DereferenceShape(prim.BSShape, true, shapeCallback);
476
477 newShape = CreatePhysicalMesh(prim.PhysObjectName, newMeshKey, pbs, shapeData.Size, lod);
478
479 ReferenceShape(newShape);
480
481 // meshes are already scaled by the meshmerizer
482 prim.Scale = new OMV.Vector3(1f, 1f, 1f);
483 prim.BSShape = newShape;
484
485 return true; // 'true' means a new shape has been added to this prim
486 }
487
488 private BulletShape CreatePhysicalMesh(string objName, System.UInt64 newMeshKey, PrimitiveBaseShape pbs, OMV.Vector3 size, float lod)
489 {
490 IMesh meshData = null;
491 IntPtr meshPtr;
492 MeshDesc meshDesc;
493 if (Meshes.TryGetValue(newMeshKey, out meshDesc))
494 {
495 // If the mesh has already been built just use it.
496 meshPtr = meshDesc.ptr;
497 }
498 else
499 {
500 // Pass false for physicalness as this creates some sort of bounding box which we don't need
501 meshData = PhysicsScene.mesher.CreateMesh(objName, pbs, size, lod, false);
502
503 int[] indices = meshData.getIndexListAsInt();
504 List<OMV.Vector3> vertices = meshData.getVertexList();
505
506 float[] verticesAsFloats = new float[vertices.Count * 3];
507 int vi = 0;
508 foreach (OMV.Vector3 vv in vertices)
509 {
510 verticesAsFloats[vi++] = vv.X;
511 verticesAsFloats[vi++] = vv.Y;
512 verticesAsFloats[vi++] = vv.Z;
513 }
514
515 // m_log.DebugFormat("{0}: CreateGeomMesh: calling CreateMesh. lid={1}, key={2}, indices={3}, vertices={4}",
516 // LogHeader, prim.LocalID, newMeshKey, indices.Length, vertices.Count);
517
518 meshPtr = BulletSimAPI.CreateMeshShape2(PhysicsScene.World.ptr,
519 indices.GetLength(0), indices, vertices.Count, verticesAsFloats);
520 }
521 BulletShape newShape = new BulletShape(meshPtr, ShapeData.PhysicsShapeType.SHAPE_MESH);
522 newShape.shapeKey = newMeshKey;
523
524 return newShape;
525 }
526
527 // See that hull shape exists in the physical world and update prim.BSShape.
528 // We could be creating the hull because scale changed or whatever.
529 private bool GetReferenceToHull(BSPrim prim, ShapeData shapeData, PrimitiveBaseShape pbs,
530 ShapeDestructionCallback shapeCallback)
531 {
532 BulletShape newShape;
533
534 float lod;
535 System.UInt64 newHullKey = ComputeShapeKey(shapeData, pbs, out lod);
536
537 // if the hull hasn't changed, don't rebuild it
538 if (newHullKey == prim.BSShape.shapeKey && prim.BSShape.type == ShapeData.PhysicsShapeType.SHAPE_HULL)
539 return false;
540
541 DetailLog("{0},BSShapeCollection.CreateGeomHull,create,oldKey={1},newKey={2}",
542 prim.LocalID, prim.BSShape.shapeKey.ToString("X"), newHullKey.ToString("X"));
543
544 // Remove usage of the previous shape.
545 DereferenceShape(prim.BSShape, true, shapeCallback);
546
547 newShape = CreatePhysicalHull(prim.PhysObjectName, newHullKey, pbs, shapeData.Size, lod);
548
549 ReferenceShape(newShape);
550
551 // hulls are already scaled by the meshmerizer
552 prim.Scale = new OMV.Vector3(1f, 1f, 1f);
553 prim.BSShape = newShape;
554 return true; // 'true' means a new shape has been added to this prim
555 }
556
557 List<ConvexResult> m_hulls;
558 private BulletShape CreatePhysicalHull(string objName, System.UInt64 newHullKey, PrimitiveBaseShape pbs, OMV.Vector3 size, float lod)
559 {
560
561 IntPtr hullPtr;
562 HullDesc hullDesc;
563 if (Hulls.TryGetValue(newHullKey, out hullDesc))
564 {
565 // If the hull shape already is created, just use it.
566 hullPtr = hullDesc.ptr;
567 }
568 else
569 {
570 // Build a new hull in the physical world
571 // Pass false for physicalness as this creates some sort of bounding box which we don't need
572 IMesh meshData = PhysicsScene.mesher.CreateMesh(objName, pbs, size, lod, false);
573
574 int[] indices = meshData.getIndexListAsInt();
575 List<OMV.Vector3> vertices = meshData.getVertexList();
576
577 //format conversion from IMesh format to DecompDesc format
578 List<int> convIndices = new List<int>();
579 List<float3> convVertices = new List<float3>();
580 for (int ii = 0; ii < indices.GetLength(0); ii++)
581 {
582 convIndices.Add(indices[ii]);
583 }
584 foreach (OMV.Vector3 vv in vertices)
585 {
586 convVertices.Add(new float3(vv.X, vv.Y, vv.Z));
587 }
588
589 // setup and do convex hull conversion
590 m_hulls = new List<ConvexResult>();
591 DecompDesc dcomp = new DecompDesc();
592 dcomp.mIndices = convIndices;
593 dcomp.mVertices = convVertices;
594 ConvexBuilder convexBuilder = new ConvexBuilder(HullReturn);
595 // create the hull into the _hulls variable
596 convexBuilder.process(dcomp);
597
598 // Convert the vertices and indices for passing to unmanaged.
599 // The hull information is passed as a large floating point array.
600 // The format is:
601 // convHulls[0] = number of hulls
602 // convHulls[1] = number of vertices in first hull
603 // convHulls[2] = hull centroid X coordinate
604 // convHulls[3] = hull centroid Y coordinate
605 // convHulls[4] = hull centroid Z coordinate
606 // convHulls[5] = first hull vertex X
607 // convHulls[6] = first hull vertex Y
608 // convHulls[7] = first hull vertex Z
609 // convHulls[8] = second hull vertex X
610 // ...
611 // convHulls[n] = number of vertices in second hull
612 // convHulls[n+1] = second hull centroid X coordinate
613 // ...
614 //
615 // TODO: is is very inefficient. Someday change the convex hull generator to return
616 // data structures that do not need to be converted in order to pass to Bullet.
617 // And maybe put the values directly into pinned memory rather than marshaling.
618 int hullCount = m_hulls.Count;
619 int totalVertices = 1; // include one for the count of the hulls
620 foreach (ConvexResult cr in m_hulls)
621 {
622 totalVertices += 4; // add four for the vertex count and centroid
623 totalVertices += cr.HullIndices.Count * 3; // we pass just triangles
624 }
625 float[] convHulls = new float[totalVertices];
626
627 convHulls[0] = (float)hullCount;
628 int jj = 1;
629 foreach (ConvexResult cr in m_hulls)
630 {
631 // copy vertices for index access
632 float3[] verts = new float3[cr.HullVertices.Count];
633 int kk = 0;
634 foreach (float3 ff in cr.HullVertices)
635 {
636 verts[kk++] = ff;
637 }
638
639 // add to the array one hull's worth of data
640 convHulls[jj++] = cr.HullIndices.Count;
641 convHulls[jj++] = 0f; // centroid x,y,z
642 convHulls[jj++] = 0f;
643 convHulls[jj++] = 0f;
644 foreach (int ind in cr.HullIndices)
645 {
646 convHulls[jj++] = verts[ind].x;
647 convHulls[jj++] = verts[ind].y;
648 convHulls[jj++] = verts[ind].z;
649 }
650 }
651 // create the hull data structure in Bullet
652 hullPtr = BulletSimAPI.CreateHullShape2(PhysicsScene.World.ptr, hullCount, convHulls);
653 }
654
655 BulletShape newShape = new BulletShape(hullPtr, ShapeData.PhysicsShapeType.SHAPE_HULL);
656 newShape.shapeKey = newHullKey;
657
658 return newShape; // 'true' means a new shape has been added to this prim
659 }
660
661 // Callback from convex hull creater with a newly created hull.
662 // Just add it to our collection of hulls for this shape.
663 private void HullReturn(ConvexResult result)
664 {
665 m_hulls.Add(result);
666 return;
667 }
668
669 // Create a hash of all the shape parameters to be used as a key
670 // for this particular shape.
671 private System.UInt64 ComputeShapeKey(ShapeData shapeData, PrimitiveBaseShape pbs, out float retLod)
672 {
673 // level of detail based on size and type of the object
674 float lod = PhysicsScene.MeshLOD;
675 if (pbs.SculptEntry)
676 lod = PhysicsScene.SculptLOD;
677
678 // Mega prims usually get more detail because one can interact with shape approximations at this size.
679 float maxAxis = Math.Max(shapeData.Size.X, Math.Max(shapeData.Size.Y, shapeData.Size.Z));
680 if (maxAxis > PhysicsScene.MeshMegaPrimThreshold)
681 lod = PhysicsScene.MeshMegaPrimLOD;
682
683 retLod = lod;
684 return pbs.GetMeshKey(shapeData.Size, lod);
685 }
686 // For those who don't want the LOD
687 private System.UInt64 ComputeShapeKey(ShapeData shapeData, PrimitiveBaseShape pbs)
688 {
689 float lod;
690 return ComputeShapeKey(shapeData, pbs, out lod);
691 }
692
693 // Create a body object in Bullet.
694 // Updates prim.BSBody with the information about the new body if one is created.
695 // Returns 'true' if an object was actually created.
696 // Called at taint-time.
697 private bool CreateBody(bool forceRebuild, BSPrim prim, BulletSim sim, BulletShape shape,
698 ShapeData shapeData, BodyDestructionCallback bodyCallback)
699 {
700 bool ret = false;
701
702 // the mesh, hull or native shape must have already been created in Bullet
703 bool mustRebuild = (prim.BSBody.ptr == IntPtr.Zero);
704
705 // If there is an existing body, verify it's of an acceptable type.
706 // If not a solid object, body is a GhostObject. Otherwise a RigidBody.
707 if (!mustRebuild)
708 {
709 CollisionObjectTypes bodyType = (CollisionObjectTypes)BulletSimAPI.GetBodyType2(prim.BSBody.ptr);
710 if (prim.IsSolid && bodyType != CollisionObjectTypes.CO_RIGID_BODY
711 || !prim.IsSolid && bodyType != CollisionObjectTypes.CO_GHOST_OBJECT)
712 {
713 // If the collisionObject is not the correct type for solidness, rebuild what's there
714 mustRebuild = true;
715 }
716
717 }
718
719 if (mustRebuild || forceRebuild)
720 {
721 // Free any old body
722 DereferenceBody(prim.BSBody, true, bodyCallback);
723
724 BulletBody aBody;
725 IntPtr bodyPtr = IntPtr.Zero;
726 if (prim.IsSolid)
727 {
728 bodyPtr = BulletSimAPI.CreateBodyFromShape2(sim.ptr, shape.ptr,
729 shapeData.ID, shapeData.Position, shapeData.Rotation);
730 DetailLog("{0},BSShapeCollection.CreateBody,mesh,ptr={1}", prim.LocalID, bodyPtr.ToString("X"));
731 }
732 else
733 {
734 bodyPtr = BulletSimAPI.CreateGhostFromShape2(sim.ptr, shape.ptr,
735 shapeData.ID, shapeData.Position, shapeData.Rotation);
736 DetailLog("{0},BSShapeCollection.CreateBody,ghost,ptr={1}", prim.LocalID, bodyPtr.ToString("X"));
737 }
738 aBody = new BulletBody(shapeData.ID, bodyPtr);
739
740 ReferenceBody(aBody, true);
741
742 prim.BSBody = aBody;
743
744 ret = true;
745 }
746
747 return ret;
748 }
749
750 private void DetailLog(string msg, params Object[] args)
751 {
752 if (PhysicsScene.PhysicsLogging.Enabled)
753 PhysicsScene.DetailLog(msg, args);
754 }
755}
756}