private void SetCameraPose()
        {
            var vehiclePose = _vehicle.Pose;

            if (_useSpectatorView)
            {
                // Spectator Mode:
                // Camera is looking at the car from a fixed location in the level.
                Vector3F position = new Vector3F(10, 8, 10);
                Vector3F target   = vehiclePose.Position;
                Vector3F up       = Vector3F.UnitY;

                // Set the new camera view matrix. (Setting the View matrix changes the Pose.
                // The pose is simply the inverse of the view matrix).
                CameraNode.View = Matrix44F.CreateLookAt(position, target, up);
            }
            else
            {
                // Player Camera:
                // Camera moves with the car. The look direction can be changed by moving the mouse.
                Matrix33F yaw         = Matrix33F.CreateRotationY(_yaw);
                Matrix33F pitch       = Matrix33F.CreateRotationX(_pitch);
                Matrix33F orientation = vehiclePose.Orientation * yaw * pitch;
                Vector3F  forward     = orientation * -Vector3F.UnitZ;
                Vector3F  up          = Vector3F.UnitY;
                Vector3F  position    = vehiclePose.Position - 10 * forward + 5 * up;
                Vector3F  target      = vehiclePose.Position + 1 * up;

                CameraNode.View = Matrix44F.CreateLookAt(position, target, up);
            }
        }
Esempio n. 2
0
        public override void Update(GameTime gameTime)
        {
            // Move the directional light in a circle.
            float deltaTimeF = (float)gameTime.ElapsedGameTime.TotalSeconds;

            _lightAngle += 0.3f * deltaTimeF;
            var position = QuaternionF.CreateRotationY(_lightAngle).Rotate(new Vector3F(6, 6, 0));

            // Make the light look at the world space origin.
            var lightTarget  = Vector3F.Zero;
            var lookAtMatrix = Matrix44F.CreateLookAt(position, lightTarget, Vector3F.Up);

            // A look-at matrix is the inverse of a normal world or pose matrix.
            _mainDirectionalLightNode.PoseWorld =
                new Pose(lookAtMatrix.Translation, lookAtMatrix.Minor).Inverse;

            // Compute shadow matrix for the new light direction.
            var lightRayDirection = (lightTarget - position);

            _shadowMatrix = ProjectedShadowRenderer.CreateShadowMatrix(
                new Plane(new Vector3F(0, 1, 0), 0.01f), new Vector4F(-lightRayDirection, 0));

            // Update the scene - this must be called once per frame.
            _scene.Update(gameTime.ElapsedGameTime);

            base.Update(gameTime);
        }
Esempio n. 3
0
        public void GetBoundsOrthographic()
        {
            // Get bounds of AABB in clip space. (Used in OcclusionCulling.fx.)
            Vector3F cameraPosition = new Vector3F(100, -200, 345);
            Vector3F cameraForward  = new Vector3F(1, 2, 3).Normalized;
            Vector3F cameraUp       = new Vector3F(-1, 0.5f, -2).Normalized;

            Matrix44F view     = Matrix44F.CreateLookAt(cameraPosition, cameraPosition + cameraForward, cameraUp);
            Matrix44F proj     = Matrix44F.CreateOrthographic(16, 9, -10, 100);
            Matrix44F viewProj = proj * view;

            Vector3F center = new Vector3F();
            Vector3F halfExtent = new Vector3F();
            Aabb     aabb = new Aabb(center - halfExtent, center + halfExtent);
            Aabb     aabb0, aabb1;

            GetBoundsOrtho(aabb, viewProj, out aabb0);
            GetBoundsOrthoSmart(aabb, viewProj, out aabb1);
            Assert.IsTrue(Aabb.AreNumericallyEqual(aabb0, aabb1));

            center     = new Vector3F(-9, 20, -110);
            halfExtent = new Vector3F(5, 2, 10);
            aabb       = new Aabb(center - halfExtent, center + halfExtent);
            GetBoundsOrtho(aabb, viewProj, out aabb0);
            GetBoundsOrthoSmart(aabb, viewProj, out aabb1);
            Assert.IsTrue(Aabb.AreNumericallyEqual(aabb0, aabb1));
        }
Esempio n. 4
0
        public MyGraphicsScreen(IGraphicsService graphicsService)
            : base(graphicsService)
        {
            _meshRenderer = new MeshRenderer();

            var contentManager = ServiceLocator.Current.GetInstance <ContentManager>();
            var spriteFont     = contentManager.Load <SpriteFont>("SpriteFont1");

            _debugRenderer = new DebugRenderer(graphicsService, spriteFont);

            Scene = new Scene();

            // Add a camera with a perspective projection.
            var projection = new PerspectiveProjection();

            projection.SetFieldOfView(
                ConstantsF.PiOver4,
                graphicsService.GraphicsDevice.Viewport.AspectRatio,
                0.1f,
                100.0f);
            CameraNode = new CameraNode(new Camera(projection))
            {
                Name      = "CameraPerspective",
                PoseWorld = Pose.FromMatrix(Matrix44F.CreateLookAt(new Vector3F(10, 5, 10), new Vector3F(0, 1, 0), new Vector3F(0, 1, 0)).Inverse),
            };
            Scene.Children.Add(CameraNode);
        }
Esempio n. 5
0
        public WpRagdollSample(Microsoft.Xna.Framework.Game game)
            : base(game)
        {
            GraphicsScreen.ClearBackground = true;
            GraphicsScreen.BackgroundColor = Color.CornflowerBlue;

            // Set a fixed camera.
            var projection = new PerspectiveProjection();

            projection.SetFieldOfView(
                MathHelper.ToRadians(30),
                GraphicsService.GraphicsDevice.Viewport.AspectRatio,
                1f,
                100.0f);
            Vector3F cameraTarget   = new Vector3F(0, 1, 0);
            Vector3F cameraPosition = new Vector3F(0, 12, 0);
            Vector3F cameraUpVector = new Vector3F(0, 0, -1);

            GraphicsScreen.CameraNode = new CameraNode(new Camera(projection))
            {
                View = Matrix44F.CreateLookAt(cameraPosition, cameraTarget, cameraUpVector),
            };

            InitializePhysics();
        }
