/// <summary>
        /// Constructs a new demo.
        /// </summary>
        /// <param name="game">Game owning this demo.</param>
        public CharacterPlaygroundDemo(DemosGame game)
            : base(game)
        {

            game.Camera.Position = new Vector3(-10, 7, 5);
            game.Camera.ViewDirection = new Vector3(0, 0, 1);
            //Since this is the character playground, turn on the character by default.
            character.Activate();
            //Having the character body visible would be a bit distracting.
            character.CharacterController.Body.Tag = "noDisplayObject";

            //Load in mesh data for the environment.
            Vector3[] staticTriangleVertices;
            int[] staticTriangleIndices;


            var playgroundModel = game.Content.Load<Model>("CharacterControllerTestTerrain");
            //This is a little convenience method used to extract vertices and indices from a model.
            //It doesn't do anything special; any approach that gets valid vertices and indices will work.
            ModelDataExtractor.GetVerticesAndIndicesFromModel(playgroundModel, out staticTriangleVertices, out staticTriangleIndices);
            var staticMesh = new StaticMesh(staticTriangleVertices, staticTriangleIndices, new AffineTransform(new Vector3(0.01f, 0.01f, 0.01f), Quaternion.Identity, new Vector3(0, 0, 0)));
            staticMesh.Sidedness = TriangleSidedness.Counterclockwise;

            Space.Add(staticMesh);
            game.ModelDrawer.Add(staticMesh);



            //Add a spinning blade for the character to ram itself into.
            var fanBase = new Cylinder(new Vector3(-13, .5f, 50), 1.1f, 1);
            var fanBlade = new Box(fanBase.Position + new Vector3(0, .8f, 0), 5, .1f, 1f, 5);
            var fanJoint = new RevoluteJoint(fanBase, fanBlade, (fanBase.Position + fanBlade.Position) * .5f, Vector3.Up);
            fanJoint.Motor.IsActive = true;
            fanJoint.Motor.Settings.VelocityMotor.GoalVelocity = 30;
            fanJoint.Motor.Settings.MaximumForce = 300;
            Space.Add(fanBase);
            Space.Add(fanBlade);
            Space.Add(fanJoint);

            //Add a bridge connecting the two towers.
            Vector3 startPosition = new Vector3(-19.3f, 10.5f - .25f, 23 - .85f);
            var startPlatform = new Box(startPosition - new Vector3(0, 0, 2.2f), 4, .5f, 6);
            Space.Add(startPlatform);
            Vector3 offset = new Vector3(0, 0, 1.7f);
            Box previousLink = startPlatform;
            Vector3 position = new Vector3();
            for (int i = 1; i <= 7; i++)
            {
                position = startPosition + offset * i;
                Box link = new Box(position, 3, .3f, 1.5f, 50);
                link.LinearDamping = .1f;
                link.AngularDamping = .1f;
                Space.Add(link);
                Space.Add(new RevoluteJoint(previousLink, link, position - offset * .5f, Vector3.Right));

                previousLink = link;
            }
            var endPlatform = new Box(position - new Vector3(0, 0, -3.8f), 4, .5f, 6);
            Space.Add(endPlatform);

            Space.Add(new RevoluteJoint(previousLink, endPlatform, position + offset * .5f, Vector3.Right));


            //Add in a floating platform controlled by a curve to serve as an elevator.
            Entity movingEntity = new Box(new Vector3(-10, 0, -10), 3, 1, 3);

            var positionCurve = new CardinalSpline3D();

            positionCurve.PreLoop = CurveEndpointBehavior.Mirror;
            positionCurve.PostLoop = CurveEndpointBehavior.Mirror;

            positionCurve.ControlPoints.Add(-1, new Vector3(-19.3f, 0, 43));
            positionCurve.ControlPoints.Add(0, new Vector3(-19.3f, 0, 43));
            positionCurve.ControlPoints.Add(2, new Vector3(-19.3f, 0, 43));
            positionCurve.ControlPoints.Add(3, new Vector3(-19.3f, 0, 43));
            positionCurve.ControlPoints.Add(4, new Vector3(-19.3f, 5, 43));
            positionCurve.ControlPoints.Add(5f, new Vector3(-19.3f, 10, 43));
            positionCurve.ControlPoints.Add(6f, new Vector3(-19.3f, 10, 43));
            positionCurve.ControlPoints.Add(8f, new Vector3(-19.3f, 10, 43));
            positionCurve.ControlPoints.Add(9f, new Vector3(-19.3f, 10, 43));

            elevatorMover = new EntityMover(movingEntity);
            Space.Add(elevatorMover);
            Space.Add(movingEntity);

            elevatorPath = positionCurve;

            //Add in another floating platform controlled by a curve for horizontal transport.
            movingEntity = new Box(new Vector3(-10, 0, -10), 2.5f, .5f, 2.5f);

            var platformCurve = new LinearInterpolationCurve3D();

            platformCurve.PreLoop = CurveEndpointBehavior.Mirror;
            platformCurve.PostLoop = CurveEndpointBehavior.Mirror;

            platformCurve.ControlPoints.Add(0, new Vector3(-1.75f, 10, 21.5f));
            platformCurve.ControlPoints.Add(2, new Vector3(-1.75f, 10, 21.5f));
            platformCurve.ControlPoints.Add(5, new Vector3(-1.75f, 10, 15.5f));
            platformCurve.ControlPoints.Add(10, new Vector3(-19.3f, 10, 15.5f));
            platformCurve.ControlPoints.Add(12, new Vector3(-19.3f, 10, 15.5f));
            platformCurve.ControlPoints.Add(15, new Vector3(-25, 10, 15.5f));
            platformCurve.ControlPoints.Add(22, new Vector3(-25, 10, 38));
            platformCurve.ControlPoints.Add(23, new Vector3(-22.75f, 10, 38));
            platformCurve.ControlPoints.Add(25, new Vector3(-22.75f, 10, 38));

            //Make it spin too.  That'll be fun.  Or something.
            var platformRotationCurve = new QuaternionSlerpCurve();
            platformRotationCurve.PreLoop = CurveEndpointBehavior.Mirror;
            platformRotationCurve.PostLoop = CurveEndpointBehavior.Mirror;
            platformRotationCurve.ControlPoints.Add(0, Quaternion.Identity);
            platformRotationCurve.ControlPoints.Add(15, Quaternion.Identity);
            platformRotationCurve.ControlPoints.Add(22, Quaternion.CreateFromAxisAngle(Vector3.Up, MathHelper.PiOver2));
            platformRotationCurve.ControlPoints.Add(25, Quaternion.CreateFromAxisAngle(Vector3.Up, MathHelper.PiOver2));

            platformMover = new EntityMover(movingEntity);
            platformRotator = new EntityRotator(movingEntity);
            Space.Add(platformMover);
            Space.Add(platformRotator);
            Space.Add(movingEntity);

            platformPath = platformCurve;
            platformOrientationPath = platformRotationCurve;

            //Add in a diving board.

            Box divingBoardBase = new Box(new Vector3(-9, 10, 39.3f), 5, 1, 3);
            Box divingBoard = new Box(divingBoardBase.Position + new Vector3(-2, 0, 3.5f), 1, .3f, 3, 5);
            var divingBoardJoint = new RevoluteJoint(divingBoardBase, divingBoard, divingBoard.Position + new Vector3(0, 0, -1.5f), Vector3.Right);
            divingBoardJoint.Motor.IsActive = true;
            divingBoardJoint.Motor.Settings.Mode = MotorMode.Servomechanism;
            divingBoardJoint.Motor.Settings.Servo.Goal = 0;
            divingBoardJoint.Motor.Settings.Servo.SpringSettings.Stiffness = 5000;
            divingBoardJoint.Motor.Settings.Servo.SpringSettings.Damping = 0;

            Space.Add(divingBoardBase);
            Space.Add(divingBoard);
            Space.Add(divingBoardJoint);


            //Add a second diving board for comparison.

            Box divingBoard2 = new Box(divingBoardBase.Position + new Vector3(2, 0, 5f), 1, .3f, 6, 5);
            var divingBoardJoint2 = new RevoluteJoint(divingBoardBase, divingBoard2, divingBoard2.Position + new Vector3(0, 0, -3), Vector3.Right);
            divingBoardJoint2.Motor.IsActive = true;
            divingBoardJoint2.Motor.Settings.Mode = MotorMode.Servomechanism;
            divingBoardJoint2.Motor.Settings.Servo.Goal = 0;
            divingBoardJoint2.Motor.Settings.Servo.SpringSettings.Stiffness = 10000;
            divingBoardJoint2.Motor.Settings.Servo.SpringSettings.Damping = 0;

            Space.Add(divingBoard2);
            Space.Add(divingBoardJoint2);

            //Add a seesaw for people to jump on.
            Box seesawBase = new Box(new Vector3(-7, .45f, 52), 1, .9f, .3f);
            Box seesawPlank = new Box(seesawBase.Position + new Vector3(0, .65f, 0), 1.2f, .2f, 6, 3);
            RevoluteJoint seesawJoint = new RevoluteJoint(seesawBase, seesawPlank, seesawPlank.Position, Vector3.Right);
            Space.Add(seesawJoint);
            Space.Add(seesawBase);
            Space.Add(seesawPlank);

            Space.Add(new Box(seesawPlank.Position + new Vector3(0, 1.3f, 2), 1, 1, 1, 5));


            //Add in some boxes to bump and jump on.
            int numColumns = 3;
            int numRows = 3;
            int numHigh = 3;
            float xSpacing = 1.01f;
            float ySpacing = 1.01f;
            float zSpacing = 1.01f;
            for (int i = 0; i < numRows; i++)
                for (int j = 0; j < numColumns; j++)
                    for (int k = 0; k < numHigh; k++)
                    {
                        Space.Add(new Box(new Vector3(
                                                 5 + xSpacing * i - (numRows - 1) * xSpacing / 2f,
                                                 1.58f + k * (ySpacing),
                                                 45 + zSpacing * j - (numColumns - 1) * zSpacing / 2f),
                                             .5f, .5f, .5f, 5));
                    }



            //Add a log to roll!
            //Make it a compound so some boxes can be added to let the player know it's actually spinning.
            CompoundBody log = new CompoundBody(new List<CompoundShapeEntry>()
            {
                new CompoundShapeEntry(new CylinderShape(4, 1.8f), Quaternion.CreateFromAxisAngle(Vector3.Forward, MathHelper.PiOver2), 20),
                new CompoundShapeEntry(new BoxShape(.5f, .5f, 3.7f),  new Vector3(1.75f, 0,0), 0),
                new CompoundShapeEntry(new BoxShape(.5f, 3.7f, .5f), new Vector3(1.75f, 0,0), 0),
                new CompoundShapeEntry(new BoxShape(.5f, .5f, 3.7f),  new Vector3(-1.75f, 0,0), 0),
                new CompoundShapeEntry(new BoxShape(.5f, 3.7f, .5f), new Vector3(-1.75f, 0,0), 0)
            }, 50);
            log.Position = new Vector3(-14.5f, 10, 41);
            log.AngularDamping = 0;


            RevoluteJoint logJointA = new RevoluteJoint(divingBoardBase, log, log.Position + new Vector3(2.5f, 0, 0), Vector3.Right);
            RevoluteJoint logJointB = new RevoluteJoint(endPlatform, log, log.Position + new Vector3(-2.5f, 0, 0), Vector3.Right);
            Space.Add(logJointA);
            Space.Add(logJointB);

            Space.Add(log);


            //Put some planks to stand on that show various slopes.
            int numPads = 10;
            for (int i = 0; i < numPads; i++)
            {
                offset = new Vector3(0, 0, 4);
                Box a = new Box(new Vector3(i * 1.5f + 3.5f, 10, 24), 1.5f, 1, 4);
                Box b = new Box(new Vector3(i * 1.5f + 3.5f, 10, 24), 1.5f, 1, 4);
                float angle = -i * MathHelper.PiOver2 / numPads;
                b.Orientation = Quaternion.CreateFromAxisAngle(Vector3.Right, angle);
                b.Position += offset * .5f + Quaternion.Transform(offset * .5f, b.Orientation);

                Space.Add(a);
                Space.Add(b);
            }

        }
        /// <summary>
        /// Constructs a new demo.
        /// </summary>
        /// <param name="game">Game owning this demo.</param>
        public PathFollowingDemo(DemosGame game)
            : base(game)
        {
            Entity movingEntity;
            //The moving entity can be either kinematic or dynamic; the EntityMover/Rotator works for either.
            movingEntity = new Box(new Vector3(-10, 0, -10), 3, 1, 1);

            //We're going to use a speed-controlled curve that wraps another curve.
            //This is the internal curve.
            //Speed-controlled curves let the user specify speeds at which an evaluator
            //will move along the curve.  Non-speed controlled curves can move at variable
            //rates based on the interpolation.
            var wrappedPositionCurve = new CardinalSpline3D();

            //Since the endpoints don't overlap, just reverse direction when they're hit.
            //The default is wrapping around; if there's a distance between the starting
            //and ending endpoints, the entity will jump very quickly (smashing anything in the way).
            wrappedPositionCurve.PreLoop = CurveEndpointBehavior.Mirror;
            wrappedPositionCurve.PostLoop = CurveEndpointBehavior.Mirror;

            //Start the curve up above the blocks.
            //There's two control points because the very first and very last control points
            //aren't actually reached by the curve in a CardinalSpline3D; they are used
            //to define the tangents on the interior points.
            wrappedPositionCurve.ControlPoints.Add(-1, new Vector3(0, 30, 0));
            wrappedPositionCurve.ControlPoints.Add(0f, new Vector3(0, 20, 0));
            //Add a bunch of random control points to the curve.
            var random = new Random();
            for (int i = 1; i <= 10; i++)
            {
                wrappedPositionCurve.ControlPoints.Add(i, new Vector3(
                                                              (float) random.NextDouble() * 20 - 10,
                                                              (float) random.NextDouble() * 12,
                                                              (float) random.NextDouble() * 20 - 10));
            }

            positionPath = wrappedPositionCurve;

            //There's also a constant speed and variable speed curve type that can be used.
            //Try the following instead to move the entity at a constant rate:
            //positionPath = new ConstantLinearSpeedCurve(5, wrappedPositionCurve);

            var slerpCurve = new QuaternionSlerpCurve();
            slerpCurve.ControlPoints.Add(0, Quaternion.Identity);
            slerpCurve.ControlPoints.Add(1, Quaternion.CreateFromAxisAngle(Vector3.Up, MathHelper.PiOver2));
            slerpCurve.ControlPoints.Add(2, Quaternion.CreateFromAxisAngle(Vector3.Up, MathHelper.Pi));
            slerpCurve.ControlPoints.Add(3, Quaternion.CreateFromAxisAngle(Vector3.Up, 3 * MathHelper.PiOver2));
            slerpCurve.ControlPoints.Add(4, Quaternion.Identity);

            slerpCurve.PostLoop = CurveEndpointBehavior.Mirror;
            orientationPath = slerpCurve;


            mover = new EntityMover(movingEntity);
            //Offset the place that the mover tries to reach a little.
            //Now, when the entity spins, it acts more like a hammer swing than a saw.
            mover.LocalOffset = new Vector3(3, 0, 0);
            rotator = new EntityRotator(movingEntity);

            //Add the entity and movers to the space.
            Space.Add(movingEntity);
            Space.Add(mover);
            Space.Add(rotator);

            //Add some extra stuff to the space.
            Space.Add(new Box(new Vector3(0, -5, 0), 25, 10, 25));

            int numColumns = 7;
            int numRows = 7;
            int numHigh = 3;
            float xSpacing = 2.09f;
            float ySpacing = 2.08f;
            float zSpacing = 2.09f;
            for (int i = 0; i < numRows; i++)
                for (int j = 0; j < numColumns; j++)
                    for (int k = 0; k < numHigh; k++)
                    {
                        Space.Add(new Box(new Vector3(
                                              xSpacing * i - (numRows - 1) * xSpacing / 2f,
                                              1.58f + k * (ySpacing),
                                              2 + zSpacing * j - (numColumns - 1) * zSpacing / 2f),
                                          2, 2, 2, 10));
                    }

            game.Camera.Position = new Vector3(0, 5, 30);
        }
