XNA Game Development Forums are located here: thehazymind.com/smf

Archive for June, 2005

Going to Billings

Thursday, June 30th, 2005

So I am finally going to visit home this weekend. My dad’s birthday is on the 3rd, and he is doing a little get together thing tonight for that and one this weekend for the 4th celebrations, so I will be hanging out at those.

I’m also gonna try to get together with some friends for as much time as possible since I haven’t seen some of them in around a year :/ and I hate losing touch with people (as long as they hate losing touch with me ;) ). Hopefully, it will be a stress free ordeal and I will be able to just take a fun break from work for a change. We’ll see!

Download Times are Killing me

Tuesday, June 28th, 2005

I am working on a game project with a team from the UK, and I am being forced (not really forced, but definately coerced) into using C++ to program my modules for the game engine. Not that anyone really cares, but I hate C++ and would much prefer to be using my more familiar C# to do everything.

The problem with using C++ is that I now have to get the program to write the code in (not required but it’s a pain to write code without it) and this will take about 2 hours to download/install. This wouldn’t bother me too much except for the fact that if I install this I have to uninstall the older version of the C# editor I have and download the new one of that as well, another 2 hours.

Altogether it is going to be a long day of watching blue bars slowly creep to the right side of the screen. Oh well, it doesn’t look very nice outside today anyway :/

Clubbin’ it up in the BZMT… Sort of

Saturday, June 25th, 2005

Well there was finally an outing to go to that doesn’t require you to be 21 in Bozeman and let me tell you I jumped in and took advantage of it as there are very few of these around here. Being 18-19-20 in Bozeman just plain sucks. Anywhere cool you might want to go hang out they assume you will be getting drunk, so you can’t go unless you can legally get drunk, so I am stuck outside even when I wouldn’t have been going to get drunk in the first place. All I wanna do is dance!!!

Anyway, tonight at the Emmerson (I think that is the name) there was a dance that supposedly cost $5 at the door, but being that we had genius timing and showed up fashionably late, they had already stopped taking money up front and just started selling drinks to the people that were already there (no, mom. I didn’t buy any alcohol, just Mt. Dew). Being the me that I am I hopped right into the middle and got down and dirty (excuse the term, but I really like dancing) with lots of people I have never seen and probably never will see again. I even got up on a table with 5 or so girls (uh huh, nerds do that kind of thing too) and put on a little show, although it wasn’t quite up to par with the guy that lapdanced for some girl whose birthday was today.

All in all it was loads of fun, and I just thought I was going out to get a Blizzard at Dairy Queen :/ Oh, and for people who I didn’t call tonight, that is why, and I promise I didn’t forget, but I really needed to get out and do something to cure the boredom plague, and I think this hit the spot nicely; although, I am not looking forward to sore muscles in the morning.

Now it’s 3:30 A.M., and I am going to sleep.,

Excitement… I Guess

Thursday, June 23rd, 2005

Today I decided something. If you have a friend, and he/she is excited about something, even if you think it is the most meaningless thing in the world, just pretend like you actually give a shit about it. There’s nothing in the world that makes someone feel stupider than telling them something they are excited about is nothing at all and that they might as well be excited about watching the grass grow.

Now I am not saying that I never do this, because I am sure I have many times in the past and will many times in the future which will undoubtedly cause someone to think a little less of themselves without my meaning it to. All I am saying is you should definately try, because you never know when you will need someone to be excited with you about something :)

Rendering Full Textured Meshes

Friday, June 17th, 2005

Now that we have the ability to construct and render textured objects, you are probably thinking, “Do I really have to create a vertex for every single point on my game models?” The answer is basically yes, but it does not apply to the code itself. You will have to create your own models or obtain some that are made in a 3D modelling program like Blender, or 3D Studio Max.

Once you have these models you can add the code into your engine to render them. This way, you can visually create your models and load them into your game without having to specify any buffers or vertices along the way (actually, the Mesh class that DirectX supplies creates the vertex and index buffers for us.) With that said, lets see how we can load these meshes.

Step 1: Loading the Mesh, its Materials and Textures

The first thing we want to do before we can draw our mesh is to load the mesh file itself and make sure we get all of the textures it contains loaded correctly. Here is how we do that:

public class HMMesh : HMObject { private Material[] myMaterials; private Texture[] myTextures; private Mesh myMesh; private string myPath; public HMMesh(string meshPath, Vector3 pos, Vector3 rot, Vector3 scl, Device myDevice) { myPath = meshPath; myPosition = pos; myRotation = rot; myScaling = scl; ReloadResources(myDevice); } public override void ReloadResources(Device myDevice) { ExtendedMaterial[] mtrls; myMesh = Mesh.FromFile(myPath, MeshFlags.Managed, myDevice, out mtrls); // Store any mtrls included in the mesh if((mtrls != null) && (mtrls.Length > 0)){ myMaterials = new Material[mtrls.Length]; myTextures = new Texture[mtrls.Length]; // Store each material and texture for(int i = 0; i < mtrls.Length; i++){ myMaterials[i] = mtrls[i].Material3D; if((mtrls[i].TextureFilename != null) && (mtrls[i].TextureFilename != string.Empty)) { // This material has a texture associated with it myTextures[i] = TextureLoader.FromFile(myDevice, mtrls[i].TextureFilename); } } } } }

This will load and retrieve all the materials and textures along with the main mesh file and store them all in our custom Mesh class.

Step 2: Putting our Mesh on the Screen

Now all that is left to do is render the mesh. Here is the function for doing so:

public override void Render(Device myDevice) { // Set the video card to use our objects tranformations myDevice.Transform.World = Matrix.Scaling(myScaling) * Matrix.RotationYawPitchRoll(myRotation.Y, myRotation.X, myRotation.Z) * Matrix.Translation(myPosition); for(int i = 0; i < myMaterials.Length; i++) { myDevice.Material = myMaterials[i]; myDevice.SetTexture(0, myTextures[i]); myMesh.DrawSubset(i); } }

Now if you create a new Mesh object in the Main class of the demo and add it, you will see a lovely rendered tiger mesh in the middle of the screen (remember to put the mesh and all its textures in the bin folder of the demo project.) In case you weren’t sure how to do this (you should know by now), here is the code:

HMMesh mesh1 = new HMMesh(”tiger.x”, new Vector3(), new Vector3(), new Vector3(), demo1.MyDevice); demo1.MyScene.AddObject(mesh1);

More Rendering, Vertices and Indexes

Wednesday, June 15th, 2005

The next step in the natural progression of rendering objects in 3D space is to take some vertices, assign an index list, and render the object using Vertex and Index buffers. We will do this in a building process so you can slowly get the idea of why we would want to use the buffers DirectX provides for us.

Step 1: Putting Together a Vertex Renderer

Since the vertex groupings that we will be rendering will most likely be part of a whole object, I am going to make the VertexGroup class inherit from our main Object. Here is what VertexGroup looks like right now:

public class HMVertexGroup : HMObject { private CustomVertex.PositionColored[] verts; public HMVertexGroup(CustomVertex.PositionColored[] vertex, Device myDevice) { verts = vertex; } public override void Render(Device myDevice) { myDevice.VertexFormat = CustomVertex.PositionColored.Format; myDevice.DrawUserPrimitives(PrimitiveType.TriangleList, 1, verts); } }

Not a whole lot to it eh? If we created an object from this and ran the demo as-is though, we wouldn’t see anything. Why? Because we haven’t set up a camera yet. The camera tutorial comes later, but for now we will set up the matrices on the device that we will need to see our new vertex object. First of all, in the System class, make a new function called SetupCamera inside our HMSystem class like this:

private void SetupCamera() { myDevice.Transform.World = Matrix.Identity; myDevice.Transform.View = Matrix.LookAtLH( new Vector3(0, 0,-5), new Vector3(0, 0, 0), new Vector3(0, 1, 0) ); myDevice.Transform.Projection = Matrix.PerspectiveFovLH( (float)Math.PI / 4, (float)this.Width / (float)this.Height, // floats for decimal based division, not integer 1.0f, 100.0f ); myDevice.RenderState.Lighting = false; }

Also, be sure to add a call to the setup camera function in the contructor for the system somewhere. I put mine inside an if(InitializeGraphics()) call. Now, with our camera all set up, we can create a vertex object in the demo’s Main class and add it to the scene.

CustomVertex.PositionColored[] verts = new CustomVertex.PositionColored[3]; verts[0] = new CustomVertex.PositionColored(new Vector3( 0, 1, 1), Color.Red.ToArgb()); verts[1] = new CustomVertex.PositionColored(new Vector3(-1,-1, 1), Color.Green.ToArgb()); verts[2] = new CustomVertex.PositionColored(new Vector3( 1,-1, 1), Color.Blue.ToArgb()); HMVertexGroup vg1 = new HMVertexGroup(verts, demo1.MyDevice); demo1.MyScene.AddObject(vg1);

Running this now will give us a big red, green, and blue filled triangle. Nice, but it isn’t very useful.

Step 2: Adding a texture to our VertexGroup

Let’s try something different. Change the vertices in the Main class to look like this:

CustomVertex.PositionTextured[] verts = new CustomVertex.PositionTextured[6]; verts[0] = new CustomVertex.PositionTextured(new Vector3(-1, 1, 1), 0, 0); verts[1] = new CustomVertex.PositionTextured(new Vector3( 1, 1, 1), 1, 0); verts[2] = new CustomVertex.PositionTextured(new Vector3(-1,-1, 1), 0, 1); verts[3] = new CustomVertex.PositionTextured(new Vector3( 1, 1, 1), 1, 0); verts[4] = new CustomVertex.PositionTextured(new Vector3( 1,-1, 1), 1, 1); verts[5] = new CustomVertex.PositionTextured(new Vector3(-1,-1, 1), 0, 1); HMVertexGroup vg1 = new HMVertexGroup(verts, “hmglogo.tga”, demo1.MyDevice);

The first part of the new vertices is obvious, it is their position in world space, but what do the second numbers mean? They are texture coordinates. A texture coordinate is assigned as a number from 0 to 1, (0,0) being the top left corner of the texture, and (1,1) being the bottom right. With this knowledge and a bit of a sketch, we can assign the correct coordinates to each of our vertices fairly easily.

Now that we have new vertices, we need to change the VertexGroup class to use the PositionColored format and to have a new parameter for a texture path, and load the texture from it like we did in our sprite class.

public class HMVertexGroup : HMObject { private CustomVertex.PositionTextured[] verts; private Texture myTexture; private string myPath; public HMVertexGroup(CustomVertex.PositionTextured[] vertex, string imagePath, Device myDevice) { verts = vertex; myPath = imagePath; myTexture = TextureLoader.FromFile(myDevice, myPath); } public override void ReloadResources(Device myDevice) { myTexture = TextureLoader.FromFile(myDevice, myPath); } public override void Render(Device myDevice) { myDevice.VertexFormat = CustomVertex.PositionTextured.Format; myDevice.SetTexture(0, myTexture); myDevice.DrawUserPrimitives(PrimitiveType.TriangleList, verts.Length/3, verts); } }

Now you will have a textured quad on the screen that shows the image you supplied in the parameter of the object creation. This doesn’t seem very useful because we already have this exact same ability with our sprite class, and it uses transparency! Also, it seems kind of dumb to be creating 6 vertex elements when we are only making a square.

Step 3: Switching to Vertex and Index buffers

Before we start making a more ‘useful’ use for our VertexGroup, let’s make it make a bit more sense. Wouldn’t it be great if there was some way we could make just 4 vertices and use them in both of our triangles to create our final textured quad? There is! It is called the index buffer, but to use it, we also need to convert to using vertex buffers in our class. Here is what the new class will look like:

public class HMVertexGroup : HMObject { private CustomVertex.PositionTextured[] verts; private int[] inds; private Texture myTexture; private string myPath; private VertexBuffer vb; private IndexBuffer ib; public HMVertexGroup( CustomVertex.PositionTextured[] vertex, int[] index, string imagePath, Device myDevice ) { verts = vertex; inds = index; myPath = imagePath; ReloadResources(myDevice); // This way we don’t have to type the buffer code twice } public override void ReloadResources(Device myDevice) { myTexture = TextureLoader.FromFile(myDevice, myPath); vb = new VertexBuffer( typeof(CustomVertex.PositionTextured), verts.Length, myDevice, Usage.WriteOnly, CustomVertex.PositionTextured.Format, Pool.Default ); vb.SetData(verts, 0, LockFlags.None); ib = new IndexBuffer( typeof(int), inds.Length, myDevice, Usage.WriteOnly, Pool.Default ); ib.SetData(inds, 0, LockFlags.None); } public override void Render(Device myDevice) { myDevice.VertexFormat = CustomVertex.PositionTextured.Format; myDevice.SetTexture(0, myTexture); myDevice.SetStreamSource(0, vb, 0); myDevice.Indices = ib; myDevice.DrawIndexedPrimitives( PrimitiveType.TriangleList, 0, 0, verts.Length, 0, inds.Length / 3 ); } }

The new vertex and index information that you will pass to this class looks like this:

CustomVertex.PositionTextured[] verts = new CustomVertex.PositionTextured[4]; verts[0] = new CustomVertex.PositionTextured(new Vector3(-1, 1, 1), 0, 0); verts[1] = new CustomVertex.PositionTextured(new Vector3( 1, 1, 1), 1, 0); verts[2] = new CustomVertex.PositionTextured(new Vector3(-1,-1, 1), 0, 1); verts[3] = new CustomVertex.PositionTextured(new Vector3( 1,-1, 1), 1, 1); int[] inds = { 0,1,2, 1,3,2 }; HMVertexGroup vg1 = new HMVertexGroup(verts, inds, “hmglogo.tga”, demo1.MyDevice);

Now we are getting somewhere. We have simplified the process of creating and rendering a small number of vertices using index values so we don’t have to create duplicates into some arrays and a single call to our VertexGroup class. Now that we have this great class, what can we do with it? One of the best examples of an object to create this with is a skybox, now that we have our code down for rendering with these buffers, we will make a simple skybox object to add to the engine

Step 4: Taking Advantage of VertexGroup, Making a Skybox

The skybox code will not contain any real DirectX code, it will only be a storage and creation place for the vertex and index arrays and a list of the textures we want to use for each side of the box. Here’s how I have this implemented:

public class HMSkyBox : HMObject { private string[] textures; HMVertexGroup[] faces = new HMVertexGroup[6]; public HMSkyBox(string[] tex, Device myDevice) { textures = tex; CustomVertex.PositionTextured[] verts; int[] inds = { 0,1,2, 1,3,2 }; // Front Face verts = new CustomVertex.PositionTextured[4]; // Do this every face so they reload correctly verts[0] = new CustomVertex.PositionTextured(new Vector3(-10, 10, 10), 0, 0); verts[1] = new CustomVertex.PositionTextured(new Vector3(10, 10, 10), 1, 0); verts[2] = new CustomVertex.PositionTextured(new Vector3(-10, -10, 10), 0, 1); verts[3] = new CustomVertex.PositionTextured(new Vector3(10, -10, 10), 1, 1); faces[0] = new HMVertexGroup(verts, inds, textures[0], myDevice); // Right Face verts = new CustomVertex.PositionTextured[4]; verts[0] = new CustomVertex.PositionTextured(new Vector3(10, 10, 10), 0, 0); verts[1] = new CustomVertex.PositionTextured(new Vector3(10, 10, -10), 1, 0); verts[2] = new CustomVertex.PositionTextured(new Vector3(10, -10, 10), 0, 1); verts[3] = new CustomVertex.PositionTextured(new Vector3(10, -10, -10), 1, 1); faces[1] = new HMVertexGroup(verts, inds, textures[1], myDevice); // Back Face verts = new CustomVertex.PositionTextured[4]; verts[0] = new CustomVertex.PositionTextured(new Vector3(10, 10, -10), 0, 0); verts[1] = new CustomVertex.PositionTextured(new Vector3(-10, 10, -10), 1, 0); verts[2] = new CustomVertex.PositionTextured(new Vector3(10, -10, -10), 0, 1); verts[3] = new CustomVertex.PositionTextured(new Vector3(-10, -10, -10), 1, 1); faces[2] = new HMVertexGroup(verts, inds, textures[2], myDevice); // Left Face verts = new CustomVertex.PositionTextured[4]; verts[0] = new CustomVertex.PositionTextured(new Vector3(-10, 10, -10), 0, 0); verts[1] = new CustomVertex.PositionTextured(new Vector3(-10, 10, 10), 1, 0); verts[2] = new CustomVertex.PositionTextured(new Vector3(-10, -10, -10), 0, 1); verts[3] = new CustomVertex.PositionTextured(new Vector3(-10, -10, 10), 1, 1); faces[3] = new HMVertexGroup(verts, inds, textures[3], myDevice); // Top Face verts = new CustomVertex.PositionTextured[4]; verts[0] = new CustomVertex.PositionTextured(new Vector3(-10, 10, -10), 0, 0); verts[1] = new CustomVertex.PositionTextured(new Vector3(10, 10, -10), 1, 0); verts[2] = new CustomVertex.PositionTextured(new Vector3(-10, 10, 10), 0, 1); verts[3] = new CustomVertex.PositionTextured(new Vector3(10, 10, 10), 1, 1); faces[4] = new HMVertexGroup(verts, inds, textures[4], myDevice); // Bottom Face verts = new CustomVertex.PositionTextured[4]; verts[0] = new CustomVertex.PositionTextured(new Vector3(-10, -10, 10), 0, 0); verts[1] = new CustomVertex.PositionTextured(new Vector3(10, -10, 10), 1, 0); verts[2] = new CustomVertex.PositionTextured(new Vector3(-10, -10, -10), 0, 1); verts[3] = new CustomVertex.PositionTextured(new Vector3(10, -10, -10), 1, 1); faces[5] = new HMVertexGroup(verts, inds, textures[5], myDevice); } public override void ReloadResources(Device myDevice) { foreach(HMVertexGroup vg in faces) { vg.ReloadResources(myDevice); } } public override void Render(Device myDevice) { foreach(HMVertexGroup vg in faces) { vg.Render(myDevice); } } }

The main class of the demo project has also changed to use the new class.

public static void Main() { string[] textures = { @”skybox\jv_arizona_matin_1.jpg”, @”skybox\jv_arizona_matin_2.jpg”, @”skybox\jv_arizona_matin_3.jpg”, @”skybox\jv_arizona_matin_4.jpg”, @”skybox\jv_arizona_matin_5.jpg”, @”skybox\jv_arizona_matin_6.jpg” }; HMSkyBox sky1 = new HMSkyBox(textures, demo1.MyDevice); demo1.MyScene.AddObject(sky1); demo1.Show(); Application.Run(demo1); }

Now, we have a wonderfully textured skybox in our 3D world. Here is where the part that excites me about 3D engines first shows a small glimmer of itself. Inside the .dll file that gets compiled from the main engine code, we have created the tools that will allow us to later create, texture, and render and full skybox with only a few lines of code in our demos. I don’t know about you, but that sure sounds exciting to me.

One last thing before we finish up this tutorial. If you played around with adding more VertexGroups into the scene you have probably noticed that there are some funny things going on. Some of the objects behind the skybox or other objects are getting rendered in front of them, and it altogether just looks strange. The problem is that we haven’t set up the video card to know when a pixel that was already drawn is in front of a pixel that is being drawn, so it just sticks it on top regardless. The solution to our problem is adding a zbuffer, and it only takes a few lines of code.

In the InitializeGraphics function, below the first group of parameter settings, add these lines:

presentParams.EnableAutoDepthStencil = true; presentParams.AutoDepthStencilFormat = DepthFormat.D16;

Now, change the device Clear call to look like this:

myDevice.Clear(ClearFlags.Target | ClearFlags.ZBuffer, Color.CornflowerBlue, 1.0f, 0);

Now everything should render correctly.

Crazy Hectic Full Days

Tuesday, June 14th, 2005

As I am sure many of you have noticed (if there are enough of you to be called many :/) It has been a short while since I have posted in here. The reasons for this are many. First off, the hosting company we are using at work is being a pain in the ass and lots of things are breaking that make mine and my coworkers jobs very difficult/impossible. I have been busy trying to make things work around the broken parts, only to have it be fixed after much uneeded effort.

Also, I have been working on writing the tutorials for the 3D Engine section (located in the sidebar near the bottom), and those take a bit of time to separate the right code and figure out how to explain it to people who are new to that kind of programming. I am also trying to work in time to do programming of my own on projects I have either just started or have been working on for quite some time, and that combined with sleep is not leaving me much writing time.

On top of that, I have been looking in the online version of our local paper or places to get an appartment since I am sure I will not want to drive from Belgrade to Bozeman every day (well, lots of days) again like last semester as gas prices have definately not gone down at all :/

All in all there’s just a lot going on. Not all of it is big, but there’s enough little to take most of my time away from me. I’m trying though! Try not to be too disappointed if you visit me and see there isn’t a new entry yet

Rendering to the Screen, Simple Sprites

Saturday, June 11th, 2005

Now that we have the ability to startup and render with Direct3D let’s get to rendering something! I am going to keep these tutorial sets geared towards creating a full engine, so everything will be as object oriented as possible to make it easy to add to and use later. To start out, let’s create a file that will contain all of the code for objects our engine will render. I am calling mine HMObject.cs, but you can use whatever you want. Make sure you create this in the 3D Engine project and not the demo one

Step 1: Creating the basic object class

Start the file out by adding in the proper using and namespace code:

using System.Drawing; using Microsoft.DirectX; using Microsoft.DirectX.Direct3D; namespace HMEngine { // Whatever your main namespace is }

Since we will be creating more than one kind of object, let’s create a class that holds all of the things each object will need to use so we can inherit from it later.

public class HMObject { protected Vector3 myPosition = new Vector3(0, 0, 0); // We will use these for protected Vector3 myRotation = new Vector3(0, 0, 0); // building our transformation protected Vector3 myScaling = new Vector3(1, 1, 1); // matrices in the camera later public virtual void ReloadResources(Device myDevice){} // This will be used for device resets public virtual void Render(Device myDevice){} // Basic render call for every object }

We will not be using those vectors until step 3, but I thought we should add them now since we will end up doing so later anyway.

Step 2: Rendering a simple sprite

The first useful object to start creating a game with is probably a sprite. Now I know sprites are only 2D and you are probably thinking “This is a 3D engine!! We don’t need sprites!”, but you would be surprised at what they might come in handy for later. That said I should inform you that DirectX already has a Sprite class that is very easy to use, but to cut down on our game coding time later, we will write a custom Sprite class to use in our games that will take care of many repetitive things for us. Here’s what my custom sprite class looks like:

public class HMSprite : HMObject { private Sprite mySprite; // Sprite object that does the rendering private Texture myTexture; // Texture object used for rendering private string myPath; // Path to the texture file so we can use on resets public HMSprite(string imagePath, Vector3 location, int width, int height, Device myDevice) { myPosition = location; // Keep location in inherited vector mySprite = new Sprite(myDevice); // Create a new sprite object myScaling = new Vector3(width, height, 1); // Keep size in inherited vector myPath = imagePath; // Store the texture’s path myTexture = TextureLoader.FromFile(myDevice, myPath); // Load our texture } // This will be called if the device resets and resources had to be acquired again public override void ReloadResources(Device myDevice) { myTexture = TextureLoader.FromFile(myDevice, myPath); } public override void Render(Device myDevice) { // Here we find the center of the texture dimensions Vector3 texCenter = new Vector3( myTexture.GetSurfaceLevel(0).Description.Width/2, myTexture.GetSurfaceLevel(0).Description.Height/2, 0 ); mySprite.Transform = Matrix.Transformation( new Vector3(), // Scaling Center, blank scales from the top corner new Quaternion(), // Scaling Rotation, blank to scale down and right (normally) // Scale ratio: newSize/texSize, since we stored half the size in texCenter we use texCenter*2 new Vector3(myScaling.X/(texCenter.X*2), myScaling.Y/(texCenter.Y*2),0), texCenter, // Rotation Center new Quaternion(), // Rotation amount new Vector3() // Translation amount (location) ); mySprite.Begin(SpriteFlags.AlphaBlend); // Start drawing with out sprite, and use Transparency // The parameters for the following .Draw call are: // myTexture - the Texture object to draw // new Vector3 - center to draw from, we are positioning from top left so use an empty vector // myLocation - the screen offset to draw at // Color.White - Color to modulate the texture with, this value keeps original colors mySprite.Draw(myTexture, new Vector3(), myPosition, Color.White.ToArgb()); mySprite.End(); // All done drawing } }

You will also need to add references to Microsoft.DirectX and Microsoft.DirectX.Direct3D to the demo project, and you need to add a reference to Miscrosoft.DirectX.Direct3DX as well since the Sprite class we are using is in that library.

In our sprite code, the first thing we did was create some storage spots for the sprites size, location, texture, and the actual sprite object DirectX will use to draw with. The constructor takes a path to the image we are using for our texture, the location in 3D space of the sprite (most of them will be at <X, Y, 0>), the width and height of the sprite we want to draw, and the device we will use to create the texture and sprite objects.

The path can be passed as relative or absolute in the filesystem. If you just want to pass something like “image1.jpg” to the constructor, then you will have to place the image file in the same directory that the .exe will be running from (in VC# this will be in the /bin folder of the Debug or Release sections of the demo application. You can use any standard image format you would like (.bmp, .gif, .jpg, .tga) and they can contain transparent sections as well.

Step 3: Adding a Simple Scene Graph

Now that we have an object to render, we need a good method to actually render it that we can extend for a more complicated use later. For this task we will create a very simple Scene class that we will add on to later when more complicated tasks come around (like frustum culling).

Start out by creating a new code file with the normal using and namespace statements and a using for System.Collections as well, and call it something similar to mine: HMScene. Here is our very, very simple scene class.

public class HMScene { ArrayList WorldObjects = new ArrayList(); public void AddObject(HMObject newObject) { WorldObjects.Add(newObject); } public void RemoveObject(HMObject oldObject) { WorldObjects.Remove(oldObject); } public void ReloadResources(Device myDevice) { foreach(HMObject o in WorldObjects) { o.ReloadResources(myDevice); } } public void Render(Device myDevice) { foreach(HMObject o in WorldObjects) { o.Render(myDevice); } } }

Ok, so we have objects and a scene, let’s add a scene object to our main system class and get it rendering. In the main system class with the rest of its instance variables (at the top) add a new scene variable and a way to access it from the outside:

private HMScene myScene = new HMScene(); public HMScene MyScene { get { return myScene; } } public Device MyDevice { get { return myDevice; } }

I also made the device available from outside the system. Now, add the call to render the scene into the main Render function between the Clear and Present calls, like this:

myDevice.BeginScene(); myScene.Render(myDevice); myDevice.EndScene();

That’s it for the engine code. Now all we have to do is add a sprite object to the scene in the demo code and watch it go. Here’s how you do that. In the Main() function of the demo add this:

HMSprite sprite1 = new HMSprite(”hmglogo.tga”, new Vector3(), 320, 240, demo1.MyDevice); demo1.MyScene.AddObject(sprite1);

Creating a Window and Rendering a Blank Screen

Friday, June 10th, 2005

Since we are going to be rendering inside a window, I thought the best place to start would be with that class. To create this initial class, right click on your 3D engine project and choose Add-New Item-Windows Form and name it. I am using HMSystem, since this is part of the Hazy Mind engine, and it will be our main system class for initializing Direct3D.

Go into the code of the main form by right clicking it in the solution explorer and choosing View Code. Before we add any code that relies on DirectX, we need to add references to it in the References section of the project. Right click on References and choose Add Reference. Scroll down and add Microsoft.DirectX and Microsoft.DirectX.Direct3D. Now we can start coding!

Step 1: Creating and Initializing the Device

The first thing we need to do is create the device we will use to render with and initialize it. In the using section at the top of the code add these statements:

using Microsoft.DirectX; using Microsoft.DirectX.Direct3D;

At the top of your system class add these variables:

private Device myDevice; private PresentParameters presentParams;

Now, we need to think about the different settings we will want to be able to control when we first create our system. One of the most important things I can think of for this stage of creation is the size and windowed mode of our engine. We want to be able to specify whether our window is fullscreen or not, and if not, what size to make it. We probably also want to give our window/demo a name, so let’s add that too. To make these settings easy to get at later, lets add some variables to the class, and put them into the constructor so we can specify them at creation time:

private bool isWindowed; public HMSystem(int width, int height, bool windowed, string name) { InitializeComponent(); // This is already here for window creation isWindowed = windowed; this.Name = name; this.Text = name; this.Size = new Size(width, height); this.ClientSize = this.Size; if(!windowed) this.FormBorderStyle = FormBorderStyle.None; // If fullscreen, remove window borders // This will save us grief later // this.SetStyle(ControlStyles.AllPaintingInWmPaint | ControlStyles.Opaque, true); }

Now that we have all of the information we need to create our device, lets write the function we will use to do exactly that:

private bool InitializeGraphics(){ // We specify bool so we can catch failures try { // This first group of settings applies to both windowed and fullscreen modes presentParams = new PresentParameters(); presentParams.Windowed = isWindowed; presentParams.SwapEffect = SwapEffect.Discard; if(!isWindowed) { presentParams.BackBufferFormat = Manager.Adapters[0].CurrentDisplayMode.Format; presentParams.BackBufferCount = 1; presentParams.BackBufferWidth = Manager.Adapters[0].CurrentDisplayMode.Width; presentParams.BackBufferHeight = Manager.Adapters[0].CurrentDisplayMode.Height; presentParams.FullScreenRefreshRateInHz = Manager.Adapters[0].CurrentDisplayMode.RefreshRate; presentParams.PresentationInterval = PresentInterval.Immediate; } myDevice = new Device( 0, DeviceType.Hardware, this, CreateFlags.SoftwareVertexProcessing, presentParams ); } catch(DirectXException) { MessageBox.Show(”Unable to initialize Direct3D”); return false; } return true; }

Ok, we have the function to create our device. Now let’s write the function that will make our device do things (namely rendering):

// These parameters are required to use this as an addition to the Paint delegate of the Form private void Render(object sender, PaintEventArgs e) { myDevice.Clear(ClearFlags.Target, Color.CornflowerBlue, 1.0f, 0); myDevice.Present(); this.Invalidate(); // This will tell the window to redraw immediately when it finishes each frame }

With these functions completed let’s add calls to them where appropriate so their functionality actually gets used. In the constructor, at the very bottom, add a call for InitializeGraphics(); To get the Render() function to be called continuously, we will add it to the Paint delegate of the Form. In the constructor, below the SetStyle call, add this:

this.Paint += new PaintEventHandler(Render);

That’s it! Our form is ready to create a Direct3D device and render to the screen with it. Since we are creating this engine as a class library, let’s move on to the main application part of the demo and write a Main() class to start and run this.

Step 2: Creating the Main() class of the Demo Program

Start out by adding a new class to the demo project by right click its name and choosing Add-Class… Name it whatever you want and click Add. Before we can create the objects we need from our engine library, we have to add a reference to it in the same way that we added references to the DirectX libraries. Right click on the project title and choose Add Reference. From the tabs at the top select Projects and add the project listed there (our engine project.) Now you can delete the namespace line and corresponding brackets and the existing constructor code and add this code:

using System.Windows.Forms; using HMEngine; static HMSystem demo1 = new HMSystem(800, 600, true, “Hazy Mind 3D Engine Demo Application”); [STAThread] public static void Main() { demo1.Show(); Application.Run(demo1); }

All done! Now you should be able to run this Direct3D application in windowed or fullscreen mode just by changing the windowed parameter in the constructor. If you want to close the demo in fullscreen mode, just do a simple Alt+F4. Stay tuned as we continue our delve into the world of Managed DirectX.

The Ramblings of a Disoriented Mind??!?!?!!?

Thursday, June 9th, 2005

Stumbling around in Google today, Devon found a blog entitled The Ramblings of a Disoriented Mind being published by a 20 year old female in the U.K. who has interests in… interestingly enough internet/web design, jazz music, and a few other things that are eerily from my list-o-things I find interesting/fun. Hmm. Can’t ask her to marry me though, she’s already getting married. Oh, well. Long distance relationships never work out anyway.

Her archive list goes back to 2003, so I guess she wins in the creativity area and can say I copied her and not vice versa. Actually my blog title came from a spoof on the movie title “Eternal Sunshine of the Spotless Mind.” Oh well, I guess now I’m really unoriginal :/