From 7d30637d51c64a582cc55d41546af8f0cfc889ba Mon Sep 17 00:00:00 2001
From: Robert Adams
Date: Thu, 26 Jul 2012 14:54:57 -0700
Subject: BulletSim: refactor all the linkset logic out of the prim class and
 into its own class. The BulletSim data structures track individual prims as
 linksets of 1 so most of the prim code is not different between a linked and
 unlinked object.

---
 OpenSim/Region/Physics/BulletSPlugin/BSLinkset.cs  | 308 +++++++++++++++++++
 OpenSim/Region/Physics/BulletSPlugin/BSPrim.cs     | 327 ++++++---------------
 OpenSim/Region/Physics/BulletSPlugin/BSScene.cs    |   7 +-
 .../Region/Physics/BulletSPlugin/BulletSimAPI.cs   |   2 +-
 4 files changed, 409 insertions(+), 235 deletions(-)
 create mode 100755 OpenSim/Region/Physics/BulletSPlugin/BSLinkset.cs

(limited to 'OpenSim/Region')

diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSLinkset.cs b/OpenSim/Region/Physics/BulletSPlugin/BSLinkset.cs
new file mode 100755
index 0000000..a1027ee
--- /dev/null
+++ b/OpenSim/Region/Physics/BulletSPlugin/BSLinkset.cs
@@ -0,0 +1,308 @@
+/*
+ * 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 class BSLinkset
+{
+    private static string LogHeader = "[BULLETSIM LINKSET]";
+
+    private BSPrim m_linksetRoot;
+    public BSPrim Root { get { return m_linksetRoot; } }
+
+    private BSScene m_scene;
+
+    private List<BSPrim> 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.
+    private object m_linksetActivityLock = new Object();
+
+    // We keep the prim's mass in the linkset structure since it could be dependent on other prims
+    private float m_mass;
+    public float Mass 
+    { 
+        get 
+        {
+            m_mass = ComputeLinksetMass();
+            return m_mass;
+        }
+    }
+
+    public OMV.Vector3 CenterOfMass
+    {
+        get { return ComputeLinksetCenterOfMass(); }
+    }
+
+    public OMV.Vector3 GeometricCenter
+    {
+        get { return ComputeLinksetGeometricCenter(); }
+    }
+
+    public BSLinkset(BSScene scene, BSPrim parent)
+    {
+        // A simple linkset of one (no children)
+        m_scene = scene;
+        m_linksetRoot = parent;
+        m_children = new List<BSPrim>();
+        m_mass = parent.MassRaw;
+    }
+
+    // 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 it's membership.
+    public BSLinkset AddMeToLinkset(BSPrim child, BSPrim parent)
+    {
+        lock (m_linksetActivityLock)
+        {
+            parent.Linkset.AddChildToLinkset(child);
+        }
+        return parent.Linkset;
+    }
+
+    public BSLinkset RemoveMeFromLinkset(BSPrim child)
+    {
+        lock (m_linksetActivityLock)
+        {
+            if (IsRoot(child))
+            {
+                // if root of linkset, take the linkset apart
+                while (m_children.Count > 0)
+                {
+                    // Note that we don't do a foreach because the remove routine
+                    //    takes it out of the list.
+                    RemoveChildFromLinkset(m_children[0]);
+                }
+                m_children.Clear(); // just to make sure
+            }
+            else
+            {
+                // Just removing a child from an existing linkset
+                RemoveChildFromLinkset(child);
+            }
+        }
+
+        // The child is down to a linkset of just itself
+        return new BSLinkset(m_scene, child);
+    }
+
+    // An existing linkset had one of its members rebuilt or something.
+    // Undo all the physical linking and rebuild the physical linkset.
+    public bool RefreshLinkset(BSPrim requestor)
+    {
+        return true;
+    }
+
+
+    // Return 'true' if the passed object is the root object of this linkset
+    public bool IsRoot(BSPrim requestor)
+    {
+        return (requestor.LocalID == m_linksetRoot.LocalID);
+    }
+
+    // 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(BSPrim child)
+    {
+        bool ret = false;
+        foreach (BSPrim bp in m_children)
+        {
+            if (child.LocalID == bp.LocalID)
+            {
+                ret = true;
+                break;
+            }
+        }
+        return ret;
+    }
+
+    private float ComputeLinksetMass()
+    {
+        float mass = m_linksetRoot.Mass;
+        foreach (BSPrim bp in m_children)
+        {
+            mass += bp.Mass;
+        }
+        return mass;
+    }
+
+    private OMV.Vector3 ComputeLinksetCenterOfMass()
+    {
+        OMV.Vector3 com = m_linksetRoot.Position * m_linksetRoot.MassRaw;
+        float totalMass = m_linksetRoot.MassRaw;
+
+        foreach (BSPrim bp in m_children)
+        {
+            com += bp.Position * bp.MassRaw;
+            totalMass += bp.MassRaw;
+        }
+        com /= totalMass;
+
+        return com;
+    }
+
+    private OMV.Vector3 ComputeLinksetGeometricCenter()
+    {
+        OMV.Vector3 com = m_linksetRoot.Position;
+
+        foreach (BSPrim bp in m_children)
+        {
+            com += bp.Position * bp.MassRaw;
+        }
+        com /= m_children.Count + 1;
+
+        return com;
+    }
+
+    // I am the root of a linkset and a new child is being added
+    public void AddChildToLinkset(BSPrim pchild)
+    {
+        BSPrim child = pchild;
+        if (!HasChild(child))
+        {
+            m_children.Add(child);
+
+            m_scene.TaintedObject(delegate()
+            {
+                DebugLog("{0}: AddChildToLinkset: adding child {1} to {2}", LogHeader, child.LocalID, m_linksetRoot.LocalID);
+                DetailLog("{0},AddChildToLinkset,child={1}", m_linksetRoot.LocalID, pchild.LocalID);
+                PhysicallyLinkAChildToRoot(pchild);     // build the physical binding between me and the child
+            });
+        }
+        return;
+    }
+
+    // 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.
+    public void RemoveChildFromLinkset(BSPrim pchild)
+    {
+        BSPrim child = pchild;
+
+        if (m_children.Remove(child))
+        {
+            m_scene.TaintedObject(delegate()
+            {
+                DebugLog("{0}: RemoveChildFromLinkset: Removing constraint to {1}", LogHeader, child.LocalID);
+                DetailLog("{0},RemoveChildFromLinkset,child={1}", m_linksetRoot.LocalID, pchild.LocalID);
+
+                if (m_children.Count == 0)
+                {
+                    // if the linkset is empty, make sure all linkages have been removed
+                    PhysicallyUnlinkAllChildrenFromRoot();
+                }
+                else
+                {
+                    PhysicallyUnlinkAChildFromRoot(pchild);
+                }
+            });
+        }
+        else
+        {
+            // This will happen if we remove the root of the linkset first. Non-fatal occurance.
+            // m_scene.Logger.ErrorFormat("{0}: Asked to remove child from linkset that was not in linkset", LogHeader);
+        }
+        return;
+    }
+
+    // Create a constraint between me (root of linkset) and the passed prim (the child).
+    // Called at taint time!
+    private void PhysicallyLinkAChildToRoot(BSPrim childPrim)
+    {
+        // Zero motion for children so they don't interpolate
+        childPrim.ZeroMotion();
+
+        // relative position normalized to the root prim
+        OMV.Quaternion invThisOrientation = OMV.Quaternion.Inverse(m_linksetRoot.Orientation);
+        OMV.Vector3 childRelativePosition = (childPrim.Position - m_linksetRoot.Position) * invThisOrientation;
+
+        // relative rotation of the child to the parent
+        OMV.Quaternion childRelativeRotation = invThisOrientation * childPrim.Orientation;
+
+        // create a constraint that allows no freedom of movement between the two objects
+        // http://bulletphysics.org/Bullet/phpBB3/viewtopic.php?t=4818
+        // DebugLog("{0}: CreateLinkset: Adding a constraint between root prim {1} and child prim {2}", LogHeader, LocalID, childPrim.LocalID);
+        DetailLog("{0},LinkAChildToMe,taint,root={1},child={2}", m_linksetRoot.LocalID, m_linksetRoot.LocalID, childPrim.LocalID);
+        BSConstraint constrain = m_scene.Constraints.CreateConstraint(
+                        m_scene.World, m_linksetRoot.Body, childPrim.Body,
+                        childRelativePosition,
+                        childRelativeRotation,
+                        OMV.Vector3.Zero,
+                        OMV.Quaternion.Identity);
+        constrain.SetLinearLimits(OMV.Vector3.Zero, OMV.Vector3.Zero);
+        constrain.SetAngularLimits(OMV.Vector3.Zero, OMV.Vector3.Zero);
+
+        // tweek the constraint to increase stability
+        constrain.UseFrameOffset(m_scene.BoolNumeric(m_scene.Params.linkConstraintUseFrameOffset));
+        constrain.TranslationalLimitMotor(m_scene.BoolNumeric(m_scene.Params.linkConstraintEnableTransMotor),
+                        m_scene.Params.linkConstraintTransMotorMaxVel,
+                        m_scene.Params.linkConstraintTransMotorMaxForce);
+
+    }
+
+    // Remove linkage between myself and a particular child
+    // Called at taint time!
+    private void PhysicallyUnlinkAChildFromRoot(BSPrim childPrim)
+    {
+        DebugLog("{0}: PhysicallyUnlinkAChildFromRoot: RemoveConstraint between root prim {1} and child prim {2}", 
+                    LogHeader, m_linksetRoot.LocalID, childPrim.LocalID);
+        DetailLog("{0},PhysicallyUnlinkAChildFromRoot,taint,root={1},child={2}", m_linksetRoot.LocalID, m_linksetRoot.LocalID, childPrim.LocalID);
+        // BulletSimAPI.RemoveConstraint(_scene.WorldID, LocalID, childPrim.LocalID);
+        m_scene.Constraints.RemoveAndDestroyConstraint(m_linksetRoot.Body, childPrim.Body);
+    }
+
+    // Remove linkage between myself and any possible children I might have
+    // Called at taint time!
+    private void PhysicallyUnlinkAllChildrenFromRoot()
+    {
+        // DebugLog("{0}: PhysicallyUnlinkAllChildren:", LogHeader);
+        DetailLog("{0},PhysicallyUnlinkAllChildren,taint", m_linksetRoot.LocalID);
+        m_scene.Constraints.RemoveAndDestroyConstraint(m_linksetRoot.Body);
+        // BulletSimAPI.RemoveConstraintByID(_scene.WorldID, LocalID);
+    }
+
+    // Invoke the detailed logger and output something if it's enabled.
+    private void DebugLog(string msg, params Object[] args)
+    {
+        m_scene.Logger.DebugFormat(msg, args);
+    }
+
+    // Invoke the detailed logger and output something if it's enabled.
+    private void DetailLog(string msg, params Object[] args)
+    {
+        m_scene.PhysicsLogging.Write(msg, args);
+    }
+
+}
+}
diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSPrim.cs b/OpenSim/Region/Physics/BulletSPlugin/BSPrim.cs
index 3be28e3..d604f9c 100644
--- a/OpenSim/Region/Physics/BulletSPlugin/BSPrim.cs
+++ b/OpenSim/Region/Physics/BulletSPlugin/BSPrim.cs
@@ -66,7 +66,7 @@ public sealed class BSPrim : PhysicsActor
     private bool _isSelected;
     private bool _isVolumeDetect;
     private OMV.Vector3 _position;
-    private float _mass;
+    private float _mass;    // the mass of this object
     private float _density;
     private OMV.Vector3 _force;
     private OMV.Vector3 _velocity;
@@ -89,8 +89,13 @@ public sealed class BSPrim : PhysicsActor
     private bool _kinematic;
     private float _buoyancy;
 
-    private BSPrim _parentPrim;
-    private List<BSPrim> _childrenPrims;
+    // Membership in a linkset is controlled by this class.
+    private BSLinkset _linkset;
+    public BSLinkset Linkset
+    {
+        get { return _linkset; }
+        set { _linkset = value; }
+    }
 
     private int _subscribedEventsMs = 0;
     private int _nextCollisionOkTime = 0;
@@ -133,9 +138,8 @@ public sealed class BSPrim : PhysicsActor
         _friction = _scene.Params.defaultFriction; // TODO: compute based on object material
         _density = _scene.Params.defaultDensity; // TODO: compute based on object material
         _restitution = _scene.Params.defaultRestitution;
-        _parentPrim = null;     // not a child or a parent
+        _linkset = new BSLinkset(_scene, this);     // a linkset of one
         _vehicle = new BSDynamics(this);    // add vehicleness
-        _childrenPrims = new List<BSPrim>();
         _mass = CalculateMass();
         // do the actual object creation at taint time
         _scene.TaintedObject(delegate()
@@ -161,16 +165,8 @@ public sealed class BSPrim : PhysicsActor
 
         _scene.TaintedObject(delegate()
         {
-            // undo any dependance with/on other objects
-            if (_parentPrim != null)
-            {
-                // If I'm someone's child, tell them to forget about me.
-                _parentPrim.RemoveChildFromLinkset(this);
-                _parentPrim = null;
-            }
-
-            // make sure there are no other prims linked to me
-            UnlinkAllChildren();
+            // Undo any links between me and any other object
+            _linkset = _linkset.RemoveMeFromLinkset(this);
 
             // everything in the C# world will get garbage collected. Tell the C++ world to free stuff.
             BulletSimAPI.DestroyObject(_scene.WorldID, LocalID);
@@ -187,7 +183,7 @@ public sealed class BSPrim : PhysicsActor
             _scene.TaintedObject(delegate()
             {
                 _mass = CalculateMass();   // changing size changes the mass
-                BulletSimAPI.SetObjectScaleMass(_scene.WorldID, _localID, _scale, Mass, IsPhysical);
+                BulletSimAPI.SetObjectScaleMass(_scene.WorldID, _localID, _scale, (IsPhysical ? _mass : 0f), IsPhysical);
                 RecreateGeomAndObject();
             });
         } 
@@ -226,32 +222,8 @@ public sealed class BSPrim : PhysicsActor
         BSPrim parent = obj as BSPrim;
         DebugLog("{0}: link {1}/{2} to {3}", LogHeader, _avName, _localID, obj.LocalID);
         DetailLog("{0},link,parent={1}", LocalID, obj.LocalID);
-        // TODO: decide if this parent checking needs to happen at taint time
-        if (_parentPrim == null)
-        {
-            if (parent != null)
-            {
-                // I don't have a parent so I am joining a linkset
-                parent.AddChildToLinkset(this);
-            }
-        }
-        else
-        {
-            // I already have a parent, is parenting changing?
-            if (parent != _parentPrim)
-            {
-                if (parent == null)
-                {
-                    // we are being removed from a linkset
-                    _parentPrim.RemoveChildFromLinkset(this);
-                }
-                else
-                {
-                    // asking to reparent a prim should not happen
-                    m_log.ErrorFormat("{0}: link(): Reparenting a prim. ", LogHeader);
-                }
-            }
-        }
+
+        _linkset = _linkset.AddMeToLinkset(this, parent);
         return; 
     }
 
@@ -260,81 +232,18 @@ public sealed class BSPrim : PhysicsActor
         // TODO: decide if this parent checking needs to happen at taint time
         // Race condition here: if link() and delink() in same simulation tick, the delink will not happen
         DebugLog("{0}: delink {1}/{2}. Parent={3}", LogHeader, _avName, _localID, 
-                (_parentPrim==null ? "NULL" : _parentPrim._avName+"/"+_parentPrim.LocalID.ToString()));
-        DetailLog("{0},delink,parent={1}", LocalID, (_parentPrim==null ? "NULL" : _parentPrim.LocalID.ToString()));
-        if (_parentPrim != null)
-        {
-            _parentPrim.RemoveChildFromLinkset(this);
-        }
-        return; 
-    }
+                            _linkset.Root._avName+"/"+_linkset.Root.LocalID.ToString());
+        DetailLog("{0},delink,parent={1}", LocalID, _linkset.Root.LocalID.ToString());
 
-    // I am the root of a linkset and a new child is being added
-    public void AddChildToLinkset(BSPrim pchild)
-    {
-        BSPrim child = pchild;
-        _scene.TaintedObject(delegate()
-        {
-            if (!_childrenPrims.Contains(child))
-            {
-                DebugLog("{0}: AddChildToLinkset: adding child {1} to {2}", LogHeader, child.LocalID, this.LocalID);
-                DetailLog("{0},AddChildToLinkset,child={1}", LocalID, pchild.LocalID);
-                _childrenPrims.Add(child);
-                child._parentPrim = this;    // the child has gained a parent
-                // RecreateGeomAndObject();    // rebuild my shape with the new child added
-                LinkAChildToMe(pchild);     // build the physical binding between me and the child
-
-                _mass = CalculateMass();
-            }
-        });
-        return;
-    }
-
-    // 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.
-    public void RemoveChildFromLinkset(BSPrim pchild)
-    {
-        BSPrim child = pchild;
-        _scene.TaintedObject(delegate()
-        {
-            if (_childrenPrims.Contains(child))
-            {
-                DebugLog("{0}: RemoveChildFromLinkset: Removing constraint to {1}", LogHeader, child.LocalID);
-                DetailLog("{0},RemoveChildFromLinkset,child={1}", LocalID, pchild.LocalID);
-                _childrenPrims.Remove(child);
-                child._parentPrim = null;    // the child has lost its parent
-                if (_childrenPrims.Count == 0)
-                {
-                    // if the linkset is empty, make sure all linkages have been removed
-                    UnlinkAllChildren();
-                }
-                else
-                {
-                    // RecreateGeomAndObject();    // rebuild my shape with the child removed
-                    UnlinkAChildFromMe(pchild);
-                }
-
-                _mass = CalculateMass();
-            }
-            else
-            {
-                m_log.ErrorFormat("{0}: Asked to remove child from linkset that was not in linkset");
-            }
-        });
-        return;
-    }
-
-    // return true if we are the root of a linkset (there are children to manage)
-    public bool IsRootOfLinkset
-    {
-        get { return (_parentPrim == null && _childrenPrims.Count != 0); }
+        _linkset.RemoveMeFromLinkset(this);
+        return; 
     }
 
     // Set motion values to zero.
     // Do it to the properties so the values get set in the physics engine.
     // Push the setting of the values to the viewer.
     // Called at taint time!
-    private void ZeroMotion()
+    public void ZeroMotion()
     {
         _velocity = OMV.Vector3.Zero;
         _acceleration = OMV.Vector3.Zero;
@@ -355,9 +264,10 @@ public sealed class BSPrim : PhysicsActor
 
     public override OMV.Vector3 Position { 
         get { 
-            // child prims move around based on their parent. Need to get the latest location
-            if (_parentPrim != null)
+            if (!_linkset.IsRoot(this))
+                // child prims move around based on their parent. Need to get the latest location
                 _position = BulletSimAPI.GetObjectPosition(_scene.WorldID, _localID);
+
             // don't do the GetObjectPosition for root elements because this function is called a zillion times
             // _position = BulletSimAPI.GetObjectPosition(_scene.WorldID, _localID);
             return _position; 
@@ -373,16 +283,31 @@ public sealed class BSPrim : PhysicsActor
         } 
     }
 
-    // Return the effective mass of the object. Non-physical objects do not have mass.
-    public override float Mass { 
-        get {
-            if (IsPhysical)
-                return _mass;
-            else
-                return 0f;
+    // Return the effective mass of the object.
+    // If there are multiple items in the linkset, add them together for the root
+    public override float Mass
+    { 
+        get
+        {
+            return _linkset.Mass;
         }
     }
 
+    // used when we only want this prim's mass and not the linkset thing
+    public float MassRaw { get { return _mass; } }
+
+    // Is this used?
+    public override OMV.Vector3 CenterOfMass
+    {
+        get { return _linkset.CenterOfMass; }
+    }
+
+    // Is this used?
+    public override OMV.Vector3 GeometricCenter
+    {
+        get { return _linkset.GeometricCenter; }
+    }
+
     public override OMV.Vector3 Force { 
         get { return _force; } 
         set {
@@ -473,8 +398,6 @@ public sealed class BSPrim : PhysicsActor
         return; 
     }
 
-    public override OMV.Vector3 GeometricCenter { get { return OMV.Vector3.Zero; } }
-    public override OMV.Vector3 CenterOfMass { get { return OMV.Vector3.Zero; } }
     public override OMV.Vector3 Velocity { 
         get { return _velocity; } 
         set {
@@ -503,9 +426,9 @@ public sealed class BSPrim : PhysicsActor
     }
     public override OMV.Quaternion Orientation { 
         get {
-            if (_parentPrim != null)
+            if (!_linkset.IsRoot(this))
             {
-                // children move around because tied to parent. Get a fresh value.
+                // Children move around because tied to parent. Get a fresh value.
                 _orientation = BulletSimAPI.GetObjectOrientation(_scene.WorldID, LocalID);
             }
             return _orientation;
@@ -555,14 +478,16 @@ public sealed class BSPrim : PhysicsActor
     private void SetObjectDynamic()
     {
         // m_log.DebugFormat("{0}: ID={1}, SetObjectDynamic: IsStatic={2}, IsSolid={3}", LogHeader, _localID, IsStatic, IsSolid);
-        // non-physical things work best with a mass of zero
-        if (!IsStatic)
-        {
-            _mass = CalculateMass();
-            RecreateGeomAndObject();
-        }
-        DetailLog("{0},SetObjectDynamic,taint,static={1},solid={2},mass={3}", LocalID, IsStatic, IsSolid, Mass);
-        BulletSimAPI.SetObjectProperties(_scene.WorldID, LocalID, IsStatic, IsSolid, SubscribedEvents(), Mass);
+
+        RecreateGeomAndObject();
+
+        float mass = _mass;
+        // Bullet wants static objects have a mass of zero
+        if (IsStatic) 
+            mass = 0f;
+
+        DetailLog("{0},SetObjectDynamic,taint,static={1},solid={2},mass={3}", LocalID, IsStatic, IsSolid, mass);
+        BulletSimAPI.SetObjectProperties(_scene.WorldID, LocalID, IsStatic, IsSolid, SubscribedEvents(), mass);
     }
 
     // prims don't fly
@@ -1004,6 +929,9 @@ public sealed class BSPrim : PhysicsActor
 
         returnMass = _density * volume;
 
+        /*
+         * This change means each object keeps its own mass and the Mass property
+         * will return the sum if we're part of a linkset.
         if (IsRootOfLinkset)
         {
             foreach (BSPrim prim in _childrenPrims)
@@ -1011,6 +939,7 @@ public sealed class BSPrim : PhysicsActor
                 returnMass += prim.CalculateMass();
             }
         }
+         */
 
         if (returnMass <= 0)
             returnMass = 0.0001f;