Beispiel #3
0
        protected override void Start(SystemRegistry registry)
        {
            if (SkipCinematicCamera)
            {
                SkipCinematicCamera = false;
                Enabled             = false;
                GameObject.RemoveComponent(this);
                return;
            }

            _input = registry.GetSystem <InputSystem>();
            GameObjectQuerySystem goqs = registry.GetSystem <GameObjectQuerySystem>();

            if (ExtraDisabledGameObjectNames != null)
            {
                _extraDisabledGameObjects = ExtraDisabledGameObjectNames.Select(name => goqs.FindByName(name)).ToArray();
            }
            else
            {
                _extraDisabledGameObjects = Array.Empty <GameObject>();
            }
            foreach (var go in _extraDisabledGameObjects)
            {
                go.Enabled = false;
            }

            GameObject ball = goqs.FindByName(BallName);

            _cameraGO = goqs.FindByName(PlayerCameraName);
            BallController bc = _cameraGO.GetComponent <BallController>();

            bc.Enabled = false;
            bc.GetEffectiveCameraTransform(
                ball.Transform.Position,
                registry.GetSystem <PhysicsSystem>().Space.ForceUpdater.Gravity,
                out _finalPosition,
                out _finalRotation);

            GameObject waypointParentGo = goqs.FindByName(WaypointParentName);
            var        wps = waypointParentGo.Transform.Children.Select(t => t.GameObject.GetComponent <CinematicCameraWaypoint>())
                             .OrderBy(ccw => ccw.Time).Select(ccw => ccw.GetWaypointInfo());

            _initialPosition = Transform.Position;
            _initialRotation = Transform.Rotation;
            float lastWaypointTime = wps.Last().Time;

            if (RestAtCameraPositionTime != -1f && lastWaypointTime > RestAtCameraPositionTime)
            {
                throw new InvalidOperationException(
                          "Cannot rest at camera at time " + RestAtCameraPositionTime + ". The last waypoint has time " + lastWaypointTime);
            }
            else if (RestAtCameraPositionTime == -1f)
            {
                _endTime = lastWaypointTime;
            }
            else
            {
                _endTime = RestAtCameraPositionTime;
            }

            WaypointInfo cameraRestWP = new WaypointInfo(RestAtCameraPositionTime, _finalPosition, _finalRotation);

            _waypoints = wps.Append(cameraRestWP).ToArray();

            while (_waypoints[_currentWaypointIndex].Time == 0)
            {
                _initialPosition       = _waypoints[_currentWaypointIndex].Position;
                _initialRotation       = _waypoints[_currentWaypointIndex].Rotation;
                _currentWaypointIndex += 1;
            }

            _positionCurve          = new CardinalSpline3D();
            _positionCurve.Tension  = 0.1f;
            _positionCurve.PreLoop  = CurveEndpointBehavior.Clamp;
            _positionCurve.PostLoop = CurveEndpointBehavior.Clamp;

            _lookDirCurve          = new QuaternionSlerpCurve();
            _lookDirCurve.PreLoop  = CurveEndpointBehavior.Clamp;
            _lookDirCurve.PostLoop = CurveEndpointBehavior.Clamp;

            if (_waypoints.Any())
            {
                WaypointInfo firstWP = _waypoints.First();
                _positionCurve.ControlPoints.Add(firstWP.Time, firstWP.Position);
                _lookDirCurve.ControlPoints.Add(firstWP.Time, firstWP.Rotation);
            }
            foreach (var waypoint in _waypoints)
            {
                _positionCurve.ControlPoints.Add(waypoint.Time, waypoint.Position);
                _lookDirCurve.ControlPoints.Add(waypoint.Time, waypoint.Rotation);
            }

            if (_waypoints.Any())
            {
                WaypointInfo lastWP = _waypoints.Last();
                _positionCurve.ControlPoints.Add(lastWP.Time, lastWP.Position);
                _lookDirCurve.ControlPoints.Add(lastWP.Time, lastWP.Rotation);
            }
        }
