/* * 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 OMV = OpenMetaverse; namespace OpenSim.Region.Physics.BulletSPlugin { public abstract class BSLinkset { // private static string LogHeader = "[BULLETSIM LINKSET]"; public enum LinksetImplementation { Constraint = 0, // linkset tied together with constraints Compound = 1, // linkset tied together as a compound object Manual = 2 // linkset tied together manually (code moves all the pieces) } // Create the correct type of linkset for this child public static BSLinkset Factory(BSScene physScene, BSPrimLinkable parent) { BSLinkset ret = null; switch (parent.LinksetType) { case LinksetImplementation.Constraint: ret = new BSLinksetConstraints(physScene, parent); break; case LinksetImplementation.Compound: ret = new BSLinksetCompound(physScene, parent); break; case LinksetImplementation.Manual: // ret = new BSLinksetManual(physScene, parent); break; default: ret = new BSLinksetCompound(physScene, parent); break; } if (ret == null) { physScene.Logger.ErrorFormat("[BULLETSIM LINKSET] Factory could not create linkset. Parent name={1}, ID={2}", parent.Name, parent.LocalID); } return ret; } public BSPrimLinkable LinksetRoot { get; protected set; } protected BSScene m_physicsScene { get; private set; } static int m_nextLinksetID = 1; public int LinksetID { get; private set; } // The children under the root in this linkset. protected HashSet m_children; // We lock the diddling of linkset classes to prevent any badness. // This locks the modification of the instances of this class. Changes // to the physical representation is done via the tainting mechenism. protected object m_linksetActivityLock = new Object(); // We keep the prim's mass in the linkset structure since it could be dependent on other prims public float LinksetMass { get; protected set; } public virtual bool LinksetIsColliding { get { return false; } } public OMV.Vector3 CenterOfMass { get { return ComputeLinksetCenterOfMass(); } } public OMV.Vector3 GeometricCenter { get { return ComputeLinksetGeometricCenter(); } } protected BSLinkset(BSScene scene, BSPrimLinkable parent) { // A simple linkset of one (no children) LinksetID = m_nextLinksetID++; // We create LOTS of linksets. if (m_nextLinksetID <= 0) m_nextLinksetID = 1; m_physicsScene = scene; LinksetRoot = parent; m_children = new HashSet(); LinksetMass = parent.RawMass; Rebuilding = false; parent.ClearDisplacement(); } // Link to a linkset where the child knows the parent. // Parent changing should not happen so do some sanity checking. // We return the parent's linkset so the child can track its membership. // Called at runtime. public BSLinkset AddMeToLinkset(BSPrimLinkable child) { lock (m_linksetActivityLock) { // Don't add the root to its own linkset if (!IsRoot(child)) AddChildToLinkset(child); LinksetMass = ComputeLinksetMass(); } return this; } // Remove a child from a linkset. // Returns a new linkset for the child which is a linkset of one (just the // orphened child). // Called at runtime. public BSLinkset RemoveMeFromLinkset(BSPrimLinkable child) { lock (m_linksetActivityLock) { if (IsRoot(child)) { // Cannot remove the root from a linkset. return this; } RemoveChildFromLinkset(child); LinksetMass = ComputeLinksetMass(); } // The child is down to a linkset of just itself return BSLinkset.Factory(m_physicsScene, child); } // Return 'true' if the passed object is the root object of this linkset public bool IsRoot(BSPrimLinkable requestor) { return (requestor.LocalID == LinksetRoot.LocalID); } public int NumberOfChildren { get { return m_children.Count; } } // Return 'true' if this linkset has any children (more than the root member) public bool HasAnyChildren { get { return (m_children.Count > 0); } } // Return 'true' if this child is in this linkset public bool HasChild(BSPrimLinkable child) { bool ret = false; lock (m_linksetActivityLock) { ret = m_children.Contains(child); /* Safer version but the above should work foreach (BSPrimLinkable bp in m_children) { if (child.LocalID == bp.LocalID) { ret = true; break; } } */ } return ret; } // Perform an action on each member of the linkset including root prim. // Depends on the action on whether this should be done at taint time. public delegate bool ForEachMemberAction(BSPrimLinkable obj); public virtual bool ForEachMember(ForEachMemberAction action) { bool ret = false; lock (m_linksetActivityLock) { action(LinksetRoot); foreach (BSPrimLinkable po in m_children) { if (action(po)) break; } } return ret; } // Called after a simulation step to post a collision with this object. // Return 'true' if linkset processed the collision. 'false' says the linkset didn't have // anything to add for the collision and it should be passed through normal processing. // Default processing for a linkset. public virtual bool HandleCollide(uint collidingWith, BSPhysObject collidee, OMV.Vector3 contactPoint, OMV.Vector3 contactNormal, float pentrationDepth) { bool ret = false; // prims in the same linkset cannot collide with each other BSPrimLinkable convCollidee = collidee as BSPrimLinkable; if (convCollidee != null && (LinksetID == convCollidee.Linkset.LinksetID)) { // By returning 'true', we tell the caller the collision has been 'handled' so it won't // do anything about this collision and thus, effectivily, ignoring the collision. ret = true; } else { // Not a collision between members of the linkset. Must be a real collision. // So the linkset root can know if there is a collision anywhere in the linkset. LinksetRoot.SomeCollisionSimulationStep = m_physicsScene.SimulationStep; } return ret; } // I am the root of a linkset and a new child is being added // Called while LinkActivity is locked. protected abstract void AddChildToLinkset(BSPrimLinkable child); // I am the root of a linkset and one of my children is being removed. // Safe to call even if the child is not really in my linkset. protected abstract void RemoveChildFromLinkset(BSPrimLinkable child); // When physical properties are changed the linkset needs to recalculate // its internal properties. // May be called at runtime or taint-time. public virtual void Refresh(BSPrimLinkable requestor) { LinksetMass = ComputeLinksetMass(); } // Flag denoting the linkset is in the process of being rebuilt. // Used to know not the schedule a rebuild in the middle of a rebuild. protected bool Rebuilding { get; set; } // 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 abstract bool MakeDynamic(BSPrimLinkable child); // The object is going static (non-physical). Do any setup necessary // for a static linkset. // Return 'true' if any properties updated on the passed object. // Called at taint-time! public abstract bool MakeStatic(BSPrimLinkable child); // Called when a parameter update comes from the physics engine for any object // of the linkset is received. // Passed flag is update came from physics engine (true) or the user (false). // Called at taint-time!! public abstract void UpdateProperties(UpdatedProperties whichUpdated, BSPrimLinkable physObject); // Routine used when rebuilding the body of the root of the linkset // Destroy all the constraints have have been made to root. // This is called when the root body is changing. // Returns 'true' of something was actually removed and would need restoring // Called at taint-time!! public abstract bool RemoveDependencies(BSPrimLinkable child); // ================================================================ // Some physical setting happen to all members of the linkset public virtual void SetPhysicalFriction(float friction) { ForEachMember((member) => { if (member.PhysBody.HasPhysicalBody) m_physicsScene.PE.SetFriction(member.PhysBody, friction); return false; // 'false' says to continue looping } ); } public virtual void SetPhysicalRestitution(float restitution) { ForEachMember((member) => { if (member.PhysBody.HasPhysicalBody) m_physicsScene.PE.SetRestitution(member.PhysBody, restitution); return false; // 'false' says to continue looping } ); } public virtual void SetPhysicalGravity(OMV.Vector3 gravity) { ForEachMember((member) => { if (member.PhysBody.HasPhysicalBody) m_physicsScene.PE.SetGravity(member.PhysBody, gravity); return false; // 'false' says to continue looping } ); } public virtual void ComputeLocalInertia() { ForEachMember((member) => { if (member.PhysBody.HasPhysicalBody) { OMV.Vector3 inertia = m_physicsScene.PE.CalculateLocalInertia(member.PhysShape.physShapeInfo, member.Mass); member.Inertia = inertia * BSParam.VehicleInertiaFactor; m_physicsScene.PE.SetMassProps(member.PhysBody, member.Mass, member.Inertia); m_physicsScene.PE.UpdateInertiaTensor(member.PhysBody); } return false; // 'false' says to continue looping } ); } // ================================================================ protected virtual float ComputeLinksetMass() { float mass = LinksetRoot.RawMass; if (HasAnyChildren) { lock (m_linksetActivityLock) { foreach (BSPrimLinkable bp in m_children) { mass += bp.RawMass; } } } return mass; } // Computes linkset's center of mass in world coordinates. protected virtual OMV.Vector3 ComputeLinksetCenterOfMass() { OMV.Vector3 com; lock (m_linksetActivityLock) { com = LinksetRoot.Position * LinksetRoot.RawMass; float totalMass = LinksetRoot.RawMass; foreach (BSPrimLinkable bp in m_children) { com += bp.Position * bp.RawMass; totalMass += bp.RawMass; } if (totalMass != 0f) com /= totalMass; } return com; } protected virtual OMV.Vector3 ComputeLinksetGeometricCenter() { OMV.Vector3 com; lock (m_linksetActivityLock) { com = LinksetRoot.Position; foreach (BSPrimLinkable bp in m_children) { com += bp.Position; } com /= (m_children.Count + 1); } return com; } // Invoke the detailed logger and output something if it's enabled. protected void DetailLog(string msg, params Object[] args) { if (m_physicsScene.PhysicsLogging.Enabled) m_physicsScene.DetailLog(msg, args); } } }