Esempio n. 6
0
        /// <summary>
        /// Changes the camera based on the tilt of the Windows Phone.
        /// </summary>
        /// <param name="deltaTime">The elapsed time since the last call of <see cref="Update"/>.</param>
        private void TiltCamera(TimeSpan deltaTime)
        {
            // (Note: We use DigitalRune Mathematics instead of the XNA math types - mainly because
            // DigitalRune Mathematics provides a 3x3 matrix to describe rotations and the QuaternionF
            // provides a nice helper function to create a rotation from two given vectors.
            //
            // Please note that DigitalRune Mathematics uses column vectors whereas XNA uses row vectors.
            // When using Vector3F and Matrix33F we need to multiple them in this order: v' = M * v.
            // When using Vector3 and Matrix we need to multiple them in this order: v' = v * M.)

            // Get accelerometer value transformed into world space.
            Vector3F accelerometerVector = new Vector3F(
                -InputService.AccelerometerValue.Y,
                InputService.AccelerometerValue.Z,
                -InputService.AccelerometerValue.X);

            // Run the accelerometer signal through a low-pass filter to remove noise and jitter.
            Vector3F currentGravityDirection = _lowPassFilter.Filter(accelerometerVector, (float)deltaTime.TotalSeconds);

            Matrix33F cameraTilt;

            if (!currentGravityDirection.IsNumericallyZero)
            {
                // We have some valid sensor readings.
                // Let's compute the tilt of the camera. When the phone is lying flat on a table the camera
                // looks down onto the scene. When the phone is tilted we want to rotate the position of
                // the camera. QuaternionF contains a useful helper function that creates a rotation from
                // two given directions - exactly what we need here.
                cameraTilt = QuaternionF.CreateRotation(currentGravityDirection, new Vector3F(0, -1, 0)).ToRotationMatrix33();
            }
            else
            {
                // Current acceleration is nearly (0, 0, 0). We cannot infer any useful direction from
                // this vector. Reset the camera tilt.
                cameraTilt = Matrix33F.Identity;
            }

            // ----- Set up the view matrix (= the position and orientation of the camera).
            Vector3F cameraTarget   = new Vector3F(0, 2, 0);               // That's were the camera is looking at.
            Vector3F cameraPosition = new Vector3F(0, _cameraDistance, 0); // That's the default position of the camera.
            Vector3F cameraUpVector = new Vector3F(0, 0, -1);              // That's the up-vector of the camera.

            // Apply the camera tilt to the position and orientation.
            cameraPosition = cameraTilt * cameraPosition;
            cameraUpVector = cameraTilt * cameraUpVector;

            // Keep the camera above the ground.
            cameraPosition.Y = Math.Max(0.5f, cameraPosition.Y);

            // Create the view matrix from these points.
            GraphicsScreen.CameraNode.View = Matrix44F.CreateLookAt(cameraPosition, cameraTarget, cameraUpVector);

            // Save the camera position because it is required for hit-testing in DragBodies().
            _cameraPosition = cameraPosition;
        }
Esempio n. 7
0
        public override void Update(GameTime gameTime)
        {
            // This sample clears the debug renderer each frame.
            _graphicsScreen.DebugRenderer.Clear();

            // A second camera for player B.
            var totalTime = (float)gameTime.TotalGameTime.TotalSeconds;
            var position  = Matrix33F.CreateRotationY(totalTime * 0.1f) * new Vector3F(4, 2, 4);

            _cameraNodeB.View = Matrix44F.CreateLookAt(position, new Vector3F(0, 0, 0), new Vector3F(0, 1, 0));
        }
        /// <summary>
        /// Positions the camera so that it sees the whole model and is not to near or to far away.
        /// </summary>
        private void ResetCameraPose()
        {
            if (ModelNode == null && Document.Model == null)
            {
                var lookAtMatrix = Matrix44F.CreateLookAt(new Vector3F(10, 10, 10), new Vector3F(0, 1, 0), new Vector3F(0, 1, 0));
                _cameraNode.PoseWorld = Pose.FromMatrix(lookAtMatrix).Inverse;
                ModelCenter           = new Vector3F(0, 1, 0);
                MoveSpeed             = 5;
                ZoomSpeed             = MoveSpeed / 10;
                return;
            }

            // Get combined AABB of scene nodes.
            Aabb aabb = new Aabb();

            if (ModelNode != null)
            {
                foreach (var meshNode in ModelNode.GetSubtree().OfType <MeshNode>())
                {
                    aabb.Grow(meshNode.Aabb);
                }
            }
            else
            {
                Matrix[] boneTransforms = new Matrix[Document.Model.Bones.Count];
                Document.Model.CopyAbsoluteBoneTransformsTo(boneTransforms);
                foreach (var mesh in Document.Model.Meshes)
                {
                    var sphere = mesh.BoundingSphere;
                    sphere = sphere.Transform(boneTransforms[mesh.ParentBone.Index]);
                    var partCenter = (Vector3F)sphere.Center;
                    var partRadius = new Vector3F(sphere.Radius);
                    aabb.Grow(new Aabb(partCenter - partRadius, partCenter + partRadius));
                }
            }

            // Set camera position.
            var center   = aabb.Center;
            var radius   = aabb.Extent.Length / 2;
            var gamma    = _cameraNode.Camera.Projection.FieldOfViewY / 2;
            var distance = Math.Max((float)(radius / Math.Tan(gamma)) * 0.7f, 1);  // * 0.x to move a bit closer otherwise distance is usually to large.

            _cameraNode.PoseWorld = Pose.FromMatrix(
                Matrix44F.CreateLookAt(center + new Vector3F(distance), center, Vector3F.Up)).Inverse;

            // Center for camera orbiting.
            ModelCenter = center;

            // Make navigation speed relative to model size.
            MoveSpeed = Math.Max(radius * 4, 1);
            ZoomSpeed = MoveSpeed / 10;
        }
Esempio n. 9
0
        protected override void OnUpdate(TimeSpan deltaTime)
        {
            // Rotate camera around origin.
            _cameraRotation += 0.1f * (float)deltaTime.TotalSeconds;
            var cameraPosition = Matrix33F.CreateRotationY(_cameraRotation) * new Vector3F(10, 5, 10);
            var targetPosition = new Vector3F(0, 1, 0);
            var up             = new Vector3F(0, 1, 0);
            var viewMatrix     = Matrix44F.CreateLookAt(cameraPosition, targetPosition, up);

            CameraNode.PoseWorld = Pose.FromMatrix(viewMatrix.Inverse);

            Scene.Update(deltaTime);
        }
Esempio n. 10
0
        /// <summary>
        /// Moves and rotates the scene node so that it faces a certain direction (in world space).
        /// </summary>
        /// <param name="node">The scene node.</param>
        /// <param name="position">The new position in world space.</param>
        /// <param name="target">
        /// The target coordinates in world space at which the scene node is "looking".
        /// </param>
        /// <param name="upVector">
        /// The direction that is "up" from the scene node's point of view given in world space. (Does
        /// not need to be normalized.)
        /// </param>
        /// <remarks>
        /// A scene node uses the same coordinate system as the <strong>XNA Framework</strong>:
        /// By default, the positive x-axis points to the right, the positive y-axis points up, and the
        /// positive z-axis points towards the viewer. This method moves the scene node to
        /// <paramref name="position"/> and rotates it so that its local forward direction (0, 0, -1) is
        /// pointing towards <paramref name="target"/>.
        /// </remarks>
        /// <exception cref="ArgumentNullException">
        /// <paramref name="node"/> is <see langword="null"/>.
        /// </exception>
        /// <exception cref="ArgumentException">
        /// <paramref name="position"/> is the same as <paramref name="target"/>.
        /// </exception>
        /// <exception cref="ArgumentException">
        /// <paramref name="upVector"/> is (0, 0, 0).
        /// </exception>
        /// <exception cref="DivideByZeroException">
        /// The camera direction (<paramref name="target"/> - <paramref name="position"/>) is probably
        /// pointing in the same or opposite direction as <paramref name="upVector"/>. (The two vectors
        /// must not be parallel.)
        /// </exception>
        public static void LookAt(this SceneNode node, Vector3F position, Vector3F target, Vector3F upVector)
        {
            if (node == null)
            {
                throw new ArgumentNullException("node");
            }

            Matrix44F   view        = Matrix44F.CreateLookAt(position, target, upVector);
            Matrix44F   viewInverse = view.Inverse;
            QuaternionF orientation = QuaternionF.CreateRotation(viewInverse.Minor);

            node.PoseWorld = new Pose(position, orientation);
        }
