|
|
|
|
Recycling vertices using inidices |
The triangle was nice, but what about a lot of triangles? We would need to specify 3 vertices for each triangle. Consider next example:

Only 4 out of 6 vertices are unique. So the other 2 are simply a waste of bandwidth to your graphics card! It would be better to define the 4 vertices in an array from 0 to 3, and to define triangle 1 as vertices 1,2 and 3 and triangle 2 as vertices 2,3 and 4. This way, the complex vertex data is not duplicated. This is exactly the idea behind indices. Suppose we would like to draw these 2 triangles :

Normally we would have to define 6 vertices, now we will define only 5. So change our SetUpVertices method as follows:
private void SetUpVertices() { vertices = new VertexPositionColor[5]; vertices[0].Position = new Vector3(0f, 0f, 0f); vertices[0].Color = Color.White; vertices[1].Position = new Vector3(5f, 0f, 0f); vertices[1].Color = Color.White; vertices[2].Position = new Vector3(10f, 0f, 0f); vertices[2].Color = Color.White; vertices[3].Position = new Vector3(5f, 0f, -5f); vertices[3].Color = Color.White; vertices[4].Position = new Vector3(10f, 0f, -5f); vertices[4].Color = Color.White; myVertexDeclaration = new VertexDeclaration(device, VertexPositionColor.VertexElements); }
Vertices 0 to 2 are positioned on the positive X axis. Vertices 3 and 4 have a negative Z component, as XNA considers the negative Z axis to be ‘Forward’. As your vertices are defined along the Right and the Forward directions, the resulting triangles will be lying flat on the ground.
Next, we will create a list of indices. As discussed above, the indices refer to vertices in our array of vertices. The indices define the triangles, so for 2 triangles we will need to define 6 indices. Start by defining the array at the top of your class. Since indices are integer numbers, you will define an array capable of storing ints:
int[] indices;
Let’s create another small method that fills the array of indices.
private void SetUpIndices() { indices = new ints[6]; indices[0] = 3; indices[1] = 1; indices[2] = 0; indices[3] = 4; indices[4] = 2; indices[5] = 1; }
As you can see, this method defines 6 indices, defining 2 triangles. Vertex number 1 is referred to twice, which was our initial goal as you can see in the image above. In this case, the profit is rather small, but in bigger applications (as you will see soon ;) this is the way to go. Also note that the triangles have been defined in a clockwise order again, so XNA will see them as facing the camera and will not cull them away.
Make sure to call this method from our LoadContent method :
SetUpIndices();
All that's left for this chapter is to draw the triangles from our buffer! Change the following line in your Draw method:
device.DrawUserIndexedPrimitives(PrimitiveType.TriangleList, vertices, 0, vertices.Length, indices, 0, indices.Length / 3);
Instead of using the DrawUserPrimitives method, this time we call the DrawUserIndexedPrimitives method. This allows us to specify both an array of vertices and an array of indices. The last argument specifies how many triangles are defined by the indices. Since one triangle is defined by 3 indices, we specify the number of indices divided by 3.
Before your try this code, make your triangles stop rotating by resetting their World matrix to the unity matrix. The Identity matrix is the unity matrix, so your original World space coordinates will be used.
Matrix worldMatrix = Matrix.Identity;
That's it! When you run the program, however, there will be not too much to see. This is because both your triangles and your camera are positioned on the floor! We’ll have to reposition our camera so the triangles are in sight of the camera. So go to our SetUpCamera method, and change the position of the camera so it is positioned above our (0,0,0) 3D origin:
viewMatrix = Matrix.CreateLookAt(new Vector3(0, 50, 0), new Vector3(0, 0, 0), new Vector3(0, 0, -1));
We've positioned our camera 50 units above the (0,0,0) 3D origin, as the Y axis is considered as Up axis by XNA. However, because the camera is looking down, you can no longer specify the (0,1,0) Up vector as Up vector for the camera! Therefore, you specify the (0,0,-1) Forward vector as Up vector for the camera.
See Recipe 2-1 for more information about the View matrix.
Now when you run this code, you should see both triangles, but they’re still solid. Try putting this line as first line of the Draw method :
device.RenderState.FillMode = FillMode.WireFrame;
This will only draw the edges of our triangles, instead of solid triangles.

