Index: OpenSim/Region/Environment/Scenes/SceneObjectPart.cs
===================================================================
--- OpenSim/Region/Environment/Scenes/SceneObjectPart.cs	(revision 7781)
+++ OpenSim/Region/Environment/Scenes/SceneObjectPart.cs	(working copy)
@@ -414,7 +414,14 @@
         public virtual string Name
         {
             get { return m_name; }
-            set { m_name = value; }
+            set 
+            { 
+                m_name = value;
+                if (PhysActor != null)
+                {
+                    PhysActor.SOPName = value;
+                }
+            }
         }
 
         public byte Material
@@ -676,7 +683,14 @@
         public string Description
         {
             get { return m_description; }
-            set { m_description = value; }
+            set 
+            {
+                m_description = value;
+                if (PhysActor != null)
+                {
+                    PhysActor.SOPDescription = value;
+                }
+            }
         }
 
         public Color Color
@@ -1274,28 +1288,37 @@
         /// <param name="m_physicalPrim"></param>
         public void ApplyPhysics(uint rootObjectFlags, bool m_physicalPrim)
         {
+            //m_log.Debug("[NINJA] applying physics to " + Name);
             bool isPhysical = (((rootObjectFlags & (uint) PrimFlags.Physics) != 0) && m_physicalPrim);
             bool isPhantom = ((rootObjectFlags & (uint) PrimFlags.Phantom) != 0);
 
-            // Added clarification..   since A rigid body is an object that you can kick around, etc.
-            bool RigidBody = isPhysical && !isPhantom;
-
-            // The only time the physics scene shouldn't know about the prim is if it's phantom or an attachment, which is phantom by definition
-            if (!isPhantom && !IsAttachment)
+            if (IsJoint())
             {
-                PhysActor = m_parentGroup.Scene.PhysicsScene.AddPrimShape(
-                    Name,
-                    Shape,
-                    new PhysicsVector(AbsolutePosition.X, AbsolutePosition.Y, AbsolutePosition.Z),
-                    new PhysicsVector(Scale.X, Scale.Y, Scale.Z),
-                    RotationOffset,
-                    RigidBody);
+                DoPhysicsPropertyUpdate(isPhysical, true);
+            }
+            else
+            {
+                // Added clarification..   since A rigid body is an object that you can kick around, etc.
+                bool RigidBody = isPhysical && !isPhantom;
+                // The only time the physics scene shouldn't know about the prim is if it's phantom
+                if (!isPhantom && !IsAttachment)
+                {
+                    PhysActor = m_parentGroup.Scene.PhysicsScene.AddPrimShape(
+                        Name,
+                        Shape,
+                        new PhysicsVector(AbsolutePosition.X, AbsolutePosition.Y, AbsolutePosition.Z),
+                        new PhysicsVector(Scale.X, Scale.Y, Scale.Z),
+                        RotationOffset,
+                        RigidBody);
 
-                // Basic Physics returns null..  joy joy joy.
-                if (PhysActor != null)
-                {
-                    PhysActor.LocalID = LocalId;
-                    DoPhysicsPropertyUpdate(RigidBody, true);
+                    // Basic Physics returns null..  joy joy joy.
+                    if (PhysActor != null)
+                    {
+                        PhysActor.SOPName = this.Name; // save object name and desc into the PhysActor so ODE internals know the joint/body info
+                        PhysActor.SOPDescription = this.Description;
+                        PhysActor.LocalID = LocalId;
+                        DoPhysicsPropertyUpdate(RigidBody, true);
+                    }
                 }
             }
         }
@@ -1406,47 +1429,138 @@
 
         public void DoPhysicsPropertyUpdate(bool UsePhysics, bool isNew)
         {
-            if (PhysActor != null)
+            //m_log.Debug("[NINJA] updating physics property of " + Name + " to be " + UsePhysics);
+
+            if (IsJoint())
             {
-                if (UsePhysics != PhysActor.IsPhysical || isNew)
+                if (UsePhysics)
                 {
-                    if (PhysActor.IsPhysical)
+                    // by turning a joint proxy object physical, we cause creation of a joint in the ODE scene.
+                    // note that, as a special case, joints have no bodies or geoms in the physics scene, even though they are physical.
+
+                    PhysicsJointType jointType;
+                    if (IsHingeJoint())
                     {
-                        if (!isNew)
-                            ParentGroup.Scene.RemovePhysicalPrim(1);
+                        jointType = PhysicsJointType.Hinge;
+                    }
+                    else if (IsBallJoint())
+                    {
+                        jointType = PhysicsJointType.Ball;
+                    }
+                    else
+                    {
+                        jointType = PhysicsJointType.Ball;
+                    }
 
-                        PhysActor.OnRequestTerseUpdate -= PhysicsRequestingTerseUpdate;
-                        PhysActor.OnOutOfBounds -= PhysicsOutOfBounds;
-                        PhysActor.delink();
+                    List<string> bodyNames = new List<string>();
+                    string RawParams = Description;
+                    string[] jointParams = RawParams.Split(' ');
+                    string trackedBodyName = null;
+                    if (jointParams.Length >= 2)
+                    {
+                        for (int iBodyName = 0; iBodyName < 2; iBodyName++)
+                        {
+                            string bodyName = jointParams[iBodyName];
+                            bodyNames.Add(bodyName);
+                            if (bodyName != "NULL")
+                            {
+                                if (trackedBodyName == null)
+                                {
+                                    trackedBodyName = bodyName;
+                                }
+                            }
+                        }
                     }
 
-                    PhysActor.IsPhysical = UsePhysics;
+                    SceneObjectPart trackedBody = m_parentGroup.Scene.GetSceneObjectPart(trackedBodyName); // FIXME: causes a sequential lookup
+                    Quaternion localRotation = Quaternion.Identity;
+                    if (trackedBody != null)
+                    {
+                        localRotation = Quaternion.Inverse(trackedBody.RotationOffset) * this.RotationOffset;
+                    }
+                    else
+                    {
+                        m_log.Warn("[NINUA] warning: tracked body name not found! joint: " + Name);
+                    }
 
+                    PhysicsJoint joint;
 
-                    // If we're not what we're supposed to be in the physics scene, recreate ourselves.
-                    //m_parentGroup.Scene.PhysicsScene.RemovePrim(PhysActor);
-                    /// that's not wholesome.  Had to make Scene public
-                    //PhysActor = null;
+                    joint = m_parentGroup.Scene.PhysicsScene.RequestJointCreation(Name, jointType,
+                        new PhysicsVector(AbsolutePosition.X, AbsolutePosition.Y, AbsolutePosition.Z),
+                        this.RotationOffset,
+                        Description,
+                        bodyNames,
+                        trackedBodyName,
+                        localRotation);
+                }
+                else
+                {
+                    // here we turn off the joint object, so remove the joint from the physics scene
+                    m_parentGroup.Scene.PhysicsScene.RequestJointDeletion(Name); // FIXME: what if the name changed?
 
-                    if ((ObjectFlags & (uint) PrimFlags.Phantom) == 0)
+                    // make sure client isn't interpolating the joint proxy object
+                    Velocity = new Vector3(0, 0, 0);
+                    RotationalVelocity = new Vector3(0, 0, 0);
+                    Acceleration = new Vector3(0, 0, 0);
+                }
+            }
+            else
+            {
+                if (PhysActor != null)
+                {
+                    //m_log.Debug("[NINJA] physics actor is not null");
+                    if (UsePhysics != PhysActor.IsPhysical || isNew)
                     {
-                        if (UsePhysics)
+                        if (PhysActor.IsPhysical)
                         {
-                            ParentGroup.Scene.AddPhysicalPrim(1);
+                            if (!isNew)
+                                ParentGroup.Scene.RemovePhysicalPrim(1);
 
-                            PhysActor.OnRequestTerseUpdate += PhysicsRequestingTerseUpdate;
-                            PhysActor.OnOutOfBounds += PhysicsOutOfBounds;
-                            if (_parentID != 0 && _parentID != LocalId)
+                            PhysActor.OnRequestTerseUpdate -= PhysicsRequestingTerseUpdate;
+                            PhysActor.OnOutOfBounds -= PhysicsOutOfBounds;
+                            PhysActor.delink();
+
+                            // make sure client isn't interpolating the turned-off prim (necessary for jointed assemblies that fly apart when non-physicalized)
+                            Velocity = new Vector3(0, 0, 0);
+                            RotationalVelocity = new Vector3(0, 0, 0);
+                            Acceleration = new Vector3(0, 0, 0);
+
+                            // destroy all joints connected to this now deactivated body
+                            m_parentGroup.Scene.PhysicsScene.RemoveAllJointsConnectedToActorThreadLocked(PhysActor);
+
+                            // stop client-side interpolation of all joint proxy objects that have just been deleted
+                            // this is done because RemoveAllJointsConnectedToActor invokes the OnJointDeactivated callback,
+                            // which stops client-side interpolation of deactivated joint proxy objects.
+                        }
+
+                        PhysActor.IsPhysical = UsePhysics;
+
+                        // If we're not what we're supposed to be in the physics scene, recreate ourselves.
+                        //m_parentGroup.Scene.PhysicsScene.RemovePrim(PhysActor);
+                        /// that's not wholesome.  Had to make Scene public
+                        //PhysActor = null;
+
+                        if ((ObjectFlags & (uint)PrimFlags.Phantom) == 0)
+                        {
+                            if (UsePhysics)
                             {
-                                if (ParentGroup.RootPart.PhysActor != null)
+                                ParentGroup.Scene.AddPhysicalPrim(1);
+
+                                PhysActor.OnRequestTerseUpdate += PhysicsRequestingTerseUpdate;
+                                PhysActor.OnOutOfBounds += PhysicsOutOfBounds;
+                                if (_parentID != 0 && _parentID != LocalId)
                                 {
-                                    PhysActor.link(ParentGroup.RootPart.PhysActor);
+                                    if (ParentGroup.RootPart.PhysActor != null)
+                                    {
+                                        PhysActor.link(ParentGroup.RootPart.PhysActor);
+                                    }
                                 }
                             }
                         }
+
                     }
+                    m_parentGroup.Scene.PhysicsScene.AddPhysicsActorTaint(PhysActor);
                 }
-                m_parentGroup.Scene.PhysicsScene.AddPhysicsActorTaint(PhysActor);
             }
         }
 
@@ -3166,6 +3280,44 @@
             }
         }
 