Esempio n. 11
0
        public void GetBoundsPerspective()
        {
            // Get bounds of AABB in clip space. (Used in OcclusionCulling.fx.)
            // Note: Z = 0 or negative is handled conservatively. Point (0, 0, 0) is returned.
            Vector3F cameraPosition = new Vector3F(100, -200, 345);
            Vector3F cameraForward  = new Vector3F(1, 2, 3).Normalized;
            Vector3F cameraUp       = new Vector3F(-1, 0.5f, -2).Normalized;

            Matrix44F view     = Matrix44F.CreateLookAt(cameraPosition, cameraPosition + cameraForward, cameraUp);
            Matrix44F proj     = Matrix44F.CreatePerspectiveFieldOfView(MathHelper.ToRadians(90), 16.0f / 9.0f, 1, 100);
            Matrix44F viewProj = proj * view;

            // Empty AABB at center of near plane.
            Vector3F center = cameraPosition + cameraForward;
            Vector3F halfExtent = new Vector3F();
            Aabb     aabb = new Aabb(center - halfExtent, center + halfExtent);
            Aabb     aabb0, aabb1;

            GetBoundsPersp(aabb, viewProj, out aabb0);
            GetBoundsPerspSmart(aabb, viewProj, out aabb1);
            Assert.IsTrue(Aabb.AreNumericallyEqual(aabb0, aabb1));

            // AABB inside frustum.
            center     = view.Inverse.TransformPosition(new Vector3F(2, -3, -50));
            halfExtent = new Vector3F(1, 6, 10);
            aabb       = new Aabb(center - halfExtent, center + halfExtent);
            GetBoundsPersp(aabb, viewProj, out aabb0);
            GetBoundsPerspSmart(aabb, viewProj, out aabb1);
            Assert.IsTrue(Aabb.AreNumericallyEqual(aabb0, aabb1));

            // Behind camera.
            center     = view.Inverse.TransformPosition(new Vector3F(2, -3, 50));
            halfExtent = new Vector3F(1, 6, 10);
            aabb       = new Aabb(center - halfExtent, center + halfExtent);
            GetBoundsPersp(aabb, viewProj, out aabb0);
            GetBoundsPerspSmart(aabb, viewProj, out aabb1);
            Assert.IsTrue(Aabb.AreNumericallyEqual(aabb0, aabb1));

            // Camera inside AABB.
            center     = view.Inverse.TransformPosition(new Vector3F(2, -3, -50));
            halfExtent = new Vector3F(100, 100, 100);
            aabb       = new Aabb(center - halfExtent, center + halfExtent);
            GetBoundsPersp(aabb, viewProj, out aabb0);
            GetBoundsPerspSmart(aabb, viewProj, out aabb1);
            Assert.IsTrue(Aabb.AreNumericallyEqual(aabb0, aabb1));
        }
Esempio n. 12
0
        public void InverseViewTest()
        {
            CameraInstance cameraInstance = new CameraInstance(new Camera(new PerspectiveProjection()));

            Assert.AreEqual(Matrix44F.Identity, cameraInstance.View);
            Assert.AreEqual(Matrix44F.Identity, cameraInstance.ViewInverse);

            Vector3F  position = new Vector3F(1, 2, 3);
            Vector3F  target   = new Vector3F(2, 5, 4);
            Vector3F  upVector = new Vector3F(1, 1, 1);
            Matrix44F view     = Matrix44F.CreateLookAt(position, target, upVector);

            cameraInstance.ViewInverse = view.Inverse;
            Assert.IsTrue(Matrix44F.AreNumericallyEqual(view, cameraInstance.View));
            Assert.IsTrue(Matrix44F.AreNumericallyEqual(view.Inverse, cameraInstance.ViewInverse));
            Assert.IsTrue(Matrix44F.AreNumericallyEqual(view.Inverse, cameraInstance.PoseWorld.ToMatrix44F()));
        }
Esempio n. 13
0
        public WindowsPhoneSample(Microsoft.Xna.Framework.Game game)
            : base(game)
        {
            GraphicsScreen.ClearBackground = true;
            GraphicsScreen.BackgroundColor = Color.CornflowerBlue;

            // Set a fixed camera.
            var projection = new PerspectiveProjection();

            projection.SetFieldOfView(
                MathHelper.ToRadians(30),
                GraphicsService.GraphicsDevice.Viewport.AspectRatio,
                1f,
                1000.0f);
            Vector3F cameraTarget   = new Vector3F(0, 1, 0);
            Vector3F cameraPosition = new Vector3F(0, 12, 0);
            Vector3F cameraUpVector = new Vector3F(0, 0, -1);

            GraphicsScreen.CameraNode = new CameraNode(new Camera(projection))
            {
                View = Matrix44F.CreateLookAt(cameraPosition, cameraTarget, cameraUpVector),
            };

            // We use the accelerometer to control the camera view. The accelerometer registers every
            // little change, but we do not want a shaky camera. We can use a low-pass filter to smooth
            // the sensor signal.
            _lowPassFilter = new LowPassFilter(new Vector3F(0, -1, 0))
            {
                TimeConstant = 0.15f, // Let's try a time constant of 0.15 seconds.
                // When increasing the time constant the camera becomes more stable,
                // but also slower.
            };

            // Enable touch gestures
            _originalEnabledGestures   = TouchPanel.EnabledGestures;
            TouchPanel.EnabledGestures =
                GestureType.Tap      // Tap is used to drop new bodies.
                | GestureType.Flick  // Flick creates an explosion.
                | GestureType.Hold   // Hold to clear the scene.
                | GestureType.Pinch; // Pinch can be used to zoom in or out.

            InitializePhysics();
        }
Esempio n. 14
0
        public void LookAtTest()
        {
            CameraInstance cameraInstance = new CameraInstance(new Camera(new PerspectiveProjection()));

            Vector3F position = new Vector3F(1, 2, 3);
            Vector3F target   = new Vector3F(2, 5, 4);
            Vector3F upVector = new Vector3F(1, 1, 1);

            cameraInstance.PoseWorld = new Pose(new Vector3F(1, 2, 3));
            Matrix44F expected = Matrix44F.CreateLookAt(position, target, upVector);

            cameraInstance.LookAt(target, upVector);
            Assert.That(Matrix44F.AreNumericallyEqual(expected, cameraInstance.View));

            position = new Vector3F(-2, 3, -7.5f);
            expected = Matrix44F.CreateLookAt(position, target, upVector);
            cameraInstance.LookAt(position, target, upVector);
            Assert.That(Vector3F.AreNumericallyEqual(position, cameraInstance.PoseWorld.Position));
            Assert.That(Matrix44F.AreNumericallyEqual(expected, cameraInstance.View));
        }
