// Create a static constraint between the two passed objects
        private BSConstraint BuildConstraint(BSPrimLinkable rootPrim, BSLinkInfo li)
        {
            BSLinkInfoConstraint linkInfo = li as BSLinkInfoConstraint;

            if (linkInfo == null)
            {
                return(null);
            }

            // Zero motion for children so they don't interpolate
            li.member.ZeroMotion(true);

            BSConstraint constrain = null;

            switch (linkInfo.constraintType)
            {
            case ConstraintType.BS_FIXED_CONSTRAINT_TYPE:
            case ConstraintType.D6_CONSTRAINT_TYPE:
                // Relative position normalized to the root prim
                // Essentually a vector pointing from center of rootPrim to center of li.member
                OMV.Vector3 childRelativePosition = linkInfo.member.Position - rootPrim.Position;

                // real world coordinate of midpoint between the two objects
                OMV.Vector3 midPoint = rootPrim.Position + (childRelativePosition / 2);

                DetailLog("{0},BSLinksetConstraint.BuildConstraint,6Dof,rBody={1},cBody={2},rLoc={3},cLoc={4},midLoc={5}",
                          rootPrim.LocalID, rootPrim.PhysBody, linkInfo.member.PhysBody,
                          rootPrim.Position, linkInfo.member.Position, midPoint);

                // create a constraint that allows no freedom of movement between the two objects
                // http://bulletphysics.org/Bullet/phpBB3/viewtopic.php?t=4818

                constrain = new BSConstraint6Dof(
                    m_physicsScene.World, rootPrim.PhysBody, linkInfo.member.PhysBody, midPoint, true, true);

                /* NOTE: below is an attempt to build constraint with full frame computation, etc.
                 *     Using the midpoint is easier since it lets the Bullet code manipulate the transforms
                 *     of the objects.
                 * Code left for future programmers.
                 * // ==================================================================================
                 * // relative position normalized to the root prim
                 * OMV.Quaternion invThisOrientation = OMV.Quaternion.Inverse(rootPrim.Orientation);
                 * OMV.Vector3 childRelativePosition = (liConstraint.member.Position - rootPrim.Position) * invThisOrientation;
                 *
                 * // relative rotation of the child to the parent
                 * OMV.Quaternion childRelativeRotation = invThisOrientation * liConstraint.member.Orientation;
                 * OMV.Quaternion inverseChildRelativeRotation = OMV.Quaternion.Inverse(childRelativeRotation);
                 *
                 * DetailLog("{0},BSLinksetConstraint.PhysicallyLinkAChildToRoot,taint,root={1},child={2}", rootPrim.LocalID, rootPrim.LocalID, liConstraint.member.LocalID);
                 * constrain = new BS6DofConstraint(
                 *              PhysicsScene.World, rootPrim.Body, liConstraint.member.Body,
                 *              OMV.Vector3.Zero,
                 *              OMV.Quaternion.Inverse(rootPrim.Orientation),
                 *              OMV.Vector3.Zero,
                 *              OMV.Quaternion.Inverse(liConstraint.member.Orientation),
                 *              true,
                 *              true
                 *              );
                 * // ==================================================================================
                 */

                break;

            case ConstraintType.D6_SPRING_CONSTRAINT_TYPE:
                constrain = new BSConstraintSpring(m_physicsScene.World, rootPrim.PhysBody, linkInfo.member.PhysBody,
                                                   linkInfo.frameInAloc, linkInfo.frameInArot, linkInfo.frameInBloc, linkInfo.frameInBrot,
                                                   linkInfo.useLinearReferenceFrameA,
                                                   true /*disableCollisionsBetweenLinkedBodies*/);
                DetailLog("{0},BSLinksetConstraint.BuildConstraint,spring,root={1},rBody={2},child={3},cBody={4},rLoc={5},cLoc={6}",
                          rootPrim.LocalID,
                          rootPrim.LocalID, rootPrim.PhysBody.AddrString,
                          linkInfo.member.LocalID, linkInfo.member.PhysBody.AddrString,
                          rootPrim.Position, linkInfo.member.Position);

                break;

            default:
                break;
            }

            linkInfo.SetLinkParameters(constrain);

            m_physicsScene.Constraints.AddConstraint(constrain);

            return(constrain);
        }
    // Create a static constraint between the two passed objects
    private BSConstraint BuildConstraint(BSPrimLinkable rootPrim, BSLinkInfo li)
    {
        BSLinkInfoConstraint linkInfo = li as BSLinkInfoConstraint;
        if (linkInfo == null)
            return null;

        // Zero motion for children so they don't interpolate
        li.member.ZeroMotion(true);

        BSConstraint constrain = null;

        switch (linkInfo.constraintType)
        {
            case ConstraintType.BS_FIXED_CONSTRAINT_TYPE:
            case ConstraintType.D6_CONSTRAINT_TYPE:
                // Relative position normalized to the root prim
                // Essentually a vector pointing from center of rootPrim to center of li.member
                OMV.Vector3 childRelativePosition = linkInfo.member.Position - rootPrim.Position;

                // real world coordinate of midpoint between the two objects
                OMV.Vector3 midPoint = rootPrim.Position + (childRelativePosition / 2);

                DetailLog("{0},BSLinksetConstraint.BuildConstraint,6Dof,rBody={1},cBody={2},rLoc={3},cLoc={4},midLoc={5}",
                                                rootPrim.LocalID, rootPrim.PhysBody, linkInfo.member.PhysBody,
                                                rootPrim.Position, linkInfo.member.Position, midPoint);

                // create a constraint that allows no freedom of movement between the two objects
                // http://bulletphysics.org/Bullet/phpBB3/viewtopic.php?t=4818

                constrain = new BSConstraint6Dof(
                                    m_physicsScene.World, rootPrim.PhysBody, linkInfo.member.PhysBody, midPoint, true, true );

                /* NOTE: below is an attempt to build constraint with full frame computation, etc.
                 *     Using the midpoint is easier since it lets the Bullet code manipulate the transforms
                 *     of the objects.
                 * Code left for future programmers.
                // ==================================================================================
                // relative position normalized to the root prim
                OMV.Quaternion invThisOrientation = OMV.Quaternion.Inverse(rootPrim.Orientation);
                OMV.Vector3 childRelativePosition = (liConstraint.member.Position - rootPrim.Position) * invThisOrientation;

                // relative rotation of the child to the parent
                OMV.Quaternion childRelativeRotation = invThisOrientation * liConstraint.member.Orientation;
                OMV.Quaternion inverseChildRelativeRotation = OMV.Quaternion.Inverse(childRelativeRotation);

                DetailLog("{0},BSLinksetConstraint.PhysicallyLinkAChildToRoot,taint,root={1},child={2}", rootPrim.LocalID, rootPrim.LocalID, liConstraint.member.LocalID);
                constrain = new BS6DofConstraint(
                                PhysicsScene.World, rootPrim.Body, liConstraint.member.Body,
                                OMV.Vector3.Zero,
                                OMV.Quaternion.Inverse(rootPrim.Orientation),
                                OMV.Vector3.Zero,
                                OMV.Quaternion.Inverse(liConstraint.member.Orientation),
                                true,
                                true
                                );
                // ==================================================================================
                */

                break;
            case ConstraintType.D6_SPRING_CONSTRAINT_TYPE:
                constrain = new BSConstraintSpring(m_physicsScene.World, rootPrim.PhysBody, linkInfo.member.PhysBody,
                                linkInfo.frameInAloc, linkInfo.frameInArot, linkInfo.frameInBloc, linkInfo.frameInBrot,
                                linkInfo.useLinearReferenceFrameA,
                                true /*disableCollisionsBetweenLinkedBodies*/);
                DetailLog("{0},BSLinksetConstraint.BuildConstraint,spring,root={1},rBody={2},child={3},cBody={4},rLoc={5},cLoc={6}",
                                                rootPrim.LocalID,
                                                rootPrim.LocalID, rootPrim.PhysBody.AddrString,
                                                linkInfo.member.LocalID, linkInfo.member.PhysBody.AddrString,
                                                rootPrim.Position, linkInfo.member.Position);

                break;
            default:
                break;
        }

        linkInfo.SetLinkParameters(constrain);

        m_physicsScene.Constraints.AddConstraint(constrain);

        return constrain;
    }
            // Given a constraint, apply the current constraint parameters to same.
            public override void SetLinkParameters(BSConstraint constrain)
            {
                member.PhysScene.DetailLog("{0},BSLinkInfoConstraint.SetLinkParameters,type={1}", member.LocalID, constraintType);
                switch (constraintType)
                {
                case ConstraintType.BS_FIXED_CONSTRAINT_TYPE:
                case ConstraintType.D6_CONSTRAINT_TYPE:
                    BSConstraint6Dof constrain6dof = constrain as BSConstraint6Dof;
                    if (constrain6dof != null)
                    {
                        // NOTE: D6_SPRING_CONSTRAINT_TYPE should be updated if you change any of this code.
                        // zero linear and angular limits makes the objects unable to move in relation to each other
                        constrain6dof.SetLinearLimits(linearLimitLow, linearLimitHigh);
                        constrain6dof.SetAngularLimits(angularLimitLow, angularLimitHigh);

                        // tweek the constraint to increase stability
                        constrain6dof.UseFrameOffset(useFrameOffset);
                        constrain6dof.TranslationalLimitMotor(enableTransMotor, transMotorMaxVel, transMotorMaxForce);
                        constrain6dof.SetCFMAndERP(cfm, erp);
                        if (solverIterations != 0f)
                        {
                            constrain6dof.SetSolverIterations(solverIterations);
                        }
                    }
                    break;

                case ConstraintType.D6_SPRING_CONSTRAINT_TYPE:
                    BSConstraintSpring constrainSpring = constrain as BSConstraintSpring;
                    if (constrainSpring != null)
                    {
                        // zero linear and angular limits makes the objects unable to move in relation to each other
                        constrainSpring.SetLinearLimits(linearLimitLow, linearLimitHigh);
                        constrainSpring.SetAngularLimits(angularLimitLow, angularLimitHigh);

                        // tweek the constraint to increase stability
                        constrainSpring.UseFrameOffset(useFrameOffset);
                        constrainSpring.TranslationalLimitMotor(enableTransMotor, transMotorMaxVel, transMotorMaxForce);
                        constrainSpring.SetCFMAndERP(cfm, erp);
                        if (solverIterations != 0f)
                        {
                            constrainSpring.SetSolverIterations(solverIterations);
                        }
                        for (int ii = 0; ii < springAxisEnable.Length; ii++)
                        {
                            constrainSpring.SetAxisEnable(ii, springAxisEnable[ii]);
                            if (springDamping[ii] != BSAPITemplate.SPRING_NOT_SPECIFIED)
                            {
                                constrainSpring.SetDamping(ii, springDamping[ii]);
                            }
                            if (springStiffness[ii] != BSAPITemplate.SPRING_NOT_SPECIFIED)
                            {
                                constrainSpring.SetStiffness(ii, springStiffness[ii]);
                            }
                        }
                        constrainSpring.CalculateTransforms();

                        if (springLinearEquilibriumPoint != OMV.Vector3.Zero)
                        {
                            constrainSpring.SetEquilibriumPoint(springLinearEquilibriumPoint, springAngularEquilibriumPoint);
                        }
                        else
                        {
                            constrainSpring.SetEquilibriumPoint(BSAPITemplate.SPRING_NOT_SPECIFIED, BSAPITemplate.SPRING_NOT_SPECIFIED);
                        }
                    }
                    break;

                default:
                    break;
                }
            }