@@ -1026,9 +955,11 @@ public sealed class BSPrim : PhysicsActor
     // The objects needs a hull if it's physical otherwise a mesh is enough
     // No locking here because this is done when we know physics is not simulating
     // if 'forceRebuild' is true, the geometry is rebuilt. Otherwise a previously built version is used
-    private void CreateGeom(bool forceRebuild)
+    // Returns 'true' if the geometry was rebuilt
+    private bool CreateGeom(bool forceRebuild)
     {
         // the mesher thought this was too simple to mesh. Use a native Bullet collision shape.
+        bool ret = false;
         if (!_scene.NeedsMeshing(_pbs))
         {
             if (_pbs.ProfileShape == ProfileShape.HalfCircle && _pbs.PathCurve == (byte)Extrusion.Curve1)
@@ -1036,18 +967,26 @@ public sealed class BSPrim : PhysicsActor
                 if (_size.X == _size.Y && _size.Y == _size.Z && _size.X == _size.Z)
                 {
                     // m_log.DebugFormat("{0}: CreateGeom: Defaulting to sphere of size {1}", LogHeader, _size);
-                    _shapeType = ShapeData.PhysicsShapeType.SHAPE_SPHERE;
-                    DetailLog("{0},CreateGeom,sphere", LocalID);
-                    // Bullet native objects are scaled by the Bullet engine so pass the size in
-                    _scale = _size;
+                    if (_shapeType != ShapeData.PhysicsShapeType.SHAPE_SPHERE)
+                    {
+                        DetailLog("{0},CreateGeom,sphere", LocalID);
+                        _shapeType = ShapeData.PhysicsShapeType.SHAPE_SPHERE;
+                        ret = true;
+                        // Bullet native objects are scaled by the Bullet engine so pass the size in
+                        _scale = _size;
+                    }
                 }
             }
             else
             {
                 // m_log.DebugFormat("{0}: CreateGeom: Defaulting to box. lid={1}, size={2}", LogHeader, LocalID, _size);
-                DetailLog("{0},CreateGeom,box", LocalID);
-                _shapeType = ShapeData.PhysicsShapeType.SHAPE_BOX;
-                _scale = _size;
+                if (_shapeType != ShapeData.PhysicsShapeType.SHAPE_BOX)
+                {
+                    DetailLog("{0},CreateGeom,box", LocalID);
+                    _shapeType = ShapeData.PhysicsShapeType.SHAPE_BOX;
+                    ret = true;
+                    _scale = _size;
+                }
             }
         }
         else
