OpenSim
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Events Macros
BSLinksetCompound.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 
31 using OpenSim.Framework;
32 
33 using OMV = OpenMetaverse;
34 
35 namespace OpenSim.Region.PhysicsModule.BulletS
36 {
37 
38 public sealed class BSLinksetCompound : BSLinkset
39 {
40 #pragma warning disable 414
41  private static string LogHeader = "[BULLETSIM LINKSET COMPOUND]";
42 #pragma warning restore 414
43 
45  : base(scene, parent)
46  {
47  LinksetImpl = LinksetImplementation.Compound;
48  }
49 
50  // ================================================================
51  // Changing the physical property of the linkset only needs to change the root
52  public override void SetPhysicalFriction(float friction)
53  {
54  if (LinksetRoot.PhysBody.HasPhysicalBody)
55  m_physicsScene.PE.SetFriction(LinksetRoot.PhysBody, friction);
56  }
57  public override void SetPhysicalRestitution(float restitution)
58  {
59  if (LinksetRoot.PhysBody.HasPhysicalBody)
60  m_physicsScene.PE.SetRestitution(LinksetRoot.PhysBody, restitution);
61  }
62  public override void SetPhysicalGravity(OMV.Vector3 gravity)
63  {
64  if (LinksetRoot.PhysBody.HasPhysicalBody)
65  m_physicsScene.PE.SetGravity(LinksetRoot.PhysBody, gravity);
66  }
67  public override void ComputeAndSetLocalInertia(OMV.Vector3 inertiaFactor, float linksetMass)
68  {
69  OMV.Vector3 inertia = m_physicsScene.PE.CalculateLocalInertia(LinksetRoot.PhysShape.physShapeInfo, linksetMass);
70  LinksetRoot.Inertia = inertia * inertiaFactor;
71  m_physicsScene.PE.SetMassProps(LinksetRoot.PhysBody, linksetMass, LinksetRoot.Inertia);
72  m_physicsScene.PE.UpdateInertiaTensor(LinksetRoot.PhysBody);
73  }
74  public override void SetPhysicalCollisionFlags(CollisionFlags collFlags)
75  {
76  if (LinksetRoot.PhysBody.HasPhysicalBody)
77  m_physicsScene.PE.SetCollisionFlags(LinksetRoot.PhysBody, collFlags);
78  }
79  public override void AddToPhysicalCollisionFlags(CollisionFlags collFlags)
80  {
81  if (LinksetRoot.PhysBody.HasPhysicalBody)
82  m_physicsScene.PE.AddToCollisionFlags(LinksetRoot.PhysBody, collFlags);
83  }
84  public override void RemoveFromPhysicalCollisionFlags(CollisionFlags collFlags)
85  {
86  if (LinksetRoot.PhysBody.HasPhysicalBody)
87  m_physicsScene.PE.RemoveFromCollisionFlags(LinksetRoot.PhysBody, collFlags);
88  }
89  // ================================================================
90 
91  // When physical properties are changed the linkset needs to recalculate
92  // its internal properties.
93  public override void Refresh(BSPrimLinkable requestor)
94  {
95  // Something changed so do the rebuilding thing
96  ScheduleRebuild(requestor);
97  base.Refresh(requestor);
98  }
99 
100  // Schedule a refresh to happen after all the other taint processing.
101  private void ScheduleRebuild(BSPrimLinkable requestor)
102  {
103  // When rebuilding, it is possible to set properties that would normally require a rebuild.
104  // If already rebuilding, don't request another rebuild.
105  // If a linkset with just a root prim (simple non-linked prim) don't bother rebuilding.
106  lock (m_linksetActivityLock)
107  {
108  if (!RebuildScheduled && !Rebuilding && HasAnyChildren)
109  {
110  InternalScheduleRebuild(requestor);
111  }
112  }
113  }
114 
115  // Must be called with m_linksetActivityLock or race conditions will haunt you.
116  private void InternalScheduleRebuild(BSPrimLinkable requestor)
117  {
118  DetailLog("{0},BSLinksetCompound.InternalScheduleRebuild,,rebuilding={1},hasChildren={2}",
119  requestor.LocalID, Rebuilding, HasAnyChildren);
120  RebuildScheduled = true;
121  m_physicsScene.PostTaintObject("BSLinksetCompound.ScheduleRebuild", LinksetRoot.LocalID, delegate()
122  {
123  if (HasAnyChildren)
124  {
125  if (this.AllPartsComplete)
126  {
127  RecomputeLinksetCompound();
128  }
129  else
130  {
131  DetailLog("{0},BSLinksetCompound.InternalScheduleRebuild,,rescheduling because not all children complete",
132  requestor.LocalID);
133  InternalScheduleRebuild(requestor);
134  }
135  }
136  RebuildScheduled = false;
137  });
138  }
139 
140  // The object is going dynamic (physical). Do any setup necessary for a dynamic linkset.
141  // Only the state of the passed object can be modified. The rest of the linkset
142  // has not yet been fully constructed.
143  // Return 'true' if any properties updated on the passed object.
144  // Called at taint-time!
145  public override bool MakeDynamic(BSPrimLinkable child)
146  {
147  bool ret = false;
148  DetailLog("{0},BSLinksetCompound.MakeDynamic,call,IsRoot={1}", child.LocalID, IsRoot(child));
149  if (IsRoot(child))
150  {
151  // The root is going dynamic. Rebuild the linkset so parts and mass get computed properly.
152  Refresh(LinksetRoot);
153  }
154  return ret;
155  }
156 
157  // The object is going static (non-physical). We do not do anything for static linksets.
158  // Return 'true' if any properties updated on the passed object.
159  // Called at taint-time!
160  public override bool MakeStatic(BSPrimLinkable child)
161  {
162  bool ret = false;
163 
164  DetailLog("{0},BSLinksetCompound.MakeStatic,call,IsRoot={1}", child.LocalID, IsRoot(child));
165  child.ClearDisplacement();
166  if (IsRoot(child))
167  {
168  // Schedule a rebuild to verify that the root shape is set to the real shape.
169  Refresh(LinksetRoot);
170  }
171  return ret;
172  }
173 
174  // 'physicalUpdate' is true if these changes came directly from the physics engine. Don't need to rebuild then.
175  // Called at taint-time.
176  public override void UpdateProperties(UpdatedProperties whichUpdated, BSPrimLinkable updated)
177  {
178  if (!LinksetRoot.IsPhysicallyActive)
179  {
180  // No reason to do this physical stuff for static linksets.
181  DetailLog("{0},BSLinksetCompound.UpdateProperties,notPhysical", LinksetRoot.LocalID);
182  return;
183  }
184 
185  // The user moving a child around requires the rebuilding of the linkset compound shape
186  // One problem is this happens when a border is crossed -- the simulator implementation
187  // stores the position into the group which causes the move of the object
188  // but it also means all the child positions get updated.
189  // What would cause an unnecessary rebuild so we make sure the linkset is in a
190  // region before bothering to do a rebuild.
191  if (!IsRoot(updated) && m_physicsScene.TerrainManager.IsWithinKnownTerrain(LinksetRoot.RawPosition))
192  {
193  // If a child of the linkset is updating only the position or rotation, that can be done
194  // without rebuilding the linkset.
195  // If a handle for the child can be fetch, we update the child here. If a rebuild was
196  // scheduled by someone else, the rebuild will just replace this setting.
197 
198  bool updatedChild = false;
199  // Anything other than updating position or orientation usually means a physical update
200  // and that is caused by us updating the object.
201  if ((whichUpdated & ~(UpdatedProperties.Position | UpdatedProperties.Orientation)) == 0)
202  {
203  // Find the physical instance of the child
204  if (!RebuildScheduled // if rebuilding, let the rebuild do it
205  && !LinksetRoot.IsIncomplete // if waiting for assets or whatever, don't change
206  && LinksetRoot.PhysShape.HasPhysicalShape // there must be a physical shape assigned
207  && m_physicsScene.PE.IsCompound(LinksetRoot.PhysShape.physShapeInfo))
208  {
209  // It is possible that the linkset is still under construction and the child is not yet
210  // inserted into the compound shape. A rebuild of the linkset in a pre-step action will
211  // build the whole thing with the new position or rotation.
212  // The index must be checked because Bullet references the child array but does no validity
213  // checking of the child index passed.
214  int numLinksetChildren = m_physicsScene.PE.GetNumberOfCompoundChildren(LinksetRoot.PhysShape.physShapeInfo);
215  if (updated.LinksetChildIndex < numLinksetChildren)
216  {
217  BulletShape linksetChildShape = m_physicsScene.PE.GetChildShapeFromCompoundShapeIndex(LinksetRoot.PhysShape.physShapeInfo, updated.LinksetChildIndex);
218  if (linksetChildShape.HasPhysicalShape)
219  {
220  // Found the child shape within the compound shape
221  m_physicsScene.PE.UpdateChildTransform(LinksetRoot.PhysShape.physShapeInfo, updated.LinksetChildIndex,
222  updated.RawPosition - LinksetRoot.RawPosition,
223  updated.RawOrientation * OMV.Quaternion.Inverse(LinksetRoot.RawOrientation),
224  true /* shouldRecalculateLocalAabb */);
225  updatedChild = true;
226  DetailLog("{0},BSLinksetCompound.UpdateProperties,changeChildPosRot,whichUpdated={1},pos={2},rot={3}",
227  updated.LocalID, whichUpdated, updated.RawPosition, updated.RawOrientation);
228  }
229  else // DEBUG DEBUG
230  { // DEBUG DEBUG
231  DetailLog("{0},BSLinksetCompound.UpdateProperties,couldNotUpdateChild,noChildShape,shape={1}",
232  updated.LocalID, linksetChildShape);
233  } // DEBUG DEBUG
234  }
235  else // DEBUG DEBUG
236  { // DEBUG DEBUG
237  // the child is not yet in the compound shape. This is non-fatal.
238  DetailLog("{0},BSLinksetCompound.UpdateProperties,couldNotUpdateChild,childNotInCompoundShape,numChildren={1},index={2}",
239  updated.LocalID, numLinksetChildren, updated.LinksetChildIndex);
240  } // DEBUG DEBUG
241  }
242  else // DEBUG DEBUG
243  { // DEBUG DEBUG
244  DetailLog("{0},BSLinksetCompound.UpdateProperties,couldNotUpdateChild,noBodyOrNotCompound", updated.LocalID);
245  } // DEBUG DEBUG
246 
247  if (!updatedChild)
248  {
249  // If couldn't do the individual child, the linkset needs a rebuild to incorporate the new child info.
250  // Note: there are several ways through this code that will not update the child if
251  // the linkset is being rebuilt. In this case, scheduling a rebuild is a NOOP since
252  // there will already be a rebuild scheduled.
253  DetailLog("{0},BSLinksetCompound.UpdateProperties,couldNotUpdateChild.schedulingRebuild,whichUpdated={1}",
254  updated.LocalID, whichUpdated);
255  Refresh(updated);
256  }
257  }
258  }
259  }
260 
261  // Routine called when rebuilding the body of some member of the linkset.
262  // If one of the bodies is being changed, the linkset needs rebuilding.
263  // For instance, a linkset is built and then a mesh asset is read in and the mesh is recreated.
264  // Returns 'true' of something was actually removed and would need restoring
265  // Called at taint-time!!
266  public override bool RemoveDependencies(BSPrimLinkable child)
267  {
268  bool ret = false;
269 
270  DetailLog("{0},BSLinksetCompound.RemoveDependencies,refreshIfChild,rID={1},rBody={2},isRoot={3}",
271  child.LocalID, LinksetRoot.LocalID, LinksetRoot.PhysBody, IsRoot(child));
272 
273  Refresh(child);
274 
275  return ret;
276  }
277 
278  // ================================================================
279 
280  // Add a new child to the linkset.
281  // Called while LinkActivity is locked.
282  protected override void AddChildToLinkset(BSPrimLinkable child)
283  {
284  if (!HasChild(child))
285  {
286  m_children.Add(child, new BSLinkInfo(child));
287 
288  DetailLog("{0},BSLinksetCompound.AddChildToLinkset,call,child={1}", LinksetRoot.LocalID, child.LocalID);
289 
290  // Rebuild the compound shape with the new child shape included
291  Refresh(child);
292  }
293  return;
294  }
295 
296  // Remove the specified child from the linkset.
297  // Safe to call even if the child is not really in the linkset.
298  protected override void RemoveChildFromLinkset(BSPrimLinkable child, bool inTaintTime)
299  {
300  child.ClearDisplacement();
301 
302  if (m_children.Remove(child))
303  {
304  DetailLog("{0},BSLinksetCompound.RemoveChildFromLinkset,call,rID={1},rBody={2},cID={3},cBody={4}",
305  child.LocalID,
306  LinksetRoot.LocalID, LinksetRoot.PhysBody.AddrString,
307  child.LocalID, child.PhysBody.AddrString);
308 
309  // Cause the child's body to be rebuilt and thus restored to normal operation
310  child.ForceBodyShapeRebuild(inTaintTime);
311 
312  if (!HasAnyChildren)
313  {
314  // The linkset is now empty. The root needs rebuilding.
315  LinksetRoot.ForceBodyShapeRebuild(inTaintTime);
316  }
317  else
318  {
319  // Rebuild the compound shape with the child removed
320  Refresh(LinksetRoot);
321  }
322  }
323  return;
324  }
325 
326  // Called before the simulation step to make sure the compound based linkset
327  // is all initialized.
328  // Constraint linksets are rebuilt every time.
329  // Note that this works for rebuilding just the root after a linkset is taken apart.
330  // Called at taint time!!
331  private bool UseBulletSimRootOffsetHack = false; // Attempt to have Bullet track the coords of root compound shape
332  private void RecomputeLinksetCompound()
333  {
334  try
335  {
336  Rebuilding = true;
337 
338  // No matter what is being done, force the root prim's PhysBody and PhysShape to get set
339  // to what they should be as if the root was not in a linkset.
340  // Not that bad since we only get into this routine if there are children in the linkset and
341  // something has been updated/changed.
342  // Have to do the rebuild before checking for physical because this might be a linkset
343  // being destructed and going non-physical.
344  LinksetRoot.ForceBodyShapeRebuild(true);
345 
346  // There is no reason to build all this physical stuff for a non-physical or empty linkset.
347  if (!LinksetRoot.IsPhysicallyActive || !HasAnyChildren)
348  {
349  DetailLog("{0},BSLinksetCompound.RecomputeLinksetCompound,notPhysicalOrNoChildren", LinksetRoot.LocalID);
350  return; // Note the 'finally' clause at the botton which will get executed.
351  }
352 
353  // Get a new compound shape to build the linkset shape in.
354  BSShape linksetShape = BSShapeCompound.GetReference(m_physicsScene);
355 
356  // Compute a displacement for each component so it is relative to the center-of-mass.
357  // Bullet presumes an object's origin (relative <0,0,0>) is its center-of-mass
358  OMV.Vector3 centerOfMassW = ComputeLinksetCenterOfMass();
359 
360  OMV.Quaternion invRootOrientation = OMV.Quaternion.Normalize(OMV.Quaternion.Inverse(LinksetRoot.RawOrientation));
361  OMV.Vector3 origRootPosition = LinksetRoot.RawPosition;
362 
363  // 'centerDisplacementV' is the vehicle relative distance from the simulator root position to the center-of-mass
364  OMV.Vector3 centerDisplacementV = (centerOfMassW - LinksetRoot.RawPosition) * invRootOrientation;
365  if (UseBulletSimRootOffsetHack || !BSParam.LinksetOffsetCenterOfMass)
366  {
367  // Zero everything if center-of-mass displacement is not being done.
368  centerDisplacementV = OMV.Vector3.Zero;
369  LinksetRoot.ClearDisplacement();
370  }
371  else
372  {
373  // The actual center-of-mass could have been set by the user.
374  centerDisplacementV = LinksetRoot.SetEffectiveCenterOfMassDisplacement(centerDisplacementV);
375  }
376 
377  DetailLog("{0},BSLinksetCompound.RecomputeLinksetCompound,COM,rootPos={1},com={2},comDisp={3}",
378  LinksetRoot.LocalID, origRootPosition, centerOfMassW, centerDisplacementV);
379 
380  // Add the shapes of all the components of the linkset
381  int memberIndex = 1;
382  ForEachMember((cPrim) =>
383  {
384  if (IsRoot(cPrim))
385  {
386  // Root shape is always index zero.
387  cPrim.LinksetChildIndex = 0;
388  }
389  else
390  {
391  cPrim.LinksetChildIndex = memberIndex;
392  memberIndex++;
393  }
394 
395  // Get a reference to the shape of the child for adding of that shape to the linkset compound shape
396  BSShape childShape = cPrim.PhysShape.GetReference(m_physicsScene, cPrim);
397 
398  // Offset the child shape from the center-of-mass and rotate it to root relative.
399  OMV.Vector3 offsetPos = (cPrim.RawPosition - origRootPosition) * invRootOrientation - centerDisplacementV;
400  OMV.Quaternion offsetRot = OMV.Quaternion.Normalize(cPrim.RawOrientation) * invRootOrientation;
401 
402  // Add the child shape to the compound shape being built
403  if (childShape.physShapeInfo.HasPhysicalShape)
404  {
405  m_physicsScene.PE.AddChildShapeToCompoundShape(linksetShape.physShapeInfo, childShape.physShapeInfo, offsetPos, offsetRot);
406  DetailLog("{0},BSLinksetCompound.RecomputeLinksetCompound,addChild,indx={1},cShape={2},offPos={3},offRot={4}",
407  LinksetRoot.LocalID, cPrim.LinksetChildIndex, childShape, offsetPos, offsetRot);
408 
409  // Since we are borrowing the shape of the child, disable the origional child body
410  if (!IsRoot(cPrim))
411  {
412  m_physicsScene.PE.AddToCollisionFlags(cPrim.PhysBody, CollisionFlags.CF_NO_CONTACT_RESPONSE);
413  m_physicsScene.PE.ForceActivationState(cPrim.PhysBody, ActivationState.DISABLE_SIMULATION);
414  // We don't want collisions from the old linkset children.
415  m_physicsScene.PE.RemoveFromCollisionFlags(cPrim.PhysBody, CollisionFlags.BS_SUBSCRIBE_COLLISION_EVENTS);
416  cPrim.PhysBody.collisionType = CollisionType.LinksetChild;
417  }
418  }
419  else
420  {
421  // The linkset must be in an intermediate state where all the children have not yet
422  // been constructed. This sometimes happens on startup when everything is getting
423  // built and some shapes have to wait for assets to be read in.
424  // Just skip this linkset for the moment and cause the shape to be rebuilt next tick.
425  // One problem might be that the shape is broken somehow and it never becomes completely
426  // available. This might cause the rebuild to happen over and over.
427  InternalScheduleRebuild(LinksetRoot);
428  DetailLog("{0},BSLinksetCompound.RecomputeLinksetCompound,addChildWithNoShape,indx={1},cShape={2},offPos={3},offRot={4}",
429  LinksetRoot.LocalID, cPrim.LinksetChildIndex, childShape, offsetPos, offsetRot);
430  // Output an annoying warning. It should only happen once but if it keeps coming out,
431  // the user knows there is something wrong and will report it.
432  m_physicsScene.Logger.WarnFormat("{0} Linkset rebuild warning. If this happens more than one or two times, please report in Mantis 7191", LogHeader);
433  m_physicsScene.Logger.WarnFormat("{0} pName={1}, childIdx={2}, shape={3}",
434  LogHeader, LinksetRoot.Name, cPrim.LinksetChildIndex, childShape);
435 
436  // This causes the loop to bail on building the rest of this linkset.
437  // The rebuild operation will fix it up next tick or declare the object unbuildable.
438  return true;
439  }
440 
441  return false; // 'false' says to move onto the next child in the list
442  });
443 
444  // Replace the root shape with the built compound shape.
445  // Object removed and added to world to get collision cache rebuilt for new shape.
446  LinksetRoot.PhysShape.Dereference(m_physicsScene);
447  LinksetRoot.PhysShape = linksetShape;
448  m_physicsScene.PE.RemoveObjectFromWorld(m_physicsScene.World, LinksetRoot.PhysBody);
449  m_physicsScene.PE.SetCollisionShape(m_physicsScene.World, LinksetRoot.PhysBody, linksetShape.physShapeInfo);
450  m_physicsScene.PE.AddObjectToWorld(m_physicsScene.World, LinksetRoot.PhysBody);
451  DetailLog("{0},BSLinksetCompound.RecomputeLinksetCompound,addBody,body={1},shape={2}",
452  LinksetRoot.LocalID, LinksetRoot.PhysBody, linksetShape);
453 
454  // With all of the linkset packed into the root prim, it has the mass of everyone.
455  LinksetMass = ComputeLinksetMass();
456  LinksetRoot.UpdatePhysicalMassProperties(LinksetMass, true);
457 
458  if (UseBulletSimRootOffsetHack)
459  {
460  // Enable the physical position updator to return the position and rotation of the root shape.
461  // This enables a feature in the C++ code to return the world coordinates of the first shape in the
462  // compound shape. This aleviates the need to offset the returned physical position by the
463  // center-of-mass offset.
464  // TODO: either debug this feature or remove it.
465  m_physicsScene.PE.AddToCollisionFlags(LinksetRoot.PhysBody, CollisionFlags.BS_RETURN_ROOT_COMPOUND_SHAPE);
466  }
467  }
468  finally
469  {
470  Rebuilding = false;
471  }
472 
473  // See that the Aabb surrounds the new shape
474  m_physicsScene.PE.RecalculateCompoundShapeLocalAabb(LinksetRoot.PhysShape.physShapeInfo);
475  }
476 }
477 }
override void SetPhysicalGravity(OMV.Vector3 gravity)
override void Refresh(BSPrimLinkable requestor)
OpenMetaverse OMV
override void SetPhysicalCollisionFlags(CollisionFlags collFlags)
override bool RemoveDependencies(BSPrimLinkable child)
override void AddChildToLinkset(BSPrimLinkable child)
override void SetPhysicalRestitution(float restitution)
override void RemoveFromPhysicalCollisionFlags(CollisionFlags collFlags)
override void AddToPhysicalCollisionFlags(CollisionFlags collFlags)
BSLinksetCompound(BSScene scene, BSPrimLinkable parent)
override void UpdateProperties(UpdatedProperties whichUpdated, BSPrimLinkable updated)
override void ComputeAndSetLocalInertia(OMV.Vector3 inertiaFactor, float linksetMass)
override void RemoveChildFromLinkset(BSPrimLinkable child, bool inTaintTime)