Esempio n. 15
0
        public void UnprojectTest()
        {
            Viewport viewport = new Viewport(0, 0, 640, 480);
            PerspectiveProjection projection = new PerspectiveProjection();

            projection.SetFieldOfView(MathHelper.ToRadians(60), viewport.AspectRatio, 10, 1000);
            Matrix44F view = Matrix44F.CreateLookAt(new Vector3F(0, 0, 0), new Vector3F(0, 0, -1), Vector3F.Up);

            Assert.IsTrue(Vector3F.AreNumericallyEqual(new Vector3F(0, 0, -10), viewport.Unproject(new Vector3F(320, 240, 0), projection, view)));
            Assert.IsTrue(Vector3F.AreNumericallyEqual(new Vector3F(projection.Left, projection.Top, -10), viewport.Unproject(new Vector3F(0, 0, 0), projection, view)));
            Assert.IsTrue(Vector3F.AreNumericallyEqual(new Vector3F(projection.Right, projection.Top, -10), viewport.Unproject(new Vector3F(640, 0, 0), projection, view)));
            Assert.IsTrue(Vector3F.AreNumericallyEqual(new Vector3F(projection.Left, projection.Bottom, -10), viewport.Unproject(new Vector3F(0, 480, 0), projection, view)));
            Assert.IsTrue(Vector3F.AreNumericallyEqual(new Vector3F(projection.Right, projection.Bottom, -10), viewport.Unproject(new Vector3F(640, 480, 0), projection, view)));

            Vector3[] farCorners = new Vector3[4];
            GraphicsHelper.GetFrustumFarCorners(projection, farCorners);
            Assert.IsTrue(Vector3F.AreNumericallyEqual((Vector3F)farCorners[0], viewport.Unproject(new Vector3F(0, 0, 1), projection, view)));
            Assert.IsTrue(Vector3F.AreNumericallyEqual((Vector3F)farCorners[1], viewport.Unproject(new Vector3F(640, 0, 1), projection, view)));
            Assert.IsTrue(Vector3F.AreNumericallyEqual((Vector3F)farCorners[2], viewport.Unproject(new Vector3F(0, 480, 1), projection, view)));
            Assert.IsTrue(Vector3F.AreNumericallyEqual((Vector3F)farCorners[3], viewport.Unproject(new Vector3F(640, 480, 1), projection, view)));
        }
Esempio n. 16
0
        public void ViewTest()
        {
            CameraInstance cameraInstance = new CameraInstance(new Camera(new PerspectiveProjection()));

            Assert.AreEqual(Matrix44F.Identity, cameraInstance.View);
            Assert.AreEqual(Matrix44F.Identity, cameraInstance.ViewInverse);

            Vector3F  position = new Vector3F(1, 2, 3);
            Vector3F  target   = new Vector3F(2, 5, 4);
            Vector3F  upVector = new Vector3F(1, 1, 1);
            Matrix44F view     = Matrix44F.CreateLookAt(position, target, upVector);

            cameraInstance.View = view;

            Assert.AreEqual(view, cameraInstance.View);
            Assert.AreEqual(view.Inverse, cameraInstance.ViewInverse);

            Vector3F originOfCamera = cameraInstance.PoseWorld.Position;

            originOfCamera = cameraInstance.View.TransformPosition(originOfCamera);
            Assert.IsTrue(Vector3F.AreNumericallyEqual(Vector3F.Zero, originOfCamera));

            Vector4F positionView = new Vector4F(0, 0, -1, 1);
            Vector4F positionView2;

            // Transform a point from view space to world space.
            Vector4F positionWorld  = cameraInstance.PoseWorld * positionView;
            Vector4F positionWorld2 = cameraInstance.ViewInverse * positionView;

            Assert.IsTrue(Vector4F.AreNumericallyEqual(positionWorld, positionWorld2));

            // Transform a point from world space to view space.
            positionView  = cameraInstance.PoseWorld.Inverse * positionWorld;
            positionView2 = cameraInstance.View * positionWorld;
            Assert.IsTrue(Vector4F.AreNumericallyEqual(positionView, positionView2));

            cameraInstance.View = Matrix44F.Identity;
            Assert.AreEqual(Vector3F.Zero, cameraInstance.PoseWorld.Position);
            Assert.AreEqual(Matrix33F.Identity, cameraInstance.PoseWorld.Orientation);
        }
