BSLinksetCompound.cs 26 KB

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