Restricts two degrees of linear freedom and all three degrees of angular freedom.
Inheritance: SolverGroup
        RevoluteJoint wandHinge; // Moves the wand

        #endregion Fields

        #region Constructors

        public Vehicle(MemoryBank memBank, float distance)
        {
            // Figure out the positions of the vehicle and it's sliderBase
            Vector3 position = new Vector3(memBank.Position.X, memBank.Position.Y, memBank.Position.Z + 10);
            Vector3 motorPos = new Vector3(position.X - memBank.Length - distance, position.Y, position.Z);
            byteDist = memBank.ByteDistance;
            bytes = memBank.NumberOfBytes;

            wandGoal = sliderGoal = false;
            delay = 0;

            // Build the main vechicle
            chassis = XNAGame.Instance.createVehicle(position);

            // Build the slider, and attatch
            sliderBase = new Box(motorPos, 1, 1, 1);
            slider = new PrismaticJoint(sliderBase, chassis.body, sliderBase.Position, Vector3.UnitX, chassis.body.Position);
            XNAGame.Instance.space.Add(slider);
            XNAGame.Instance.space.Add(sliderBase);

            // Change slider settings
            slider.Motor.Settings.Mode = MotorMode.Servomechanism;
            slider.Motor.Settings.Servo.SpringSettings.StiffnessConstant /= 20;
            slider.Motor.Settings.Servo.MaxCorrectiveVelocity = 200;
            slider.Motor.Settings.Servo.BaseCorrectiveSpeed = 120;

            // Create the wand
            setUpWand(position);
        }
        /// <summary>
        /// Creates a new, cool, advanced Model. For Accelerated Delivery. Otherwise I get
        /// lots of silly repetative variables. Note that all rendering options are hardcoded.
        /// </summary>
        /// <param name="machineNo">The machine number. For any numbers outside of 1-10, it is treated as the number of
        /// milliseconds until this machine automatically activates.</param>
        /// <param name="machines">An array of machines this machine holds. Null is acceptable.</param>
        /// <param name="models">A list of BaseModels and Tubes that the Machine contains.</param>
        /// <param name="targetPos">The amount of X, Y, and Z to translate.</param>
        /// <param name="timestep">The amount of time to use to translate.</param>
        public TranslateMachine(int machineNo, int soundIndex, Vector3 targetPos, float timestep, bool automatic, params BaseModel[] models)
            : base(machineNo, soundIndex, models)
        {
            joints = new List<WeldJoint>();
            timeStep = timestep;
            targetPosition = targetPos;
            this.automatic = automatic;
            baseJoint = new PrismaticJoint(null, modelList[0].Ent, modelList[0].Ent.Position, Vector3.Normalize(targetPos), modelList[0].Ent.Position);
            baseJoint.Motor.IsActive = true;
            baseJoint.Motor.Settings.Mode = MotorMode.Servomechanism;
            baseJoint.Motor.Settings.Servo.Goal = 0;
            baseJoint.Limit.Maximum = targetPos.Length();
            baseJoint.Limit.Minimum = 0;
            baseJoint.Limit.IsActive = true;
            baseJoint.Motor.Settings.Servo.BaseCorrectiveSpeed = targetPos.Length() / timestep;
            baseJoint.Motor.Settings.Servo.MaxCorrectiveVelocity = targetPos.Length() / timestep;
            baseJoint.Motor.Settings.Servo.SpringSettings.StiffnessConstant = 0;
            baseJoint.Motor.Settings.Servo.SpringSettings.DampingConstant /= 15;

            foreach(BaseModel m in modelList)
            {
                if(m == modelList[0])
                    continue;
                WeldJoint j = new WeldJoint(modelList[0].Ent, m.Ent);
                joints.Add(j);
            }
            foreach(Tube t in tubeList)
                t.SetParent(modelList[0].Ent);

            if(automatic)
            {
                inputs.Clear();
                inputs.Add(new MachineTimer(machineNo / 1000f));
            }
        }