Click here to go to the forum on this chapter!
Or click on one of the topics on this chapter to go there: Blank Screen So I'm writing code for a psychology research pro...VertexDeclaration? Hi, the line:
myVertexDeclaration = new VertexD...Camera position Hi,
I am currently on your terrain tutorial. If...Setting The Vertex Buffer Error Hey, Firstly i would like to say.. This time i che...Help with Indices Im haveing trouble with the commented part.
Err...Code Corrections Hi,
With release 2.0 there are some corrections...Indices fail with XNA game 2 Hi
In SetUp Vertices you must replace as follow...Graphics.graphicsDevice I was just curious why you define a graphics devic...Indices while drawing the two triangles using indices, but...
If this chapter or next chapter doesn’t render any triangles to your window, chances are your graphics card isn’t capable of rendering from many indices. To solve this, store your indices as shorts instead of ints. Define your array like this:
short[] indices;
And in your SetUpIndices method, initialize it like this:
indices = new short[6];
This should work, even on pcs with lower-end graphics cards.
The benefit of using indices in this example is not very big: we’re passing 5 vertices instead of 6. In the next chapter, however, the benefit will be a lot larger.
You can try these exercises to practice what you've learned:
Try to render the triangle that connects vertices 1, 3 and 4. The only changes you need to make are in the SetUpIndices method. Use another vector, such as (1,0,0) as Up vector for your camera’s View matrix.
Here's the whole code so far:
using System; using System.Collections.Generic; 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.Net; using Microsoft.Xna.Framework.Storage; namespace XNAtutorial { public class Game1 : Microsoft.Xna.Framework.Game { GraphicsDeviceManager graphics; SpriteBatch spriteBatch; GraphicsDevice device; Effect effect; VertexPositionColor[] vertices; VertexDeclaration myVertexDeclaration;
int[] indices;
Matrix viewMatrix; Matrix projectionMatrix; private float angle = 0f; public Game1() { graphics = new GraphicsDeviceManager(this); Content.RootDirectory = "Content"; } protected override void Initialize() { graphics.PreferredBackBufferWidth = 500; graphics.PreferredBackBufferHeight = 500; graphics.IsFullScreen = false; graphics.ApplyChanges(); Window.Title = "Riemer's XNA Tutorials -- Series 1"; base.Initialize(); } protected override void LoadContent() { device = graphics.GraphicsDevice; spriteBatch = new SpriteBatch(GraphicsDevice);
effect = Content.Load<Effect>
("effects"); SetUpVertices(); SetUpCamera();
SetUpIndices();
} private void SetUpVertices() { vertices = new VertexPositionColor[5];
vertices[0].Position = new Vector3(0f, 0f, 0f); vertices[0].Color = Color.White; vertices[1].Position = new Vector3(5f, 0f, 0f); vertices[1].Color = Color.White; vertices[2].Position = new Vector3(10f, 0f, 0f); vertices[2].Color = Color.White; vertices[3].Position = new Vector3(5f, 0f, -5f); vertices[3].Color = Color.White; vertices[4].Position = new Vector3(10f, 0f, -5f); vertices[4].Color = Color.White;
myVertexDeclaration = new VertexDeclaration(device, VertexPositionColor.VertexElements); }
private void SetUpIndices() { indices = new int[6]; indices[0] = 3; indices[1] = 1; indices[2] = 0; indices[3] = 4; indices[4] = 2; indices[5] = 1; }
private void SetUpCamera() {
viewMatrix = Matrix.CreateLookAt(new Vector3(0, 50, 0), new Vector3(0, 0, 0), new Vector3(0, 0, -1));
projectionMatrix = Matrix.CreatePerspectiveFieldOfView(MathHelper.PiOver4, device.Viewport.AspectRatio, 1.0f, 300.0f); } protected override void UnloadContent() { } protected override void Update(GameTime gameTime) { if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed) this.Exit(); base.Update(gameTime); } protected override void Draw(GameTime gameTime) { device.Clear(Color.DarkSlateBlue); device.RenderState.CullMode = CullMode.None;
device.RenderState.FillMode = FillMode.WireFrame;
effect.CurrentTechnique = effect.Techniques["Colored"]; effect.Parameters["xView"].SetValue(viewMatrix); effect.Parameters["xProjection"].SetValue(projectionMatrix); effect.Parameters["xWorld"].SetValue(Matrix.Identity); effect.Begin(); foreach (EffectPass pass in effect.CurrentTechnique.Passes) { pass.Begin(); device.VertexDeclaration = myVertexDeclaration;
device.DrawUserIndexedPrimitives(PrimitiveType.TriangleList, vertices, 0, vertices.Length, indices, 0, indices.Length / 3);
pass.End(); } effect.End(); base.Draw(gameTime); } } }
- Website design & XNA + DirectX code : Riemer Grootjans - ©2003 - 2008 Riemer Grootjans
|
|
|
|
|