BSLinksetConstraints.cs 18 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 OMV = OpenMetaverse;
  31. namespace OpenSim.Region.Physics.BulletSPlugin
  32. {
  33. public sealed class BSLinksetConstraints : BSLinkset
  34. {
  35. // private static string LogHeader = "[BULLETSIM LINKSET CONSTRAINTS]";
  36. public BSLinksetConstraints(BSScene scene, BSPhysObject parent)
  37. {
  38. base.Initialize(scene, parent);
  39. }
  40. // When physical properties are changed the linkset needs to recalculate
  41. // its internal properties.
  42. // May be called at runtime or taint-time (just pass the appropriate flag).
  43. public override void Refresh(BSPhysObject requestor, bool inTaintTime)
  44. {
  45. // If there are no children or not root, I am not the one that recomputes the constraints
  46. if (!HasAnyChildren || !IsRoot(requestor))
  47. return;
  48. BSScene.TaintCallback refreshOperation = delegate()
  49. {
  50. RecomputeLinksetConstraintVariables();
  51. DetailLog("{0},BSLinkset.Refresh,complete,rBody={1}",
  52. LinksetRoot.LocalID, LinksetRoot.BSBody.ptr.ToString("X"));
  53. };
  54. if (inTaintTime)
  55. refreshOperation();
  56. else
  57. PhysicsScene.TaintedObject("BSLinkSet.Refresh", refreshOperation);
  58. }
  59. // The object is going dynamic (physical). Do any setup necessary
  60. // for a dynamic linkset.
  61. // Only the state of the passed object can be modified. The rest of the linkset
  62. // has not yet been fully constructed.
  63. // Return 'true' if any properties updated on the passed object.
  64. // Called at taint-time!
  65. public override bool MakeDynamic(BSPhysObject child)
  66. {
  67. // What is done for each object in BSPrim is what we want.
  68. return false;
  69. }
  70. // The object is going static (non-physical). Do any setup necessary
  71. // for a static linkset.
  72. // Return 'true' if any properties updated on the passed object.
  73. // Called at taint-time!
  74. public override bool MakeStatic(BSPhysObject child)
  75. {
  76. // What is done for each object in BSPrim is what we want.
  77. return false;
  78. }
  79. // Called at taint-time!!
  80. public override void UpdateProperties(BSPhysObject updated)
  81. {
  82. // Nothing to do for constraints on property updates
  83. }
  84. // Routine used when rebuilding the body of the root of the linkset
  85. // Destroy all the constraints have have been made to root.
  86. // This is called when the root body is changing.
  87. // Returns 'true' of something eas actually removed and would need restoring
  88. // Called at taint-time!!
  89. public override bool RemoveBodyDependencies(BSPrim child)
  90. {
  91. bool ret = false;
  92. lock (m_linksetActivityLock)
  93. {
  94. if (IsRoot(child))
  95. {
  96. // If the one with the dependency is root, must undo all children
  97. DetailLog("{0},BSLinkset.RemoveBodyDependencies,removeChildrenForRoot,rID={1},rBody={2}",
  98. child.LocalID, LinksetRoot.LocalID, LinksetRoot.BSBody.ptr.ToString("X"));
  99. ret = PhysicallyUnlinkAllChildrenFromRoot(LinksetRoot);
  100. }
  101. else
  102. {
  103. DetailLog("{0},BSLinkset.RemoveBodyDependencies,removeSingleChild,rID={1},rBody={2},cID={3},cBody={4}",
  104. child.LocalID,
  105. LinksetRoot.LocalID, LinksetRoot.BSBody.ptr.ToString("X"),
  106. child.LocalID, child.BSBody.ptr.ToString("X"));
  107. // ret = PhysicallyUnlinkAChildFromRoot(LinksetRoot, child);
  108. // Despite the function name, this removes any link to the specified object.
  109. ret = PhysicallyUnlinkAllChildrenFromRoot(child);
  110. }
  111. }
  112. return ret;
  113. }
  114. // Companion to RemoveBodyDependencies(). If RemoveBodyDependencies() returns 'true',
  115. // this routine will restore the removed constraints.
  116. // Called at taint-time!!
  117. public override void RestoreBodyDependencies(BSPrim child)
  118. {
  119. lock (m_linksetActivityLock)
  120. {
  121. if (IsRoot(child))
  122. {
  123. DetailLog("{0},BSLinkset.RestoreBodyDependencies,restoreChildrenForRoot,rID={1},numChild={2}",
  124. child.LocalID, LinksetRoot.LocalID, m_taintChildren.Count);
  125. foreach (BSPhysObject bpo in m_taintChildren)
  126. {
  127. PhysicallyLinkAChildToRoot(LinksetRoot, bpo);
  128. }
  129. }
  130. else
  131. {
  132. DetailLog("{0},BSLinkset.RestoreBodyDependencies,restoreSingleChild,rID={1},rBody={2},cID={3},cBody={4}",
  133. LinksetRoot.LocalID,
  134. LinksetRoot.LocalID, LinksetRoot.BSBody.ptr.ToString("X"),
  135. child.LocalID, child.BSBody.ptr.ToString("X"));
  136. PhysicallyLinkAChildToRoot(LinksetRoot, child);
  137. }
  138. }
  139. }
  140. // ================================================================
  141. // Below this point is internal magic
  142. // I am the root of a linkset and a new child is being added
  143. // Called while LinkActivity is locked.
  144. protected override void AddChildToLinkset(BSPhysObject child)
  145. {
  146. if (!HasChild(child))
  147. {
  148. m_children.Add(child);
  149. BSPhysObject rootx = LinksetRoot; // capture the root as of now
  150. BSPhysObject childx = child;
  151. DetailLog("{0},AddChildToLinkset,call,child={1}", LinksetRoot.LocalID, child.LocalID);
  152. PhysicsScene.TaintedObject("AddChildToLinkset", delegate()
  153. {
  154. DetailLog("{0},AddChildToLinkset,taint,rID={1},rBody={2},cID={3},cBody={4}",
  155. rootx.LocalID,
  156. rootx.LocalID, rootx.BSBody.ptr.ToString("X"),
  157. childx.LocalID, childx.BSBody.ptr.ToString("X"));
  158. // Since this is taint-time, the body and shape could have changed for the child
  159. rootx.ForcePosition = rootx.Position; // DEBUG
  160. childx.ForcePosition = childx.Position; // DEBUG
  161. PhysicallyLinkAChildToRoot(rootx, childx);
  162. m_taintChildren.Add(child);
  163. });
  164. }
  165. return;
  166. }
  167. // Forcefully removing a child from a linkset.
  168. // This is not being called by the child so we have to make sure the child doesn't think
  169. // it's still connected to the linkset.
  170. // Normal OpenSimulator operation will never do this because other SceneObjectPart information
  171. // also has to be updated (like pointer to prim's parent).
  172. protected override void RemoveChildFromOtherLinkset(BSPhysObject pchild)
  173. {
  174. pchild.Linkset = BSLinkset.Factory(PhysicsScene, pchild);
  175. RemoveChildFromLinkset(pchild);
  176. }
  177. // I am the root of a linkset and one of my children is being removed.
  178. // Safe to call even if the child is not really in my linkset.
  179. protected override void RemoveChildFromLinkset(BSPhysObject child)
  180. {
  181. if (m_children.Remove(child))
  182. {
  183. BSPhysObject rootx = LinksetRoot; // capture the root and body as of now
  184. BSPhysObject childx = child;
  185. DetailLog("{0},RemoveChildFromLinkset,call,rID={1},rBody={2},cID={3},cBody={4}",
  186. childx.LocalID,
  187. rootx.LocalID, rootx.BSBody.ptr.ToString("X"),
  188. childx.LocalID, childx.BSBody.ptr.ToString("X"));
  189. PhysicsScene.TaintedObject("RemoveChildFromLinkset", delegate()
  190. {
  191. m_taintChildren.Remove(child);
  192. PhysicallyUnlinkAChildFromRoot(rootx, childx);
  193. RecomputeLinksetConstraintVariables();
  194. });
  195. }
  196. else
  197. {
  198. // This will happen if we remove the root of the linkset first. Non-fatal occurance.
  199. // PhysicsScene.Logger.ErrorFormat("{0}: Asked to remove child from linkset that was not in linkset", LogHeader);
  200. }
  201. return;
  202. }
  203. // Create a constraint between me (root of linkset) and the passed prim (the child).
  204. // Called at taint time!
  205. private void PhysicallyLinkAChildToRoot(BSPhysObject rootPrim, BSPhysObject childPrim)
  206. {
  207. // Zero motion for children so they don't interpolate
  208. childPrim.ZeroMotion();
  209. // Relative position normalized to the root prim
  210. // Essentually a vector pointing from center of rootPrim to center of childPrim
  211. OMV.Vector3 childRelativePosition = childPrim.Position - rootPrim.Position;
  212. // real world coordinate of midpoint between the two objects
  213. OMV.Vector3 midPoint = rootPrim.Position + (childRelativePosition / 2);
  214. DetailLog("{0},BSLinkset.PhysicallyLinkAChildToRoot,taint,root={1},rBody={2},child={3},cBody={4},rLoc={5},cLoc={6},midLoc={7}",
  215. rootPrim.LocalID,
  216. rootPrim.LocalID, rootPrim.BSBody.ptr.ToString("X"),
  217. childPrim.LocalID, childPrim.BSBody.ptr.ToString("X"),
  218. rootPrim.Position, childPrim.Position, midPoint);
  219. // create a constraint that allows no freedom of movement between the two objects
  220. // http://bulletphysics.org/Bullet/phpBB3/viewtopic.php?t=4818
  221. BS6DofConstraint constrain = new BS6DofConstraint(
  222. PhysicsScene.World, rootPrim.BSBody, childPrim.BSBody, midPoint, true, true );
  223. /* NOTE: below is an attempt to build constraint with full frame computation, etc.
  224. * Using the midpoint is easier since it lets the Bullet code manipulate the transforms
  225. * of the objects.
  226. * Code left as a warning to future programmers.
  227. // ==================================================================================
  228. // relative position normalized to the root prim
  229. OMV.Quaternion invThisOrientation = OMV.Quaternion.Inverse(rootPrim.Orientation);
  230. OMV.Vector3 childRelativePosition = (childPrim.Position - rootPrim.Position) * invThisOrientation;
  231. // relative rotation of the child to the parent
  232. OMV.Quaternion childRelativeRotation = invThisOrientation * childPrim.Orientation;
  233. OMV.Quaternion inverseChildRelativeRotation = OMV.Quaternion.Inverse(childRelativeRotation);
  234. // create a constraint that allows no freedom of movement between the two objects
  235. // http://bulletphysics.org/Bullet/phpBB3/viewtopic.php?t=4818
  236. DetailLog("{0},BSLinkset.PhysicallyLinkAChildToRoot,taint,root={1},child={2}", rootPrim.LocalID, rootPrim.LocalID, childPrim.LocalID);
  237. BS6DofConstraint constrain = new BS6DofConstraint(
  238. PhysicsScene.World, rootPrim.Body, childPrim.Body,
  239. OMV.Vector3.Zero,
  240. OMV.Quaternion.Inverse(rootPrim.Orientation),
  241. OMV.Vector3.Zero,
  242. OMV.Quaternion.Inverse(childPrim.Orientation),
  243. // A point half way between the parent and child
  244. // childRelativePosition/2,
  245. // childRelativeRotation,
  246. // childRelativePosition/2,
  247. // inverseChildRelativeRotation,
  248. true,
  249. true
  250. );
  251. // ==================================================================================
  252. */
  253. PhysicsScene.Constraints.AddConstraint(constrain);
  254. // zero linear and angular limits makes the objects unable to move in relation to each other
  255. constrain.SetLinearLimits(OMV.Vector3.Zero, OMV.Vector3.Zero);
  256. constrain.SetAngularLimits(OMV.Vector3.Zero, OMV.Vector3.Zero);
  257. // tweek the constraint to increase stability
  258. constrain.UseFrameOffset(PhysicsScene.BoolNumeric(PhysicsScene.Params.linkConstraintUseFrameOffset));
  259. constrain.TranslationalLimitMotor(PhysicsScene.BoolNumeric(PhysicsScene.Params.linkConstraintEnableTransMotor),
  260. PhysicsScene.Params.linkConstraintTransMotorMaxVel,
  261. PhysicsScene.Params.linkConstraintTransMotorMaxForce);
  262. constrain.SetCFMAndERP(PhysicsScene.Params.linkConstraintCFM, PhysicsScene.Params.linkConstraintERP);
  263. if (PhysicsScene.Params.linkConstraintSolverIterations != 0f)
  264. {
  265. constrain.SetSolverIterations(PhysicsScene.Params.linkConstraintSolverIterations);
  266. }
  267. }
  268. // Remove linkage between myself and a particular child
  269. // The root and child bodies are passed in because we need to remove the constraint between
  270. // the bodies that were at unlink time.
  271. // Called at taint time!
  272. private bool PhysicallyUnlinkAChildFromRoot(BSPhysObject rootPrim, BSPhysObject childPrim)
  273. {
  274. bool ret = false;
  275. DetailLog("{0},BSLinkset.PhysicallyUnlinkAChildFromRoot,taint,root={1},rBody={2},child={3},cBody={4}",
  276. rootPrim.LocalID,
  277. rootPrim.LocalID, rootPrim.BSBody.ptr.ToString("X"),
  278. childPrim.LocalID, childPrim.BSBody.ptr.ToString("X"));
  279. // Find the constraint for this link and get rid of it from the overall collection and from my list
  280. if (PhysicsScene.Constraints.RemoveAndDestroyConstraint(rootPrim.BSBody, childPrim.BSBody))
  281. {
  282. // Make the child refresh its location
  283. BulletSimAPI.PushUpdate2(childPrim.BSBody.ptr);
  284. ret = true;
  285. }
  286. return ret;
  287. }
  288. // Remove linkage between myself and any possible children I might have.
  289. // Called at taint time!
  290. private bool PhysicallyUnlinkAllChildrenFromRoot(BSPhysObject rootPrim)
  291. {
  292. DetailLog("{0},BSLinkset.PhysicallyUnlinkAllChildren,taint", rootPrim.LocalID);
  293. bool ret = false;
  294. if (PhysicsScene.Constraints.RemoveAndDestroyConstraint(rootPrim.BSBody))
  295. {
  296. ret = true;
  297. }
  298. return ret;
  299. }
  300. // Call each of the constraints that make up this linkset and recompute the
  301. // various transforms and variables. Used when objects are added or removed
  302. // from a linkset to make sure the constraints know about the new mass and
  303. // geometry.
  304. // Must only be called at taint time!!
  305. private void RecomputeLinksetConstraintVariables()
  306. {
  307. float linksetMass = LinksetMass;
  308. foreach (BSPhysObject child in m_taintChildren)
  309. {
  310. BSConstraint constrain;
  311. if (PhysicsScene.Constraints.TryGetConstraint(LinksetRoot.BSBody, child.BSBody, out constrain))
  312. {
  313. // DetailLog("{0},BSLinkset.RecomputeLinksetConstraintVariables,taint,child={1},mass={2},A={3},B={4}",
  314. // LinksetRoot.LocalID, child.LocalID, linksetMass, constrain.Body1.ID, constrain.Body2.ID);
  315. constrain.RecomputeConstraintVariables(linksetMass);
  316. }
  317. else
  318. {
  319. // Non-fatal error that happens when children are being added to the linkset but
  320. // their constraints have not been created yet.
  321. break;
  322. }
  323. }
  324. // If the whole linkset is not here, doesn't make sense to recompute linkset wide values
  325. if (m_children.Count == m_taintChildren.Count)
  326. {
  327. // If this is a multiple object linkset, set everybody's center of mass to the set's center of mass
  328. OMV.Vector3 centerOfMass = ComputeLinksetCenterOfMass();
  329. BulletSimAPI.SetCenterOfMassByPosRot2(LinksetRoot.BSBody.ptr,
  330. centerOfMass, OMV.Quaternion.Identity);
  331. DetailLog("{0},BSLinkset.RecomputeLinksetConstraintVariables,setCenterOfMass,COM={1},rBody={2}",
  332. LinksetRoot.LocalID, centerOfMass, LinksetRoot.BSBody.ptr.ToString("X"));
  333. foreach (BSPhysObject child in m_taintChildren)
  334. {
  335. BulletSimAPI.SetCenterOfMassByPosRot2(child.BSBody.ptr,
  336. centerOfMass, OMV.Quaternion.Identity);
  337. }
  338. // BulletSimAPI.DumpAllInfo2(PhysicsScene.World.ptr); // DEBUG DEBUG DEBUG
  339. }
  340. return;
  341. }
  342. }
  343. }