OpenSim
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Events Macros
BSShapeCollection.cs
Go to the documentation of this file.
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  */
27 using System;
28 using System.Collections.Generic;
29 using System.Text;
30 using OMV = OpenMetaverse;
31 using OpenSim.Framework;
32 using OpenSim.Region.PhysicsModules.SharedBase;
33 
34 namespace OpenSim.Region.PhysicsModule.BulletS
35 {
36 public sealed class BSShapeCollection : IDisposable
37 {
38 #pragma warning disable 414
39  private static string LogHeader = "[BULLETSIM SHAPE COLLECTION]";
40 #pragma warning restore 414
41 
42  private BSScene m_physicsScene { get; set; }
43 
44  private Object m_collectionActivityLock = new Object();
45 
46  private bool DDetail = false;
47 
48  public BSShapeCollection(BSScene physScene)
49  {
50  m_physicsScene = physScene;
51  // Set the next to 'true' for very detailed shape update detailed logging (detailed details?)
52  // While detailed debugging is still active, this is better than commenting out all the
53  // DetailLog statements. When debugging slows down, this and the protected logging
54  // statements can be commented/removed.
55  DDetail = true;
56  }
57 
58  public void Dispose()
59  {
60  // TODO!!!!!!!!!
61  }
62 
63  // Callbacks called just before either the body or shape is destroyed.
64  // Mostly used for changing bodies out from under Linksets.
65  // Useful for other cases where parameters need saving.
66  // Passing 'null' says no callback.
67  public delegate void PhysicalDestructionCallback(BulletBody pBody, BulletShape pShape);
68 
69  // Called to update/change the body and shape for an object.
70  // The object has some shape and body on it. Here we decide if that is the correct shape
71  // for the current state of the object (static/dynamic/...).
72  // If bodyCallback is not null, it is called if either the body or the shape are changed
73  // so dependencies (like constraints) can be removed before the physical object is dereferenced.
74  // Return 'true' if either the body or the shape changed.
75  // Called at taint-time.
76  public bool GetBodyAndShape(bool forceRebuild, BulletWorld sim, BSPhysObject prim, PhysicalDestructionCallback bodyCallback)
77  {
78  m_physicsScene.AssertInTaintTime("BSShapeCollection.GetBodyAndShape");
79 
80  bool ret = false;
81 
82  // This lock could probably be pushed down lower but building shouldn't take long
83  lock (m_collectionActivityLock)
84  {
85  // Do we have the correct geometry for this type of object?
86  // Updates prim.BSShape with information/pointers to shape.
87  // Returns 'true' of BSShape is changed to a new shape.
88  bool newGeom = CreateGeom(forceRebuild, prim, bodyCallback);
89  // If we had to select a new shape geometry for the object,
90  // rebuild the body around it.
91  // Updates prim.BSBody with information/pointers to requested body
92  // Returns 'true' if BSBody was changed.
93  bool newBody = CreateBody((newGeom || forceRebuild), prim, m_physicsScene.World, bodyCallback);
94  ret = newGeom || newBody;
95  }
96  DetailLog("{0},BSShapeCollection.GetBodyAndShape,taintExit,force={1},ret={2},body={3},shape={4}",
97  prim.LocalID, forceRebuild, ret, prim.PhysBody, prim.PhysShape);
98 
99  return ret;
100  }
101 
102  public bool GetBodyAndShape(bool forceRebuild, BulletWorld sim, BSPhysObject prim)
103  {
104  return GetBodyAndShape(forceRebuild, sim, prim, null);
105  }
106 
107  // If the existing prim's shape is to be replaced, remove the tie to the existing shape
108  // before replacing it.
109  private void DereferenceExistingShape(BSPhysObject prim, PhysicalDestructionCallback shapeCallback)
110  {
111  if (prim.PhysShape.HasPhysicalShape)
112  {
113  if (shapeCallback != null)
114  shapeCallback(prim.PhysBody, prim.PhysShape.physShapeInfo);
115  prim.PhysShape.Dereference(m_physicsScene);
116  }
117  prim.PhysShape = new BSShapeNull();
118  }
119 
120  // Create the geometry information in Bullet for later use.
121  // The objects needs a hull if it's physical otherwise a mesh is enough.
122  // if 'forceRebuild' is true, the geometry is unconditionally rebuilt. For meshes and hulls,
123  // shared geometries will be used. If the parameters of the existing shape are the same
124  // as this request, the shape is not rebuilt.
125  // Info in prim.BSShape is updated to the new shape.
126  // Returns 'true' if the geometry was rebuilt.
127  // Called at taint-time!
128  public const int AvatarShapeCapsule = 0;
129  public const int AvatarShapeCube = 1;
130  public const int AvatarShapeOvoid = 2;
131  public const int AvatarShapeMesh = 3;
132  private bool CreateGeom(bool forceRebuild, BSPhysObject prim, PhysicalDestructionCallback shapeCallback)
133  {
134  bool ret = false;
135  bool haveShape = false;
136  bool nativeShapePossible = true;
137  PrimitiveBaseShape pbs = prim.BaseShape;
138 
139  // Kludge to create the capsule for the avatar.
140  // TDOD: Remove/redo this when BSShapeAvatar is working!!
141  BSCharacter theChar = prim as BSCharacter;
142  if (theChar != null)
143  {
144  DereferenceExistingShape(prim, shapeCallback);
145  switch (BSParam.AvatarShape)
146  {
147  case AvatarShapeCapsule:
148  prim.PhysShape = BSShapeNative.GetReference(m_physicsScene, prim,
149  BSPhysicsShapeType.SHAPE_CAPSULE, FixedShapeKey.KEY_CAPSULE);
150  ret = true;
151  haveShape = true;
152  break;
153  case AvatarShapeCube:
154  prim.PhysShape = BSShapeNative.GetReference(m_physicsScene, prim,
155  BSPhysicsShapeType.SHAPE_BOX, FixedShapeKey.KEY_CAPSULE);
156  ret = true;
157  haveShape = true;
158  break;
159  case AvatarShapeOvoid:
160  // Saddly, Bullet doesn't scale spheres so this doesn't work as an avatar shape
161  prim.PhysShape = BSShapeNative.GetReference(m_physicsScene, prim,
162  BSPhysicsShapeType.SHAPE_SPHERE, FixedShapeKey.KEY_CAPSULE);
163  ret = true;
164  haveShape = true;
165  break;
166  case AvatarShapeMesh:
167  break;
168  default:
169  break;
170  }
171  }
172 
173  // If the prim attributes are simple, this could be a simple Bullet native shape
174  // Native shapes work whether to object is static or physical.
175  if (!haveShape
176  && nativeShapePossible
177  && pbs != null
178  && PrimHasNoCuts(pbs)
179  && ( !pbs.SculptEntry || (pbs.SculptEntry && !BSParam.ShouldMeshSculptedPrim) )
180  )
181  {
182  // Get the scale of any existing shape so we can see if the new shape is same native type and same size.
183  OMV.Vector3 scaleOfExistingShape = OMV.Vector3.Zero;
184  if (prim.PhysShape.HasPhysicalShape)
185  scaleOfExistingShape = m_physicsScene.PE.GetLocalScaling(prim.PhysShape.physShapeInfo);
186 
187  if (DDetail) DetailLog("{0},BSShapeCollection.CreateGeom,maybeNative,force={1},primScale={2},primSize={3},primShape={4}",
188  prim.LocalID, forceRebuild, prim.Scale, prim.Size, prim.PhysShape.physShapeInfo.shapeType);
189 
190  // It doesn't look like Bullet scales native spheres so make sure the scales are all equal
191  if ((pbs.ProfileShape == ProfileShape.HalfCircle && pbs.PathCurve == (byte)Extrusion.Curve1)
192  && pbs.Scale.X == pbs.Scale.Y && pbs.Scale.Y == pbs.Scale.Z)
193  {
194  haveShape = true;
195  if (forceRebuild
196  || prim.PhysShape.ShapeType != BSPhysicsShapeType.SHAPE_SPHERE
197  )
198  {
199  DereferenceExistingShape(prim, shapeCallback);
200  prim.PhysShape = BSShapeNative.GetReference(m_physicsScene, prim,
201  BSPhysicsShapeType.SHAPE_SPHERE, FixedShapeKey.KEY_SPHERE);
202  ret = true;
203  }
204  if (DDetail) DetailLog("{0},BSShapeCollection.CreateGeom,sphere,force={1},rebuilt={2},shape={3}",
205  prim.LocalID, forceRebuild, ret, prim.PhysShape);
206  }
207  // If we didn't make a sphere, maybe a box will work.
208  if (!haveShape && pbs.ProfileShape == ProfileShape.Square && pbs.PathCurve == (byte)Extrusion.Straight)
209  {
210  haveShape = true;
211  if (forceRebuild
212  || prim.Scale != scaleOfExistingShape
213  || prim.PhysShape.ShapeType != BSPhysicsShapeType.SHAPE_BOX
214  )
215  {
216  DereferenceExistingShape(prim, shapeCallback);
217  prim.PhysShape = BSShapeNative.GetReference(m_physicsScene, prim,
218  BSPhysicsShapeType.SHAPE_BOX, FixedShapeKey.KEY_BOX);
219  ret = true;
220  }
221  if (DDetail) DetailLog("{0},BSShapeCollection.CreateGeom,box,force={1},rebuilt={2},shape={3}",
222  prim.LocalID, forceRebuild, ret, prim.PhysShape);
223  }
224  }
225 
226  // If a simple shape is not happening, create a mesh and possibly a hull.
227  if (!haveShape && pbs != null)
228  {
229  ret = CreateGeomMeshOrHull(prim, shapeCallback);
230  }
231 
232  return ret;
233  }
234 
235  // return 'true' if this shape description does not include any cutting or twisting.
236  public static bool PrimHasNoCuts(PrimitiveBaseShape pbs)
237  {
238  return pbs.ProfileBegin == 0 && pbs.ProfileEnd == 0
239  && pbs.ProfileHollow == 0
240  && pbs.PathTwist == 0 && pbs.PathTwistBegin == 0
241  && pbs.PathBegin == 0 && pbs.PathEnd == 0
242  && pbs.PathTaperX == 0 && pbs.PathTaperY == 0
243  && pbs.PathScaleX == 100 && pbs.PathScaleY == 100
244  && pbs.PathShearX == 0 && pbs.PathShearY == 0;
245  }
246 
247  // return 'true' if the prim's shape was changed.
248  private bool CreateGeomMeshOrHull(BSPhysObject prim, PhysicalDestructionCallback shapeCallback)
249  {
250 
251  bool ret = false;
252  // Note that if it's a native shape, the check for physical/non-physical is not
253  // made. Native shapes work in either case.
254  if (prim.IsPhysical && BSParam.ShouldUseHullsForPhysicalObjects)
255  {
256  // Use a simple, single mesh convex hull shape if the object is simple enough
257  BSShape potentialHull = null;
258 
259  PrimitiveBaseShape pbs = prim.BaseShape;
260  // Use a simple, one section convex shape for prims that are probably convex (no cuts or twists)
261  if (BSParam.ShouldUseSingleConvexHullForPrims
262  && pbs != null
263  && !pbs.SculptEntry
264  && PrimHasNoCuts(pbs)
265  )
266  {
267  potentialHull = BSShapeConvexHull.GetReference(m_physicsScene, false /* forceRebuild */, prim);
268  }
269  // Use the GImpact shape if it is a prim that has some concaveness
270  if (potentialHull == null
271  && BSParam.ShouldUseGImpactShapeForPrims
272  && pbs != null
273  && !pbs.SculptEntry
274  )
275  {
276  potentialHull = BSShapeGImpact.GetReference(m_physicsScene, false /* forceRebuild */, prim);
277  }
278  // If not any of the simple cases, just make a hull
279  if (potentialHull == null)
280  {
281  potentialHull = BSShapeHull.GetReference(m_physicsScene, false /*forceRebuild*/, prim);
282  }
283 
284  // If the current shape is not what is on the prim at the moment, time to change.
285  if (!prim.PhysShape.HasPhysicalShape
286  || potentialHull.ShapeType != prim.PhysShape.ShapeType
287  || potentialHull.physShapeInfo.shapeKey != prim.PhysShape.physShapeInfo.shapeKey)
288  {
289  DereferenceExistingShape(prim, shapeCallback);
290  prim.PhysShape = potentialHull;
291  ret = true;
292  }
293  else
294  {
295  // The current shape on the prim is the correct one. We don't need the potential reference.
296  potentialHull.Dereference(m_physicsScene);
297  }
298  if (DDetail) DetailLog("{0},BSShapeCollection.CreateGeom,hull,shape={1}", prim.LocalID, prim.PhysShape);
299  }
300  else
301  {
302  // Non-physical objects should be just meshes.
303  BSShape potentialMesh = BSShapeMesh.GetReference(m_physicsScene, false /*forceRebuild*/, prim);
304  // If the current shape is not what is on the prim at the moment, time to change.
305  if (!prim.PhysShape.HasPhysicalShape
306  || potentialMesh.ShapeType != prim.PhysShape.ShapeType
307  || potentialMesh.physShapeInfo.shapeKey != prim.PhysShape.physShapeInfo.shapeKey)
308  {
309  DereferenceExistingShape(prim, shapeCallback);
310  prim.PhysShape = potentialMesh;
311  ret = true;
312  }
313  else
314  {
315  // We don't need this reference to the mesh that is already being using.
316  potentialMesh.Dereference(m_physicsScene);
317  }
318  if (DDetail) DetailLog("{0},BSShapeCollection.CreateGeom,mesh,shape={1}", prim.LocalID, prim.PhysShape);
319  }
320  return ret;
321  }
322 
323  // Track another user of a body.
324  // We presume the caller has allocated the body.
325  // Bodies only have one user so the body is just put into the world if not already there.
326  private void ReferenceBody(BulletBody body)
327  {
328  lock (m_collectionActivityLock)
329  {
330  if (DDetail) DetailLog("{0},BSShapeCollection.ReferenceBody,newBody,body={1}", body.ID, body);
331  if (!m_physicsScene.PE.IsInWorld(m_physicsScene.World, body))
332  {
333  m_physicsScene.PE.AddObjectToWorld(m_physicsScene.World, body);
334  if (DDetail) DetailLog("{0},BSShapeCollection.ReferenceBody,addedToWorld,ref={1}", body.ID, body);
335  }
336  }
337  }
338 
339  // Release the usage of a body.
340  // Called when releasing use of a BSBody. BSShape is handled separately.
341  // Called in taint time.
342  public void DereferenceBody(BulletBody body, PhysicalDestructionCallback bodyCallback )
343  {
344  if (!body.HasPhysicalBody)
345  return;
346 
347  m_physicsScene.AssertInTaintTime("BSShapeCollection.DereferenceBody");
348 
349  lock (m_collectionActivityLock)
350  {
351  if (DDetail) DetailLog("{0},BSShapeCollection.DereferenceBody,DestroyingBody,body={1}", body.ID, body);
352  // If the caller needs to know the old body is going away, pass the event up.
353  if (bodyCallback != null)
354  bodyCallback(body, null);
355 
356  // Removing an object not in the world is a NOOP
357  m_physicsScene.PE.RemoveObjectFromWorld(m_physicsScene.World, body);
358 
359  // Zero any reference to the shape so it is not freed when the body is deleted.
360  m_physicsScene.PE.SetCollisionShape(m_physicsScene.World, body, null);
361 
362  m_physicsScene.PE.DestroyObject(m_physicsScene.World, body);
363  }
364  }
365 
366  // Create a body object in Bullet.
367  // Updates prim.BSBody with the information about the new body if one is created.
368  // Returns 'true' if an object was actually created.
369  // Called at taint-time.
370  private bool CreateBody(bool forceRebuild, BSPhysObject prim, BulletWorld sim, PhysicalDestructionCallback bodyCallback)
371  {
372  bool ret = false;
373 
374  // the mesh, hull or native shape must have already been created in Bullet
375  bool mustRebuild = !prim.PhysBody.HasPhysicalBody;
376 
377  // If there is an existing body, verify it's of an acceptable type.
378  // If not a solid object, body is a GhostObject. Otherwise a RigidBody.
379  if (!mustRebuild)
380  {
381  CollisionObjectTypes bodyType = (CollisionObjectTypes)m_physicsScene.PE.GetBodyType(prim.PhysBody);
382  if (prim.IsSolid && bodyType != CollisionObjectTypes.CO_RIGID_BODY
383  || !prim.IsSolid && bodyType != CollisionObjectTypes.CO_GHOST_OBJECT)
384  {
385  // If the collisionObject is not the correct type for solidness, rebuild what's there
386  mustRebuild = true;
387  if (DDetail) DetailLog("{0},BSShapeCollection.CreateBody,forceRebuildBecauseChangingBodyType,bodyType={1}", prim.LocalID, bodyType);
388  }
389  }
390 
391  if (mustRebuild || forceRebuild)
392  {
393  // Free any old body
394  DereferenceBody(prim.PhysBody, bodyCallback);
395 
396  BulletBody aBody;
397  if (prim.IsSolid)
398  {
399  aBody = m_physicsScene.PE.CreateBodyFromShape(sim, prim.PhysShape.physShapeInfo, prim.LocalID, prim.RawPosition, prim.RawOrientation);
400  if (DDetail) DetailLog("{0},BSShapeCollection.CreateBody,rigid,body={1}", prim.LocalID, aBody);
401  }
402  else
403  {
404  aBody = m_physicsScene.PE.CreateGhostFromShape(sim, prim.PhysShape.physShapeInfo, prim.LocalID, prim.RawPosition, prim.RawOrientation);
405  if (DDetail) DetailLog("{0},BSShapeCollection.CreateBody,ghost,body={1}", prim.LocalID, aBody);
406  }
407 
408  ReferenceBody(aBody);
409 
410  prim.PhysBody = aBody;
411 
412  ret = true;
413  }
414 
415  return ret;
416  }
417 
418  private void DetailLog(string msg, params Object[] args)
419  {
420  if (m_physicsScene.PhysicsLogging.Enabled)
421  m_physicsScene.DetailLog(msg, args);
422  }
423 }
424 }
OpenMetaverse OMV
bool GetBodyAndShape(bool forceRebuild, BulletWorld sim, BSPhysObject prim, PhysicalDestructionCallback bodyCallback)
bool GetBodyAndShape(bool forceRebuild, BulletWorld sim, BSPhysObject prim)
virtual BSPhysicsShapeType ShapeType
Definition: BSShapes.cs:168
void DereferenceBody(BulletBody body, PhysicalDestructionCallback bodyCallback)