+        public bool IsHingeJoint()
+        {
+            if (m_parentGroup.Scene.NINJAPhysicsJointsEnabled)
+            {
+                string hingeString = "hingejoint";
+                return (Name.Length >= hingeString.Length && Name.Substring(0, hingeString.Length) == hingeString);
+            }
+            else
+            {
+                return false;
+            }
+        }
+
+        public bool IsBallJoint()
+        {
+            if (m_parentGroup.Scene.NINJAPhysicsJointsEnabled)
+            {
+                string ballString = "balljoint";
+                return (Name.Length >= ballString.Length && Name.Substring(0, ballString.Length) == ballString);
+            }
+            else
+            {
+                return false;
+            }
+        }
+
+        public bool IsJoint()
+        {
+            if (m_parentGroup.Scene.NINJAPhysicsJointsEnabled)
+            {
+                return IsHingeJoint() || IsBallJoint();
+            }
+            else
+            {
+                return false;
+            }
+        }
+
         public void UpdatePrimFlags(bool UsePhysics, bool IsTemporary, bool IsPhantom)
         {
             bool wasUsingPhysics = ((ObjectFlags & (uint) PrimFlags.Physics) != 0);
@@ -3177,6 +3329,11 @@
                 return;
             }
 
+            if (UsePhysics && IsJoint())
+            {
+                IsPhantom = true;
+            }
+
             if (UsePhysics)
             {
                 AddFlag(PrimFlags.Physics);
@@ -3204,7 +3361,7 @@
                 }
             }
 
-            if (IsPhantom || IsAttachment)
+            if (IsPhantom || IsAttachment) // note: this may have been changed above in the case of joints
             {
                 AddFlag(PrimFlags.Phantom);
                 if (PhysActor != null)
