The Holodeck - Part IV

Adding a Model to the Scene

In the last part, we created the room of our virtual holodeck, but a room by itself is pretty boring. Let's start "turning on" our holodeck by bringing in an object.

Source Code

The room itself is a model that we created from a .X file. There are several ways to get .X files for your scenes. Later, we'll be using the Blender program to make models that can later be exported as .X files. But I want to start with a program that you can use to make your own .X models that is much easier to use than Blender, Google's Sketchup. (Sketchup was just bought by another company a few weeks ago - as I'm writing this. I think the name of the company is Trimble. I have no idea what their long term plans are for Sketchup.)

Google built up a large library of user created models called 3D Warehouse. We'll be using a chair model from 3D Warehouse since the models are Sketchup, public domain, and easy to turn into .X models - oh... and I had to pick something to use as a model.  The only license information I've found on these models is here.

You can download the Chair from 3D Warehouse, or you can get the files directly here from XNA3D101. If you want to get it from 3D Warehouse, you will need to install an exporter add-on for Sketchup. If I remember correctly, I used this one. But I don't know who's site that is or whether it's reliable etc. That's part of the reason that I'm making all the files for this chair available here.

Be aware that this chair model had to be exported as a .X file with the textures. Also, I remember having to fix a "problem" with one of the textures being a size that XNA would not allow by resizing it. And I'm sure I've renamed the files; you'll need to use the names I've chosen or match the names up in your code with the names of the files. It's probably easiest to just use my files here. But going through the whole process in Sketchup, with the exporting would be a very "educational" process. I could walk you through the problems I had with this model (mainly just the texture size problem), but every model that you import is going to have it's own specific problems and that's too much to address here. But if you go through the whole process in Sketchup, you can choose your own model rather than the chair that I've picked. The idea here is just to get you started, and then you can import models to your heart's content.

There are three files that Sketchup exported for this model, two textures, and one .X file. All three files need to be placed into the Content folder of your holodeck project, and all three files need to be added to your project. So put them in the folder and add them to HolodeckContent the same way that you placed the .X file for the room into the Contents folder and added it to the HolodeckContent in Part III.

Once the files are part of the project, you're ready to start modifying the code to bring the model into the scene.

Start by modifying the Game1 class's fields to include a declaration for the Model.

Model Chair;

This will store the data for the chair model. Next, declare a world matrix for the chair model like this:

Matrix ChairsWorldMatrix;

This matrix will contain all the information of how the chair model is to be placed into our scene, such as position, the direction it will face, and how much to scale it's size up or down.

I also changed the holodeck width to 40, which should make it 80 units wide on all sides since our box model went from -1 to +1 on every side (a distance of 2 units).

Here's what the code changes should look like:

    public class Game1 : Microsoft.Xna.Framework.Game
    {
        GraphicsDeviceManager GraphicsManager;  //Rename this like so.
        GraphicsDevice GraphicsCard;            //You can basically think of this as your graphics card or screen.
        SpriteBatch spriteBatch;                //You can leave this in even though we could remove it from this program.
        
        Texture2D GridTexture;  //Stores our texture in memory.
        Model HoloDeckModel;    //Stores our model in memory.
        Matrix HoloDeckModelWorldMatrix = Matrix.Identity;    //Stores the position and sizing info for our model.
        Model Chair;     //Stores the model named Chair.
        Matrix ChairsWorldMatrix;    //Stores Chair's position, facing and scaling.
        Matrix View;    //This stores the info for where our camera is positioned and what direction it's facing.
        Matrix Projection;  //Think of this as the "lens system" for our camera.
        //BasicEffect Shader; //This is like a "pen" that we'll be using to draw our world.
        Vector3 CameraPostion;  //This is just going to be x,y,z coordinates that stores our camera's location.
        Vector3 CameraLookAt;   //This is the spot the camera is "looking" at in our 3D world.
        const float HoloDeckWidth = 40f;    //Our model is 2 units cubed. This will be the number to multiply that size by.

        public Game1()
        {

Next, we'll modify the LoadContent() method so that it will load our model data from the .X file with the statement:

Chair = Content.Load<Model>("Chair");

The <Model> part just tells it what type of data it is loading from the file. And our Chair model is included in the project in the HolodeckContents under Solution Explorer. And "Chair" references our Chair .X file that we got from Sketchup 3D Warehouse.

Now we need to define the matrices that will tell XNA where to place this chair on the holodeck. If you were to just leave it like things are currently, you should see the chair appear dead center of the holodeck, facing straight down the axis, and large enough for the jolly green giant to use. We need to resize it, place it somewhere in the room, and I want it to kind of face the center of the room.

Let's start with this statement to resize it. I believe that this will decrease it to 3.5% of it's original size. This will conform much better to our rule that 1 unit equals 1 meter on the holodeck.

ChairsWorldMatrix = Matrix.CreateScale(0.035f);

Next, we'll rotate it to face the center of the room with this statement:

ChairsWorldMatrix *= Matrix.CreateRotationY(MathHelper.ToRadians(30f));

Notice the "*=" which tells XNA to multiply the ChairsWorldMatrix by Matrix.CreateRotationY(). Multiplication combines matrices. We are creating a matrix that stores the math formulas needed to rotate the chair around the Y axis, which is the one that represents up and down. In other words, we are spinning it around on the horizontal plane. It's really important to notice that we are rotating it around before we move it. All models can only be rotated around their center when their center is placed at the world origin. In other words, if you want to rotate something, you need to move it to the center of your universe here to rotate it around itself; if you don't, you'll likely see it orbit the center of your universe instead. When your rotations don't work right remember that.

Also, notice that the order that we multiply, or combine, our matrices is very important. For one thing, we've got to rotate the object before moving it.

It looks like CreateRotationY takes the angle to rotate around the Y axis as it's parameter. This angle is in radians, not degrees. It's good to learn to use radians as soon as possible, but I have to admit that I haven't used radians in awhile and can't remember how many radians is 30 degrees. That combined with the fact that this line of code will only get called once, at startup, makes me think we'll just call on "MathHelper" to convert 30 degrees to radians for us.

Now that we've got it sized correctly for the scene and rotated around it's center, we can move it to the spot that we want it to sit at.

 ChairsWorldMatrix *= Matrix.CreateTranslation(new Vector3(-8f, 0f, -8f));

Previously, we used the "CreateRotationY" method of the Matrix class to create a rotation. Now we need to call the "CreateTranslation" method to move the model. I want to place the chair 8 meters to the left and 8 meters back. CreateTranslation takes a Vector3 as a parameter. Don't get confused here. This is a position. It really isn't a vector. It's just the X,Y,Z coordinates where I want the chair to be at.

Again, this matirx is multiplied in order to combine it with the Scale and Translation matrices which have already been loaded into ChairsWorldMatrix. So, now ChairsWorldMatrix contains the results of all three changes. When we apply this matrix to the model, as we're drawing it, it will be drawn in the correct place and orientation. You may want to play around with commenting out these lines to see what happens without each line.

  protected override void LoadContent()
        {
            // Create a new SpriteBatch, which can be used to draw textures.
            spriteBatch = new SpriteBatch(GraphicsDevice);
        
            // TODO: use this.Content to load your game content here
            GridTexture = Content.Load<Texture2D>("GridTexture");   //Load the Grid texture into memory from disk.
            HoloDeckModel = Content.Load<Model>("HoloDeckMesh");    //Load our .X file model for the room into memory.

            //We load up a CreateScale matrix to resize our model since it's about 2 units cubed in size.
            //The CreateScale matrix will have the info in it to correctly size our model according to the value
            //we pass to it. We combine it (through multiplication) with a CreateTranslation matrix loaded
            //up with a position change that moves our model upwards by half the width of our cube. 
            HoloDeckModelWorldMatrix = Matrix.CreateScale(HoloDeckWidth) * Matrix.CreateTranslation(0.0f,HoloDeckWidth,0.0f);   //Resize our model larger and move it.

            //Load the "Chair" model into memory and position it at x,y,z coordinates 4,0,-2.
            //Chair Silla by "Ketchup" from Sketchup 8 Model Warehouse
            //Chair is an absolutely enormous model for our scale of one unit equals one meter.
            //We need to shrink Chair down to a normal size. I also want to have it facing more towards 
            //the center of the room. You can comment out the various matrices to see what happens
            //without them.
            Chair = Content.Load<Model>("Chair");
            ChairsWorldMatrix = Matrix.CreateScale(0.035f);   //Chair is WAY too big.
            ChairsWorldMatrix *= Matrix.CreateRotationY(MathHelper.ToRadians(30f)); //Combine a rotation of Chair by 30 degrees.
            ChairsWorldMatrix *= Matrix.CreateTranslation(new Vector3(-8f, 0f, -8f)); //Combine its world matrix with a movement/translation.

        }

Most of the rest of the source code remains unchanged except the Draw method, because we have to Draw our chair model on the screen. So, let's look at that.

I've set the background color to black so that it feels like the empty space that it really is when you go outside of the boundaries of the holodeck walls like this:

GraphicsDevice.Clear(Color.Black);

I've set the rasterizer state to turn off back face culling because we're still basically debugging and so that you can see the outside of the holodeck cube when you step outside of the boundary of the walls.

RS.CullMode = CullMode.None;

You may want to turn backface culling back on in order to play around with and see the difference of how the holodeck walls are drawn or not drawn from both the inside and the outside.

The next part is where we actually draw the holodeck room model like we did before.

  foreach (ModelMesh Mesh in HoloDeckModel.Meshes)
            {
                foreach (BasicEffect Shader in Mesh.Effects) 
                {
                    Shader.EnableDefaultLighting();
                    Shader.TextureEnabled = true;   
                    Shader.Texture = GridTexture;   
                    Shader.LightingEnabled = false; 
                    Shader.World = HoloDeckModelWorldMatrix;    
                    Shader.View = View;             
                    Shader.Projection = Projection; 
                }
                Mesh.Draw();  //Draw the mesh
            }

But we need to also draw our chair.

We're looping through all the sub-meshes(meshes) in our model. Our holodeck room really only had one mesh in the .X files, but it's possible to have several. I haven't opened up the Chair.X file to see how many meshes are in it, but since there are two textures, that probably means there are at least two meshes.

We also loop through every effect/shader used to draw each mesh. We're using BasicEffect, so it's probably just one shader, but that's not a problem because it will just go through the loop once and then go on.

We've got Default Lighting enabled because we're not doing anything unusual with the lighting.

We have to have TextureEnabled enabled in order to draw the textures on the model. Without it you will probably just see a gray model.

 Shader.TextureEnabled = true;

The next line is where the payoff comes for all those formulas that we loaded up into ChairsWorldMatrix. Here we tell XNA to use our Chair's world matrix to place it into our 3D scene like so:

Shader.World = ChairsWorldMatrix;

The view and projection matrices are just passed in so that XNA will have them, but they are the same for all objects that we draw. I suppose you may eventually find a need to have different models drawn with different view or projection matrices, but it's going to be rare and for specific purposes. The only thing I can think of off of the top of my head is that I've tried playing with the Projection matrix's clipping planes for various objects.

Finally, we tell XNA to draw each mesh.

 Mesh.Draw();

            foreach (ModelMesh Mesh in Chair.Meshes)
            {
                foreach (BasicEffect Shader in Mesh.Effects) 
                {
                    Shader.EnableDefaultLighting();
                    Shader.TextureEnabled = true;   
                    Shader.World = ChairsWorldMatrix;    
                    Shader.View = View;             
                    Shader.Projection = Projection; 
                }
                Mesh.Draw();  //Draw the mesh
            }

You may notice this code at the bottom of the Draw method:

base.Draw(gameTime);

You may be tempted to remove it from the code, and it will probably run the same if you do. You probably shouldn't though. Primarily, it's there to call any "Game Components" that you've created. We haven't created any, so it's largely going to do nothing. However, you should eventually learn to create game components and then you will be using this all the time.

 /// <summary>
        /// This is called when the game should draw itself.
        /// </summary>
        /// <param name="gameTime">Provides a snapshot of timing values.</param>
        protected override void Draw(GameTime gameTime)
        {
            GraphicsDevice.Clear(Color.Black);

            // TODO: Add your drawing code here
            //Normally you don't HAVE to change the RasterizerState but we want to change the 
            //CullMode. The rasterizer is part of the screen drawing mechanism and we're telling
            //XNA how things should be drawn. The cull mode tries to cull out triangles that don't
            //"need" to be drawn. The default is CullCounterClockwiseFace which tells XNA not to 
            //draw the back sides of triangles. Most of the time this makes sense; you can't "see"
            //the triangles that are facing away from you, so why draw them; it's just wasting CPU
            //time. It decides which side is the front of the triangle by the cull mode and the
            //direction you "wind", or define, the vertices in. This is why you should always 
            //wind your vertices clockwise according to the direction you want them to face.
            //Since we're debugging, there's a good chance that we messed up and wound our
            //vertices in the wrong direction. So, we turn off culling and draw both sides of 
            //all triangles by setting the cull mode to None. Once we get the program working,
            //we can change CullMode back to CullCounterClockwiseFace and our game will run
            //faster.
            RasterizerState RS = new RasterizerState();
            RS.CullMode = CullMode.None; 
            RS.FillMode = FillMode.Solid;
            GraphicsCard.RasterizerState = RS;

            
            foreach (ModelMesh Mesh in HoloDeckModel.Meshes)
            {
                //Here we define an effect. The effect is kind of like the pen or brush that we will "draw" our
                //3D world with. It's basically what they call a "shader". Shaders are code that draw things on
                //the screen. XNA 4.0 has about 5 different effects, or shaders, built into it. BasicEffect is the one
                //you will use most of the time. The others are more for special purposes.
                //We're loading up a BasicEffect here so that we can use it. We could have loaded up one of the other
                //effects or written our own effect and loaded it up. Writing your own effects is done in a language
                //called HLSL (High Level Shader Language). You don't really need to do that starting out though;
                //because BasicEffect does a pretty good job and you really need to know what you're doing to write a
                //shader that does a better job drawing the screen than the built in effects.
                foreach (BasicEffect Shader in Mesh.Effects) //Define an "effect" to draw our model with.
                {
                    Shader.EnableDefaultLighting();
                    Shader.TextureEnabled = true;   //We're going to draw using a texture.
                    Shader.Texture = GridTexture;   //The model will draw without this call, but it will use the texture in the file.
                    Shader.LightingEnabled = false; //Don't use BasicEffect's "advanced" lighting for now.
                    Shader.World = HoloDeckModelWorldMatrix;    //Tell the shader what World Matrix to use to draw the object.
                    Shader.View = View;             //Tell the shader where to draw from (the camera).
                    Shader.Projection = Projection; //Tell the shader how to draw (like the camera lens... sort of).
                }
                Mesh.Draw();  //Draw the mesh
            }


            foreach (ModelMesh Mesh in Chair.Meshes)
            {
                foreach (BasicEffect Shader in Mesh.Effects) //Define an "effect" to draw our model with.
                {
                    Shader.EnableDefaultLighting();
                    Shader.TextureEnabled = true;   //We're going to draw using a texture.
                    //Shader.Texture = WingsTexture;   //The model will draw without this call, but it will use the texture in the file.
                    //Shader.LightingEnabled = false; //Don't use BasicEffect's "advanced" lighting for now.
                    Shader.World = ChairsWorldMatrix;    //Tell the shader what World Matrix to use to draw the object.
                    Shader.View = View;             //Tell the shader where to draw from (the camera).
                    Shader.Projection = Projection; //Tell the shader how to draw (like the camera lens... sort of).
                }
                Mesh.Draw();  //Draw the mesh
            }


            base.Draw(gameTime);    //Always include this.
        }
    }

Anyway, that's all the code changes to add a chair model, or really any .X file model, to the scene. We have enough rudimentary camera code in the Update method to allow you to move back and forth and see that things are 3D here.

Source Code

The complete source code listing is shown below.

In the next part we will create a class to represent the player's character. It will mostly serve as a "camera" class and will allow us to move around the room as if we're a person in the room, rather than the "rail" that our camera is moving back and forth on.

The Complete Source Code:

using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;

namespace Holodeck
{
    /// <summary>
    /// This is the main type for your game
    /// </summary>
    public class Game1 : Microsoft.Xna.Framework.Game
    {
        GraphicsDeviceManager GraphicsManager;  //Rename this like so.
        GraphicsDevice GraphicsCard;            //You can basically think of this as your graphics card or screen.
        SpriteBatch spriteBatch;                //You can leave this in even though we could remove it from this program.
        
        Texture2D GridTexture;  //Stores our texture in memory.
        Model HoloDeckModel;    //Stores our model in memory.
        Matrix HoloDeckModelWorldMatrix = Matrix.Identity;    //Stores the position and sizing info for our model.
        Model Chair;     //Stores the model named Chair.
        Matrix ChairsWorldMatrix;    //Stores Chair's position, facing and scaling.
        Matrix View;    //This stores the info for where our camera is positioned and what direction it's facing.
        Matrix Projection;  //Think of this as the "lens system" for our camera.
        //BasicEffect Shader; //This is like a "pen" that we'll be using to draw our world.
        Vector3 CameraPostion;  //This is just going to be x,y,z coordinates that stores our camera's location.
        Vector3 CameraLookAt;   //This is the spot the camera is "looking" at in our 3D world.
        const float HoloDeckWidth = 40f;    //Our model is 2 units cubed. This will be the number to multiply that size by.

        public Game1()
        {
            GraphicsManager = new GraphicsDeviceManager(this);  //Since we renamed this it has to be changed here too.
            //Changing the backbuffer is not absolutely necessary. I have a 16:9 screen and I like
            //to set the backbuffer to what I want it to be rather then let XNA pick for me.
            //The backbuffer is where things are drawn. Once the drawing is complete and ready to
            //be displayed on the screen the drawing on the backbuffer is sent to the front buffer.
            //The front buffer is basically your screen. That's why the size needs to match the 
            //resolution of your screen.
            GraphicsManager.PreferredBackBufferWidth = 1280;    //Screen width horizontal. Change this to fit your screen.
            GraphicsManager.PreferredBackBufferHeight = 720;    //Screen width vertical. Change this to fit your screen.
            //You can run your game in full screen or windowed mode. Sometimes you write bugs
            //into your code that make it hard to recover in full screen mode. (Try Alt+F4 or 
            //Ctrl+Alt+Del to get control in those situations.)
            GraphicsManager.IsFullScreen = false;  //Feel free to set this to true once your code works. 

            Content.RootDirectory = "Content";
        }

        /// <summary>
        /// Allows the game to perform any initialization it needs to before starting to run.
        /// This is where it can query for any required services and load any non-graphic
        /// related content.  Calling base.Initialize will enumerate through any components
        /// and initialize them as well.
        /// </summary>
        protected override void Initialize()
        {
            // TODO: Add your initialization logic here
            GraphicsCard = GraphicsManager.GraphicsDevice;  //Introduce the Graphics Manager to our graphics card.
            Window.Title = "The Holo-Deck"; //Your window's title displays in the top left corner in windowed mode.

            //We need to load up our projection matrix with our camera's Field of View,
            //our screen aspect ratio, and the clipping planes. Changes this are only
            //updated when you change the aspect ratio, I believe. So it's pretty much
            //set it up once and leave it alone.
            Projection = Matrix.CreatePerspectiveFieldOfView(MathHelper.ToRadians(45f),
                GraphicsCard.Adapter.CurrentDisplayMode.AspectRatio, 0.1f, 10000f);

            CameraPostion = new Vector3(0f, 1.8f, 0f);    //Start the camera in the center of our world but 1.8 unit above ground.
            CameraLookAt = CameraPostion + Vector3.Forward;
            //Think of the View Matrix as our camera. I'm loading it with our 3D position. And 
            //then I'm telling it to look straight forward by telling it to look at a point
            //that is 1 unit directly in front of the camera. Vector3.Forward is a unit
            //vector of 1 length that points in the forward direction, but it's tail
            //is at 0,0,0 and we need it to be pointing "from" the camera. Instead of
            //defining it this way you can just load up a Vector3 with the position
            //you want it to look at just like we did with Camera position.
            //CreateLookAt wants you to define "up" for it and so you send it an up vector.
            View = Matrix.CreateLookAt(CameraPostion, CameraLookAt, Vector3.Up);


            base.Initialize();
        }

        /// <summary>
        /// LoadContent will be called once per game and is the place to load
        /// all of your content.
        /// </summary>
        protected override void LoadContent()
        {
            // Create a new SpriteBatch, which can be used to draw textures.
            spriteBatch = new SpriteBatch(GraphicsDevice);
        
            // TODO: use this.Content to load your game content here
            GridTexture = Content.Load<Texture2D>("GridTexture");   //Load the Grid texture into memory from disk.
            HoloDeckModel = Content.Load<Model>("HoloDeckMesh");    //Load our .X file model for the room into memory.

            //We load up a CreateScale matrix to resize our model since it's about 2 units cubed in size.
            //The CreateScale matrix will have the info in it to correctly size our model according to the value
            //we pass to it. We combine it (through multiplication) with a CreateTranslation matrix loaded
            //up with a position change that moves our model upwards by half the width of our cube. 
            HoloDeckModelWorldMatrix = Matrix.CreateScale(HoloDeckWidth) * Matrix.CreateTranslation(0.0f,HoloDeckWidth,0.0f);   //Resize our model larger and move it.

            //Load the "Chair" model into memory and position it at x,y,z coordinates 4,0,-2.
            //Chair Silla by "Ketchup" from Sketchup 8 Model Warehouse
            //Chair is an absolutely enormous model for our scale of one unit equals one meter.
            //We need to shrink Chair down to a normal size. I also want to have it facing more towards 
            //the center of the room. You can comment out the various matrices to see what happens
            //without them.
            Chair = Content.Load<Model>("Chair");
            ChairsWorldMatrix = Matrix.CreateScale(0.035f);   //Chair is WAY too big.
            ChairsWorldMatrix *= Matrix.CreateRotationY(MathHelper.ToRadians(30f)); //Combine a rotation of Chair by 30 degrees.
            ChairsWorldMatrix *= Matrix.CreateTranslation(new Vector3(-8f, 0f, -8f)); //Combine its world matrix with a movement/translation.

        }

        /// <summary>
        /// UnloadContent will be called once per game and is the place to unload
        /// all content.
        /// </summary>
        protected override void UnloadContent()
        {
            // TODO: Unload any non ContentManager content here
        }

        /// <summary>
        /// Allows the game to run logic such as updating the world,
        /// checking for collisions, gathering input, and playing audio.
        /// </summary>
        /// <param name="gameTime">Provides a snapshot of timing values.</param>
        protected override void Update(GameTime gameTime)
        {
            KeyboardState KBState;
            // Allows the game to exit
            if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed)
                this.Exit();

            // TODO: Add your update logic here
            KBState = Keyboard.GetState();
            if (KBState.IsKeyDown(Keys.Escape)) this.Exit();

            float Speed = 0.1f;
            if (KBState.IsKeyDown(Keys.Up) || KBState.IsKeyDown(Keys.W))
            {
                CameraPostion += Vector3.Forward * Speed;
                CameraLookAt = CameraPostion + Vector3.Forward;
            }

            if (KBState.IsKeyDown(Keys.Down) || KBState.IsKeyDown(Keys.S))
            {
                CameraPostion += Vector3.Backward * Speed;
                CameraLookAt = CameraPostion + Vector3.Forward;
            }

            View = Matrix.CreateLookAt(CameraPostion,CameraLookAt, Vector3.Up);
            

            base.Update(gameTime);
        }

        /// <summary>
        /// This is called when the game should draw itself.
        /// </summary>
        /// <param name="gameTime">Provides a snapshot of timing values.</param>
        protected override void Draw(GameTime gameTime)
        {
            GraphicsDevice.Clear(Color.Black);

            // TODO: Add your drawing code here
            //Normally you don't HAVE to change the RasterizerState but we want to change the 
            //CullMode. The rasterizer is part of the screen drawing mechanism and we're telling
            //XNA how things should be drawn. The cull mode tries to cull out triangles that don't
            //"need" to be drawn. The default is CullCounterClockwiseFace which tells XNA not to 
            //draw the back sides of triangles. Most of the time this makes sense; you can't "see"
            //the triangles that are facing away from you, so why draw them; it's just wasting CPU
            //time. It decides which side is the front of the triangle by the cull mode and the
            //direction you "wind", or define, the vertices in. This is why you should always 
            //wind your vertices clockwise according to the direction you want them to face.
            //Since we're debugging, there's a good chance that we messed up and wound our
            //vertices in the wrong direction. So, we turn off culling and draw both sides of 
            //all triangles by setting the cull mode to None. Once we get the program working,
            //we can change CullMode back to CullCounterClockwiseFace and our game will run
            //faster.
            RasterizerState RS = new RasterizerState();
            RS.CullMode = CullMode.None; 
            RS.FillMode = FillMode.Solid;
            GraphicsCard.RasterizerState = RS;

            
            foreach (ModelMesh Mesh in HoloDeckModel.Meshes)
            {
                //Here we define an effect. The effect is kind of like the pen or brush that we will "draw" our
                //3D world with. It's basically what they call a "shader". Shaders are code that draw things on
                //the screen. XNA 4.0 has about 5 different effects, or shaders, built into it. BasicEffect is the one
                //you will use most of the time. The others are more for special purposes.
                //We're loading up a BasicEffect here so that we can use it. We could have loaded up one of the other
                //effects or written our own effect and loaded it up. Writing your own effects is done in a language
                //called HLSL (High Level Shader Language). You don't really need to do that starting out though;
                //because BasicEffect does a pretty good job and you really need to know what you're doing to write a
                //shader that does a better job drawing the screen than the built in effects.
                foreach (BasicEffect Shader in Mesh.Effects) //Define an "effect" to draw our model with.
                {
                    Shader.EnableDefaultLighting();
                    Shader.TextureEnabled = true;   //We're going to draw using a texture.
                    Shader.Texture = GridTexture;   //The model will draw without this call, but it will use the texture in the file.
                    Shader.LightingEnabled = false; //Don't use BasicEffect's "advanced" lighting for now.
                    Shader.World = HoloDeckModelWorldMatrix;    //Tell the shader what World Matrix to use to draw the object.
                    Shader.View = View;             //Tell the shader where to draw from (the camera).
                    Shader.Projection = Projection; //Tell the shader how to draw (like the camera lens... sort of).
                }
                Mesh.Draw();  //Draw the mesh
            }


            foreach (ModelMesh Mesh in Chair.Meshes)
            {
                foreach (BasicEffect Shader in Mesh.Effects) //Define an "effect" to draw our model with.
                {
                    Shader.EnableDefaultLighting();
                    Shader.TextureEnabled = true;   //We're going to draw using a texture.
                    Shader.World = ChairsWorldMatrix;    //Tell the shader what World Matrix to use to draw the object.
                    Shader.View = View;             //Tell the shader where to draw from (the camera).
                    Shader.Projection = Projection; //Tell the shader how to draw (like the camera lens... sort of).
                }
                Mesh.Draw();  //Draw the mesh
            }


            base.Draw(gameTime);    //Always include this.
        }
    }
}

 

 







 

Summary

In this part we add a model so that we have more than just an empty room. We'll chose a model from Google's Sketchup 3D Warehouse, and import it in so that it appears on the holodeck.

Source Code





Tutorials

The Holodeck

Subsites

Blog

Files


Future Use