Esempio n. 17
0
        public override void Update(GameTime gameTime)
        {
            // Move the directional light in a circle.
            float deltaTimeF = (float)gameTime.ElapsedGameTime.TotalSeconds;

            _lightAngle += 0.3f * deltaTimeF;
            var position = QuaternionF.CreateRotationY(_lightAngle).Rotate(new Vector3F(6, 6, 0));

            // Make the light look at the world space origin.
            var lightTarget  = Vector3F.Zero;
            var lookAtMatrix = Matrix44F.CreateLookAt(position, lightTarget, Vector3F.Up);

            // A look-at matrix is the inverse of a normal world or pose matrix.
            _mainDirectionalLightNode.PoseWorld =
                new Pose(lookAtMatrix.Translation, lookAtMatrix.Minor).Inverse;

            // Update the light position of the renderer.
            // To create a local light shadow (light rays are not parallel), we have to set the light
            // position and a 4th component of 1.
            //_projectedShadowRenderer.LightPosition = new Vector4F(position, 1);
            // To create a directional light shadow (light rays are parallel), we have to set the inverse
            // light direction and 0.
            var lightRayDirection = (lightTarget - position);

            _projectedShadowRenderer.LightPosition = new Vector4F(-lightRayDirection, 0);

            // For debugging: Draw coordinate axes at (0, 0, 0).
            _debugRenderer.Clear();
            _debugRenderer.DrawAxes(Pose.Identity, 1, true);

            // Draw light node. (Will be drawn as a coordinate cross.)
            _debugRenderer.DrawObject(_mainDirectionalLightNode, Color.Yellow, false, true);

            // Update the scene - this must be called once per frame.
            _scene.Update(gameTime.ElapsedGameTime);

            base.Update(gameTime);
        }
        private bool _cullingEnabled = true; // True to use frustum culling. False to disable frustum culling.


        public FrustumCullingSample(Microsoft.Xna.Framework.Game game)
            : base(game)
        {
            GraphicsScreen.ClearBackground = true;
            GraphicsScreen.BackgroundColor = Color.CornflowerBlue;

            // The top-down camera.
            var orthographicProjection = new OrthographicProjection();

            orthographicProjection.Set(
                LevelSize * 1.1f * GraphicsService.GraphicsDevice.Viewport.AspectRatio,
                LevelSize * 1.1f,
                1,
                10000f);
            var topDownCamera = new Camera(orthographicProjection);

            _topDownCameraNode = new CameraNode(topDownCamera)
            {
                View = Matrix44F.CreateLookAt(new Vector3F(0, 1000, 0), new Vector3F(0, 0, 0), -Vector3F.UnitZ),
            };

            // The perspective camera moving through the scene.
            var perspectiveProjection = new PerspectiveProjection();

            perspectiveProjection.SetFieldOfView(
                MathHelper.ToRadians(45),
                GraphicsService.GraphicsDevice.Viewport.AspectRatio,
                1,
                500);
            var sceneCamera = new Camera(perspectiveProjection);

            _sceneCameraNode = new CameraNode(sceneCamera);

            // Initialize collision detection.
            // We use one collision domain that manages all objects.
            _domain = new CollisionDomain(new CollisionDetection())
            {
                // We exchange the default broad phase with a DualPartition. The DualPartition
                // has special support for frustum culling.
                BroadPhase = new DualPartition <CollisionObject>(),
            };

            // Create a lot of random objects and add them to the collision domain.
            RandomHelper.Random = new Random(12345);
            for (int i = 0; i < NumberOfObjects; i++)
            {
                // A real scene consists of a lot of complex objects such as characters, vehicles,
                // buildings, lights, etc. When doing frustum culling we need to test each objects against
                // the viewing frustum. If it intersects with the viewing frustum, the object is visible
                // from the camera's point of view. However, in practice we do not test the exact object
                // against the viewing frustum. Each objects is approximated by a simpler shape. In our
                // example, we assume that each object is approximated with an oriented bounding box.
                // (We could also use an other shape, such as a bounding sphere.)

                // Create a random box.
                Shape randomShape = new BoxShape(RandomHelper.Random.NextVector3F(1, 10));

                // Create a random position.
                Vector3F randomPosition;
                randomPosition.X = RandomHelper.Random.NextFloat(-LevelSize / 2, LevelSize / 2);
                randomPosition.Y = RandomHelper.Random.NextFloat(0, 2);
                randomPosition.Z = RandomHelper.Random.NextFloat(-LevelSize / 2, LevelSize / 2);

                // Create a random orientation.
                QuaternionF randomOrientation = RandomHelper.Random.NextQuaternionF();

                // Create object and add it to collision domain.
                var geometricObject = new GeometricObject(randomShape, new Pose(randomPosition, randomOrientation));
                var collisionObject = new CollisionObject(geometricObject)
                {
                    CollisionGroup = 0,
                };
                _domain.CollisionObjects.Add(collisionObject);
            }

            // Per default, the collision domain computes collision between all objects.
            // In this sample we do not need this information and disable it with a collision
            // filter.
            // In a real application, we would use this collision information for rendering,
            // for example, to find out which lights overlap with which meshes, etc.
            var filter = new CollisionFilter();

            // Disable collision between objects in collision group 0.
            filter.Set(0, 0, false);
            _domain.CollisionDetection.CollisionFilter = filter;

            // Start with the top-down camera.
            GraphicsScreen.CameraNode = _topDownCameraNode;

            // We will collect a few statistics for debugging.
            Profiler.SetFormat("NoCull", 1000, "Time in ms to submit DebugRenderer draw jobs without frustum culling.");
            Profiler.SetFormat("WithCull", 1000, "Time in ms to submit DebugRenderer draw jobs with frustum culling.");
        }
Esempio n. 19
0
        protected override void OnLoad()
        {
            var contentManager = _services.GetInstance <ContentManager>();

            _lights.Add(new LightNode(new AmbientLight
            {
                Color     = new Vector3F(0.9f, 0.9f, 1f),
                Intensity = 0.05f,
                HemisphericAttenuation = 1,
            })
            {
                Name = "AmbientLight",

                // This ambient light is "infinite", the pose is irrelevant for the lighting. It is only
                // used for the debug rendering below.
                PoseWorld = new Pose(new Vector3F(0, 4, 0)),
            });

            _lights.Add(new LightNode(new DirectionalLight
            {
                Color             = new Vector3F(0.6f, 0.8f, 1f),
                DiffuseIntensity  = 0.1f,
                SpecularIntensity = 0.1f,
            })
            {
                Name      = "DirectionalLightWithShadow",
                Priority  = 10, // This is the most important light.
                PoseWorld = new Pose(new Vector3F(0, 5, 0), Matrix33F.CreateRotationY(-1.4f) * Matrix33F.CreateRotationX(-0.6f)),
                Shadow    = new CascadedShadow
                {
                    PreferredSize     = 1024,
                    DepthBiasScale    = new Vector4F(0.99f),
                    DepthBiasOffset   = new Vector4F(0),
                    FadeOutDistance   = 20,
                    MaxDistance       = 30,
                    MinLightDistance  = 5,
                    ShadowFog         = 0.5f,
                    JitterResolution  = 3000,
                    SplitDistribution = 0.7f
                }
            });

            _lights.Add(new LightNode(new DirectionalLight
            {
                Color             = new Vector3F(0.8f, 0.4f, 0.0f),
                DiffuseIntensity  = 0.1f,
                SpecularIntensity = 0.0f,
            })
            {
                Name      = "DirectionalLight",
                PoseWorld = new Pose(new Vector3F(0, 6, 0), Matrix33F.CreateRotationY(-1.4f) * Matrix33F.CreateRotationX(-0.6f) * Matrix33F.CreateRotationX(ConstantsF.Pi)),
            });

            _lights.Add(new LightNode(new PointLight
            {
                Color             = new Vector3F(0, 1, 0),
                DiffuseIntensity  = 2,
                SpecularIntensity = 2,
                Range             = 3,
                Attenuation       = 1f,
            })
            {
                Name      = "PointLight",
                PoseWorld = new Pose(new Vector3F(-7, 1, 0))
            });

            _lights.Add(new LightNode(new PointLight
            {
                DiffuseIntensity  = 4,
                SpecularIntensity = 4,
                Range             = 3,
                Attenuation       = 1f,
                Texture           = contentManager.Load <TextureCube>("LavaBall/LavaCubeMap"),
            })
            {
                Name      = "PointLightWithTexture",
                PoseWorld = new Pose(new Vector3F(-2, 1, 0))
            });

            _lights.Add(new LightNode(new PointLight
            {
                Color             = new Vector3F(1, 1, 1),
                DiffuseIntensity  = 2,
                SpecularIntensity = 2,
                Range             = 3,
                Attenuation       = 1f,
            })
            {
                Name      = "PointLightWithShadow",
                PoseWorld = new Pose(new Vector3F(2, 1, 0)),
                Shadow    = new CubeMapShadow
                {
                    PreferredSize   = 128,
                    DepthBiasScale  = 0.9f,
                    DepthBiasOffset = -0.01f,
                }
            });

            _lights.Add(new LightNode(new PointLight
            {
                Color             = new Vector3F(1, 1, 1),
                DiffuseIntensity  = 4,
                SpecularIntensity = 4,
                Range             = 3,
                Attenuation       = 1f,
                Texture           = contentManager.Load <TextureCube>("MagicSphere/ColorCube"),
            })
            {
                Name      = "PointLightWithTextureAndShadow",
                PoseWorld = new Pose(new Vector3F(7, 1, 0)),
                Shadow    = new CubeMapShadow
                {
                    PreferredSize   = 128,
                    DepthBiasScale  = 0.9f,
                    DepthBiasOffset = -0.01f,
                }
            });

            _lights.Add(new LightNode(new ProjectorLight
            {
                Texture = contentManager.Load <Texture2D>("TVBox/TestCard"),
            })
            {
                Name      = "ProjectorLight",
                PoseWorld = Pose.FromMatrix(Matrix44F.CreateLookAt(new Vector3F(-1, 1, -6), new Vector3F(-5, 0, -6), new Vector3F(0, 1, 0))).Inverse,
            });

            _lights.Add(new LightNode(new ProjectorLight
            {
                Texture = contentManager.Load <Texture2D>("TVBox/TestCard"),
            })
            {
                Name      = "ProjectorLightWithShadow",
                PoseWorld = Pose.FromMatrix(Matrix44F.CreateLookAt(new Vector3F(5, 1, -6), new Vector3F(1, 0, -6), new Vector3F(0, 1, 0))).Inverse,
                Shadow    = new StandardShadow
                {
                    PreferredSize = 128,
                }
            });

            _lights.Add(new LightNode(new Spotlight
            {
                Color             = new Vector3F(0, 1, 0),
                DiffuseIntensity  = 2,
                SpecularIntensity = 2,
            })
            {
                Name      = "Spotlight",
                PoseWorld = Pose.FromMatrix(Matrix44F.CreateLookAt(new Vector3F(-7, 1, -12), new Vector3F(-10, 0, -12), new Vector3F(0, 1, 0))).Inverse,
            });

            _lights.Add(new LightNode(new Spotlight
            {
                DiffuseIntensity  = 2,
                SpecularIntensity = 2,
                Texture           = contentManager.Load <Texture2D>("TVBox/TestCard"),
            })
            {
                Name      = "SpotlightWithTexture",
                PoseWorld = Pose.FromMatrix(Matrix44F.CreateLookAt(new Vector3F(-1, 1, -12), new Vector3F(-5, 0, -12), new Vector3F(0, 1, 0))).Inverse,
            });

            _lights.Add(new LightNode(new Spotlight
            {
                DiffuseIntensity  = 2,
                SpecularIntensity = 2,
            })
            {
                Name      = "SpotlightWithShadow",
                PoseWorld = Pose.FromMatrix(Matrix44F.CreateLookAt(new Vector3F(5, 1, -12), new Vector3F(1, 0, -12), new Vector3F(0, 1, 0))).Inverse,
                Shadow    = new StandardShadow
                {
                    PreferredSize = 128,
                }
            });

            _lights.Add(new LightNode(new Spotlight
            {
                Color             = new Vector3F(1, 1, 0),
                DiffuseIntensity  = 2,
                SpecularIntensity = 2,
                Texture           = contentManager.Load <Texture2D>("TVBox/TestCard"),
            })
            {
                Name      = "SpotlightWithTextureAndShadow",
                PoseWorld = Pose.FromMatrix(Matrix44F.CreateLookAt(new Vector3F(11, 1, -12), new Vector3F(5, 0, -12), new Vector3F(0, 1, 0))).Inverse,
                Shadow    = new StandardShadow
                {
                    PreferredSize = 128,
                }
            });

            var scene = _services.GetInstance <IScene>();

            _debugRenderer = _services.GetInstance <DebugRenderer>();

            foreach (var lightNode in _lights)
            {
                scene.Children.Add(lightNode);
            }
        }