@@ -1059,6 +998,7 @@ public sealed class BSPrim : PhysicsActor
                     // physical objects require a hull for interaction.
                     // This will create the mesh if it doesn't already exist
                     CreateGeomHull();
+                    ret = true;
                 }
             }
             else
@@ -1067,9 +1007,11 @@ public sealed class BSPrim : PhysicsActor
                 {
                     // Static (non-physical) objects only need a mesh for bumping into
                     CreateGeomMesh();
+                    ret = true;
                 }
             }
         }
+        return ret;
     }
 
     // No locking here because this is done when we know physics is not simulating
@@ -1254,20 +1196,18 @@ public sealed class BSPrim : PhysicsActor
     // No locking here because this is done when the physics engine is not simulating
     private void CreateObject()
     {
-        if (IsRootOfLinkset)
-        {
-            // Create a linkset around this object
-            CreateLinkset();
-        }
-        else
-        {
-            // simple object
-            // the mesh or hull must have already been created in Bullet
-            ShapeData shape;
-            FillShapeInfo(out shape);
-            // m_log.DebugFormat("{0}: CreateObject: lID={1}, shape={2}", LogHeader, _localID, shape.Type);
-            BulletSimAPI.CreateObject(_scene.WorldID, shape);
-        }
+        // this routine is called when objects are rebuilt. 
+
+        // the mesh or hull must have already been created in Bullet
+        ShapeData shape;
+        FillShapeInfo(out shape);
+        // m_log.DebugFormat("{0}: CreateObject: lID={1}, shape={2}", LogHeader, _localID, shape.Type);
+        BulletSimAPI.CreateObject(_scene.WorldID, shape);
+        // the CreateObject() may have recreated the rigid body. Make sure we have the latest.
+        m_body.Ptr = BulletSimAPI.GetBodyHandle2(_scene.World.Ptr, LocalID);
+
+        // The root object could have been recreated. Make sure everything linksety is up to date.
+        _linkset.RefreshLinkset(this);
     }
 
     // Copy prim's info into the BulletSim shape description structure
