123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478 |
- /*
- * Copyright (c) Contributors, http://opensimulator.org/
- * See CONTRIBUTORS.TXT for a full list of copyright holders.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * * Redistributions in binary form must reproduce the above copyrightD
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * * Neither the name of the OpenSimulator Project nor the
- * names of its contributors may be used to endorse or promote products
- * derived from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
- * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
- * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
- * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
- * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
- * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
- * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
- * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
- using System;
- using System.Collections.Generic;
- using System.Text;
- using OpenSim.Framework;
- using OMV = OpenMetaverse;
- namespace OpenSim.Region.Physics.BulletSPlugin
- {
- public sealed class BSLinksetCompound : BSLinkset
- {
- #pragma warning disable 414
- private static string LogHeader = "[BULLETSIM LINKSET COMPOUND]";
- #pragma warning restore 414
- public BSLinksetCompound(BSScene scene, BSPrimLinkable parent)
- : base(scene, parent)
- {
- LinksetImpl = LinksetImplementation.Compound;
- }
- // ================================================================
- // Changing the physical property of the linkset only needs to change the root
- public override void SetPhysicalFriction(float friction)
- {
- if (LinksetRoot.PhysBody.HasPhysicalBody)
- m_physicsScene.PE.SetFriction(LinksetRoot.PhysBody, friction);
- }
- public override void SetPhysicalRestitution(float restitution)
- {
- if (LinksetRoot.PhysBody.HasPhysicalBody)
- m_physicsScene.PE.SetRestitution(LinksetRoot.PhysBody, restitution);
- }
- public override void SetPhysicalGravity(OMV.Vector3 gravity)
- {
- if (LinksetRoot.PhysBody.HasPhysicalBody)
- m_physicsScene.PE.SetGravity(LinksetRoot.PhysBody, gravity);
- }
- public override void ComputeAndSetLocalInertia(OMV.Vector3 inertiaFactor, float linksetMass)
- {
- OMV.Vector3 inertia = m_physicsScene.PE.CalculateLocalInertia(LinksetRoot.PhysShape.physShapeInfo, linksetMass);
- LinksetRoot.Inertia = inertia * inertiaFactor;
- m_physicsScene.PE.SetMassProps(LinksetRoot.PhysBody, linksetMass, LinksetRoot.Inertia);
- m_physicsScene.PE.UpdateInertiaTensor(LinksetRoot.PhysBody);
- }
- public override void SetPhysicalCollisionFlags(CollisionFlags collFlags)
- {
- if (LinksetRoot.PhysBody.HasPhysicalBody)
- m_physicsScene.PE.SetCollisionFlags(LinksetRoot.PhysBody, collFlags);
- }
- public override void AddToPhysicalCollisionFlags(CollisionFlags collFlags)
- {
- if (LinksetRoot.PhysBody.HasPhysicalBody)
- m_physicsScene.PE.AddToCollisionFlags(LinksetRoot.PhysBody, collFlags);
- }
- public override void RemoveFromPhysicalCollisionFlags(CollisionFlags collFlags)
- {
- if (LinksetRoot.PhysBody.HasPhysicalBody)
- m_physicsScene.PE.RemoveFromCollisionFlags(LinksetRoot.PhysBody, collFlags);
- }
- // ================================================================
- // When physical properties are changed the linkset needs to recalculate
- // its internal properties.
- public override void Refresh(BSPrimLinkable requestor)
- {
- // Something changed so do the rebuilding thing
- ScheduleRebuild(requestor);
- base.Refresh(requestor);
- }
- // Schedule a refresh to happen after all the other taint processing.
- private void ScheduleRebuild(BSPrimLinkable requestor)
- {
- // When rebuilding, it is possible to set properties that would normally require a rebuild.
- // If already rebuilding, don't request another rebuild.
- // If a linkset with just a root prim (simple non-linked prim) don't bother rebuilding.
- lock (m_linksetActivityLock)
- {
- if (!RebuildScheduled && !Rebuilding && HasAnyChildren)
- {
- InternalScheduleRebuild(requestor);
- }
- }
- }
- // Must be called with m_linksetActivityLock or race conditions will haunt you.
- private void InternalScheduleRebuild(BSPrimLinkable requestor)
- {
- DetailLog("{0},BSLinksetCompound.InternalScheduleRebuild,,rebuilding={1},hasChildren={2}",
- requestor.LocalID, Rebuilding, HasAnyChildren);
- RebuildScheduled = true;
- m_physicsScene.PostTaintObject("BSLinksetCompound.ScheduleRebuild", LinksetRoot.LocalID, delegate()
- {
- if (HasAnyChildren)
- {
- if (this.AllPartsComplete)
- {
- RecomputeLinksetCompound();
- }
- else
- {
- DetailLog("{0},BSLinksetCompound.InternalScheduleRebuild,,rescheduling because not all children complete",
- requestor.LocalID);
- InternalScheduleRebuild(requestor);
- }
- }
- RebuildScheduled = false;
- });
- }
- // The object is going dynamic (physical). Do any setup necessary for a dynamic linkset.
- // Only the state of the passed object can be modified. The rest of the linkset
- // has not yet been fully constructed.
- // Return 'true' if any properties updated on the passed object.
- // Called at taint-time!
- public override bool MakeDynamic(BSPrimLinkable child)
- {
- bool ret = false;
- DetailLog("{0},BSLinksetCompound.MakeDynamic,call,IsRoot={1}", child.LocalID, IsRoot(child));
- if (IsRoot(child))
- {
- // The root is going dynamic. Rebuild the linkset so parts and mass get computed properly.
- Refresh(LinksetRoot);
- }
- return ret;
- }
- // The object is going static (non-physical). We do not do anything for static linksets.
- // Return 'true' if any properties updated on the passed object.
- // Called at taint-time!
- public override bool MakeStatic(BSPrimLinkable child)
- {
- bool ret = false;
- DetailLog("{0},BSLinksetCompound.MakeStatic,call,IsRoot={1}", child.LocalID, IsRoot(child));
- child.ClearDisplacement();
- if (IsRoot(child))
- {
- // Schedule a rebuild to verify that the root shape is set to the real shape.
- Refresh(LinksetRoot);
- }
- return ret;
- }
- // 'physicalUpdate' is true if these changes came directly from the physics engine. Don't need to rebuild then.
- // Called at taint-time.
- public override void UpdateProperties(UpdatedProperties whichUpdated, BSPrimLinkable updated)
- {
- if (!LinksetRoot.IsPhysicallyActive)
- {
- // No reason to do this physical stuff for static linksets.
- DetailLog("{0},BSLinksetCompound.UpdateProperties,notPhysical", LinksetRoot.LocalID);
- return;
- }
- // The user moving a child around requires the rebuilding of the linkset compound shape
- // One problem is this happens when a border is crossed -- the simulator implementation
- // stores the position into the group which causes the move of the object
- // but it also means all the child positions get updated.
- // What would cause an unnecessary rebuild so we make sure the linkset is in a
- // region before bothering to do a rebuild.
- if (!IsRoot(updated) && m_physicsScene.TerrainManager.IsWithinKnownTerrain(LinksetRoot.RawPosition))
- {
- // If a child of the linkset is updating only the position or rotation, that can be done
- // without rebuilding the linkset.
- // If a handle for the child can be fetch, we update the child here. If a rebuild was
- // scheduled by someone else, the rebuild will just replace this setting.
- bool updatedChild = false;
- // Anything other than updating position or orientation usually means a physical update
- // and that is caused by us updating the object.
- if ((whichUpdated & ~(UpdatedProperties.Position | UpdatedProperties.Orientation)) == 0)
- {
- // Find the physical instance of the child
- if (LinksetRoot.PhysShape.HasPhysicalShape && m_physicsScene.PE.IsCompound(LinksetRoot.PhysShape.physShapeInfo))
- {
- // It is possible that the linkset is still under construction and the child is not yet
- // inserted into the compound shape. A rebuild of the linkset in a pre-step action will
- // build the whole thing with the new position or rotation.
- // The index must be checked because Bullet references the child array but does no validity
- // checking of the child index passed.
- int numLinksetChildren = m_physicsScene.PE.GetNumberOfCompoundChildren(LinksetRoot.PhysShape.physShapeInfo);
- if (updated.LinksetChildIndex < numLinksetChildren)
- {
- BulletShape linksetChildShape = m_physicsScene.PE.GetChildShapeFromCompoundShapeIndex(LinksetRoot.PhysShape.physShapeInfo, updated.LinksetChildIndex);
- if (linksetChildShape.HasPhysicalShape)
- {
- // Found the child shape within the compound shape
- m_physicsScene.PE.UpdateChildTransform(LinksetRoot.PhysShape.physShapeInfo, updated.LinksetChildIndex,
- updated.RawPosition - LinksetRoot.RawPosition,
- updated.RawOrientation * OMV.Quaternion.Inverse(LinksetRoot.RawOrientation),
- true /* shouldRecalculateLocalAabb */);
- updatedChild = true;
- DetailLog("{0},BSLinksetCompound.UpdateProperties,changeChildPosRot,whichUpdated={1},pos={2},rot={3}",
- updated.LocalID, whichUpdated, updated.RawPosition, updated.RawOrientation);
- }
- else // DEBUG DEBUG
- { // DEBUG DEBUG
- DetailLog("{0},BSLinksetCompound.UpdateProperties,couldNotUpdateChild,noChildShape,shape={1}",
- updated.LocalID, linksetChildShape);
- } // DEBUG DEBUG
- }
- else // DEBUG DEBUG
- { // DEBUG DEBUG
- // the child is not yet in the compound shape. This is non-fatal.
- DetailLog("{0},BSLinksetCompound.UpdateProperties,couldNotUpdateChild,childNotInCompoundShape,numChildren={1},index={2}",
- updated.LocalID, numLinksetChildren, updated.LinksetChildIndex);
- } // DEBUG DEBUG
- }
- else // DEBUG DEBUG
- { // DEBUG DEBUG
- DetailLog("{0},BSLinksetCompound.UpdateProperties,couldNotUpdateChild,noBodyOrNotCompound", updated.LocalID);
- } // DEBUG DEBUG
- if (!updatedChild)
- {
- // If couldn't do the individual child, the linkset needs a rebuild to incorporate the new child info.
- // Note: there are several ways through this code that will not update the child if
- // the linkset is being rebuilt. In this case, scheduling a rebuild is a NOOP since
- // there will already be a rebuild scheduled.
- DetailLog("{0},BSLinksetCompound.UpdateProperties,couldNotUpdateChild.schedulingRebuild,whichUpdated={1}",
- updated.LocalID, whichUpdated);
- Refresh(updated);
- }
- }
- }
- }
- // Routine called when rebuilding the body of some member of the linkset.
- // If one of the bodies is being changed, the linkset needs rebuilding.
- // For instance, a linkset is built and then a mesh asset is read in and the mesh is recreated.
- // Returns 'true' of something was actually removed and would need restoring
- // Called at taint-time!!
- public override bool RemoveDependencies(BSPrimLinkable child)
- {
- bool ret = false;
- DetailLog("{0},BSLinksetCompound.RemoveDependencies,refreshIfChild,rID={1},rBody={2},isRoot={3}",
- child.LocalID, LinksetRoot.LocalID, LinksetRoot.PhysBody, IsRoot(child));
- Refresh(child);
- return ret;
- }
- // ================================================================
- // Add a new child to the linkset.
- // Called while LinkActivity is locked.
- protected override void AddChildToLinkset(BSPrimLinkable child)
- {
- if (!HasChild(child))
- {
- m_children.Add(child, new BSLinkInfo(child));
- DetailLog("{0},BSLinksetCompound.AddChildToLinkset,call,child={1}", LinksetRoot.LocalID, child.LocalID);
- // Rebuild the compound shape with the new child shape included
- Refresh(child);
- }
- return;
- }
- // Remove the specified child from the linkset.
- // Safe to call even if the child is not really in the linkset.
- protected override void RemoveChildFromLinkset(BSPrimLinkable child, bool inTaintTime)
- {
- child.ClearDisplacement();
- if (m_children.Remove(child))
- {
- DetailLog("{0},BSLinksetCompound.RemoveChildFromLinkset,call,rID={1},rBody={2},cID={3},cBody={4}",
- child.LocalID,
- LinksetRoot.LocalID, LinksetRoot.PhysBody.AddrString,
- child.LocalID, child.PhysBody.AddrString);
- // Cause the child's body to be rebuilt and thus restored to normal operation
- child.ForceBodyShapeRebuild(inTaintTime);
- if (!HasAnyChildren)
- {
- // The linkset is now empty. The root needs rebuilding.
- LinksetRoot.ForceBodyShapeRebuild(inTaintTime);
- }
- else
- {
- // Rebuild the compound shape with the child removed
- Refresh(LinksetRoot);
- }
- }
- return;
- }
- // Called before the simulation step to make sure the compound based linkset
- // is all initialized.
- // Constraint linksets are rebuilt every time.
- // Note that this works for rebuilding just the root after a linkset is taken apart.
- // Called at taint time!!
- private bool UseBulletSimRootOffsetHack = false; // Attempt to have Bullet track the coords of root compound shape
- // Number of times to perform rebuilds on broken linkset children. This should only happen when
- // a linkset is initially being created and should happen only one or two times at the most.
- // This exists to cause a looping problem to be reported while not rebuilding a linkset forever.
- private static int LinksetRebuildFailureLoopPrevention = 10;
- private void RecomputeLinksetCompound()
- {
- try
- {
- Rebuilding = true;
- // No matter what is being done, force the root prim's PhysBody and PhysShape to get set
- // to what they should be as if the root was not in a linkset.
- // Not that bad since we only get into this routine if there are children in the linkset and
- // something has been updated/changed.
- // Have to do the rebuild before checking for physical because this might be a linkset
- // being destructed and going non-physical.
- LinksetRoot.ForceBodyShapeRebuild(true);
- // There is no reason to build all this physical stuff for a non-physical or empty linkset.
- if (!LinksetRoot.IsPhysicallyActive || !HasAnyChildren)
- {
- DetailLog("{0},BSLinksetCompound.RecomputeLinksetCompound,notPhysicalOrNoChildren", LinksetRoot.LocalID);
- return; // Note the 'finally' clause at the botton which will get executed.
- }
- // Get a new compound shape to build the linkset shape in.
- BSShape linksetShape = BSShapeCompound.GetReference(m_physicsScene);
- // Compute a displacement for each component so it is relative to the center-of-mass.
- // Bullet presumes an object's origin (relative <0,0,0>) is its center-of-mass
- OMV.Vector3 centerOfMassW = ComputeLinksetCenterOfMass();
- OMV.Quaternion invRootOrientation = OMV.Quaternion.Normalize(OMV.Quaternion.Inverse(LinksetRoot.RawOrientation));
- OMV.Vector3 origRootPosition = LinksetRoot.RawPosition;
- // 'centerDisplacementV' is the vehicle relative distance from the simulator root position to the center-of-mass
- OMV.Vector3 centerDisplacementV = (centerOfMassW - LinksetRoot.RawPosition) * invRootOrientation;
- if (UseBulletSimRootOffsetHack || !BSParam.LinksetOffsetCenterOfMass)
- {
- // Zero everything if center-of-mass displacement is not being done.
- centerDisplacementV = OMV.Vector3.Zero;
- LinksetRoot.ClearDisplacement();
- }
- else
- {
- // The actual center-of-mass could have been set by the user.
- centerDisplacementV = LinksetRoot.SetEffectiveCenterOfMassDisplacement(centerDisplacementV);
- }
- DetailLog("{0},BSLinksetCompound.RecomputeLinksetCompound,COM,rootPos={1},com={2},comDisp={3}",
- LinksetRoot.LocalID, origRootPosition, centerOfMassW, centerDisplacementV);
- // Add the shapes of all the components of the linkset
- int memberIndex = 1;
- ForEachMember((cPrim) =>
- {
- if (IsRoot(cPrim))
- {
- // Root shape is always index zero.
- cPrim.LinksetChildIndex = 0;
- }
- else
- {
- cPrim.LinksetChildIndex = memberIndex;
- memberIndex++;
- }
- // Get a reference to the shape of the child for adding of that shape to the linkset compound shape
- BSShape childShape = cPrim.PhysShape.GetReference(m_physicsScene, cPrim);
- // Offset the child shape from the center-of-mass and rotate it to root relative.
- OMV.Vector3 offsetPos = (cPrim.RawPosition - origRootPosition) * invRootOrientation - centerDisplacementV;
- OMV.Quaternion offsetRot = OMV.Quaternion.Normalize(cPrim.RawOrientation) * invRootOrientation;
- // Add the child shape to the compound shape being built
- if (childShape.physShapeInfo.HasPhysicalShape)
- {
- m_physicsScene.PE.AddChildShapeToCompoundShape(linksetShape.physShapeInfo, childShape.physShapeInfo, offsetPos, offsetRot);
- DetailLog("{0},BSLinksetCompound.RecomputeLinksetCompound,addChild,indx={1},cShape={2},offPos={3},offRot={4}",
- LinksetRoot.LocalID, cPrim.LinksetChildIndex, childShape, offsetPos, offsetRot);
- // Since we are borrowing the shape of the child, disable the origional child body
- if (!IsRoot(cPrim))
- {
- m_physicsScene.PE.AddToCollisionFlags(cPrim.PhysBody, CollisionFlags.CF_NO_CONTACT_RESPONSE);
- m_physicsScene.PE.ForceActivationState(cPrim.PhysBody, ActivationState.DISABLE_SIMULATION);
- // We don't want collisions from the old linkset children.
- m_physicsScene.PE.RemoveFromCollisionFlags(cPrim.PhysBody, CollisionFlags.BS_SUBSCRIBE_COLLISION_EVENTS);
- cPrim.PhysBody.collisionType = CollisionType.LinksetChild;
- }
- }
- else
- {
- // The linkset must be in an intermediate state where all the children have not yet
- // been constructed. This sometimes happens on startup when everything is getting
- // built and some shapes have to wait for assets to be read in.
- // Just skip this linkset for the moment and cause the shape to be rebuilt next tick.
- // One problem might be that the shape is broken somehow and it never becomes completely
- // available. This might cause the rebuild to happen over and over.
- InternalScheduleRebuild(LinksetRoot);
- DetailLog("{0},BSLinksetCompound.RecomputeLinksetCompound,addChildWithNoShape,indx={1},cShape={2},offPos={3},offRot={4}",
- LinksetRoot.LocalID, cPrim.LinksetChildIndex, childShape, offsetPos, offsetRot);
- // Output an annoying warning. It should only happen once but if it keeps coming out,
- // the user knows there is something wrong and will report it.
- m_physicsScene.Logger.WarnFormat("{0} Linkset rebuild warning. If this happens more than one or two times, please report in Mantis 7191", LogHeader);
- m_physicsScene.Logger.WarnFormat("{0} pName={1}, childIdx={2}, shape={3}",
- LogHeader, LinksetRoot.Name, cPrim.LinksetChildIndex, childShape);
- // This causes the loop to bail on building the rest of this linkset.
- // The rebuild operation will fix it up next tick or declare the object unbuildable.
- return true;
- }
- return false; // 'false' says to move onto the next child in the list
- });
- // Replace the root shape with the built compound shape.
- // Object removed and added to world to get collision cache rebuilt for new shape.
- LinksetRoot.PhysShape.Dereference(m_physicsScene);
- LinksetRoot.PhysShape = linksetShape;
- m_physicsScene.PE.RemoveObjectFromWorld(m_physicsScene.World, LinksetRoot.PhysBody);
- m_physicsScene.PE.SetCollisionShape(m_physicsScene.World, LinksetRoot.PhysBody, linksetShape.physShapeInfo);
- m_physicsScene.PE.AddObjectToWorld(m_physicsScene.World, LinksetRoot.PhysBody);
- DetailLog("{0},BSLinksetCompound.RecomputeLinksetCompound,addBody,body={1},shape={2}",
- LinksetRoot.LocalID, LinksetRoot.PhysBody, linksetShape);
- // With all of the linkset packed into the root prim, it has the mass of everyone.
- LinksetMass = ComputeLinksetMass();
- LinksetRoot.UpdatePhysicalMassProperties(LinksetMass, true);
- if (UseBulletSimRootOffsetHack)
- {
- // Enable the physical position updator to return the position and rotation of the root shape.
- // This enables a feature in the C++ code to return the world coordinates of the first shape in the
- // compound shape. This aleviates the need to offset the returned physical position by the
- // center-of-mass offset.
- // TODO: either debug this feature or remove it.
- m_physicsScene.PE.AddToCollisionFlags(LinksetRoot.PhysBody, CollisionFlags.BS_RETURN_ROOT_COMPOUND_SHAPE);
- }
- }
- finally
- {
- Rebuilding = false;
- }
- // See that the Aabb surrounds the new shape
- m_physicsScene.PE.RecalculateCompoundShapeLocalAabb(LinksetRoot.PhysShape.physShapeInfo);
- }
- }
- }
|