Esempio n. 20
0
        protected override void OnUpdate(TimeSpan deltaTime)
        {
            // Mouse centering (controlled by the MenuComponent) is disabled if the game
            // is inactive or if the GUI is active. In these cases, we do not want to move
            // the player.
            if (!_inputService.EnableMouseCentering)
            {
                return;
            }

            if (_inputService.IsPressed(Keys.Enter, true))
            {
                // Toggle between player camera and spectator view.
                _useSpectatorView = !_useSpectatorView;
            }
            else
            {
                float deltaTimeF = (float)deltaTime.TotalSeconds;

                // Compute new yaw and pitch from mouse movement.
                float deltaYaw = 0;
                deltaYaw -= _inputService.MousePositionDelta.X;
                deltaYaw -= _inputService.GetGamePadState(LogicalPlayerIndex.One).ThumbSticks.Right.X * 10;
                _yaw     += deltaYaw * deltaTimeF * 0.1f;

                float deltaPitch = 0;
                deltaPitch -= _inputService.MousePositionDelta.Y;
                deltaPitch += _inputService.GetGamePadState(LogicalPlayerIndex.One).ThumbSticks.Right.Y * 10;
                _pitch     += deltaPitch * deltaTimeF * 0.1f;

                // Limit the pitch angle to less than +/- 90°.
                float limit = ConstantsF.PiOver2 - 0.01f;
                _pitch = MathHelper.Clamp(_pitch, -limit, limit);
            }

            // Update SceneNode.LastPoseWorld - this is required for some effects, like
            // camera motion blur.
            CameraNode.LastPoseWorld = CameraNode.PoseWorld;

            var vehiclePose = _vehicle.Pose;

            if (_useSpectatorView)
            {
                // Spectator Mode:
                // Camera is looking at the car from a fixed location in the level.
                Vector3F position = new Vector3F(10, 8, 10);
                Vector3F target   = vehiclePose.Position;
                Vector3F up       = Vector3F.UnitY;

                // Set the new camera view matrix. (Setting the View matrix changes the Pose.
                // The pose is simply the inverse of the view matrix).
                CameraNode.View = Matrix44F.CreateLookAt(position, target, up);
            }
            else
            {
                // Player Camera:
                // Camera moves with the car. The look direction can be changed by moving the mouse.
                Matrix33F yaw         = Matrix33F.CreateRotationY(_yaw);
                Matrix33F pitch       = Matrix33F.CreateRotationX(_pitch);
                Matrix33F orientation = vehiclePose.Orientation * yaw * pitch;
                Vector3F  forward     = orientation * -Vector3F.UnitZ;
                Vector3F  up          = Vector3F.UnitY;
                Vector3F  position    = vehiclePose.Position - 10 * forward + 5 * up;
                Vector3F  target      = vehiclePose.Position + 1 * up;

                CameraNode.View = Matrix44F.CreateLookAt(position, target, up);
            }
        }