@@ -1279,7 +1219,7 @@ public sealed class BSPrim : PhysicsActor
         shape.Rotation = _orientation;
         shape.Velocity = _velocity;
         shape.Scale = _scale;
-        shape.Mass = Mass;
+        shape.Mass = _isPhysical ? _mass : 0f;
         shape.Buoyancy = _buoyancy;
         shape.HullKey = _hullKey;
         shape.MeshKey = _meshKey;
@@ -1289,83 +1229,6 @@ public sealed class BSPrim : PhysicsActor
         shape.Static = _isPhysical ? ShapeData.numericFalse : ShapeData.numericTrue;
     }
 
-    #region Linkset creation and destruction
-
-    // Create the linkset by putting constraints between the objects of the set so they cannot move
-    // relative to each other.
-    void CreateLinkset()
-    {
-        // DebugLog("{0}: CreateLinkset. Root prim={1}, prims={2}", LogHeader, LocalID, _childrenPrims.Count+1);
-
-        // remove any constraints that might be in place
-        UnlinkAllChildren();
-
-        // create constraints between the root prim and each of the children
-        foreach (BSPrim prim in _childrenPrims)
-        {
-            LinkAChildToMe(prim);
-        }
-    }
-
-    // Create a constraint between me (root of linkset) and the passed prim (the child).
-    // Called at taint time!
-    private void LinkAChildToMe(BSPrim childPrim)
-    {
-        // Zero motion for children so they don't interpolate
-        childPrim.ZeroMotion();
-
-        // relative position normalized to the root prim
-        OMV.Quaternion invThisOrientation = OMV.Quaternion.Inverse(this._orientation);
-        OMV.Vector3 childRelativePosition = (childPrim._position - this._position) * invThisOrientation;
-
-        // relative rotation of the child to the parent
-        OMV.Quaternion childRelativeRotation = invThisOrientation * childPrim._orientation;
-
-        // create a constraint that allows no freedom of movement between the two objects
-        // http://bulletphysics.org/Bullet/phpBB3/viewtopic.php?t=4818
-        // DebugLog("{0}: CreateLinkset: Adding a constraint between root prim {1} and child prim {2}", LogHeader, LocalID, childPrim.LocalID);
-        DetailLog("{0},LinkAChildToMe,taint,root={1},child={2}", LocalID, LocalID, childPrim.LocalID);
-        BSConstraint constrain = _scene.Constraints.CreateConstraint(
-                        _scene.World, this.Body, childPrim.Body,
-                        childRelativePosition,
-                        childRelativeRotation,
-                        OMV.Vector3.Zero,
-                        OMV.Quaternion.Identity);
-        constrain.SetLinearLimits(OMV.Vector3.Zero, OMV.Vector3.Zero);
-        constrain.SetAngularLimits(OMV.Vector3.Zero, OMV.Vector3.Zero);
-
-        // tweek the constraint to increase stability
-        constrain.UseFrameOffset(_scene.BoolNumeric(_scene.Params.linkConstraintUseFrameOffset));
-        if (_scene.BoolNumeric(_scene.Params.linkConstraintEnableTransMotor))
-        {
-            constrain.TranslationalLimitMotor(true, 
-                            _scene.Params.linkConstraintTransMotorMaxVel,
-                            _scene.Params.linkConstraintTransMotorMaxForce);
-        }
-    }
-
-    // Remove linkage between myself and a particular child
-    // Called at taint time!
-    private void UnlinkAChildFromMe(BSPrim childPrim)
-    {
-        DebugLog("{0}: UnlinkAChildFromMe: RemoveConstraint between root prim {1} and child prim {2}", 
-                    LogHeader, LocalID, childPrim.LocalID);
-        DetailLog("{0},UnlinkAChildFromMe,taint,root={1},child={2}", LocalID, LocalID, childPrim.LocalID);
-        // BulletSimAPI.RemoveConstraint(_scene.WorldID, LocalID, childPrim.LocalID);
-        _scene.Constraints.RemoveAndDestroyConstraint(this.Body, childPrim.Body);
-    }
-
-    // Remove linkage between myself and any possible children I might have
-    // Called at taint time!
-    private void UnlinkAllChildren()
-    {
-        DebugLog("{0}: UnlinkAllChildren:", LogHeader);
-        DetailLog("{0},UnlinkAllChildren,taint", LocalID);
-        _scene.Constraints.RemoveAndDestroyConstraint(this.Body);
-        // BulletSimAPI.RemoveConstraintByID(_scene.WorldID, LocalID);
-    }
-
-    #endregion // Linkset creation and destruction
 
     // Rebuild the geometry and object.
     // This is called when the shape changes so we need to recreate the mesh/hull.
