protected override void DoRender()
        {
            // Early exit
            if (Lights.Count == 0)
            {
                return;
            }

            // Retrieve device context
            var context = this.DeviceManager.Direct3DContext;

            // backup existing context state
            int       oldStencilRef = 0;
            RawColor4 oldBlendFactor;
            int       oldSampleMaskRef;

            using (var oldVertexLayout = context.InputAssembler.InputLayout)
                using (var oldPixelShader = context.PixelShader.Get())
                    using (var oldVertexShader = context.VertexShader.Get())
                        using (var oldBlendState = context.OutputMerger.GetBlendState(out oldBlendFactor, out oldSampleMaskRef))
                            using (var oldDepthState = context.OutputMerger.GetDepthStencilState(out oldStencilRef))
                                using (var oldRSState = context.Rasterizer.State)
                                {
                                    // Assign shader resources - TODO: create array in CreateDeviceDependentResources instead
                                    context.PixelShader.SetShaderResources(0, gbuffer.SRVs.ToArray().Concat(new[] { gbuffer.DSSRV }).ToArray());

                                    // Assign the additive blend state
                                    context.OutputMerger.BlendState = blendStateAdd;

                                    // Retrieve camera parameters
                                    SharpDX.FrustumCameraParams cameraParams = Frustum.GetCameraParams();

                                    // For each configured light
                                    for (var i = 0; i < Lights.Count; i++)
                                    {
                                        PerLight light = Lights[i];

                                        PixelShader shader = null; // Assign shader
                                        if (light.Type == LightType.Ambient)
                                        {
                                            shader = psAmbientLight;
                                        }
                                        else if (light.Type == LightType.Directional)
                                        {
                                            shader = psDirectionalLight;
                                        }
                                        else if (light.Type == LightType.Point)
                                        {
                                            shader = psPointLight;
                                        }
                                        //else if (light.Type == LightType.Spot)
                                        //    shader = psSpotLight;

                                        // Update the perLight constant buffer
                                        // Calculate view space position (for frustum checks)
                                        Vector3 lightDir     = Vector3.Normalize(Lights[i].Direction);
                                        Vector4 viewSpaceDir = Vector4.Transform(Vector3.Transform(lightDir, PerObject.World), PerObject.View);
                                        light.Direction = new Vector3(viewSpaceDir.X, viewSpaceDir.Y, viewSpaceDir.Z);
                                        Vector4 viewSpacePos = Vector4.Transform(Vector3.Transform(Lights[i].Position, PerObject.World), PerObject.View);
                                        light.Position = new Vector3(viewSpacePos.X, viewSpacePos.Y, viewSpacePos.Z);

                                        context.UpdateSubresource(ref light, perLightBuffer);
                                        context.PixelShader.SetConstantBuffer(4, perLightBuffer);

                                        light.Position  = Lights[i].Position;
                                        light.Direction = Lights[i].Direction;

                                        // Check if the light should be considered full screen
                                        bool isFullScreen = light.Type == LightType.Directional ||
                                                            light.Type == LightType.Ambient;
                                        if (!isFullScreen)
                                        {
                                            isFullScreen = (cameraParams.ZNear > viewSpacePos.Z - light.Range &&
                                                            cameraParams.ZFar < viewSpacePos.Z + light.Range);
                                        }
                                        if (isFullScreen)
                                        {
                                            context.OutputMerger.DepthStencilState = depthDisabled;
                                            // Use SAQuad
                                            saQuad.ShaderResources = null;
                                            saQuad.Shader          = shader;
                                            saQuad.Render();
                                        }
                                        else // Render volume
                                        {
                                            context.PixelShader.Set(shader);
                                            context.VertexShader.Set(vertexShader);

                                            Matrix world = Matrix.Identity;

                                            MeshRenderer volume = null;;
                                            switch (light.Type)
                                            {
                                            case LightType.Point:
                                                // Prepare world matrix
                                                // Ensure no abrupt light edges with +50%
                                                world.ScaleVector = Vector3.One * light.Range * 2f;
                                                volume            = pointLightVolume;
                                                break;

                                            /* TODO: Spot light support
                                             * case LightType.Spot:
                                             *  // Determine rotation!
                                             *  var D = Vector3.Normalize(light.Direction);
                                             *  var s1 = Vector3.Cross(D, Vector3.UnitZ);
                                             *  var s2 = Vector3.Cross(D, Vector3.UnitY);
                                             *  Vector3 S;
                                             *  if (s1.LengthSquared() > s2.LengthSquared())
                                             *      S = s1;
                                             *  else
                                             *      S = s2;
                                             *  var U = Vector3.Cross(D, S);
                                             *  Matrix rotate = Matrix.Identity;
                                             *  rotate.Forward = D;
                                             *  rotate.Down = U;
                                             *  rotate.Left = S;
                                             *
                                             *  float scaleZ = light.Range;
                                             *  // Need to Abs - if negative it will invert our model and result in incorrect normals
                                             *  float scaleXY = light.Range * Math.Abs((float)Math.Tan(Math.Acos(light.SpotOuterCosine*2)/2));
                                             *
                                             *  world.ScaleVector = new Vector3(scaleXY, scaleXY, scaleZ);
                                             *  world *= rotate;
                                             *  volume = spotLightVolume;
                                             *  break;
                                             * */
                                            default:
                                                continue;
                                            }
                                            world.TranslationVector = light.Position;
                                            volume.World            = world;
                                            // Transpose the PerObject matrices
                                            var transposed = PerObject;
                                            transposed.World = volume.World * PerObject.World;
                                            transposed.WorldViewProjection = transposed.World * PerObject.ViewProjection;
                                            transposed.Transpose();
                                            context.UpdateSubresource(ref transposed, PerObjectBuffer);

                                            if (cameraParams.ZFar < viewSpacePos.Z + light.Range)
                                            {
                                                // Cull the back face and only render where there is something
                                                // behind the front face.
                                                context.Rasterizer.State = rsCullBack;
                                                context.OutputMerger.DepthStencilState = depthLessThan;
                                            }
                                            else
                                            {
                                                // Cull front faces and only render where there is something
                                                // before the back face.
                                                context.Rasterizer.State = rsCullFront;
                                                context.OutputMerger.DepthStencilState = depthGreaterThan;
                                            }
                                            volume.Render();

                                            // Show the light volumes for debugging
                                            if (Debug > 0)
                                            {
                                                if (Debug == 1)
                                                {
                                                    context.OutputMerger.SetDepthStencilState(depthGreaterThan);
                                                }
                                                else
                                                {
                                                    context.OutputMerger.SetDepthStencilState(depthLessThan);
                                                }
                                                context.PixelShader.Set(psDebugLight);
                                                context.Rasterizer.State = rsWireframe;
                                                volume.Render();
                                            }
                                        }
                                    }

                                    // Reset pixel shader resources (all to null)
                                    context.PixelShader.SetShaderResources(0, new ShaderResourceView[gbuffer.SRVs.Count + 1]);

                                    // Restore context states
                                    context.PixelShader.Set(oldPixelShader);
                                    context.VertexShader.Set(oldVertexShader);
                                    context.InputAssembler.InputLayout = oldVertexLayout;
                                    context.OutputMerger.SetBlendState(oldBlendState, oldBlendFactor, oldSampleMaskRef);
                                    context.OutputMerger.SetDepthStencilState(oldDepthState, oldStencilRef);
                                    context.Rasterizer.State = oldRSState;
                                }
        }
