BSLinksetCompound.cs 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385
  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 OpenSim.Framework;
  31. using OMV = OpenMetaverse;
  32. namespace OpenSim.Region.Physics.BulletSPlugin
  33. {
  34. public sealed class BSLinksetCompound : BSLinkset
  35. {
  36. private static string LogHeader = "[BULLETSIM LINKSET COMPOUND]";
  37. public BSLinksetCompound(BSScene scene, BSPrimLinkable parent)
  38. : base(scene, parent)
  39. {
  40. }
  41. // When physical properties are changed the linkset needs to recalculate
  42. // its internal properties.
  43. public override void Refresh(BSPrimLinkable requestor)
  44. {
  45. base.Refresh(requestor);
  46. // Something changed so do the rebuilding thing
  47. // ScheduleRebuild();
  48. }
  49. // Schedule a refresh to happen after all the other taint processing.
  50. private void ScheduleRebuild(BSPrimLinkable requestor)
  51. {
  52. DetailLog("{0},BSLinksetCompound.ScheduleRebuild,,rebuilding={1},hasChildren={2},actuallyScheduling={3}",
  53. requestor.LocalID, Rebuilding, HasAnyChildren, (!Rebuilding && HasAnyChildren));
  54. // When rebuilding, it is possible to set properties that would normally require a rebuild.
  55. // If already rebuilding, don't request another rebuild.
  56. // If a linkset with just a root prim (simple non-linked prim) don't bother rebuilding.
  57. if (!Rebuilding && HasAnyChildren)
  58. {
  59. m_physicsScene.PostTaintObject("BSLinksetCompound.ScheduleRebuild", LinksetRoot.LocalID, delegate()
  60. {
  61. if (HasAnyChildren)
  62. RecomputeLinksetCompound();
  63. });
  64. }
  65. }
  66. // The object is going dynamic (physical). Do any setup necessary for a dynamic linkset.
  67. // Only the state of the passed object can be modified. The rest of the linkset
  68. // has not yet been fully constructed.
  69. // Return 'true' if any properties updated on the passed object.
  70. // Called at taint-time!
  71. public override bool MakeDynamic(BSPrimLinkable child)
  72. {
  73. bool ret = false;
  74. DetailLog("{0},BSLinksetCompound.MakeDynamic,call,IsRoot={1}", child.LocalID, IsRoot(child));
  75. if (IsRoot(child))
  76. {
  77. // The root is going dynamic. Rebuild the linkset so parts and mass get computed properly.
  78. ScheduleRebuild(LinksetRoot);
  79. }
  80. return ret;
  81. }
  82. // The object is going static (non-physical). We do not do anything for static linksets.
  83. // Return 'true' if any properties updated on the passed object.
  84. // Called at taint-time!
  85. public override bool MakeStatic(BSPrimLinkable child)
  86. {
  87. bool ret = false;
  88. DetailLog("{0},BSLinksetCompound.MakeStatic,call,IsRoot={1}", child.LocalID, IsRoot(child));
  89. child.ClearDisplacement();
  90. if (IsRoot(child))
  91. {
  92. // Schedule a rebuild to verify that the root shape is set to the real shape.
  93. ScheduleRebuild(LinksetRoot);
  94. }
  95. return ret;
  96. }
  97. // 'physicalUpdate' is true if these changes came directly from the physics engine. Don't need to rebuild then.
  98. // Called at taint-time.
  99. public override void UpdateProperties(UpdatedProperties whichUpdated, BSPrimLinkable updated)
  100. {
  101. if (!LinksetRoot.IsPhysicallyActive)
  102. {
  103. // No reason to do this physical stuff for static linksets.
  104. DetailLog("{0},BSLinksetCompound.UpdateProperties,notPhysical", LinksetRoot.LocalID);
  105. return;
  106. }
  107. // The user moving a child around requires the rebuilding of the linkset compound shape
  108. // One problem is this happens when a border is crossed -- the simulator implementation
  109. // stores the position into the group which causes the move of the object
  110. // but it also means all the child positions get updated.
  111. // What would cause an unnecessary rebuild so we make sure the linkset is in a
  112. // region before bothering to do a rebuild.
  113. if (!IsRoot(updated) && m_physicsScene.TerrainManager.IsWithinKnownTerrain(LinksetRoot.RawPosition))
  114. {
  115. // If a child of the linkset is updating only the position or rotation, that can be done
  116. // without rebuilding the linkset.
  117. // If a handle for the child can be fetch, we update the child here. If a rebuild was
  118. // scheduled by someone else, the rebuild will just replace this setting.
  119. bool updatedChild = false;
  120. // Anything other than updating position or orientation usually means a physical update
  121. // and that is caused by us updating the object.
  122. if ((whichUpdated & ~(UpdatedProperties.Position | UpdatedProperties.Orientation)) == 0)
  123. {
  124. // Find the physical instance of the child
  125. if (LinksetRoot.PhysShape.HasPhysicalShape && m_physicsScene.PE.IsCompound(LinksetRoot.PhysShape.physShapeInfo))
  126. {
  127. // It is possible that the linkset is still under construction and the child is not yet
  128. // inserted into the compound shape. A rebuild of the linkset in a pre-step action will
  129. // build the whole thing with the new position or rotation.
  130. // The index must be checked because Bullet references the child array but does no validity
  131. // checking of the child index passed.
  132. int numLinksetChildren = m_physicsScene.PE.GetNumberOfCompoundChildren(LinksetRoot.PhysShape.physShapeInfo);
  133. if (updated.LinksetChildIndex < numLinksetChildren)
  134. {
  135. BulletShape linksetChildShape = m_physicsScene.PE.GetChildShapeFromCompoundShapeIndex(LinksetRoot.PhysShape.physShapeInfo, updated.LinksetChildIndex);
  136. if (linksetChildShape.HasPhysicalShape)
  137. {
  138. // Found the child shape within the compound shape
  139. m_physicsScene.PE.UpdateChildTransform(LinksetRoot.PhysShape.physShapeInfo, updated.LinksetChildIndex,
  140. updated.RawPosition - LinksetRoot.RawPosition,
  141. updated.RawOrientation * OMV.Quaternion.Inverse(LinksetRoot.RawOrientation),
  142. true /* shouldRecalculateLocalAabb */);
  143. updatedChild = true;
  144. DetailLog("{0},BSLinksetCompound.UpdateProperties,changeChildPosRot,whichUpdated={1},pos={2},rot={3}",
  145. updated.LocalID, whichUpdated, updated.RawPosition, updated.RawOrientation);
  146. }
  147. else // DEBUG DEBUG
  148. { // DEBUG DEBUG
  149. DetailLog("{0},BSLinksetCompound.UpdateProperties,couldNotUpdateChild,noChildShape,shape={1}",
  150. updated.LocalID, linksetChildShape);
  151. } // DEBUG DEBUG
  152. }
  153. else // DEBUG DEBUG
  154. { // DEBUG DEBUG
  155. // the child is not yet in the compound shape. This is non-fatal.
  156. DetailLog("{0},BSLinksetCompound.UpdateProperties,couldNotUpdateChild,childNotInCompoundShape,numChildren={1},index={2}",
  157. updated.LocalID, numLinksetChildren, updated.LinksetChildIndex);
  158. } // DEBUG DEBUG
  159. }
  160. else // DEBUG DEBUG
  161. { // DEBUG DEBUG
  162. DetailLog("{0},BSLinksetCompound.UpdateProperties,couldNotUpdateChild,noBodyOrNotCompound", updated.LocalID);
  163. } // DEBUG DEBUG
  164. if (!updatedChild)
  165. {
  166. // If couldn't do the individual child, the linkset needs a rebuild to incorporate the new child info.
  167. // Note: there are several ways through this code that will not update the child if
  168. // the linkset is being rebuilt. In this case, scheduling a rebuild is a NOOP since
  169. // there will already be a rebuild scheduled.
  170. DetailLog("{0},BSLinksetCompound.UpdateProperties,couldNotUpdateChild.schedulingRebuild,whichUpdated={1}",
  171. updated.LocalID, whichUpdated);
  172. ScheduleRebuild(updated);
  173. }
  174. }
  175. }
  176. }
  177. // Routine called when rebuilding the body of some member of the linkset.
  178. // If one of the bodies is being changed, the linkset needs rebuilding.
  179. // For instance, a linkset is built and then a mesh asset is read in and the mesh is recreated.
  180. // Returns 'true' of something was actually removed and would need restoring
  181. // Called at taint-time!!
  182. public override bool RemoveDependencies(BSPrimLinkable child)
  183. {
  184. bool ret = false;
  185. DetailLog("{0},BSLinksetCompound.RemoveBodyDependencies,refreshIfChild,rID={1},rBody={2},isRoot={3}",
  186. child.LocalID, LinksetRoot.LocalID, LinksetRoot.PhysBody, IsRoot(child));
  187. ScheduleRebuild(child);
  188. return ret;
  189. }
  190. // ================================================================
  191. // Add a new child to the linkset.
  192. // Called while LinkActivity is locked.
  193. protected override void AddChildToLinkset(BSPrimLinkable child)
  194. {
  195. if (!HasChild(child))
  196. {
  197. m_children.Add(child);
  198. DetailLog("{0},BSLinksetCompound.AddChildToLinkset,call,child={1}", LinksetRoot.LocalID, child.LocalID);
  199. // Rebuild the compound shape with the new child shape included
  200. ScheduleRebuild(child);
  201. }
  202. return;
  203. }
  204. // Remove the specified child from the linkset.
  205. // Safe to call even if the child is not really in the linkset.
  206. protected override void RemoveChildFromLinkset(BSPrimLinkable child)
  207. {
  208. child.ClearDisplacement();
  209. if (m_children.Remove(child))
  210. {
  211. DetailLog("{0},BSLinksetCompound.RemoveChildFromLinkset,call,rID={1},rBody={2},cID={3},cBody={4}",
  212. child.LocalID,
  213. LinksetRoot.LocalID, LinksetRoot.PhysBody.AddrString,
  214. child.LocalID, child.PhysBody.AddrString);
  215. // Cause the child's body to be rebuilt and thus restored to normal operation
  216. child.ForceBodyShapeRebuild(false);
  217. if (!HasAnyChildren)
  218. {
  219. // The linkset is now empty. The root needs rebuilding.
  220. LinksetRoot.ForceBodyShapeRebuild(false);
  221. }
  222. else
  223. {
  224. // Rebuild the compound shape with the child removed
  225. ScheduleRebuild(LinksetRoot);
  226. }
  227. }
  228. return;
  229. }
  230. // Called before the simulation step to make sure the compound based linkset
  231. // is all initialized.
  232. // Constraint linksets are rebuilt every time.
  233. // Note that this works for rebuilding just the root after a linkset is taken apart.
  234. // Called at taint time!!
  235. private bool UseBulletSimRootOffsetHack = false; // Attempt to have Bullet track the coords of root compound shape
  236. private void RecomputeLinksetCompound()
  237. {
  238. try
  239. {
  240. Rebuilding = true;
  241. // No matter what is being done, force the root prim's PhysBody and PhysShape to get set
  242. // to what they should be as if the root was not in a linkset.
  243. // Not that bad since we only get into this routine if there are children in the linkset and
  244. // something has been updated/changed.
  245. // Have to do the rebuild before checking for physical because this might be a linkset
  246. // being destructed and going non-physical.
  247. LinksetRoot.ForceBodyShapeRebuild(true);
  248. // There is no reason to build all this physical stuff for a non-physical linkset.
  249. if (!LinksetRoot.IsPhysicallyActive)
  250. {
  251. DetailLog("{0},BSLinksetCompound.RecomputeLinksetCompound,notPhysical", LinksetRoot.LocalID);
  252. return; // Note the 'finally' clause at the botton which will get executed.
  253. }
  254. // Get a new compound shape to build the linkset shape in.
  255. BSShape linksetShape = BSShapeCompound.GetReference(m_physicsScene);
  256. // Compute a displacement for each component so it is relative to the center-of-mass.
  257. // Bullet presumes an object's origin (relative <0,0,0>) is its center-of-mass
  258. OMV.Vector3 centerOfMassW = ComputeLinksetCenterOfMass();
  259. OMV.Quaternion invRootOrientation = OMV.Quaternion.Normalize(OMV.Quaternion.Inverse(LinksetRoot.RawOrientation));
  260. OMV.Vector3 origRootPosition = LinksetRoot.RawPosition;
  261. // 'centerDisplacementV' is the vehicle relative distance from the simulator root position to the center-of-mass
  262. OMV.Vector3 centerDisplacementV = (centerOfMassW - LinksetRoot.RawPosition) * invRootOrientation;
  263. if (UseBulletSimRootOffsetHack || !BSParam.LinksetOffsetCenterOfMass)
  264. {
  265. // Zero everything if center-of-mass displacement is not being done.
  266. centerDisplacementV = OMV.Vector3.Zero;
  267. LinksetRoot.ClearDisplacement();
  268. }
  269. else
  270. {
  271. // The actual center-of-mass could have been set by the user.
  272. centerDisplacementV = LinksetRoot.SetEffectiveCenterOfMassDisplacement(centerDisplacementV);
  273. }
  274. DetailLog("{0},BSLinksetCompound.RecomputeLinksetCompound,COM,rootPos={1},com={2},comDisp={3}",
  275. LinksetRoot.LocalID, origRootPosition, centerOfMassW, centerDisplacementV);
  276. // Add the shapes of all the components of the linkset
  277. int memberIndex = 1;
  278. ForEachMember(delegate(BSPrimLinkable cPrim)
  279. {
  280. if (IsRoot(cPrim))
  281. {
  282. // Root shape is always index zero.
  283. cPrim.LinksetChildIndex = 0;
  284. }
  285. else
  286. {
  287. cPrim.LinksetChildIndex = memberIndex;
  288. memberIndex++;
  289. }
  290. // Get a reference to the shape of the child for adding of that shape to the linkset compound shape
  291. BSShape childShape = cPrim.PhysShape.GetReference(m_physicsScene, cPrim);
  292. // Offset the child shape from the center-of-mass and rotate it to vehicle relative.
  293. OMV.Vector3 offsetPos = (cPrim.RawPosition - origRootPosition) * invRootOrientation - centerDisplacementV;
  294. OMV.Quaternion offsetRot = OMV.Quaternion.Normalize(cPrim.RawOrientation) * invRootOrientation;
  295. // Add the child shape to the compound shape being built
  296. m_physicsScene.PE.AddChildShapeToCompoundShape(linksetShape.physShapeInfo, childShape.physShapeInfo, offsetPos, offsetRot);
  297. DetailLog("{0},BSLinksetCompound.RecomputeLinksetCompound,addChild,indx={1},cShape={2},offPos={3},offRot={4}",
  298. LinksetRoot.LocalID, cPrim.LinksetChildIndex, childShape, offsetPos, offsetRot);
  299. // Since we are borrowing the shape of the child, disable the origional child body
  300. if (!IsRoot(cPrim))
  301. {
  302. m_physicsScene.PE.AddToCollisionFlags(cPrim.PhysBody, CollisionFlags.CF_NO_CONTACT_RESPONSE);
  303. m_physicsScene.PE.ForceActivationState(cPrim.PhysBody, ActivationState.DISABLE_SIMULATION);
  304. // We don't want collisions from the old linkset children.
  305. m_physicsScene.PE.RemoveFromCollisionFlags(cPrim.PhysBody, CollisionFlags.BS_SUBSCRIBE_COLLISION_EVENTS);
  306. cPrim.PhysBody.collisionType = CollisionType.LinksetChild;
  307. }
  308. return false; // 'false' says to move onto the next child in the list
  309. });
  310. // Replace the root shape with the built compound shape.
  311. // Object removed and added to world to get collision cache rebuilt for new shape.
  312. LinksetRoot.PhysShape.Dereference(m_physicsScene);
  313. LinksetRoot.PhysShape = linksetShape;
  314. m_physicsScene.PE.RemoveObjectFromWorld(m_physicsScene.World, LinksetRoot.PhysBody);
  315. m_physicsScene.PE.SetCollisionShape(m_physicsScene.World, LinksetRoot.PhysBody, linksetShape.physShapeInfo);
  316. m_physicsScene.PE.AddObjectToWorld(m_physicsScene.World, LinksetRoot.PhysBody);
  317. DetailLog("{0},BSLinksetCompound.RecomputeLinksetCompound,addBody,body={1},shape={2}",
  318. LinksetRoot.LocalID, LinksetRoot.PhysBody, linksetShape);
  319. // With all of the linkset packed into the root prim, it has the mass of everyone.
  320. LinksetMass = ComputeLinksetMass();
  321. LinksetRoot.UpdatePhysicalMassProperties(LinksetMass, true);
  322. if (UseBulletSimRootOffsetHack)
  323. {
  324. // Enable the physical position updator to return the position and rotation of the root shape.
  325. // This enables a feature in the C++ code to return the world coordinates of the first shape in the
  326. // compound shape. This aleviates the need to offset the returned physical position by the
  327. // center-of-mass offset.
  328. // TODO: either debug this feature or remove it.
  329. m_physicsScene.PE.AddToCollisionFlags(LinksetRoot.PhysBody, CollisionFlags.BS_RETURN_ROOT_COMPOUND_SHAPE);
  330. }
  331. }
  332. finally
  333. {
  334. Rebuilding = false;
  335. }
  336. // See that the Aabb surrounds the new shape
  337. m_physicsScene.PE.RecalculateCompoundShapeLocalAabb(LinksetRoot.PhysShape.physShapeInfo);
  338. }
  339. }
  340. }