#region File Description //----------------------------------------------------------------------------- // VertexLighting.cs // // Microsoft XNA Community Game Platform // Copyright (C) Microsoft Corporation. All rights reserved. //----------------------------------------------------------------------------- #endregion #region Using Statements using System; using System.Collections.Generic; using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Audio; using Microsoft.Xna.Framework.Content; using Microsoft.Xna.Framework.Graphics; using Microsoft.Xna.Framework.Input; using Microsoft.Xna.Framework.Storage; #endregion namespace VertexLightingSample { /// /// The central class for the sample Game. /// public class VertexLighting : Microsoft.Xna.Framework.Game { #region Sample Fields private GraphicsDeviceManager graphics; private SampleArcBallCamera camera; private Model[] sampleMeshes; private SampleGrid grid; private int activeMesh; private bool enableAdvancedEffect = true; private GamePadState lastGpState; private KeyboardState lastKbState; #endregion /// /// Example 1.1: Effect objects used for this example /// #region Effect Fields private Effect noLightingEffect; private Effect vertexLightingEffect; private EffectParameter projectionParameter; private EffectParameter viewParameter; private EffectParameter worldParameter; private EffectParameter lightColorParameter; private EffectParameter lightDirectionParameter; private EffectParameter ambientColorParameter; #endregion /// /// Example 1.2: Data fields corresponding to the effect paramters /// #region Uniform Data Fields private Matrix world, view, projection; private Vector3 diffuseLightDirection; private Vector4 diffuseLightColor; private Vector4 ambientLightColor; #endregion #region Initialization and Cleanup public VertexLighting() { graphics = new GraphicsDeviceManager(this); Content.RootDirectory = "Content"; } /// /// Initialize the sample. /// protected override void Initialize() { base.Initialize(); } /// /// Load the graphics content. /// protected override void LoadContent() { //Set up the reference grid and sample camera grid = new SampleGrid(); grid.GridColor = Color.LimeGreen; grid.GridScale = 1.0f; grid.GridSize = 32; grid.LoadGraphicsContent(graphics.GraphicsDevice); camera = new SampleArcBallCamera( SampleArcBallCameraMode.RollConstrained); camera.Distance = 3; //orbit the camera so we're looking down the z=-1 axis //the acr-ball camera is traditionally oriented to look //at the "front" of an object camera.OrbitRight(MathHelper.Pi); //orbit up a bit for perspective camera.OrbitUp(.2f); sampleMeshes = new Model[5]; //load meshes sampleMeshes[0] = Content.Load("Cube"); sampleMeshes[1] = Content.Load("SphereHighPoly"); sampleMeshes[2] = Content.Load("SphereLowPoly"); sampleMeshes[3] = Content.Load("Cylinder"); sampleMeshes[4] = Content.Load("Cone"); //Example 1.2 //create the effect objects that correspond to the effect files //that have been imported via the Content Pipeline noLightingEffect = Content.Load("FlatShaded"); vertexLightingEffect = Content.Load("VertexLighting"); GetEffectParameters(); //Calculate the projection properties first on any //load callback. That way if the window gets resized, //the perspective matrix is updated accordingly float aspectRatio = (float)graphics.GraphicsDevice.Viewport.Width / (float)graphics.GraphicsDevice.Viewport.Height; float fov = MathHelper.PiOver4 * aspectRatio * 3 / 4; projection = Matrix.CreatePerspectiveFieldOfView(fov, aspectRatio, .1f, 1000f); //create a default world matrix world = Matrix.Identity; //grid requires a projection matrix to draw correctly grid.ProjectionMatrix = projection; //Set the grid to draw on the x/z plane around the origin grid.WorldMatrix = Matrix.Identity; } /// /// Example 1.3 /// This function obtains EffectParameter objects from the Effect objects. /// The EffectParameters are handles to the values in the shaders and are /// effectively how your C# code and your shader code communicate. /// private void GetEffectParameters() { //These parameters are used by both vertexLightingEffect and //noLightingEffect, so we must take care to look up the correct ones. if (enableAdvancedEffect) { worldParameter = vertexLightingEffect.Parameters["world"]; viewParameter = vertexLightingEffect.Parameters["view"]; projectionParameter = vertexLightingEffect.Parameters["projection"]; } else { worldParameter = noLightingEffect.Parameters["world"]; viewParameter = noLightingEffect.Parameters["view"]; projectionParameter = noLightingEffect.Parameters["projection"]; } //These effect parameters are only used by vertexLightingEffect //to indicate the lights' colors and direction lightColorParameter = vertexLightingEffect.Parameters["lightColor"]; lightDirectionParameter = vertexLightingEffect.Parameters["lightDirection"]; ambientColorParameter = vertexLightingEffect.Parameters["ambientColor"]; } #endregion #region Update and Render /// /// Update the game world. /// /// Provides a snapshot of timing values. protected override void Update(GameTime gameTime) { GamePadState gpState = GamePad.GetState(PlayerIndex.One); KeyboardState kbState = Keyboard.GetState(); //check for exit if ((gpState.Buttons.Back == ButtonState.Pressed) || kbState.IsKeyDown(Keys.Escape)) { Exit(); } //Handle inputs for the sample camera camera.HandleDefaultGamepadControls( gpState, gameTime); camera.HandleDefaultKeyboardControls( kbState, gameTime); //handle inputs specific to this sample HandleInput(gameTime, gpState, kbState); //Set the light direction to a fixed value. //This will place the light source behind, to the right, and above the user. diffuseLightDirection = new Vector3(-1, -1, -1); //ensure the light direction is normalized, or //the shader will give some weird results diffuseLightDirection.Normalize(); //set the color of the diffuse light diffuseLightColor = Color.CornflowerBlue.ToVector4(); //set the ambient lighting color ambientLightColor = Color.DarkSlateGray.ToVector4(); //The built-in camera class provides the view matrix view = camera.ViewMatrix; //additionally, the reference grid included in the sample //requires a view matrix to draw correctly grid.ViewMatrix = camera.ViewMatrix; lastGpState = gpState; lastKbState = kbState; base.Update(gameTime); } private void HandleInput(GameTime gameTime, GamePadState gpState, KeyboardState kbState) { float elapsedTime = (float) gameTime.ElapsedGameTime.TotalSeconds; //Handle input for selecting meshes if (((gpState.Buttons.X == ButtonState.Pressed) && (lastGpState.Buttons.X == ButtonState.Released)) || (kbState.IsKeyDown(Keys.Tab) && lastKbState.IsKeyUp(Keys.Tab))) { //switch the active mesh activeMesh = (activeMesh + 1) % sampleMeshes.Length; } //Handle input for selecting the active effect if (((gpState.Buttons.Y == ButtonState.Pressed) && (lastGpState.Buttons.Y == ButtonState.Released)) || (kbState.IsKeyDown(Keys.Space) && lastKbState.IsKeyUp(Keys.Space))) { //toggle the advanced effect enableAdvancedEffect = !enableAdvancedEffect; GetEffectParameters(); } //handle mesh rotation inputs float dx = SampleArcBallCamera.ReadKeyboardAxis(kbState, Keys.Left, Keys.Right) + gpState.ThumbSticks.Left.X; float dy = SampleArcBallCamera.ReadKeyboardAxis(kbState, Keys.Down, Keys.Up) + gpState.ThumbSticks.Left.Y; //apply mesh rotation to world matrix if (dx != 0) { world = world * Matrix.CreateFromAxisAngle(camera.Up, elapsedTime * dx); } if (dy != 0) { world = world * Matrix.CreateFromAxisAngle(camera.Right, elapsedTime * -dy); } } /// /// Example 1.4 /// /// The effect parameters set in this function /// are shared between all of the rendered elements in the scene. /// private void SetSharedEffectParameters() { projectionParameter.SetValue(projection); viewParameter.SetValue(view); worldParameter.SetValue(world); } /// /// Draw the current scene. /// /// Provides a snapshot of timing values. protected override void Draw(GameTime gameTime) { graphics.GraphicsDevice.Clear(Color.Black); //draw the reference grid so it's easier to get our bearings grid.Draw(); //always set the shared effects parameters SetSharedEffectParameters(); if (enableAdvancedEffect) { //Example 1.5 //Since we're using the advanced effect, we'll be setting the effect //parameters for the lighting effect. ambientColorParameter.SetValue(ambientLightColor); lightColorParameter.SetValue(diffuseLightColor); lightDirectionParameter.SetValue(diffuseLightDirection); } //finally, draw the mesh itself DrawSampleMesh(sampleMeshes[activeMesh]); base.Draw(gameTime); } /// /// Example 1.6 /// /// Draws a sample mesh using a single effect with a single technique. /// This pattern is very common in simple effect usage. /// /// public void DrawSampleMesh(Model sampleMesh) { if (sampleMesh == null) return; //our sample meshes only contain a single part, so we don't need to bother //looping over the ModelMesh and ModelMeshPart collections. If the meshes //were more complex, we would repeat all the following code for each part ModelMesh mesh = sampleMesh.Meshes[0]; ModelMeshPart meshPart = mesh.MeshParts[0]; //set the vertex source to the mesh's vertex buffer graphics.GraphicsDevice.SetVertexBuffer(meshPart.VertexBuffer, meshPart.VertexOffset); //set the current index buffer to the sample mesh's index buffer graphics.GraphicsDevice.Indices = meshPart.IndexBuffer; //figure out which effect we're using currently Effect effect; if (enableAdvancedEffect) effect = vertexLightingEffect; else effect = noLightingEffect; //at this point' we're ready to begin drawing //now we loop through the passes in the teqnique, drawing each //one in order for (int i = 0; i < effect.CurrentTechnique.Passes.Count; i++) { //EffectPass.Apply will update the device to //begin using the state information defined in the current pass effect.CurrentTechnique.Passes[i].Apply(); //sampleMesh contains all of the information required to draw //the current mesh graphics.GraphicsDevice.DrawIndexedPrimitives( PrimitiveType.TriangleList, 0, 0, meshPart.NumVertices, meshPart.StartIndex, meshPart.PrimitiveCount); } } #endregion #region Entry Point /// /// The main entry point for the application. /// static void Main() { using (VertexLighting game = new VertexLighting()) { game.Run(); } } #endregion } }