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