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