Esempio n. 21
0
        public override void Render(IList <SceneNode> nodes, RenderContext context, RenderOrder order)
        {
            if (nodes == null)
            {
                throw new ArgumentNullException("nodes");
            }
            if (context == null)
            {
                throw new ArgumentNullException("context");
            }

            int numberOfNodes = nodes.Count;

            if (numberOfNodes == 0)
            {
                return;
            }

            context.ThrowIfCameraMissing();
            context.ThrowIfSceneMissing();

            var originalRenderTarget  = context.RenderTarget;
            var originalViewport      = context.Viewport;
            var originalReferenceNode = context.ReferenceNode;

            var cameraNode = context.CameraNode;

            // Update SceneNode.LastFrame for all visible nodes.
            int frame = context.Frame;

            cameraNode.LastFrame = frame;

            // The scene node renderer should use the light camera instead of the player camera.
            context.CameraNode = _perspectiveCameraNode;
            context.Technique  = "Omnidirectional";

            var graphicsService  = context.GraphicsService;
            var graphicsDevice   = graphicsService.GraphicsDevice;
            var renderTargetPool = graphicsService.RenderTargetPool;
            var savedRenderState = new RenderStateSnapshot(graphicsDevice);

            for (int i = 0; i < numberOfNodes; i++)
            {
                var lightNode = nodes[i] as LightNode;
                if (lightNode == null)
                {
                    continue;
                }

                var shadow = lightNode.Shadow as CubeMapShadow;
                if (shadow == null)
                {
                    continue;
                }

                var light = lightNode.Light as PointLight;
                if (light == null)
                {
                    throw new GraphicsException("CubeMapShadow can only be used with a PointLight.");
                }

                // LightNode is visible in current frame.
                lightNode.LastFrame = frame;

                if (shadow.ShadowMap == null)
                {
                    shadow.ShadowMap = renderTargetPool.ObtainCube(
                        new RenderTargetFormat(
                            shadow.PreferredSize,
                            null,
                            false,
                            shadow.Prefer16Bit ? SurfaceFormat.HalfSingle : SurfaceFormat.Single,
                            DepthFormat.Depth24));
                }

                ((PerspectiveProjection)_perspectiveCameraNode.Camera.Projection).SetFieldOfView(
                    ConstantsF.PiOver2, 1, shadow.Near, light.Range);

                // World units per texel at a planar distance of 1 world unit.
                float unitsPerTexel = _perspectiveCameraNode.Camera.Projection.Width / (shadow.ShadowMap.Size * shadow.Near);

                // Convert depth bias from "texel" to  world space.
                // Minus to move receiver closer to light.
                shadow.EffectiveDepthBias = -shadow.DepthBias * unitsPerTexel;

                // Convert normal offset from "texel" to world space.
                shadow.EffectiveNormalOffset = shadow.NormalOffset * unitsPerTexel;

                var pose = lightNode.PoseWorld;

                context.ReferenceNode = lightNode;
                context.Object        = shadow;

                bool shadowMapContainsSomething = false;
                for (int side = 0; side < 6; side++)
                {
                    context.Data[RenderContextKeys.ShadowTileIndex] = BoxedIntegers[side];

                    graphicsDevice.SetRenderTarget(shadow.ShadowMap, CubeMapFaces[side]);
                    // context.RenderTarget = shadow.ShadowMap;   // TODO: Support cube maps targets in the render context.
                    context.Viewport = graphicsDevice.Viewport;

                    graphicsDevice.Clear(Color.White);

                    _perspectiveCameraNode.View = Matrix44F.CreateLookAt(
                        pose.Position,
                        pose.ToWorldPosition(CubeMapForwardVectors[side]),
                        pose.ToWorldDirection(CubeMapUpVectors[side]));

                    // Abort if this cube map frustum does not touch the camera frustum.
                    if (!context.Scene.HaveContact(cameraNode, _perspectiveCameraNode))
                    {
                        continue;
                    }

                    graphicsDevice.DepthStencilState = DepthStencilState.Default;
                    graphicsDevice.RasterizerState   = RasterizerState.CullCounterClockwise;
                    graphicsDevice.BlendState        = BlendState.Opaque;

                    shadowMapContainsSomething |= RenderCallback(context);
                }

                // Recycle shadow map if empty.
                if (!shadowMapContainsSomething)
                {
                    renderTargetPool.Recycle(shadow.ShadowMap);
                    shadow.ShadowMap = null;
                }
            }

            graphicsDevice.SetRenderTarget(null);
            savedRenderState.Restore();

            context.CameraNode    = cameraNode;
            context.Technique     = null;
            context.RenderTarget  = originalRenderTarget;
            context.Viewport      = originalViewport;
            context.ReferenceNode = originalReferenceNode;
            context.Object        = null;
            context.Data[RenderContextKeys.ShadowTileIndex] = null;
        }
