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