Esempio n. 3
0
        public Button(Vector3 translationAxis, BaseModel button)
        {
            Model = button;
            this.translationAxis = translationAxis;

            joint = new PrismaticJoint(null, button.Ent, button.Ent.Position, translationAxis, button.Ent.Position);
            joint.Motor.IsActive = true;
            joint.Motor.Settings.Mode = BEPUphysics.Constraints.TwoEntity.Motors.MotorMode.Servomechanism;
            joint.Motor.Settings.Servo.BaseCorrectiveSpeed = joint.Motor.Settings.Servo.MaxCorrectiveVelocity = 1.5f;
            joint.Motor.Settings.Servo.SpringSettings.StiffnessConstant = 0;
            joint.Motor.Settings.Servo.SpringSettings.DampingConstant /= 12;
            joint.Limit.Minimum = -0.7f;
            joint.Limit.Maximum = 0;
        }
Esempio n. 4
0
        /// <summary>
        /// Constructs a new demo.
        /// </summary>
        /// <param name="game">Game owning this demo.</param>
        public SawContraptionDemo(DemosGame game)
            : base(game)
        {
            //Add a kinematic entity that will form the base for the strange contraption.
            var pistonGroundAttachment = new Box(new Vector3(0, -1, 0), 1, 2, .5f);
            pistonGroundAttachment.AngularVelocity = new Vector3(0, .2f, 0); //Make it spin a little to rotate the whole thing.
            Space.Add(pistonGroundAttachment);

            var pistonBox1 = new Box(pistonGroundAttachment.Position + new Vector3(0f, 0, 1), 1, 1, 1, 100);
            var pistonBox2 = new Box(pistonBox1.Position + new Vector3(0, 2, 0), .5f, .8f, .5f, 10);
            Space.Add(pistonBox1);
            Space.Add(pistonBox2);

            //Connect the piston entities to the base with a revolute joint that acts like an axis joint
            var axisJoint = new RevoluteJoint(pistonGroundAttachment, pistonBox1, (pistonGroundAttachment.Position + pistonBox1.Position) / 2, Vector3.Forward);

            //Keep the axis from rotating too far so that the saw blade won't just continually ram into the ground.
            //The limit's 'basis' and test axis used to determine the position of the limits and the current angle of the constraint
            //are automatically initialized by the RevoluteJoint constructor.  The basis and testAxis can be set afterwards;
            //the initialization is just a reasonable 'guess' at the kind of limit configuration desired.
            axisJoint.Limit.IsActive = true;
            axisJoint.Limit.MinimumAngle = -MathHelper.PiOver2;
            axisJoint.Limit.MaximumAngle = MathHelper.PiOver2;
            Space.Add(axisJoint);

            var piston = new PrismaticJoint(pistonBox1, pistonBox2, pistonBox1.Position, Vector3.Up, pistonBox2.Position);
            //Set up the piston limits.
            piston.Limit.IsActive = true; //By default, the limit and motor are both inactive.
            piston.Limit.Minimum = 2;
            piston.Limit.Maximum = 5;

            //Set up the servo motor.
            piston.Motor.IsActive = true;
            piston.Motor.Settings.Mode = MotorMode.Servomechanism;
            //Distance from the anchor that the piston will try to reach.
            piston.Motor.Settings.Servo.Goal = 5;
            //Set the maximum force the motor can use to reach its goal.
            piston.Motor.Settings.MaximumForce = 100;
            //This piston, by default, moves at a constant speed, but...
            piston.Motor.Settings.Servo.BaseCorrectiveSpeed = 1;
            //... if the stiffness constant is changed to a positive value, it can also act like a spring.
            piston.Motor.Settings.Servo.SpringSettings.StiffnessConstant = 0;
            //For a non-springy constraint like the piston, the dampingConstant can also be thought of as inverse 'softness.'
            piston.Motor.Settings.Servo.SpringSettings.DampingConstant = 1000;

            //Add the piston to the space.
            Space.Add(piston);

            //Create a saw bladey object on the end of the piston.
            var blade = new Box(pistonBox2.Position + new Vector3(0, 0, .5f), .3f, 2.5f, .1f, 5);
            Space.Add(blade);

            //Connect the saw to the piston with a second axis joint.
            axisJoint = new RevoluteJoint(pistonBox2, blade, (pistonBox2.Position + blade.Position) / 2, Vector3.Forward);
            //Revolute joints can be used to make axis joints (as it is here), but you can also use them to make hinges.
            Space.Add(axisJoint);

            //Make the blade spin.
            axisJoint.Motor.IsActive = true;
            axisJoint.Motor.Settings.VelocityMotor.GoalVelocity = 30;
            axisJoint.Motor.Settings.MaximumForce = 200;

            //Add some ground.
            Space.Add(new Box(new Vector3(0, -3f, 0), 20f, 1, 20f));

            //Make some debris for the saw to chop.
            for (double k = 0; k < Math.PI * 2; k += Math.PI / 20)
            {
                Space.Add(new Box(new Vector3((float) Math.Cos(k) * 4f, -2, (float) Math.Sin(k) * 6.5f), .5f, 1f, .5f, 10));
            }

            game.Camera.Position = new Vector3(0, 2, 20);
        }
        public TruckMachine(int machineNo, int soundIndex, float translationTime, float rotationTime, float angle,
            Vector3 translationAxis, Vector3 glassRotationAxis, Vector3 wheelRotationAxis,
            Vector3 glassZeroAxis, Vector3 glassCenter1, Vector3 glassCenter2, Vector3 wheelsCenter1,
            Vector3 wheelsCenter2, BaseModel wheels1, BaseModel wheels2, BaseModel glass1,
            BaseModel glass2, params BaseModel[] models)
            : base(machineNo, soundIndex, models.Concat(new BaseModel[] { wheels1, wheels2, glass1, glass2 }).ToArray<BaseModel>())
        {
            translation = translationAxis;
            this.translationTime = translationTime;
            this.rotationTime = rotationTime;

            originalGlassAxis = glassRotationAxis;
            originalWheelAxis = wheelRotationAxis;
            unitsToTranslate = translationAxis;

            baseJoint = new PrismaticJoint(null, models[0].Ent, models[0].Ent.Position, translationAxis, models[0].Ent.Position);
            baseJoint.Motor.IsActive = true;
            baseJoint.Motor.Settings.Mode = MotorMode.Servomechanism;
            baseJoint.Motor.Settings.Servo.Goal = 0;
            baseJoint.Limit.Maximum = translationAxis.Length();
            baseJoint.Limit.Minimum = 0;
            baseJoint.Limit.IsActive = true;
            baseJoint.Motor.Settings.Servo.BaseCorrectiveSpeed = translationAxis.Length() / translationTime;
            baseJoint.Motor.Settings.Servo.MaxCorrectiveVelocity = translationAxis.Length() / translationTime;
            baseJoint.Motor.Settings.Servo.SpringSettings.StiffnessConstant = 0;
            baseJoint.Motor.Settings.Servo.SpringSettings.DampingConstant /= 11;

            glassJoint1 = new RevoluteJoint(models[0].Ent, glass1.Ent, glassCenter1, glassRotationAxis);
            glassJoint1.Motor.IsActive = true;
            glassJoint1.Motor.Settings.Mode = BEPUphysics.Constraints.TwoEntity.Motors.MotorMode.Servomechanism;
            glassJoint1.Motor.Settings.Servo.SpringSettings.StiffnessConstant = 0;
            glassJoint1.Motor.Settings.Servo.SpringSettings.DampingConstant /= 12;
            glassJoint1.Motor.Settings.Servo.Goal = 0;
            glassJoint1.Motor.Settings.Servo.MaxCorrectiveVelocity = angle / rotationTime;
            glassJoint1.Motor.Settings.Servo.BaseCorrectiveSpeed = angle / rotationTime;
            glassJoint1.Motor.Basis.SetWorldAxes(glassRotationAxis, glassZeroAxis);
            glassJoint1.Motor.TestAxis = glassZeroAxis;
            glassJoint1.Limit.IsActive = true;
            glassJoint1.Limit.MinimumAngle = 0;
            glassJoint1.Limit.MaximumAngle = angle;
            glassJoint1.Limit.Basis.SetWorldAxes(glassRotationAxis, glassZeroAxis);
            glassJoint1.Limit.TestAxis = glassZeroAxis;

            glassJoint2 = new RevoluteJoint(models[0].Ent, glass2.Ent, glassCenter2, -glassRotationAxis);
            glassJoint2.Motor.IsActive = true;
            glassJoint2.Motor.Settings.Mode = BEPUphysics.Constraints.TwoEntity.Motors.MotorMode.Servomechanism;
            glassJoint2.Motor.Settings.Servo.SpringSettings.StiffnessConstant = 0;
            glassJoint2.Motor.Settings.Servo.SpringSettings.DampingConstant /= 12;
            glassJoint2.Motor.Settings.Servo.Goal = 0;
            glassJoint2.Motor.Settings.Servo.MaxCorrectiveVelocity = angle / rotationTime;
            glassJoint2.Motor.Settings.Servo.BaseCorrectiveSpeed = angle / rotationTime;
            glassJoint2.Motor.Basis.SetWorldAxes(-glassRotationAxis, glassZeroAxis);
            glassJoint2.Motor.TestAxis = glassZeroAxis;
            glassJoint2.Limit.IsActive = true;
            glassJoint2.Limit.MinimumAngle = 0;
            glassJoint2.Limit.MaximumAngle = angle;
            glassJoint2.Limit.Basis.SetWorldAxes(-glassRotationAxis, glassZeroAxis);
            glassJoint2.Limit.TestAxis = glassZeroAxis;

            wheelsJoint1 = new RevoluteJoint(models[0].Ent, wheels1.Ent, wheelsCenter1, wheelRotationAxis);
            wheelsJoint1.Motor.Settings.Mode = MotorMode.VelocityMotor;
            wheelsJoint1.Motor.Settings.VelocityMotor.GoalVelocity = 0f;
            wheelsJoint1.Motor.Settings.VelocityMotor.Softness = 1 / glassJoint1.Motor.Settings.Servo.SpringSettings.DampingConstant;
            wheelsJoint2 = new RevoluteJoint(models[0].Ent, wheels2.Ent, wheelsCenter2, wheelRotationAxis);
            wheelsJoint2.Motor.Settings.Mode = MotorMode.VelocityMotor;
            wheelsJoint2.Motor.Settings.VelocityMotor.GoalVelocity = 0f;
            wheelsJoint2.Motor.Settings.VelocityMotor.Softness = 1 / glassJoint2.Motor.Settings.Servo.SpringSettings.DampingConstant;

            if(models.Length > 1)
                foreach(BaseModel m in models)
                {
                    if(m == models[0])
                        continue;
                    WeldJoint j = new WeldJoint(models[0].Ent, m.Ent);
                    j.IsActive = true;
                    joints.Add(j);
                }
        }