Beispiel #4
0
        protected override void InitializeSpace()
        {
            Entity movingEntity;

            //The moving entity can be either kinematic or dynamic; the EntityMover/Rotator works for either.
            movingEntity = new Box(new Vector3(-10, 0, -10), 3, 1, 1);

            //We're going to use a speed-controlled curve that wraps another curve.
            //This is the internal curve.
            //Speed-controlled curves let the user specify speeds at which an evaluator
            //will move along the curve.  Non-speed controlled curves can move at variable
            //rates based on the interpolation.
            var wrappedPositionCurve = new CardinalSpline3D();

            //Since the endpoints don't overlap, just reverse direction when they're hit.
            //The default is wrapping around; if there's a distance between the starting
            //and ending endpoints, the entity will jump very quickly (smashing anything in the way).
            wrappedPositionCurve.PreLoop  = CurveEndpointBehavior.Mirror;
            wrappedPositionCurve.PostLoop = CurveEndpointBehavior.Mirror;

            //Start the curve up above the blocks.
            //There's two control points because the very first and very last control points
            //aren't actually reached by the curve in a CardinalSpline3D; they are used
            //to define the tangents on the interior points.
            wrappedPositionCurve.ControlPoints.Add(-1, new Vector3(0, 30, 0));
            wrappedPositionCurve.ControlPoints.Add(0, new Vector3(0, 20, 0));
            //Add a bunch of random control points to the curve.
            var random = new Random(0);

            for (int i = 1; i <= 10; i++)
            {
                wrappedPositionCurve.ControlPoints.Add(i, new Vector3(
                                                           (Fix64)random.NextDouble() * 20 - 10,
                                                           (Fix64)random.NextDouble() * 12,
                                                           (Fix64)random.NextDouble() * 20 - 10));
            }

            positionPath = wrappedPositionCurve;

            //There's also a constant speed and variable speed curve type that can be used.
            //Try the following instead to move the entity at a constant rate:
            //positionPath = new ConstantLinearSpeedCurve(5, wrappedPositionCurve);

            var slerpCurve = new QuaternionSlerpCurve();

            slerpCurve.ControlPoints.Add(0, Quaternion.Identity);
            slerpCurve.ControlPoints.Add(1, Quaternion.CreateFromAxisAngle(Vector3.Up, MathHelper.PiOver2));
            slerpCurve.ControlPoints.Add(2, Quaternion.CreateFromAxisAngle(Vector3.Up, MathHelper.Pi));
            slerpCurve.ControlPoints.Add(3, Quaternion.CreateFromAxisAngle(Vector3.Up, 3 * MathHelper.PiOver2));
            slerpCurve.ControlPoints.Add(4, Quaternion.Identity);

            slerpCurve.PostLoop = CurveEndpointBehavior.Mirror;
            orientationPath     = slerpCurve;


            mover = new EntityMover(movingEntity);
            //Offset the place that the mover tries to reach a little.
            //Now, when the entity spins, it acts more like a hammer swing than a saw.
            mover.LocalOffset = new Vector3(3, 0, 0);
            rotator           = new EntityRotator(movingEntity);

            //Add the entity and movers to the space.
            Space.Add(movingEntity);
            Space.Add(mover);
            Space.Add(rotator);

            //Add some extra stuff to the space.
            Space.Add(new Box(new Vector3(0, -5, 0), 25, 10, 25));

            int   numColumns = 7;
            int   numRows    = 7;
            int   numHigh    = 3;
            Fix64 xSpacing   = 2.09m;
            Fix64 ySpacing   = 2.08m;
            Fix64 zSpacing   = 2.09m;

            for (int i = 0; i < numRows; i++)
            {
                for (int j = 0; j < numColumns; j++)
                {
                    for (int k = 0; k < numHigh; k++)
                    {
                        Space.Add(new Box(new Vector3(
                                              xSpacing * i - (numRows - 1) * xSpacing / 2,
                                              1.58m + k * (ySpacing),
                                              2 + zSpacing * j - (numColumns - 1) * zSpacing / 2),
                                          2, 2, 2, 10));
                    }
                }
            }
        }
        /// <summary>
        /// Constructs a new demo.
        /// </summary>
        /// <param name="game">Game owning this demo.</param>
        public CharacterPlaygroundDemo(DemosGame game)
            : base(game)
        {
            game.Camera.Position      = new Vector3(-10, 7, 5);
            game.Camera.ViewDirection = new Vector3(0, 0, 1);
            //Since this is the character playground, turn on the character by default.
            character.Activate();
            //Having the character body visible would be a bit distracting.
            character.CharacterController.Body.Tag = "noDisplayObject";

            //Load in mesh data for the environment.
            Vector3[] staticTriangleVertices;
            int[]     staticTriangleIndices;


            var playgroundModel = game.Content.Load <Model>("CharacterControllerTestTerrain");

            //This is a little convenience method used to extract vertices and indices from a model.
            //It doesn't do anything special; any approach that gets valid vertices and indices will work.
            ModelDataExtractor.GetVerticesAndIndicesFromModel(playgroundModel, out staticTriangleVertices, out staticTriangleIndices);
            var staticMesh = new StaticMesh(staticTriangleVertices, staticTriangleIndices, new AffineTransform(new Vector3(0.01f, 0.01f, 0.01f), Quaternion.Identity, new Vector3(0, 0, 0)));

            staticMesh.Sidedness = TriangleSidedness.Counterclockwise;

            Space.Add(staticMesh);
            game.ModelDrawer.Add(staticMesh);



            //Add a spinning blade for the character to ram itself into.
            var fanBase  = new Cylinder(new Vector3(-13, .5f, 50), 1.1f, 1);
            var fanBlade = new Box(fanBase.Position + new Vector3(0, .8f, 0), 5, .1f, 1f, 5);
            var fanJoint = new RevoluteJoint(fanBase, fanBlade, (fanBase.Position + fanBlade.Position) * .5f, Vector3.Up);

            fanJoint.Motor.IsActive = true;
            fanJoint.Motor.Settings.VelocityMotor.GoalVelocity = 30;
            fanJoint.Motor.Settings.MaximumForce = 300;
            Space.Add(fanBase);
            Space.Add(fanBlade);
            Space.Add(fanJoint);

            //Add a bridge connecting the two towers.
            Vector3 startPosition = new Vector3(-19.3f, 10.5f - .25f, 23 - .85f);
            var     startPlatform = new Box(startPosition - new Vector3(0, 0, 2.2f), 4, .5f, 6);

            Space.Add(startPlatform);
            Vector3 offset       = new Vector3(0, 0, 1.7f);
            Box     previousLink = startPlatform;
            Vector3 position     = new Vector3();

            for (int i = 1; i <= 7; i++)
            {
                position = startPosition + offset * i;
                Box link = new Box(position, 3, .3f, 1.5f, 50);
                Space.Add(link);
                Space.Add(new RevoluteJoint(previousLink, link, position - offset * .5f, Vector3.Right));

                previousLink = link;
            }
            var endPlatform = new Box(position - new Vector3(0, 0, -3.8f), 4, .5f, 6);

            Space.Add(endPlatform);

            Space.Add(new RevoluteJoint(previousLink, endPlatform, position + offset * .5f, Vector3.Right));


            //Add in a floating platform controlled by a curve to serve as an elevator.
            Entity movingEntity = new Box(new Vector3(-10, 0, -10), 3, 1, 3);

            var positionCurve = new CardinalSpline3D();

            positionCurve.PreLoop  = CurveEndpointBehavior.Mirror;
            positionCurve.PostLoop = CurveEndpointBehavior.Mirror;

            positionCurve.ControlPoints.Add(-1, new Vector3(-19.3f, 0, 43));
            positionCurve.ControlPoints.Add(0, new Vector3(-19.3f, 0, 43));
            positionCurve.ControlPoints.Add(2, new Vector3(-19.3f, 0, 43));
            positionCurve.ControlPoints.Add(3, new Vector3(-19.3f, 0, 43));
            positionCurve.ControlPoints.Add(4, new Vector3(-19.3f, 5, 43));
            positionCurve.ControlPoints.Add(5f, new Vector3(-19.3f, 10, 43));
            positionCurve.ControlPoints.Add(6f, new Vector3(-19.3f, 10, 43));
            positionCurve.ControlPoints.Add(8f, new Vector3(-19.3f, 10, 43));
            positionCurve.ControlPoints.Add(9f, new Vector3(-19.3f, 10, 43));

            elevatorMover = new EntityMover(movingEntity);
            Space.Add(elevatorMover);
            Space.Add(movingEntity);

            elevatorPath = positionCurve;

            //Add in another floating platform controlled by a curve for horizontal transport.
            movingEntity = new Box(new Vector3(-10, 0, -10), 2.5f, .5f, 2.5f);

            var platformCurve = new LinearInterpolationCurve3D();

            platformCurve.PreLoop  = CurveEndpointBehavior.Mirror;
            platformCurve.PostLoop = CurveEndpointBehavior.Mirror;

            platformCurve.ControlPoints.Add(0, new Vector3(-1.75f, 10, 21.5f));
            platformCurve.ControlPoints.Add(2, new Vector3(-1.75f, 10, 21.5f));
            platformCurve.ControlPoints.Add(5, new Vector3(-1.75f, 10, 15.5f));
            platformCurve.ControlPoints.Add(10, new Vector3(-19.3f, 10, 15.5f));
            platformCurve.ControlPoints.Add(12, new Vector3(-19.3f, 10, 15.5f));
            platformCurve.ControlPoints.Add(15, new Vector3(-25, 10, 15.5f));
            platformCurve.ControlPoints.Add(22, new Vector3(-25, 10, 38));
            platformCurve.ControlPoints.Add(23, new Vector3(-22.75f, 10, 38));
            platformCurve.ControlPoints.Add(25, new Vector3(-22.75f, 10, 38));

            //Make it spin too.  That'll be fun.  Or something.
            var platformRotationCurve = new QuaternionSlerpCurve();

            platformRotationCurve.PreLoop  = CurveEndpointBehavior.Mirror;
            platformRotationCurve.PostLoop = CurveEndpointBehavior.Mirror;
            platformRotationCurve.ControlPoints.Add(0, Quaternion.Identity);
            platformRotationCurve.ControlPoints.Add(15, Quaternion.Identity);
            platformRotationCurve.ControlPoints.Add(22, Quaternion.CreateFromAxisAngle(Vector3.Up, MathHelper.PiOver2));
            platformRotationCurve.ControlPoints.Add(25, Quaternion.CreateFromAxisAngle(Vector3.Up, MathHelper.PiOver2));

            platformMover   = new EntityMover(movingEntity);
            platformRotator = new EntityRotator(movingEntity);
            Space.Add(platformMover);
            Space.Add(platformRotator);
            Space.Add(movingEntity);

            platformPath            = platformCurve;
            platformOrientationPath = platformRotationCurve;

            //Add in a diving board.

            Box divingBoardBase  = new Box(new Vector3(-9, 10, 39.3f), 5, 1, 3);
            Box divingBoard      = new Box(divingBoardBase.Position + new Vector3(-2, 0, 3.5f), 1, .3f, 3, 5);
            var divingBoardJoint = new RevoluteJoint(divingBoardBase, divingBoard, divingBoard.Position + new Vector3(0, 0, -1.5f), Vector3.Right);

            divingBoardJoint.Motor.IsActive            = true;
            divingBoardJoint.Motor.Settings.Mode       = MotorMode.Servomechanism;
            divingBoardJoint.Motor.Settings.Servo.Goal = 0;
            divingBoardJoint.Motor.Settings.Servo.SpringSettings.Stiffness = 5000;
            divingBoardJoint.Motor.Settings.Servo.SpringSettings.Damping   = 0;

            Space.Add(divingBoardBase);
            Space.Add(divingBoard);
            Space.Add(divingBoardJoint);


            //Add a second diving board for comparison.

            Box divingBoard2      = new Box(divingBoardBase.Position + new Vector3(2, 0, 5f), 1, .3f, 6, 5);
            var divingBoardJoint2 = new RevoluteJoint(divingBoardBase, divingBoard2, divingBoard2.Position + new Vector3(0, 0, -3), Vector3.Right);

            divingBoardJoint2.Motor.IsActive            = true;
            divingBoardJoint2.Motor.Settings.Mode       = MotorMode.Servomechanism;
            divingBoardJoint2.Motor.Settings.Servo.Goal = 0;
            divingBoardJoint2.Motor.Settings.Servo.SpringSettings.Stiffness = 10000;
            divingBoardJoint2.Motor.Settings.Servo.SpringSettings.Damping   = 0;

            Space.Add(divingBoard2);
            Space.Add(divingBoardJoint2);

            //Add a seesaw for people to jump on.
            Box           seesawBase  = new Box(new Vector3(-7, .45f, 52), 1, .9f, .3f);
            Box           seesawPlank = new Box(seesawBase.Position + new Vector3(0, .65f, 0), 1.2f, .2f, 6, 3);
            RevoluteJoint seesawJoint = new RevoluteJoint(seesawBase, seesawPlank, seesawPlank.Position, Vector3.Right);

            Space.Add(seesawJoint);
            Space.Add(seesawBase);
            Space.Add(seesawPlank);

            Space.Add(new Box(seesawPlank.Position + new Vector3(0, 1.3f, 2), 1, 1, 1, 5));


            //Add in some boxes to bump and jump on.
            int   numColumns = 3;
            int   numRows    = 3;
            int   numHigh    = 3;
            float xSpacing   = 1.01f;
            float ySpacing   = 1.01f;
            float zSpacing   = 1.01f;

            for (int i = 0; i < numRows; i++)
            {
                for (int j = 0; j < numColumns; j++)
                {
                    for (int k = 0; k < numHigh; k++)
                    {
                        Space.Add(new Box(new Vector3(
                                              5 + xSpacing * i - (numRows - 1) * xSpacing / 2f,
                                              1.58f + k * (ySpacing),
                                              45 + zSpacing * j - (numColumns - 1) * zSpacing / 2f),
                                          .5f, .5f, .5f, 5));
                    }
                }
            }



            //Add a log to roll!
            //Make it a compound so some boxes can be added to let the player know it's actually spinning.
            CompoundBody log = new CompoundBody(new List <CompoundShapeEntry>()
            {
                new CompoundShapeEntry(new CylinderShape(4, 1.8f), Quaternion.CreateFromAxisAngle(Vector3.Forward, MathHelper.PiOver2), 20),
                new CompoundShapeEntry(new BoxShape(.5f, .5f, 3.7f), new Vector3(1.75f, 0, 0), 0),
                new CompoundShapeEntry(new BoxShape(.5f, 3.7f, .5f), new Vector3(1.75f, 0, 0), 0),
                new CompoundShapeEntry(new BoxShape(.5f, .5f, 3.7f), new Vector3(-1.75f, 0, 0), 0),
                new CompoundShapeEntry(new BoxShape(.5f, 3.7f, .5f), new Vector3(-1.75f, 0, 0), 0)
            }, 50);

            log.Position       = new Vector3(-14.5f, 10, 41);
            log.AngularDamping = 0;


            RevoluteJoint logJointA = new RevoluteJoint(divingBoardBase, log, log.Position + new Vector3(2.5f, 0, 0), Vector3.Right);
            RevoluteJoint logJointB = new RevoluteJoint(endPlatform, log, log.Position + new Vector3(-2.5f, 0, 0), Vector3.Right);

            Space.Add(logJointA);
            Space.Add(logJointB);

            Space.Add(log);


            //Put some planks to stand on that show various slopes.
            int numPads = 10;

            for (int i = 0; i < numPads; i++)
            {
                offset = new Vector3(0, 0, 4);
                Box   a     = new Box(new Vector3(i * 1.5f + 3.5f, 10, 24), 1.5f, 1, 4);
                Box   b     = new Box(new Vector3(i * 1.5f + 3.5f, 10, 24), 1.5f, 1, 4);
                float angle = -i * MathHelper.PiOver2 / numPads;
                b.Orientation = Quaternion.CreateFromAxisAngle(Vector3.Right, angle);
                b.Position   += offset * .5f + Quaternion.Transform(offset * .5f, b.Orientation);

                Space.Add(a);
                Space.Add(b);
            }
        }