Esempio n. 22
0
        private void Render(RenderContext context)
        {
            var originalRenderTarget = context.RenderTarget;
            var originalViewport     = context.Viewport;

            var graphicsDevice = context.GraphicsService.GraphicsDevice;

            if (_updateCubeMap)
            {
                _updateCubeMap = false;

                _cloudMapRenderer.Render(_skyNodes, context);

                // Create a camera with 45° FOV for a single cube map face.
                var perspectiveProjection = new PerspectiveProjection();
                perspectiveProjection.SetFieldOfView(ConstantsF.PiOver2, 1, 1, 100);
                context.CameraNode = new CameraNode(new Camera(perspectiveProjection));

                var size      = _skybox.Texture.Size;
                var hdrFormat = new RenderTargetFormat(size, size, false, SurfaceFormat.HdrBlendable, DepthFormat.None);
                var hdrTarget = context.GraphicsService.RenderTargetPool.Obtain2D(hdrFormat);
                var ldrFormat = new RenderTargetFormat(size, size, false, SurfaceFormat.Color, DepthFormat.None);
                var ldrTarget = context.GraphicsService.RenderTargetPool.Obtain2D(ldrFormat);

                var spriteBatch = GraphicsService.GetSpriteBatch();
                for (int side = 0; side < 6; side++)
                {
                    // Rotate camera to face the current cube map face.
                    var cubeMapFace = (CubeMapFace)side;
                    context.CameraNode.View = Matrix44F.CreateLookAt(
                        new Vector3F(),
                        GraphicsHelper.GetCubeMapForwardDirection(cubeMapFace),
                        GraphicsHelper.GetCubeMapUpDirection(cubeMapFace));

                    // Render sky into HDR render target.
                    graphicsDevice.SetRenderTarget(hdrTarget);
                    context.RenderTarget = hdrTarget;
                    context.Viewport     = graphicsDevice.Viewport;
                    graphicsDevice.Clear(Color.Black);
                    _skyRenderer.Render(_skyNodes, context);

                    graphicsDevice.BlendState = BlendState.Opaque;

                    // Convert HDR to RGBM.
                    context.SourceTexture = hdrTarget;
                    context.RenderTarget  = ldrTarget;
                    _colorEncoder.Process(context);
                    context.SourceTexture = null;

                    // Copy RGBM texture into cube map face.
                    graphicsDevice.SetRenderTarget((RenderTargetCube)_skybox.Texture, cubeMapFace);
                    spriteBatch.Begin(SpriteSortMode.Immediate, BlendState.Opaque, null, null, null);
                    spriteBatch.Draw(ldrTarget, new Vector2(0, 0), Color.White);
                    spriteBatch.End();
                }

                context.GraphicsService.RenderTargetPool.Recycle(ldrTarget);
                context.GraphicsService.RenderTargetPool.Recycle(hdrTarget);
            }

            graphicsDevice.BlendState        = BlendState.Opaque;
            graphicsDevice.DepthStencilState = DepthStencilState.Default;
            graphicsDevice.RasterizerState   = RasterizerState.CullCounterClockwise;

            context.CameraNode = _cameraObject.CameraNode;

            var tempFormat = new RenderTargetFormat(originalRenderTarget);

            tempFormat.SurfaceFormat = SurfaceFormat.HdrBlendable;
            var tempTarget = context.GraphicsService.RenderTargetPool.Obtain2D(tempFormat);

            graphicsDevice.SetRenderTarget(tempTarget);
            graphicsDevice.Viewport = originalViewport;
            context.RenderTarget    = tempTarget;
            context.Viewport        = originalViewport;

            _skyRenderer.Render(_skybox, context);

            context.SourceTexture = tempTarget;
            context.RenderTarget  = originalRenderTarget;
            _hdrFilter.Process(context);
            context.SourceTexture = null;

            context.GraphicsService.RenderTargetPool.Recycle(tempTarget);

            RenderDebugInfo(context);

            context.CameraNode = null;
        }
        public override void Render(IList <SceneNode> nodes, RenderContext context, RenderOrder order)
        {
            if (nodes == null)
            {
                throw new ArgumentNullException("nodes");
            }
            if (context == null)
            {
                throw new ArgumentNullException("context");
            }

            int numberOfNodes = nodes.Count;

            if (numberOfNodes == 0)
            {
                return;
            }

            var graphicsService  = context.GraphicsService;
            var graphicsDevice   = graphicsService.GraphicsDevice;
            var renderTargetPool = graphicsService.RenderTargetPool;
            int frame            = context.Frame;

            var savedRenderState = new RenderStateSnapshot(graphicsDevice);

            var originalRenderTarget  = context.RenderTarget;
            var originalViewport      = context.Viewport;
            var originalCameraNode    = context.CameraNode;
            var originalLodCameraNode = context.LodCameraNode;
            var originalReferenceNode = context.ReferenceNode;

            try
            {
                // Use foreach instead of for-loop to catch InvalidOperationExceptions in
                // case the collection is modified.
                for (int i = 0; i < numberOfNodes; i++)
                {
                    var node = nodes[i] as SceneCaptureNode;
                    if (node == null)
                    {
                        continue;
                    }

                    // Update each node only once per frame.
                    if (node.LastFrame == frame)
                    {
                        continue;
                    }

                    node.LastFrame = frame;

                    var cameraNode = node.CameraNode;
                    if (cameraNode == null)
                    {
                        continue;
                    }

                    var texture = node.RenderToTexture.Texture;
                    if (texture == null)
                    {
                        continue;
                    }

                    // RenderToTexture instances can be shared. --> Update them only once per frame.
                    if (node.RenderToTexture.LastFrame == frame)
                    {
                        continue;
                    }

                    context.CameraNode    = cameraNode;
                    context.LodCameraNode = cameraNode;
                    context.ReferenceNode = node;

                    var renderTarget2D = texture as RenderTarget2D;
                    var projection     = cameraNode.Camera.Projection;
                    if (renderTarget2D != null)
                    {
                        context.RenderTarget = renderTarget2D;
                        context.Viewport     = new Viewport(0, 0, renderTarget2D.Width, renderTarget2D.Height);

                        RenderCallback(context);

                        // Update other properties of RenderToTexture.
                        node.RenderToTexture.LastFrame     = frame;
                        node.RenderToTexture.TextureMatrix = GraphicsHelper.ProjectorBiasMatrix
                                                             * projection
                                                             * cameraNode.PoseWorld.Inverse;

                        continue;
                    }

                    var renderTargetCube = texture as RenderTargetCube;
                    if (renderTargetCube != null)
                    {
                        var format = new RenderTargetFormat(renderTargetCube)
                        {
                            Mipmap = false
                        };

                        renderTarget2D = renderTargetPool.Obtain2D(format);

                        context.RenderTarget = renderTarget2D;
                        context.Viewport     = new Viewport(0, 0, renderTarget2D.Width, renderTarget2D.Height);

                        if (_spriteBatch == null)
                        {
                            _spriteBatch = graphicsService.GetSpriteBatch();
                        }

                        var perspectiveProjection = projection as PerspectiveProjection;
                        if (perspectiveProjection == null)
                        {
                            throw new GraphicsException("The camera of the SceneCaptureNode must use a perspective projection.");
                        }

                        // ReSharper disable CompareOfFloatsByEqualityOperator
                        if (perspectiveProjection.FieldOfViewX != ConstantsF.PiOver2 ||
                            perspectiveProjection.FieldOfViewY != ConstantsF.PiOver2)
                        {
                            perspectiveProjection.SetFieldOfView(ConstantsF.PiOver2, 1, projection.Near, projection.Far);
                        }
                        // ReSharper restore CompareOfFloatsByEqualityOperator

                        var originalCameraPose = cameraNode.PoseWorld;
                        for (int side = 0; side < 6; side++)
                        {
                            // Rotate camera to face the current cube map face.
                            //var cubeMapFace = (CubeMapFace)side;
                            // AMD problem: If we generate in normal order, the last cube map face contains
                            // garbage when mipmaps are created.
                            var cubeMapFace = (CubeMapFace)(5 - side);
                            var position    = cameraNode.PoseWorld.Position;
                            cameraNode.View = Matrix44F.CreateLookAt(
                                position,
                                position + originalCameraPose.ToWorldDirection(GraphicsHelper.GetCubeMapForwardDirection(cubeMapFace)),
                                originalCameraPose.ToWorldDirection(GraphicsHelper.GetCubeMapUpDirection(cubeMapFace)));

                            RenderCallback(context);

                            // Copy RGBM texture into cube map face.
                            graphicsDevice.SetRenderTarget(renderTargetCube, cubeMapFace);
                            _spriteBatch.Begin(SpriteSortMode.Immediate, BlendState.Opaque, null, null, null);
                            _spriteBatch.Draw(renderTarget2D, new Vector2(0, 0), Color.White);
                            _spriteBatch.End();
                        }
                        cameraNode.PoseWorld = originalCameraPose;

                        renderTargetPool.Recycle(renderTarget2D);

                        // Update other properties of RenderToTexture.
                        node.RenderToTexture.LastFrame     = frame;
                        node.RenderToTexture.TextureMatrix = GraphicsHelper.ProjectorBiasMatrix
                                                             * projection
                                                             * cameraNode.PoseWorld.Inverse;

                        continue;
                    }

                    throw new GraphicsException(
                              "SceneCaptureNode.RenderToTexture.Texture is invalid. The texture must be a RenderTarget2D or RenderTargetCube.");
                }
            }
            catch (InvalidOperationException exception)
            {
                throw new GraphicsException(
                          "InvalidOperationException was raised in SceneCaptureRenderer.Render(). "
                          + "This can happen if a SceneQuery instance that is currently in use is modified in the "
                          + "RenderCallback. --> Use different SceneQuery types in the method which calls "
                          + "SceneCaptureRenderer.Render() and in the RenderCallback method.",
                          exception);
            }

            graphicsDevice.SetRenderTarget(null);
            savedRenderState.Restore();

            context.RenderTarget  = originalRenderTarget;
            context.Viewport      = originalViewport;
            context.CameraNode    = originalCameraNode;
            context.LodCameraNode = originalLodCameraNode;
            context.ReferenceNode = originalReferenceNode;
        }