Esempio n. 6
0
        public override void Bind(Entity result, Main main, bool creating = false)
        {
            Property<Direction> dir = result.GetOrMakeProperty<Direction>("Direction", true);
            Property<int> minimum = result.GetOrMakeProperty<int>("Minimum", true);
            Property<int> maximum = result.GetOrMakeProperty<int>("Maximum", true);
            Property<bool> locked = result.GetOrMakeProperty<bool>("Locked", true);
            Property<float> speed = result.GetOrMakeProperty<float>("Speed", true, 5);
            Property<float> maxForce = result.GetOrMakeProperty<float>("MaxForce", true);
            Property<float> damping = result.GetOrMakeProperty<float>("Damping", true);
            Property<float> stiffness = result.GetOrMakeProperty<float>("Stiffness", true);
            Property<int> goal = result.GetOrMakeProperty<int>("Goal", true);

            PrismaticJoint joint = null;

            Action setLimits = delegate()
            {
                if (joint != null)
                {
                    int min = minimum, max = maximum;
                    if (max > min)
                    {
                        joint.Limit.IsActive = true;
                        joint.Limit.Minimum = minimum;
                        joint.Limit.Maximum = maximum;
                    }
                    else
                        joint.Limit.IsActive = false;
                }
            };
            result.Add(new NotifyBinding(setLimits, minimum, maximum));

            Action setMaxForce = delegate()
            {
                if (joint != null)
                {
                    if (maxForce > 0.001f)
                        joint.Motor.Settings.MaximumForce = maxForce * result.Get<DynamicMap>().PhysicsEntity.Mass;
                    else
                        joint.Motor.Settings.MaximumForce = float.MaxValue;
                }
            };
            result.Add(new NotifyBinding(setMaxForce, maxForce));

            Action setSpeed = delegate()
            {
                if (joint != null)
                    joint.Motor.Settings.Servo.BaseCorrectiveSpeed = speed;
            };
            result.Add(new NotifyBinding(setSpeed, speed));

            Action setLocked = delegate()
            {
                if (joint != null)
                    joint.Motor.IsActive = locked;
            };
            result.Add(new NotifyBinding(setLocked, locked));

            Action setGoal = delegate()
            {
                if (joint != null)
                    joint.Motor.Settings.Servo.Goal = goal;
            };
            result.Add(new NotifyBinding(setGoal, goal));

            Action setDamping = delegate()
            {
                if (joint != null && damping != 0)
                    joint.Motor.Settings.Servo.SpringSettings.DampingConstant = damping;
            };
            result.Add(new NotifyBinding(setDamping, damping));

            Action setStiffness = delegate()
            {
                if (joint != null && stiffness != 0)
                    joint.Motor.Settings.Servo.SpringSettings.StiffnessConstant = stiffness;
            };
            result.Add(new NotifyBinding(setStiffness, stiffness));

            Func<BEPUphysics.Entities.Entity, BEPUphysics.Entities.Entity, Vector3, Vector3, Vector3, ISpaceObject> createJoint = delegate(BEPUphysics.Entities.Entity entity1, BEPUphysics.Entities.Entity entity2, Vector3 pos, Vector3 direction, Vector3 anchor)
            {
                joint = new PrismaticJoint(entity1, entity2, pos, -direction, anchor);
                joint.Motor.Settings.Mode = MotorMode.Servomechanism;
                setLimits();
                setLocked();
                setSpeed();
                setMaxForce();
                setGoal();
                setDamping();
                setStiffness();
                return joint;
            };

            JointFactory.Bind(result, main, createJoint, false, creating);

            result.Add("Forward", new Command
            {
                Action = delegate()
                {
                    if (joint != null && locked)
                        goal.Value = maximum;
                },
            });
            result.Add("Backward", new Command
            {
                Action = delegate()
                {
                    if (joint != null && locked)
                        goal.Value = minimum;
                },
            });

            Command hitMax = new Command();
            result.Add("HitMax", hitMax);
            Command hitMin = new Command();
            result.Add("HitMin", hitMin);

            bool lastLimitExceeded = false;
            result.Add(new Updater
            {
                delegate(float dt)
                {
                    if (joint != null)
                    {
                        bool limitExceeded = joint.Limit.IsLimitExceeded;
                        if (limitExceeded && !lastLimitExceeded)
                        {
                            if (joint.Limit.Error < 0)
                                hitMin.Execute();
                            else
                                hitMax.Execute();
                        }
                        lastLimitExceeded = limitExceeded;
                    }
                }
            });
        }
        void AddBackWheel(Vector3 suspensionOffset, Entity body, bool leftSide)
        {
            var suspensionLeg = new Box(body.Position + suspensionOffset, 0.25f, 0.8f, 0.25f, 10);
            const float horizontalWheelOffset = 0.2f;

            var wheel = new Cylinder(suspensionLeg.Position + new Vector3(leftSide ? -horizontalWheelOffset : horizontalWheelOffset, -suspensionLeg.HalfHeight, 0), .2f, .3f, 5f);
            wheel.Material.KineticFriction = 2.5f;
            wheel.Material.StaticFriction = 3.5f;
            wheel.Orientation = Quaternion.CreateFromAxisAngle(Vector3.Forward, MathHelper.PiOver2);

            //Preventing the occasional pointless collision pair can speed things up.
            CollisionRules.AddRule(wheel, body, CollisionRule.NoBroadPhase);
            CollisionRules.AddRule(wheel, suspensionLeg, CollisionRule.NoBroadPhase);
            CollisionRules.AddRule(suspensionLeg, body, CollisionRule.NoBroadPhase);

            //Connect the suspension to the body.
            var bodyToSuspension = new PrismaticJoint(body, suspensionLeg, suspensionLeg.Position, Vector3.Down, suspensionLeg.Position);
            bodyToSuspension.Motor.Settings.Mode = MotorMode.Servomechanism;
            bodyToSuspension.Motor.IsActive = true;
            bodyToSuspension.Motor.Settings.Servo.SpringSettings.Stiffness = 300;
            bodyToSuspension.Motor.Settings.Servo.SpringSettings.Damping = 70;

            bodyToSuspension.Limit.IsActive = true;
            bodyToSuspension.Limit.Minimum = -0.5f;
            bodyToSuspension.Limit.Maximum = 0;

            //Connect the wheel to the suspension.
            var suspensionToWheel = new RevoluteJoint(suspensionLeg, wheel, wheel.Position, Vector3.Right);

            //Add the wheel and connection to the space.
            Space.Add(wheel);
            Space.Add(suspensionLeg);
            Space.Add(bodyToSuspension);
            Space.Add(suspensionToWheel);
        }
        /// <summary>
        /// Makes a machine that automatically translates and user-rotates.
        /// </summary>
        /// <param name="machineNo">Machine number of the machine.</param>
        /// <param name="unitsToTranslate">The distance to translate from the origin.</param>
        /// <param name="pauseTime">The time in seconds to pause at each end.</param>
        /// <param name="centerOfRotation">The center of rotation based on the starting position.</param>
        /// <param name="rotateTime">Time to take to rotate.</param>
        /// <param name="degreesToRotate">The degrees on each axis to rotate.</param>
        /// <param name="models">The models that rotate.</param>
        /// <param name="angle">The angle to rotate.</param>
        /// <param name="rotationAxis">The axis to rotate on.</param>
        /// <param name="soundIndex">The sound to use for the machine.</param>
        /// <param name="stableModels">The models that don't rotate.</param>
        /// <param name="translationTime">The time to take to translate.</param>
        public RailMachine(int machineNo, int soundIndex, bool soundWhenMoves, Vector3 unitsToTranslate, float pauseTime, float translationTime,
            Vector3 centerOfRotation, float rotateTime, Vector3 rotationAxis, float angle, Vector3 zeroAxis, BaseModel[] stableModels,
            params BaseModel[] models)
            : base(machineNo, soundIndex, models)
        {
            if(stableModels.Length == 0)
                throw new ArgumentException("There must be at least one stable model.");
            if(models.Length == 0)
                throw new ArgumentException("There must be a least rotational model.");

            this.unitsToTranslate = unitsToTranslate;
            this.translationTime = translationTime;
            this.rotateTime = rotateTime;
            this.angle = angle;
            this.soundWhenMoves = soundWhenMoves;
            lengthToPause = pauseTime;
            originalAxis = rotationAxis;

            if(angle < 0)
            {
                negativeAngle = true;
                angle = -angle;
            }

            baseJoint = new PrismaticJoint(null, stableModels[0].Ent, stableModels[0].Ent.Position, Vector3.Normalize(unitsToTranslate), stableModels[0].Ent.Position);
            baseJoint.Motor.IsActive = true;
            baseJoint.Motor.Settings.Mode = MotorMode.Servomechanism;
            baseJoint.Motor.Settings.Servo.Goal = 0;
            baseJoint.Limit.Maximum = unitsToTranslate.Length();
            baseJoint.Limit.Minimum = 0;
            baseJoint.Limit.IsActive = true;
            baseJoint.Motor.Settings.Servo.BaseCorrectiveSpeed = unitsToTranslate.Length() / translationTime;
            baseJoint.Motor.Settings.Servo.MaxCorrectiveVelocity = unitsToTranslate.Length() / translationTime;
            baseJoint.Motor.Settings.Servo.SpringSettings.StiffnessConstant = 0;
            baseJoint.Motor.Settings.Servo.SpringSettings.DampingConstant /= 2;

            rotateJoint = new RevoluteJoint(stableModels[0].Ent, models[0].Ent, centerOfRotation, rotationAxis);
            rotateJoint.Motor.IsActive = true;
            rotateJoint.Motor.Settings.Mode = BEPUphysics.Constraints.TwoEntity.Motors.MotorMode.Servomechanism;
            rotateJoint.Motor.Settings.Servo.SpringSettings.StiffnessConstant = 0;
            rotateJoint.Motor.Settings.Servo.SpringSettings.DampingConstant /= 4;
            rotateJoint.Motor.Settings.Servo.Goal = negativeAngle ? angle : 0;
            rotateJoint.Motor.Settings.Servo.MaxCorrectiveVelocity = angle / rotateTime;
            rotateJoint.Motor.Settings.Servo.BaseCorrectiveSpeed = angle / rotateTime;
            rotateJoint.Motor.Basis.SetWorldAxes(rotationAxis, zeroAxis);
            rotateJoint.Motor.TestAxis = zeroAxis;
            rotateJoint.Limit.IsActive = true;
            rotateJoint.Limit.MinimumAngle = 0;
            rotateJoint.Limit.MaximumAngle = angle;
            rotateJoint.Limit.Basis.SetWorldAxes(rotationAxis, zeroAxis);
            rotateJoint.Limit.TestAxis = zeroAxis;

            for(int i = 0; i < stableModels.Length - 1; i++)
            {
                WeldJoint j = new WeldJoint(stableModels[0].Ent, stableModels[i + 1].Ent);
                j.IsActive = true;
                welds.Add(j);
            }
            for(int i = 0; i < models.Length - 1; i++)
            {
                WeldJoint j = new WeldJoint(models[0].Ent, models[i + 1].Ent);
                j.IsActive = true;
                welds.Add(j);
            }
            foreach(Tube t in tubeList)
                t.SetParent(models[0].Ent);

            modelList.Clear();
            modelList.AddRange(stableModels);
            modelList.AddRange(models);

            //Vector3 radians = new Vector3(MathHelper.ToRadians(degreesToRotate.X),
            //    MathHelper.ToRadians(degreesToRotate.Y), MathHelper.ToRadians(degreesToRotate.Z));
            //radiansToRotate = Quaternion.CreateFromYawPitchRoll(radians.Y,
            //    radians.X, radians.Z);

            //for(int i = 1; i < modelList.Count; i++)
            //{
            //    BaseModel m = modelList[i];
            //    m.Ent.Orientation = Quaternion.Identity;
            //    m.Ent.CollisionInformation.LocalPosition = m.Ent.Position - centerOfRotation;
            //    m.Ent.Position = centerOfRotation;
            //    m.Ent.Orientation = m.OriginalOrientation; // Resets orientation
            //}
            //curve = new QuaternionSlerpCurve();
            //curve.ControlPoints.Add(0, Quaternion.Identity);
            //curve.ControlPoints.Add(rotateTime, radiansToRotate);
            //curve.PostLoop = CurveEndpointBehavior.Mirror;
            //curve.PreLoop = CurveEndpointBehavior.Mirror;
            timeStep = rotateTime * 1000;
        }