示例#2
0
        public override void Run()
        {
            GBuffer gbuffer = ToDispose(new GBuffer(this.RenderTargetSize.Width,
                this.RenderTargetSize.Height,
                new SampleDescription(1, 0),
                Format.R8G8B8A8_UNorm,
                Format.R32_UInt,
                Format.R8G8B8A8_UNorm));
            gbuffer.Initialize(this);

            GBuffer gbufferMS = ToDispose(new GBuffer(this.RenderTargetSize.Width,
                this.RenderTargetSize.Height,
                new SampleDescription(4, 0),
                Format.R8G8B8A8_UNorm,
                Format.R32_UInt,
                Format.R8G8B8A8_UNorm));
            gbufferMS.Initialize(this);

            ScreenAlignedQuadRenderer saQuad = ToDispose(new ScreenAlignedQuadRenderer());
            saQuad.Initialize(this);

            #region Create renderers

            // Note: the renderers take care of creating their own
            // device resources and listen for DeviceManager.OnInitialize

            // Create Light accumulator
            var sphereMesh = Common.Mesh.LoadFromFile("Sphere.cmo").FirstOrDefault();
            //var coneMesh = Common.Mesh.LoadFromFile("Cone.cmo").FirstOrDefault();

            var sphereRenderer = ToDispose(new MeshRenderer(sphereMesh));
            sphereRenderer.Initialize(this);
            //var coneRenderer = new MeshRenderer(coneMesh);
            //coneRenderer.Initialize(this);
            var lightRenderer = ToDispose(new LightRenderer(sphereRenderer, saQuad, gbuffer));
            var lightRendererMS = ToDispose(new LightRenderer(sphereRenderer, saQuad, gbufferMS));

            lightRenderer.Lights.Add(new PerLight
            {
                Color = new Color4(0.2f, 0.2f, 0.2f, 1.0f),
                Position = new Vector3(1, 1, 1),
                Direction = new Vector3(0, 0, 1),
                Range = 10,
                Type = LightType.Ambient
            });
            //lightRenderer.Lights.Add(new PerLight
            //{
            //    Color = new Color4(0.2f, 0.2f, 0.2f, 1.0f),
            //    Direction = new Vector3(0, -1, 1),
            //    Type = LightType.Directional
            //});

            lightRenderer.Lights.Add(new PerLight
            {
                Color = Color.LightPink,
                Position = new Vector3(22.4f, 2.6f, -8.98f),
                Range = 20,
                Type = LightType.Point
            });
            lightRenderer.Lights.Add(new PerLight
            {
                Color = new Color4(1.0f, 1.0f, 1.0f, 1.0f),
                Position = new Vector3(0, 10, 1),
                Range = 10,
                Type = LightType.Point
            });
            var aLight = new PerLight
            {
                Color = new Color4(1.0f, 0.0f, 0.0f, 1.0f),
                Position = new Vector3(9.86f, 10.92f, -6.74f),
                Range = 20,
                Type = LightType.Point
            };
            lightRenderer.Lights.Add(aLight);
            aLight.Color = Color.Blue;
            aLight.Position.X = 2.32f;
            lightRenderer.Lights.Add(aLight);
            aLight.Color = Color.LightYellow;
            aLight.Position.X = -1.0f;
            lightRenderer.Lights.Add(aLight);
            aLight.Color = Color.Cyan;
            aLight.Position.X = -5.11f;
            lightRenderer.Lights.Add(aLight);
            aLight.Color = Color.Lime;
            aLight.Position.X = -8.33f;
            lightRenderer.Lights.Add(aLight);
            aLight.Color = Color.AliceBlue;
            aLight.Position.X = -12.55f;
            lightRenderer.Lights.Add(aLight);

            aLight.Position.Z = 5.02f;

            aLight.Color = Color.Red;
            aLight.Position.X = 9.86f;
            lightRenderer.Lights.Add(aLight);
            aLight.Color = Color.Blue;
            aLight.Position.X = 2.32f;
            lightRenderer.Lights.Add(aLight);
            aLight.Color = Color.LightYellow;
            aLight.Position.X = -1.0f;
            lightRenderer.Lights.Add(aLight);
            aLight.Color = Color.Cyan;
            aLight.Position.X = -5.11f;
            lightRenderer.Lights.Add(aLight);
            aLight.Color = Color.Lime;
            aLight.Position.X = -8.33f;
            lightRenderer.Lights.Add(aLight);
            aLight.Color = Color.AliceBlue;
            aLight.Position.X = -12.55f;
            lightRenderer.Lights.Add(aLight);

            //lightRenderer.Lights.Add(new PerLight
            //{
            //    Color = new Color4(1.0f, 1.0f, 1.0f, 1.0f),
            //    Position = new Vector3(-2, 1, 2),
            //    Direction = new Vector3(0, -1, 0),
            //    Range = 2f,
            //    Type = LightType.Spot,
            //    SpotInnerCosine = (float)Math.Cos(0.4) / 2.0f,
            //    SpotOuterCosine = (float)Math.Cos(1) / 2.0f,
            //});
            lightRenderer.Initialize(this);
            lightRendererMS.Lights.AddRange(lightRenderer.Lights);
            lightRendererMS.Initialize(this);

            // Create a axis-grid renderer
            var axisGrid = ToDispose(new AxisGridRenderer());
            axisGrid.Initialize(this);

            // Create and initialize the mesh renderer
            var loadedMesh = Common.Mesh.LoadFromFile("Sponza.cmo");
            //var loadedMesh = Common.Mesh.LoadFromFile("Scene.cmo");

            List<MeshRenderer> meshes = new List<MeshRenderer>();
            meshes.AddRange((from mesh in loadedMesh
                             select ToDispose(new MeshRenderer(mesh))));

            meshes.ForEach(m => m.Initialize(this));
            var meshWorld = Matrix.Identity;

            // Set the first animation as the current animation and start clock
            meshes.ForEach(m => {
                if (m.Mesh.Animations != null && m.Mesh.Animations.Any())
                    m.CurrentAnimation = m.Mesh.Animations.First().Value;
                m.Clock.Start();
            });

            // Create and initialize a Direct2D FPS text renderer
            var fps = ToDispose(new Common.FpsRenderer("Calibri", Color.CornflowerBlue, new Point(8, 8), 16));
            fps.Initialize(this);

            // Create and initialize a general purpose Direct2D text renderer
            // This will display some instructions and the current view and rotation offsets
            var textRenderer = ToDispose(new Common.TextRenderer("Calibri", Color.CornflowerBlue, new Point(8, 40), 12));
            textRenderer.Initialize(this);

            #endregion

            // Initialize the world matrix
            var worldMatrix = Matrix.Identity;

            // Set the camera position slightly behind (z)
            var cameraPosition = new Vector3(15, 15, -1);
            var cameraTarget = new Vector3(-15, 5, -1); // Looking at the origin 0,0,0
            var cameraUp = Vector3.UnitY; // Y+ is Up

            // Prepare matrices
            // Create the view matrix from our camera position, look target and up direction
            var viewMatrix = Matrix.LookAtRH(cameraPosition, cameraTarget, cameraUp);
            viewMatrix.TranslationVector += new Vector3(0, -0.98f, 0);

            // Create the projection matrix
            /* FoV 60degrees = Pi/3 radians */
            // Aspect ratio (based on window size), Near clip, Far clip
            var projectionMatrix = Matrix.PerspectiveFovRH((float)Math.PI / 3f, Width / (float)Height, 2f, 100f);

            // Maintain the correct aspect ratio on resize
            Window.Resize += (s, e) =>
            {
                projectionMatrix = Matrix.PerspectiveFovRH((float)Math.PI / 3f, Width / (float)Height, 2f, 100f);
            };

            #region Rotation and window event handlers

            // Create a rotation vector to keep track of the rotation
            // around each of the axes
            var rotation = new Vector3(0.0f, 0.0f, 0.0f);

            var gbufferIndex = -1;

            Dictionary<Keys, bool> keyToggles = new Dictionary<Keys, bool>();
            keyToggles[Keys.Z] = false;
            keyToggles[Keys.F] = false;
            keyToggles[Keys.M] = false;
            keyToggles[Keys.PrintScreen] = false;

            // We will call this action to update text
            // for the text renderer
            Action updateText = () =>
            {
                textRenderer.Text =
                    String.Format(
                    "{0} (+/- to change)"+
                    "\n{1} (M to toggle)",
                    gbufferIndex > -1 ? DebugGBuffer[gbufferIndex].DebugName : gbufferIndex < -1 ? "Forward Render (1 light)" : "Deferred with 15 lights",
                    gbufferIndex == -2 || keyToggles[Keys.M] ? "Multisampled" : "No anti-aliasing"
                    );
            };

            // Support keyboard/mouse input to rotate or move camera view
            var moveFactor = 0.02f; // how much to change on each keypress
            var shiftKey = false;
            var ctrlKey = false;
            var background = Color.White;
            var showNormals = false;
            var enableNormalMap = true;
            Window.KeyDown += (s, e) =>
            {
                var context = DeviceManager.Direct3DContext;

                shiftKey = e.Shift;
                ctrlKey = e.Control;

                switch (e.KeyCode)
                {
                    case Keys.Add:
                        gbufferIndex++;

                        if (gbufferIndex >= DebugGBuffer.Count)
                            gbufferIndex = -2;
                        break;
                    case Keys.Subtract:
                        gbufferIndex--;

                        if (gbufferIndex < -2)
                            gbufferIndex = DebugGBuffer.Count - 1;
                        break;
                    // WASD -> pans view
                    case Keys.A:
                        viewMatrix.TranslationVector += new Vector3(moveFactor * 12, 0f, 0f);
                        break;
                    case Keys.D:
                        viewMatrix.TranslationVector -= new Vector3(moveFactor * 12, 0f, 0f);
                        break;
                    case Keys.S:
                        if (shiftKey)
                            viewMatrix.TranslationVector += new Vector3(0f, moveFactor * 12, 0f);
                        else
                            viewMatrix.TranslationVector -= new Vector3(0f, 0f, 1) * moveFactor * 12;
                        break;
                    case Keys.W:
                        if (shiftKey)
                            viewMatrix.TranslationVector -= new Vector3(0f, moveFactor * 12, 0f);
                        else
                            viewMatrix.TranslationVector += new Vector3(0f, 0f, 1) * moveFactor * 12;
                        break;
                    // Up/Down and Left/Right - rotates around X / Y respectively
                    // (Mouse wheel rotates around Z)
                    case Keys.Down:
                        worldMatrix *= Matrix.RotationX(moveFactor);
                        rotation += new Vector3(moveFactor, 0f, 0f);
                        break;
                    case Keys.Up:
                        worldMatrix *= Matrix.RotationX(-moveFactor);
                        rotation -= new Vector3(moveFactor, 0f, 0f);
                        break;
                    case Keys.Left:
                        worldMatrix *= Matrix.RotationY(moveFactor);
                        rotation += new Vector3(0f, moveFactor, 0f);
                        break;
                    case Keys.Right:
                        worldMatrix *= Matrix.RotationY(-moveFactor);
                        rotation -= new Vector3(0f, moveFactor, 0f);
                        break;
                    case Keys.T:
                        fps.Show = !fps.Show;
                        textRenderer.Show = !textRenderer.Show;
                        break;
                    case Keys.B:
                        if (background == Color.White)
                        {
                            background = new Color(30, 30, 34);
                        }
                        else
                        {
                            background = Color.White;
                        }
                        break;
                    case Keys.G:
                        axisGrid.Show = !axisGrid.Show;
                        break;
                    case Keys.P:
                        // Pause or resume mesh animation
                        meshes.ForEach(m => {
                            if (m.Clock.IsRunning)
                                m.Clock.Stop();
                            else
                                m.Clock.Start();
                        });
                        break;
                    case Keys.X:
                        // To test for correct resource recreation
                        // Simulate device reset or lost.
                        System.Diagnostics.Debug.WriteLine(SharpDX.Diagnostics.ObjectTracker.ReportActiveObjects());
                        DeviceManager.Initialize(DeviceManager.Dpi);
                        System.Diagnostics.Debug.WriteLine(SharpDX.Diagnostics.ObjectTracker.ReportActiveObjects());
                        break;
                    case Keys.Z:
                        keyToggles[Keys.Z] = !keyToggles[Keys.Z];
                        if (keyToggles[Keys.Z])
                        {
                            context.PixelShader.Set(depthPixelShader);
                        }
                        else
                        {
                            context.PixelShader.Set(pixelShader);
                        }
                        break;
                    case Keys.F:
                        keyToggles[Keys.F] = !keyToggles[Keys.F];
                        RasterizerStateDescription rasterDesc;
                        if (context.Rasterizer.State != null)
                            rasterDesc = context.Rasterizer.State.Description;
                        else
                            rasterDesc = new RasterizerStateDescription()
                            {
                                CullMode = CullMode.Back,
                                FillMode = FillMode.Solid
                            };
                        if (keyToggles[Keys.F])
                        {
                            rasterDesc.FillMode = FillMode.Wireframe;
                            context.Rasterizer.State = ToDispose(new RasterizerState(context.Device, rasterDesc));
                        }
                        else
                        {
                            rasterDesc.FillMode = FillMode.Solid;
                            context.Rasterizer.State = ToDispose(new RasterizerState(context.Device, rasterDesc));
                        }
                        break;
                    case Keys.M:
                        keyToggles[Keys.M] = !keyToggles[Keys.M];
                        break;
                    case Keys.N:
                        if (!shiftKey)
                            showNormals = !showNormals;
                        else
                            enableNormalMap = !enableNormalMap;

                        break;
                    case Keys.D1:
                        context.PixelShader.Set(pixelShader);
                        break;
                    //case Keys.D2:
                    //    context.PixelShader.Set(lambertShader);
                    //    break;
                    //case Keys.D3:
                    //    context.PixelShader.Set(phongShader);
                    //    break;
                    case Keys.D4:
                        context.PixelShader.Set(blinnPhongShader);
                        break;
                    case Keys.Insert:
                        keyToggles[Keys.PrintScreen] = true;
                        break;

                }

                updateText();
            };
            Window.KeyUp += (s, e) =>
            {
                // Clear the shift/ctrl keys so they aren't sticky
                if (e.KeyCode == Keys.ShiftKey)
                    shiftKey = false;
                if (e.KeyCode == Keys.ControlKey)
                    ctrlKey = false;
            };
            Window.MouseWheel += (s, e) =>
            {
                if (shiftKey)
                {
                    // Zoom in/out
                    viewMatrix.TranslationVector += new Vector3(0f, 0f, (e.Delta / 120f) * moveFactor * 2);
                }
                else
                {
                    // rotate around Z-axis
                    viewMatrix *= Matrix.RotationZ((e.Delta / 120f) * moveFactor);
                    rotation += new Vector3(0f, 0f, (e.Delta / 120f) * moveFactor);
                }
                updateText();
            };

            var lastX = 0;
            var lastY = 0;

            Window.MouseDown += (s, e) =>
            {
                if (e.Button == MouseButtons.Left)
                {
                    lastX = e.X;
                    lastY = e.Y;
                }
            };

            Window.MouseMove += (s, e) =>
            {
                if (e.Button == MouseButtons.Left)
                {
                    var yRotate = lastX - e.X;
                    var xRotate = lastY - e.Y;
                    lastY = e.Y;
                    lastX = e.X;

                    // Mouse move changes
                    viewMatrix *= Matrix.RotationX(-xRotate * moveFactor);
                    viewMatrix *= Matrix.RotationY(-yRotate * moveFactor);

                    updateText();
                }
            };

            // Display instructions with initial values
            updateText();

            #endregion

            var clock = new System.Diagnostics.Stopwatch();
            clock.Start();

            #region Render loop

            // Create and run the render loop
            RenderLoop.Run(Window, () =>
            {
                // Start of frame:

                // Retrieve immediate context
                var context = DeviceManager.Direct3DContext;

                // Clear depth stencil view
                context.ClearDepthStencilView(DepthStencilView, DepthStencilClearFlags.Depth | DepthStencilClearFlags.Stencil, 1.0f, 0);
                // Clear render target view
                context.ClearRenderTargetView(RenderTargetView, background);

                // Create viewProjection matrix
                var viewProjection = Matrix.Multiply(viewMatrix, projectionMatrix);

                var boundingFrustum = new BoundingFrustum(viewProjection);

                // Extract camera position from view
                var camPosition = Matrix.Transpose(Matrix.Invert(viewMatrix)).Column4;
                cameraPosition = new Vector3(camPosition.X, camPosition.Y, camPosition.Z);

                // If Keys.CtrlKey is down, auto rotate viewProjection based on time
                var time = clock.ElapsedMilliseconds / 1000.0f;

                var perFrame = new ConstantBuffers.PerFrame();
                perFrame.Light.Color = new Color(0.8f, 0.8f, 0.8f, 1.0f);
                var lightDir = Vector3.Transform(new Vector3(1f, -1f, -1f), worldMatrix);
                perFrame.Light.Direction = new Vector3(lightDir.X, lightDir.Y, lightDir.Z);// new Vector3(Vector3.Transform(new Vector3(1f, -1f, 1f), worldMatrix * Matrix.RotationAxis(Vector3.UnitY, time)).ToArray().Take(3).ToArray());

                perFrame.CameraPosition = cameraPosition;
                context.UpdateSubresource(ref perFrame, perFrameBuffer);

                // Render each object

                var perMaterial = new ConstantBuffers.PerMaterial();
                perMaterial.Ambient = new Color4(0.2f);
                perMaterial.Diffuse = Color.White;
                perMaterial.Emissive = new Color4(0);
                perMaterial.Specular = Color.White;
                perMaterial.SpecularPower = 20f;
                perMaterial.UVTransform = Matrix.Identity;
                context.UpdateSubresource(ref perMaterial, perMaterialBuffer);

                if (showNormals)
                    context.GeometryShader.Set(debugNormals);

                var perObject = new ConstantBuffers.PerObject();

                // MESH

                // Provide the material constant buffer to the mesh renderer
                context.VertexShader.Set(vertexShader);
                context.PixelShader.Set(blinnPhongShader);

                int drawn = 0;

                if (gbufferIndex == -2)
                {
                    // Regular forward rendering
                    meshes.ForEach((m) =>
                    {
                        perObject.World = m.World * worldMatrix;

                        var center = Vector3.Transform(m.Mesh.Extent.Center, perObject.World);
                        var offset = new Vector3(center.X, center.Y, center.Z) - m.Mesh.Extent.Center;
                        var cullCheck = boundingFrustum.Contains(new BoundingBox(m.Mesh.Extent.Min + offset, m.Mesh.Extent.Max + offset));
                        if (cullCheck == ContainmentType.Intersects || cullCheck == ContainmentType.Contains)
                        {
                            perObject.WorldInverseTranspose = Matrix.Transpose(Matrix.Invert(perObject.World));
                            perObject.WorldViewProjection = perObject.World * viewProjection;
                            perObject.ViewProjection = viewProjection;
                            perObject.Transpose();
                            context.UpdateSubresource(ref perObject, perObjectBuffer);

                            m.EnableNormalMap = enableNormalMap;
                            m.PerMaterialBuffer = perMaterialBuffer;
                            m.PerArmatureBuffer = perArmatureBuffer;
                            m.Render();
                            drawn++;
                        }
                    });
                }
                else
                {
                    GBuffer activeGBuffer = gbuffer;
                    LightRenderer activeLightRenderer = lightRenderer;
                    if (keyToggles[Keys.M])
                    {
                        activeGBuffer = gbufferMS;
                        activeLightRenderer = lightRendererMS;
                    }

                    // Deferred rendering
                    context.VertexShader.Set(fillGBufferVS);
                    context.PixelShader.Set(fillGBufferPS);
                    activeGBuffer.Clear(context, new Color(0, 0, 0, 0));
                    activeGBuffer.Bind(context);

                    meshes.ForEach((m) =>
                    {
                        perObject.World = m.World * worldMatrix;

                        //var center = Vector3.Transform(m.Mesh.Extent.Center, perObject.World);
                        //var offset = new Vector3(center.X, center.Y, center.Z) - m.Mesh.Extent.Center;
                        //var cullCheck = boundingFrustum.Contains(new BoundingBox(m.Mesh.Extent.Min + offset, m.Mesh.Extent.Max + offset));
                        //if (cullCheck == ContainmentType.Intersects || cullCheck == ContainmentType.Contains)
                        {
                            perObject.WorldInverseTranspose = Matrix.Transpose(Matrix.Invert(perObject.World));
                            perObject.WorldViewProjection = perObject.World * viewProjection;
                            perObject.ViewProjection = viewProjection;
                            perObject.View = viewMatrix;
                            perObject.Projection = projectionMatrix;
                            perObject.InverseView = Matrix.Invert(viewMatrix);
                            perObject.InverseProjection = Matrix.Invert(projectionMatrix);
                            perObject.Transpose();
                            context.UpdateSubresource(ref perObject, perObjectBuffer);

                            m.EnableNormalMap = enableNormalMap;
                            m.PerMaterialBuffer = perMaterialBuffer;
                            m.PerArmatureBuffer = perArmatureBuffer;
                            m.Render();
                            drawn++;
                        }
                    });
                    activeGBuffer.Unbind(context);

                    if (gbufferIndex == -1)
                    {
                        // Light pass
                        sphereRenderer.PerMaterialBuffer = perMaterialBuffer;
                        sphereRenderer.PerArmatureBuffer = perArmatureBuffer;
                        //coneRenderer.PerMaterialBuffer = perMaterialBuffer;
                        //coneRenderer.PerArmatureBuffer = perArmatureBuffer;

                        context.PixelShader.SetConstantBuffer(0, perObjectBuffer);

                        perObject.ViewProjection = viewProjection;
                        perObject.View = viewMatrix;
                        perObject.Projection = projectionMatrix;
                        perObject.InverseView = Matrix.Invert(viewMatrix);
                        perObject.InverseProjection = Matrix.Invert(projectionMatrix);

                        activeLightRenderer.Debug = gbufferIndex;
                        activeLightRenderer.Clear(context);
                        activeLightRenderer.Frustum = new BoundingFrustum(projectionMatrix);
                        activeLightRenderer.PerObject = perObject;
                        activeLightRenderer.PerObjectBuffer = perObjectBuffer;
                        activeLightRenderer.Bind(context);
                        activeLightRenderer.Render();
                        activeLightRenderer.Unbind(context);
                        context.OutputMerger.SetRenderTargets(this.DepthStencilView, this.RenderTargetView);

                        // Render the light buffer
                        saQuad.Shader = null; // use default shader
                        saQuad.ShaderResources = new[] { activeLightRenderer.SRV };
                        saQuad.Render();
                        // reset shader resources
                        saQuad.ShaderResources = null;
                    }
                    else
                    {
                        // Bind the output render targets
                        context.OutputMerger.SetRenderTargets(this.DepthStencilView, this.RenderTargetView);

                        // Render debug shaders
                        saQuad.ShaderResources = activeGBuffer.SRVs.ToArray().Concat(new[] { activeGBuffer.DSSRV }).ToArray();
                        perObject.World = worldMatrix;
                        perObject.WorldInverseTranspose = Matrix.Transpose(Matrix.Invert(perObject.World));
                        perObject.WorldViewProjection = perObject.World * viewProjection;
                        perObject.ViewProjection = viewProjection;
                        perObject.View = viewMatrix;
                        perObject.InverseView = Matrix.Invert(viewMatrix);
                        perObject.Projection = projectionMatrix;
                        perObject.InverseProjection = Matrix.Invert(projectionMatrix);
                        perObject.Transpose();
                        context.UpdateSubresource(ref perObject, perObjectBuffer);

                        context.PixelShader.SetConstantBuffer(0, perObjectBuffer);
                        if (keyToggles[Keys.M])
                            saQuad.Shader = DebugGBufferMS[gbufferIndex];
                        else
                            saQuad.Shader = DebugGBuffer[gbufferIndex];
                        saQuad.Render();
                    }
                }

                // Render FPS
                fps.Render();

                // Render instructions + position changes
                textRenderer.Render();

                if (keyToggles[Keys.PrintScreen])
                {
                    keyToggles[Keys.PrintScreen] = false;

                    //using (var resource = this.RenderTargetView.ResourceAs<Texture2D>())
                    //using (var bm = CopyTexture.SaveToBitmap(DeviceManager, resource))
                    //{
                    //    bm.Save("RT0.png");
                    //}

                    CopyTexture.SaveToFile(DeviceManager, lightRenderer.SRV.ResourceAs<Texture2D>(), "test.dds");

                    gbuffer.SaveToFiles();

                    //using (var resource = this.RenderTargetView.ResourceAs<Texture2D>())
                    //using (var bm0 = CopyTexture.SaveToWICBitmap(DeviceManager, resource))
                    //using (var bm1 = CopyTexture.SaveToWICBitmap(DeviceManager, gbuffer.RT1))
                    //using (var bm2 = CopyTexture.SaveToWICBitmap(DeviceManager, gbuffer.RT2))
                    //using (var bm3 = CopyTexture.SaveToWICBitmap(DeviceManager, gbuffer.DS0))
                    //using (var bm4 = CopyTexture.SaveToWICBitmap(DeviceManager, this.DepthBuffer))
                    //{
                        //CopyTexture.SaveToFile(DeviceManager, gbuffer.RT0, "RT0.dds");

                        //CopyTexture.SaveToFile(DeviceManager, resource, "test.dds");
                        //CopyTexture.SaveToFile(DeviceManager, this.DepthBuffer, "DepthBuffer.png", SharpDX.DXGI.Format.R32_Float);
                        //if (bm0 != null)
                        //    CopyTexture.SaveBitmap(DeviceManager, bm0, "RT0.png");
                        //if (bm1 != null)
                        //    CopyTexture.SaveBitmap(DeviceManager, bm1, "RT1.png");
                        //if (bm2 != null)
                        //    CopyTexture.SaveBitmap(DeviceManager, bm2, "RT2.png");
                        //if (bm3 != null)
                        //    CopyTexture.SaveBitmap(DeviceManager, bm3, "DS0.png");
                        //if (bm4 != null)
                        //    CopyTexture.SaveBitmap(DeviceManager, bm3, "DepthBuffer.png");
                    //}
                }

                // Present the frame
                Present();
            });
            #endregion
        }
        public override void Run()
        {
            GBuffer gbuffer = ToDispose(new GBuffer(this.RenderTargetSize.Width,
                this.RenderTargetSize.Height,
                new SampleDescription(1, 0),
                Format.R8G8B8A8_UNorm,
                Format.R32_UInt,
                Format.R8G8B8A8_UNorm));
            gbuffer.Initialize(this);

            GBuffer gbufferMS = ToDispose(new GBuffer(this.RenderTargetSize.Width,
                this.RenderTargetSize.Height,
                new SampleDescription(4, 0),
                Format.R8G8B8A8_UNorm,
                Format.R32_UInt,
                Format.R8G8B8A8_UNorm));
            gbufferMS.Initialize(this);

            ScreenAlignedQuadRenderer saQuad = ToDispose(new ScreenAlignedQuadRenderer());
            saQuad.Initialize(this);

            #region Create renderers

            // Note: the renderers take care of creating their own
            // device resources and listen for DeviceManager.OnInitialize

            // Create Light accumulator
            var sphereMesh = Common.Mesh.LoadFromFile("Sphere.cmo").FirstOrDefault();
            //var coneMesh = Common.Mesh.LoadFromFile("Cone.cmo").FirstOrDefault();

            var sphereRenderer = ToDispose(new MeshRenderer(sphereMesh));
            sphereRenderer.Initialize(this);
            //var coneRenderer = new MeshRenderer(coneMesh);
            //coneRenderer.Initialize(this);
            var lightRenderer = ToDispose(new LightRenderer(sphereRenderer, saQuad, gbuffer));
            var lightRendererMS = ToDispose(new LightRenderer(sphereRenderer, saQuad, gbufferMS));

            lightRenderer.Lights.Add(new PerLight
            {
                Color = new Color4(0.2f, 0.2f, 0.2f, 1.0f),
                Position = new Vector3(1, 1, 1),
                Direction = new Vector3(0, 0, 1),
                Range = 10,
                Type = LightType.Ambient
            });
            //lightRenderer.Lights.Add(new PerLight
            //{
            //    Color = new Color4(0.2f, 0.2f, 0.2f, 1.0f),
            //    Direction = new Vector3(0, -1, 1),
            //    Type = LightType.Directional
            //});

            lightRenderer.Lights.Add(new PerLight
            {
                Color = Color.LightPink,
                Position = new Vector3(22.4f, 2.6f, -8.98f),
                Range = 20,
                Type = LightType.Point
            });
            lightRenderer.Lights.Add(new PerLight
            {
                Color = new Color4(1.0f, 1.0f, 1.0f, 1.0f),
                Position = new Vector3(0, 10, 1),
                Range = 10,
                Type = LightType.Point
            });
            var aLight = new PerLight
            {
                Color = new Color4(1.0f, 0.0f, 0.0f, 1.0f),
                Position = new Vector3(9.86f, 10.92f, -6.74f),
                Range = 20,
                Type = LightType.Point
            };
            lightRenderer.Lights.Add(aLight);
            aLight.Color = Color.Blue;
            aLight.Position.X = 2.32f;
            lightRenderer.Lights.Add(aLight);
            aLight.Color = Color.LightYellow;
            aLight.Position.X = -1.0f;
            lightRenderer.Lights.Add(aLight);
            aLight.Color = Color.Cyan;
            aLight.Position.X = -5.11f;
            lightRenderer.Lights.Add(aLight);
            aLight.Color = Color.Lime;
            aLight.Position.X = -8.33f;
            lightRenderer.Lights.Add(aLight);
            aLight.Color = Color.AliceBlue;
            aLight.Position.X = -12.55f;
            lightRenderer.Lights.Add(aLight);

            aLight.Position.Z = 5.02f;

            aLight.Color = Color.Red;
            aLight.Position.X = 9.86f;
            lightRenderer.Lights.Add(aLight);
            aLight.Color = Color.Blue;
            aLight.Position.X = 2.32f;
            lightRenderer.Lights.Add(aLight);
            aLight.Color = Color.LightYellow;
            aLight.Position.X = -1.0f;
            lightRenderer.Lights.Add(aLight);
            aLight.Color = Color.Cyan;
            aLight.Position.X = -5.11f;
            lightRenderer.Lights.Add(aLight);
            aLight.Color = Color.Lime;
            aLight.Position.X = -8.33f;
            lightRenderer.Lights.Add(aLight);
            aLight.Color = Color.AliceBlue;
            aLight.Position.X = -12.55f;
            lightRenderer.Lights.Add(aLight);

            //lightRenderer.Lights.Add(new PerLight
            //{
            //    Color = new Color4(1.0f, 1.0f, 1.0f, 1.0f),
            //    Position = new Vector3(-2, 1, 2),
            //    Direction = new Vector3(0, -1, 0),
            //    Range = 2f,
            //    Type = LightType.Spot,
            //    SpotInnerCosine = (float)Math.Cos(0.4) / 2.0f,
            //    SpotOuterCosine = (float)Math.Cos(1) / 2.0f,
            //});
            lightRenderer.Initialize(this);
            lightRendererMS.Lights.AddRange(lightRenderer.Lights);
            lightRendererMS.Initialize(this);

            // Create a axis-grid renderer
            var axisGrid = ToDispose(new AxisGridRenderer());
            axisGrid.Initialize(this);

            // Create and initialize the mesh renderer
            var loadedMesh = Common.Mesh.LoadFromFile("Sponza.cmo");
            //var loadedMesh = Common.Mesh.LoadFromFile("Scene.cmo");

            List<MeshRenderer> meshes = new List<MeshRenderer>();
            meshes.AddRange((from mesh in loadedMesh
                             select ToDispose(new MeshRenderer(mesh))));

            meshes.ForEach(m => m.Initialize(this));
            var meshWorld = Matrix.Identity;

            // Set the first animation as the current animation and start clock
            meshes.ForEach(m => {
                if (m.Mesh.Animations != null && m.Mesh.Animations.Any())
                    m.CurrentAnimation = m.Mesh.Animations.First().Value;
                m.Clock.Start();
            });

            // Create and initialize a Direct2D FPS text renderer
            var fps = ToDispose(new Common.FpsRenderer("Calibri", Color.CornflowerBlue, new Point(8, 8), 16));
            fps.Initialize(this);

            // Create and initialize a general purpose Direct2D text renderer
            // This will display some instructions and the current view and rotation offsets
            var textRenderer = ToDispose(new Common.TextRenderer("Calibri", Color.CornflowerBlue, new Point(8, 40), 12));
            textRenderer.Initialize(this);

            #endregion

            // Initialize the world matrix
            var worldMatrix = Matrix.Identity;

            // Set the camera position slightly behind (z)
            var cameraPosition = new Vector3(15, 15, -1);
            var cameraTarget = new Vector3(-15, 5, -1); // Looking at the origin 0,0,0
            var cameraUp = Vector3.UnitY; // Y+ is Up

            // Prepare matrices
            // Create the view matrix from our camera position, look target and up direction
            var viewMatrix = Matrix.LookAtRH(cameraPosition, cameraTarget, cameraUp);
            viewMatrix.TranslationVector += new Vector3(0, -0.98f, 0);

            // Create the projection matrix
            /* FoV 60degrees = Pi/3 radians */
            // Aspect ratio (based on window size), Near clip, Far clip
            var projectionMatrix = Matrix.PerspectiveFovRH((float)Math.PI / 3f, Width / (float)Height, 2f, 100f);

            // Maintain the correct aspect ratio on resize
            Window.Resize += (s, e) =>
            {
                projectionMatrix = Matrix.PerspectiveFovRH((float)Math.PI / 3f, Width / (float)Height, 2f, 100f);
            };

            #region Rotation and window event handlers

            // Create a rotation vector to keep track of the rotation
            // around each of the axes
            var rotation = new Vector3(0.0f, 0.0f, 0.0f);

            var gbufferIndex = -1;

            Dictionary<Keys, bool> keyToggles = new Dictionary<Keys, bool>();
            keyToggles[Keys.Z] = false;
            keyToggles[Keys.F] = false;
            keyToggles[Keys.M] = false;
            keyToggles[Keys.PrintScreen] = false;

            // We will call this action to update text
            // for the text renderer
            Action updateText = () =>
            {
                textRenderer.Text =
                    String.Format(
                    "{0} (+/- to change)"+
                    "\n{1} (M to toggle)",
                    gbufferIndex > -1 ? DebugGBuffer[gbufferIndex].DebugName : gbufferIndex < -1 ? "Forward Render (1 light)" : "Deferred with 15 lights",
                    gbufferIndex == -2 || keyToggles[Keys.M] ? "Multisampled" : "No anti-aliasing"
                    );
            };

            // Support keyboard/mouse input to rotate or move camera view
            var moveFactor = 0.02f; // how much to change on each keypress
            var shiftKey = false;
            var ctrlKey = false;
            var background = Color.White;
            var showNormals = false;
            var enableNormalMap = true;
            Window.KeyDown += (s, e) =>
            {
                var context = DeviceManager.Direct3DContext;

                shiftKey = e.Shift;
                ctrlKey = e.Control;

                switch (e.KeyCode)
                {
                    case Keys.Add:
                        gbufferIndex++;

                        if (gbufferIndex >= DebugGBuffer.Count)
                            gbufferIndex = -2;
                        break;
                    case Keys.Subtract:
                        gbufferIndex--;

                        if (gbufferIndex < -2)
                            gbufferIndex = DebugGBuffer.Count - 1;
                        break;
                    // WASD -> pans view
                    case Keys.A:
                        viewMatrix.TranslationVector += new Vector3(moveFactor * 12, 0f, 0f);
                        break;
                    case Keys.D:
                        viewMatrix.TranslationVector -= new Vector3(moveFactor * 12, 0f, 0f);
                        break;
                    case Keys.S:
                        if (shiftKey)
                            viewMatrix.TranslationVector += new Vector3(0f, moveFactor * 12, 0f);
                        else
                            viewMatrix.TranslationVector -= new Vector3(0f, 0f, 1) * moveFactor * 12;
                        break;
                    case Keys.W:
                        if (shiftKey)
                            viewMatrix.TranslationVector -= new Vector3(0f, moveFactor * 12, 0f);
                        else
                            viewMatrix.TranslationVector += new Vector3(0f, 0f, 1) * moveFactor * 12;
                        break;
                    // Up/Down and Left/Right - rotates around X / Y respectively
                    // (Mouse wheel rotates around Z)
                    case Keys.Down:
                        worldMatrix *= Matrix.RotationX(moveFactor);
                        rotation += new Vector3(moveFactor, 0f, 0f);
                        break;
                    case Keys.Up:
                        worldMatrix *= Matrix.RotationX(-moveFactor);
                        rotation -= new Vector3(moveFactor, 0f, 0f);
                        break;
                    case Keys.Left:
                        worldMatrix *= Matrix.RotationY(moveFactor);
                        rotation += new Vector3(0f, moveFactor, 0f);
                        break;
                    case Keys.Right:
                        worldMatrix *= Matrix.RotationY(-moveFactor);
                        rotation -= new Vector3(0f, moveFactor, 0f);
                        break;
                    case Keys.T:
                        fps.Show = !fps.Show;
                        textRenderer.Show = !textRenderer.Show;
                        break;
                    case Keys.B:
                        if (background == Color.White)
                        {
                            background = new Color(30, 30, 34);
                        }
                        else
                        {
                            background = Color.White;
                        }
                        break;
                    case Keys.G:
                        axisGrid.Show = !axisGrid.Show;
                        break;
                    case Keys.P:
                        // Pause or resume mesh animation
                        meshes.ForEach(m => {
                            if (m.Clock.IsRunning)
                                m.Clock.Stop();
                            else
                                m.Clock.Start();
                        });
                        break;
                    case Keys.X:
                        // To test for correct resource recreation
                        // Simulate device reset or lost.
                        System.Diagnostics.Debug.WriteLine(SharpDX.Diagnostics.ObjectTracker.ReportActiveObjects());
                        DeviceManager.Initialize(DeviceManager.Dpi);
                        System.Diagnostics.Debug.WriteLine(SharpDX.Diagnostics.ObjectTracker.ReportActiveObjects());
                        break;
                    case Keys.Z:
                        keyToggles[Keys.Z] = !keyToggles[Keys.Z];
                        if (keyToggles[Keys.Z])
                        {
                            context.PixelShader.Set(depthPixelShader);
                        }
                        else
                        {
                            context.PixelShader.Set(pixelShader);
                        }
                        break;
                    case Keys.F:
                        keyToggles[Keys.F] = !keyToggles[Keys.F];
                        RasterizerStateDescription rasterDesc;
                        if (context.Rasterizer.State != null)
                            rasterDesc = context.Rasterizer.State.Description;
                        else
                            rasterDesc = new RasterizerStateDescription()
                            {
                                CullMode = CullMode.Back,
                                FillMode = FillMode.Solid
                            };
                        if (keyToggles[Keys.F])
                        {
                            rasterDesc.FillMode = FillMode.Wireframe;
                            context.Rasterizer.State = ToDispose(new RasterizerState(context.Device, rasterDesc));
                        }
                        else
                        {
                            rasterDesc.FillMode = FillMode.Solid;
                            context.Rasterizer.State = ToDispose(new RasterizerState(context.Device, rasterDesc));
                        }
                        break;
                    case Keys.M:
                        keyToggles[Keys.M] = !keyToggles[Keys.M];
                        break;
                    case Keys.N:
                        if (!shiftKey)
                            showNormals = !showNormals;
                        else
                            enableNormalMap = !enableNormalMap;

                        break;
                    case Keys.D1:
                        context.PixelShader.Set(pixelShader);
                        break;
                    //case Keys.D2:
                    //    context.PixelShader.Set(lambertShader);
                    //    break;
                    //case Keys.D3:
                    //    context.PixelShader.Set(phongShader);
                    //    break;
                    case Keys.D4:
                        context.PixelShader.Set(blinnPhongShader);
                        break;
                    case Keys.Insert:
                        keyToggles[Keys.PrintScreen] = true;
                        break;

                }

                updateText();
            };
            Window.KeyUp += (s, e) =>
            {
                // Clear the shift/ctrl keys so they aren't sticky
                if (e.KeyCode == Keys.ShiftKey)
                    shiftKey = false;
                if (e.KeyCode == Keys.ControlKey)
                    ctrlKey = false;
            };
            Window.MouseWheel += (s, e) =>
            {
                if (shiftKey)
                {
                    // Zoom in/out
                    viewMatrix.TranslationVector += new Vector3(0f, 0f, (e.Delta / 120f) * moveFactor * 2);
                }
                else
                {
                    // rotate around Z-axis
                    viewMatrix *= Matrix.RotationZ((e.Delta / 120f) * moveFactor);
                    rotation += new Vector3(0f, 0f, (e.Delta / 120f) * moveFactor);
                }
                updateText();
            };

            var lastX = 0;
            var lastY = 0;

            Window.MouseDown += (s, e) =>
            {
                if (e.Button == MouseButtons.Left)
                {
                    lastX = e.X;
                    lastY = e.Y;
                }
            };

            Window.MouseMove += (s, e) =>
            {
                if (e.Button == MouseButtons.Left)
                {
                    var yRotate = lastX - e.X;
                    var xRotate = lastY - e.Y;
                    lastY = e.Y;
                    lastX = e.X;

                    // Mouse move changes
                    viewMatrix *= Matrix.RotationX(-xRotate * moveFactor);
                    viewMatrix *= Matrix.RotationY(-yRotate * moveFactor);

                    updateText();
                }
            };

            // Display instructions with initial values
            updateText();

            #endregion

            var clock = new System.Diagnostics.Stopwatch();
            clock.Start();

            #region Render loop

            // Create and run the render loop
            RenderLoop.Run(Window, () =>
            {
                // Start of frame:

                // Retrieve immediate context
                var context = DeviceManager.Direct3DContext;

                // Clear depth stencil view
                context.ClearDepthStencilView(DepthStencilView, DepthStencilClearFlags.Depth | DepthStencilClearFlags.Stencil, 1.0f, 0);
                // Clear render target view
                context.ClearRenderTargetView(RenderTargetView, background);

                // Create viewProjection matrix
                var viewProjection = Matrix.Multiply(viewMatrix, projectionMatrix);

                var boundingFrustum = new BoundingFrustum(viewProjection);

                // Extract camera position from view
                var camPosition = Matrix.Transpose(Matrix.Invert(viewMatrix)).Column4;
                cameraPosition = new Vector3(camPosition.X, camPosition.Y, camPosition.Z);

                // If Keys.CtrlKey is down, auto rotate viewProjection based on time
                var time = clock.ElapsedMilliseconds / 1000.0f;

                var perFrame = new ConstantBuffers.PerFrame();
                perFrame.Light.Color = new Color(0.8f, 0.8f, 0.8f, 1.0f);
                var lightDir = Vector3.Transform(new Vector3(1f, -1f, -1f), worldMatrix);
                perFrame.Light.Direction = new Vector3(lightDir.X, lightDir.Y, lightDir.Z);// new Vector3(Vector3.Transform(new Vector3(1f, -1f, 1f), worldMatrix * Matrix.RotationAxis(Vector3.UnitY, time)).ToArray().Take(3).ToArray());

                perFrame.CameraPosition = cameraPosition;
                context.UpdateSubresource(ref perFrame, perFrameBuffer);

                // Render each object

                var perMaterial = new ConstantBuffers.PerMaterial();
                perMaterial.Ambient = new Color4(0.2f);
                perMaterial.Diffuse = Color.White;
                perMaterial.Emissive = new Color4(0);
                perMaterial.Specular = Color.White;
                perMaterial.SpecularPower = 20f;
                perMaterial.UVTransform = Matrix.Identity;
                context.UpdateSubresource(ref perMaterial, perMaterialBuffer);

                if (showNormals)
                    context.GeometryShader.Set(debugNormals);

                var perObject = new ConstantBuffers.PerObject();

                // MESH

                // Provide the material constant buffer to the mesh renderer
                context.VertexShader.Set(vertexShader);
                context.PixelShader.Set(blinnPhongShader);

                int drawn = 0;

                if (gbufferIndex == -2)
                {
                    // Regular forward rendering
                    meshes.ForEach((m) =>
                    {
                        perObject.World = m.World * worldMatrix;

                        var center = Vector3.Transform(m.Mesh.Extent.Center, perObject.World);
                        var offset = new Vector3(center.X, center.Y, center.Z) - m.Mesh.Extent.Center;
                        var cullCheck = boundingFrustum.Contains(new BoundingBox(m.Mesh.Extent.Min + offset, m.Mesh.Extent.Max + offset));
                        if (cullCheck == ContainmentType.Intersects || cullCheck == ContainmentType.Contains)
                        {
                            perObject.WorldInverseTranspose = Matrix.Transpose(Matrix.Invert(perObject.World));
                            perObject.WorldViewProjection = perObject.World * viewProjection;
                            perObject.ViewProjection = viewProjection;
                            perObject.Transpose();
                            context.UpdateSubresource(ref perObject, perObjectBuffer);

                            m.EnableNormalMap = enableNormalMap;
                            m.PerMaterialBuffer = perMaterialBuffer;
                            m.PerArmatureBuffer = perArmatureBuffer;
                            m.Render();
                            drawn++;
                        }
                    });
                }
                else
                {
                    GBuffer activeGBuffer = gbuffer;
                    LightRenderer activeLightRenderer = lightRenderer;
                    if (keyToggles[Keys.M])
                    {
                        activeGBuffer = gbufferMS;
                        activeLightRenderer = lightRendererMS;
                    }

                    // Deferred rendering
                    context.VertexShader.Set(fillGBufferVS);
                    context.PixelShader.Set(fillGBufferPS);
                    activeGBuffer.Clear(context, new Color(0, 0, 0, 0));
                    activeGBuffer.Bind(context);

                    meshes.ForEach((m) =>
                    {
                        perObject.World = m.World * worldMatrix;

                        //var center = Vector3.Transform(m.Mesh.Extent.Center, perObject.World);
                        //var offset = new Vector3(center.X, center.Y, center.Z) - m.Mesh.Extent.Center;
                        //var cullCheck = boundingFrustum.Contains(new BoundingBox(m.Mesh.Extent.Min + offset, m.Mesh.Extent.Max + offset));
                        //if (cullCheck == ContainmentType.Intersects || cullCheck == ContainmentType.Contains)
                        {
                            perObject.WorldInverseTranspose = Matrix.Transpose(Matrix.Invert(perObject.World));
                            perObject.WorldViewProjection = perObject.World * viewProjection;
                            perObject.ViewProjection = viewProjection;
                            perObject.View = viewMatrix;
                            perObject.Projection = projectionMatrix;
                            perObject.InverseView = Matrix.Invert(viewMatrix);
                            perObject.InverseProjection = Matrix.Invert(projectionMatrix);
                            perObject.Transpose();
                            context.UpdateSubresource(ref perObject, perObjectBuffer);

                            m.EnableNormalMap = enableNormalMap;
                            m.PerMaterialBuffer = perMaterialBuffer;
                            m.PerArmatureBuffer = perArmatureBuffer;
                            m.Render();
                            drawn++;
                        }
                    });
                    activeGBuffer.Unbind(context);

                    if (gbufferIndex == -1)
                    {
                        // Light pass
                        sphereRenderer.PerMaterialBuffer = perMaterialBuffer;
                        sphereRenderer.PerArmatureBuffer = perArmatureBuffer;
                        //coneRenderer.PerMaterialBuffer = perMaterialBuffer;
                        //coneRenderer.PerArmatureBuffer = perArmatureBuffer;

                        context.PixelShader.SetConstantBuffer(0, perObjectBuffer);

                        perObject.ViewProjection = viewProjection;
                        perObject.View = viewMatrix;
                        perObject.Projection = projectionMatrix;
                        perObject.InverseView = Matrix.Invert(viewMatrix);
                        perObject.InverseProjection = Matrix.Invert(projectionMatrix);

                        activeLightRenderer.Debug = gbufferIndex;
                        activeLightRenderer.Clear(context);
                        activeLightRenderer.Frustum = new BoundingFrustum(projectionMatrix);
                        activeLightRenderer.PerObject = perObject;
                        activeLightRenderer.PerObjectBuffer = perObjectBuffer;
                        activeLightRenderer.Bind(context);
                        activeLightRenderer.Render();
                        activeLightRenderer.Unbind(context);
                        context.OutputMerger.SetRenderTargets(this.DepthStencilView, this.RenderTargetView);

                        // Render the light buffer
                        saQuad.Shader = null; // use default shader
                        saQuad.ShaderResources = new[] { activeLightRenderer.SRV };
                        saQuad.Render();
                        // reset shader resources
                        saQuad.ShaderResources = null;
                    }
                    else
                    {
                        // Bind the output render targets
                        context.OutputMerger.SetRenderTargets(this.DepthStencilView, this.RenderTargetView);

                        // Render debug shaders
                        saQuad.ShaderResources = activeGBuffer.SRVs.ToArray().Concat(new[] { activeGBuffer.DSSRV }).ToArray();
                        perObject.World = worldMatrix;
                        perObject.WorldInverseTranspose = Matrix.Transpose(Matrix.Invert(perObject.World));
                        perObject.WorldViewProjection = perObject.World * viewProjection;
                        perObject.ViewProjection = viewProjection;
                        perObject.View = viewMatrix;
                        perObject.InverseView = Matrix.Invert(viewMatrix);
                        perObject.Projection = projectionMatrix;
                        perObject.InverseProjection = Matrix.Invert(projectionMatrix);
                        perObject.Transpose();
                        context.UpdateSubresource(ref perObject, perObjectBuffer);

                        context.PixelShader.SetConstantBuffer(0, perObjectBuffer);
                        if (keyToggles[Keys.M])
                            saQuad.Shader = DebugGBufferMS[gbufferIndex];
                        else
                            saQuad.Shader = DebugGBuffer[gbufferIndex];
                        saQuad.Render();
                    }
                }

                // Render FPS
                fps.Render();

                // Render instructions + position changes
                textRenderer.Render();

                if (keyToggles[Keys.PrintScreen])
                {
                    keyToggles[Keys.PrintScreen] = false;

                    //using (var resource = this.RenderTargetView.ResourceAs<Texture2D>())
                    //using (var bm = CopyTexture.SaveToBitmap(DeviceManager, resource))
                    //{
                    //    bm.Save("RT0.png");
                    //}

                    CopyTexture.SaveToFile(DeviceManager, lightRenderer.SRV.ResourceAs<Texture2D>(), "test.dds");

                    gbuffer.SaveToFiles();

                    //using (var resource = this.RenderTargetView.ResourceAs<Texture2D>())
                    //using (var bm0 = CopyTexture.SaveToWICBitmap(DeviceManager, resource))
                    //using (var bm1 = CopyTexture.SaveToWICBitmap(DeviceManager, gbuffer.RT1))
                    //using (var bm2 = CopyTexture.SaveToWICBitmap(DeviceManager, gbuffer.RT2))
                    //using (var bm3 = CopyTexture.SaveToWICBitmap(DeviceManager, gbuffer.DS0))
                    //using (var bm4 = CopyTexture.SaveToWICBitmap(DeviceManager, this.DepthBuffer))
                    //{
                        //CopyTexture.SaveToFile(DeviceManager, gbuffer.RT0, "RT0.dds");

                        //CopyTexture.SaveToFile(DeviceManager, resource, "test.dds");
                        //CopyTexture.SaveToFile(DeviceManager, this.DepthBuffer, "DepthBuffer.png", SharpDX.DXGI.Format.R32_Float);
                        //if (bm0 != null)
                        //    CopyTexture.SaveBitmap(DeviceManager, bm0, "RT0.png");
                        //if (bm1 != null)
                        //    CopyTexture.SaveBitmap(DeviceManager, bm1, "RT1.png");
                        //if (bm2 != null)
                        //    CopyTexture.SaveBitmap(DeviceManager, bm2, "RT2.png");
                        //if (bm3 != null)
                        //    CopyTexture.SaveBitmap(DeviceManager, bm3, "DS0.png");
                        //if (bm4 != null)
                        //    CopyTexture.SaveBitmap(DeviceManager, bm3, "DepthBuffer.png");
                    //}
                }

                // Present the frame
                Present();
            });
            #endregion
        }