1
0

BSLinksetCompound.cs 22 KB

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