@@ -1443,7 +1306,7 @@ public sealed class BSPrim : PhysicsActor
         // Don't check for damping here -- it's done in BulletSim and SceneObjectPart.
 
         // Updates only for individual prims and for the root object of a linkset.
-        if (_parentPrim == null)
+        if (_linkset.IsRoot(this))
         {
             // Assign to the local variables so the normal set action does not happen
             _position = entprop.Position;
diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSScene.cs b/OpenSim/Region/Physics/BulletSPlugin/BSScene.cs
index a1587a8..c6d622b 100644
--- a/OpenSim/Region/Physics/BulletSPlugin/BSScene.cs
+++ b/OpenSim/Region/Physics/BulletSPlugin/BSScene.cs
@@ -73,7 +73,7 @@ public class BSScene : PhysicsScene, IPhysicsParameters
     private static readonly ILog m_log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
     private static readonly string LogHeader = "[BULLETS SCENE]";
 
-    private void DebugLog(string mm, params Object[] xx) { if (shouldDebugLog) m_log.DebugFormat(mm, xx); }
+    public void DebugLog(string mm, params Object[] xx) { if (shouldDebugLog) m_log.DebugFormat(mm, xx); }
 
     public string BulletSimVersion = "?";
 
@@ -87,6 +87,9 @@ public class BSScene : PhysicsScene, IPhysicsParameters
     private uint m_worldID;
     public uint WorldID { get { return m_worldID; } }
 
+    // let my minuions use my logger
+    public ILog Logger { get { return m_log; } }
+
     private bool m_initialized = false;
 
     private int m_detailedStatsStep = 0;
@@ -1026,7 +1029,7 @@ public class BSScene : PhysicsScene, IPhysicsParameters
             (s,p,l,v) => { s.m_params[0].numberOfSolverIterations = v; } ),
 
 	    new ParameterDefn("LinkConstraintUseFrameOffset", "For linksets built with constraints, enable frame offsetFor linksets built with constraints, enable frame offset.",
-            ConfigurationParameters.numericTrue,
+            ConfigurationParameters.numericFalse,
             (s,cf,p,v) => { s.m_params[0].linkConstraintUseFrameOffset = s.NumericBool(cf.GetBoolean(p, s.BoolNumeric(v))); },
             (s) => { return s.m_params[0].linkConstraintUseFrameOffset; },
             (s,p,l,v) => { s.m_params[0].linkConstraintUseFrameOffset = v; } ),
diff --git a/OpenSim/Region/Physics/BulletSPlugin/BulletSimAPI.cs b/OpenSim/Region/Physics/BulletSPlugin/BulletSimAPI.cs
index 89fd9b7..65e3145 100644
--- a/OpenSim/Region/Physics/BulletSPlugin/BulletSimAPI.cs
+++ b/OpenSim/Region/Physics/BulletSPlugin/BulletSimAPI.cs
@@ -239,10 +239,10 @@ public static extern bool DestroyMesh(uint worldID, System.UInt64 meshKey);
 [DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
 public static extern bool CreateObject(uint worldID, ShapeData shapeData);
 
+/*  Remove old functionality
 [DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
 public static extern void CreateLinkset(uint worldID, int objectCount, ShapeData[] shapeDatas);
 
-/*  Remove old functionality
 [DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
 public static extern void AddConstraint(uint worldID, uint id1, uint id2, 
                         Vector3 frame1, Quaternion frame1rot,
-- 
cgit v1.1