|
| 1 | +--- |
| 2 | +title: Drawing 3D Primitives using Lists or Strips |
| 3 | +description: Demonstrates how to draw 3D primitives using lines and triangles arranged as strips or lists. |
| 4 | +requireMSLicense: true |
| 5 | +--- |
| 6 | + |
| 7 | +## Overview |
| 8 | + |
| 9 | +A 3D primitive describes how vertex data is ordered. This example demonstrates line and triangle primitive types that are the basis for all drawing calls in the MonoGame Framework. To render primitives, you need to create a basic effect and transformation matrices. This topic follows the steps described in [Creating a Basic Effect](HowTo_Create_a_BasicEffect.md) to create an instance of [BasicEffect](xref:Microsoft.Xna.Framework.Graphics.BasicEffect). This sample uses orthographic projection, but you can also use perspective projection to render primitives. The vertices used in the sample are of type [VertexPositionColor Structure](xref:Microsoft.Xna.Framework.Graphics.VertexPositionColor) which contains per-vertex position and color. |
| 10 | + |
| 11 | +### Prerequisites |
| 12 | + |
| 13 | +This tutorial assumes you are following on from the project created in [How to create a Basic Effect](HowTo_Create_a_BasicEffect.md) |
| 14 | + |
| 15 | +## Creating Vertices |
| 16 | + |
| 17 | +The major steps for this example are: |
| 18 | + |
| 19 | +- Creating Vertices |
| 20 | +- Drawing a Line List |
| 21 | +- Drawing a Line Strip |
| 22 | +- Drawing A Triangle List |
| 23 | +- Drawing a Triangle Strip |
| 24 | + |
| 25 | +## To create vertices |
| 26 | + |
| 27 | +First we need to setup some data to draw our primitives, essentially a list of points that we will connect together and draw in different ways. |
| 28 | + |
| 29 | +1. Position the camera and create the required transformations. |
| 30 | + |
| 31 | + ``` csharp |
| 32 | + // Matrix to translate the drawn primitives to the center of the screen. |
| 33 | + private Matrix translationMatrix; |
| 34 | + |
| 35 | + // Number of vertex points to draw the primitive with. |
| 36 | + private int points = 8; |
| 37 | + |
| 38 | + // The length of the primitive lines to draw. |
| 39 | + private int lineLength = 100; |
| 40 | + |
| 41 | + protected override void Initialize() |
| 42 | + { |
| 43 | + worldMatrix = Matrix.Identity; |
| 44 | + |
| 45 | + viewMatrix = Matrix.CreateLookAt( |
| 46 | + new Vector3(0.0f, 0.0f, 1.0f), |
| 47 | + Vector3.Zero, |
| 48 | + Vector3.Up |
| 49 | + ); |
| 50 | + |
| 51 | + projectionMatrix = Matrix.CreateOrthographicOffCenter( |
| 52 | + 0, |
| 53 | + (float)GraphicsDevice.Viewport.Width, |
| 54 | + (float)GraphicsDevice.Viewport.Height, |
| 55 | + 0, |
| 56 | + 1.0f, 1000.0f); |
| 57 | + |
| 58 | + Vector2 screenCenter = new Vector2(GraphicsDevice.Viewport.Width / 2, GraphicsDevice.Viewport.Height / 2); |
| 59 | + var primitiveCenter = new Vector2((points / 2 - 1) * lineLength / 2, lineLength / 2); |
| 60 | + translationMatrix = Matrix.CreateTranslation(screenCenter.X - primitiveCenter.X, screenCenter.Y - primitiveCenter.Y, 0); |
| 61 | + |
| 62 | + base.Initialize(); |
| 63 | + } |
| 64 | + ``` |
| 65 | + |
| 66 | +1. Create a list of vertices in 3D space that represent the points to draw. |
| 67 | + |
| 68 | + The following code creates eight vertices (determined from the `points` variable) and stores them in an array of type **VertexPositionColor**. |
| 69 | + |
| 70 | + ``` csharp |
| 71 | + // The vertex sata array. |
| 72 | + private VertexPositionColor[] primitiveList; |
| 73 | + |
| 74 | + protected override void LoadContent() |
| 75 | + { |
| 76 | + ... // <-Existing Load Content from Creating a Basic Effect |
| 77 | +
|
| 78 | + primitiveList = new VertexPositionColor[points]; |
| 79 | + |
| 80 | + for (int x = 0; x < points / 2; x++) |
| 81 | + { |
| 82 | + for (int y = 0; y < 2; y++) |
| 83 | + { |
| 84 | + primitiveList[(x * 2) + y] = new VertexPositionColor( |
| 85 | + new Vector3(x * lineLength, y * lineLength, 0), Color.White); |
| 86 | + } |
| 87 | + } |
| 88 | + ``` |
| 89 | + |
| 90 | + - These eight points form a `triangle strip` consisting of six triangles drawn along the plane `z = 0`, with the first point at `(0, 0, 0)`. |
| 91 | + - The camera is positioned at `(0, 0, 1)` looking at `(0, 0, 0)`. |
| 92 | + - An orthogonal projection matrix is created with the upper-left point at `(0, 0)` and the lower-right point at the current `GraphicsDevice` screen dimensions. |
| 93 | + - In addition, a translation matrix shifts the `primitiveList` point set to the center of the screen. |
| 94 | + |
| 95 | +1. To make the drawing of lines clearer, also change the `Clear` color in the `Draw` method to `black`. Just makes it easier to see drawn lines. |
| 96 | + |
| 97 | + ```csharp |
| 98 | + protected override void Draw(GameTime gameTime) |
| 99 | + { |
| 100 | + GraphicsDevice.Clear(Color.Black); |
| 101 | + ``` |
| 102 | + |
| 103 | +## Drawing a Line List |
| 104 | + |
| 105 | +The example in this section uses the sample vertex list created by following step 1 in the `Creating Vertices` steps. |
| 106 | + |
| 107 | + |
| 108 | + |
| 109 | +### To draw a line list |
| 110 | + |
| 111 | +1. Create an index array that indexes into the vertex buffer. |
| 112 | + |
| 113 | + This identifies a series of lines. |
| 114 | + |
| 115 | + ``` csharp |
| 116 | + private short[] lineListIndices; |
| 117 | + |
| 118 | + protected override void LoadContent() |
| 119 | + { |
| 120 | + ... // <-Existing Load Content |
| 121 | +
|
| 122 | + // Initialize an array of indices of type short. |
| 123 | + lineListIndices = new short[(points * 2) - 2]; |
| 124 | + |
| 125 | + // Populate the array with references to indices in the vertex buffer |
| 126 | + for (int i = 0; i < points - 1; i++) |
| 127 | + { |
| 128 | + lineListIndices[i * 2] = (short)(i); |
| 129 | + lineListIndices[(i * 2) + 1] = (short)(i + 1); |
| 130 | + } |
| 131 | + ``` |
| 132 | + |
| 133 | +2. Render the lines by calling [DrawUserIndexedPrimitives](xref:Microsoft.Xna.Framework.Graphics.GraphicsDevice#Microsoft_Xna_Framework_Graphics_GraphicsDevice_DrawUserIndexedPrimitives__1_Microsoft_Xna_Framework_Graphics_PrimitiveType___0___System_Int32_System_Int32_System_Int32___System_Int32_System_Int32_), which specifies [PrimitiveType.LineList](xref:Microsoft.Xna.Framework.Graphics.PrimitiveType) to determine how to interpret the data in the vertex array. |
| 134 | + |
| 135 | + ``` csharp |
| 136 | + GraphicsDevice.DrawUserIndexedPrimitives<VertexPositionColor>( |
| 137 | + PrimitiveType.LineList, |
| 138 | + primitiveList, |
| 139 | + 0, // vertex buffer offset to add to each element of the index buffer |
| 140 | + points, // number of vertices in pointList |
| 141 | + lineListIndices, // the index buffer |
| 142 | + 0, // first index element to read |
| 143 | + points - 1 // number of primitives to draw |
| 144 | + ); |
| 145 | + ``` |
| 146 | + |
| 147 | +## Drawing a Line Strip |
| 148 | + |
| 149 | +The example in this section uses the same point list and renders the same output as the Drawing a Line List procedure. However, it uses a `line strip` primitive type when it identifies the indices of the vertex array to draw. **Fewer indices are stored when you use a line strip.** |
| 150 | + |
| 151 | + |
| 152 | + |
| 153 | +### To draw a line strip |
| 154 | + |
| 155 | +1. Create a list of indices to identify the order in which to draw the points in the specified point list. |
| 156 | + |
| 157 | + Only half the number of indices used for the line list are needed here because the data consist of a series of connected lines. |
| 158 | + |
| 159 | + ``` csharp |
| 160 | + private short[] lineStripIndices; |
| 161 | + |
| 162 | + protected override void LoadContent() |
| 163 | + { |
| 164 | + ... // <-Existing Load Content |
| 165 | +
|
| 166 | + // Initialize an array of indices of type short. |
| 167 | + lineStripIndices = new short[points]; |
| 168 | + |
| 169 | + // Populate the array with references to indices in the vertex buffer. |
| 170 | + for (int i = 0; i < points; i++) |
| 171 | + { |
| 172 | + lineStripIndices[i] = (short)(i); |
| 173 | + } |
| 174 | + ``` |
| 175 | + |
| 176 | + > [!NOTE] |
| 177 | + > This is equivalent to setting `lineStripIndices` with the following array that consists of a series of **connected** lines between `pointList[0]`, `pointList[1]`, and `pointList[2]`, and so forth. |
| 178 | + > |
| 179 | + > ``` csharp |
| 180 | + > lineStripIndices = new short[8]{ 0, 1, 2, 3, 4, 5, 6, 7 }; |
| 181 | + > ``` |
| 182 | + |
| 183 | +1. Render the line strip by calling [DrawUserIndexedPrimitives](xref:Microsoft.Xna.Framework.Graphics.GraphicsDevice#Microsoft_Xna_Framework_Graphics_GraphicsDevice_DrawUserIndexedPrimitives__1_Microsoft_Xna_Framework_Graphics_PrimitiveType___0___System_Int32_System_Int32_System_Int32___System_Int32_System_Int32_), which specifies [PrimitiveType.LineStrip](xref:Microsoft.Xna.Framework.Graphics.PrimitiveType) to determine how to interpret the data in the vertex array. |
| 184 | + |
| 185 | + Fewer vertices are used to render the same number of primitives rendered earlier by the line list. |
| 186 | + |
| 187 | + ``` csharp |
| 188 | + GraphicsDevice.DrawUserIndexedPrimitives<VertexPositionColor>( |
| 189 | + PrimitiveType.LineList, |
| 190 | + primitiveList, |
| 191 | + 0, // vertex buffer offset to add to each element of the index buffer |
| 192 | + points, // number of vertices in pointList |
| 193 | + lineListIndices, // the index buffer |
| 194 | + 0, // first index element to read |
| 195 | + points - 1 // number of primitives to draw |
| 196 | + ); |
| 197 | + ``` |
| 198 | + |
| 199 | +## Drawing A Triangle List |
| 200 | + |
| 201 | +A triangle list, like a line list, is a primitive type that indicates you need to interpret the vertices in the vertex buffer as a series of separately drawn triangles. |
| 202 | + |
| 203 | +### To draw a triangle list |
| 204 | + |
| 205 | +1. Create an array to hold the list of indices that identify a series of triangles to draw from the specified point list. |
| 206 | + |
| 207 | + ``` csharp |
| 208 | + private short[] triangleStripIndices; |
| 209 | + private int triangleWidth = 10; |
| 210 | + private int triangleHeight = 10; |
| 211 | + |
| 212 | + protected override void LoadContent() |
| 213 | + { |
| 214 | + ... // <-Existing Load Content |
| 215 | + triangleListIndices = new short[(width - 1) * (height - 1) * 6]; |
| 216 | + |
| 217 | + for (int x = 0; x < width - 1; x++) |
| 218 | + { |
| 219 | + for (int y = 0; y < height - 1; y++) |
| 220 | + { |
| 221 | + triangleListIndices[(x + y * (width - 1)) * 6] = (short)(2 * x); |
| 222 | + triangleListIndices[(x + y * (width - 1)) * 6 + 1] = (short)(2 * x + 1); |
| 223 | + triangleListIndices[(x + y * (width - 1)) * 6 + 2] = (short)(2 * x + 2); |
| 224 | + |
| 225 | + triangleListIndices[(x + y * (width - 1)) * 6 + 3] = (short)(2 * x + 2); |
| 226 | + triangleListIndices[(x + y * (width - 1)) * 6 + 4] = (short)(2 * x + 1); |
| 227 | + triangleListIndices[(x + y * (width - 1)) * 6 + 5] = (short)(2 * x + 3); |
| 228 | + } |
| 229 | + } |
| 230 | + ``` |
| 231 | + |
| 232 | + > [!NOTE] |
| 233 | + > This is equivalent to setting `triangleListIndices` to the following array, which consists of a series of triangles between `pointList[0]`, `pointList[1]`, and `pointList[2]`, and so forth. |
| 234 | + > |
| 235 | + > ``` csharp |
| 236 | + > triangleListIndices = new short[18]{ 0, 1, 2, 2, 1, 3, 2, 3, 4, 4, 3, 5, 4, 5, 6, 6, 5, 7 }; |
| 237 | + > ``` |
| 238 | + |
| 239 | +2. Render the lines by calling [DrawUserIndexedPrimitives](xref:Microsoft.Xna.Framework.Graphics.GraphicsDevice#Microsoft_Xna_Framework_Graphics_GraphicsDevice_DrawUserIndexedPrimitives__1_Microsoft_Xna_Framework_Graphics_PrimitiveType___0___System_Int32_System_Int32_System_Int32___System_Int32_System_Int32_) |
| 240 | + |
| 241 | + This specifies [PrimitiveType.TriangleList](xref:Microsoft.Xna.Framework.Graphics.PrimitiveType), which determines how the data in the vertex array is interpreted. |
| 242 | + |
| 243 | + ``` csharp |
| 244 | + GraphicsDevice.DrawUserIndexedPrimitives<VertexPositionColor>( |
| 245 | + PrimitiveType.TriangleList, |
| 246 | + primitiveList, |
| 247 | + 0, // vertex buffer offset to add to each element of the index buffer |
| 248 | + 8, // number of vertices to draw |
| 249 | + triangleListIndices, |
| 250 | + 0, // first index element to read |
| 251 | + 6 // number of primitives to draw |
| 252 | + ); |
| 253 | + ``` |
| 254 | + |
| 255 | +## Drawing a Triangle Strip |
| 256 | + |
| 257 | +A triangle strip is a set of triangles that share multiple vertices. This example shows you how to render an object that looks the same as the object rendered with a triangle list. However, fewer vertices are needed because the triangles share multiple vertices. |
| 258 | + |
| 259 | +### To draw a triangle strip |
| 260 | + |
| 261 | +1. Create an array to hold the list of indices that identify a strip of triangles. |
| 262 | + |
| 263 | + ``` csharp |
| 264 | + private short[] triangleStripIndices; |
| 265 | + |
| 266 | + protected override void LoadContent() |
| 267 | + { |
| 268 | + ... // <-Existing Load Content |
| 269 | +
|
| 270 | + // Initialize an array of indices of type short. |
| 271 | + triangleStripIndices = new short[points]; |
| 272 | + |
| 273 | + // Populate the array with references to indices in the vertex buffer. |
| 274 | + for (int i = 0; i < points; i++) |
| 275 | + { |
| 276 | + triangleStripIndices[i] = (short)i; |
| 277 | + } |
| 278 | + ``` |
| 279 | + |
| 280 | + > [!NOTE] |
| 281 | + > This is equivalent to setting `triangleStripIndices` to the following array, which consists of a series of **connected** triangles between `pointList[0]`, `pointList[1]`, and `pointList[2]`, and so forth. |
| 282 | + > |
| 283 | + > ``` csharp |
| 284 | + > triangleStripIndices = new short[8]{ 0, 1, 2, 3, 4, 5, 6, 7 }; |
| 285 | + > ``` |
| 286 | + |
| 287 | +2. Render the lines by calling [DrawUserIndexedPrimitives](xref:Microsoft.Xna.Framework.Graphics.GraphicsDevice#Microsoft_Xna_Framework_Graphics_GraphicsDevice_DrawUserIndexedPrimitives__1_Microsoft_Xna_Framework_Graphics_PrimitiveType___0___System_Int32_System_Int32_System_Int32___System_Int32_System_Int32_). |
| 288 | + |
| 289 | + This specifies [PrimitiveType.TriangleStrip](xref:Microsoft.Xna.Framework.Graphics.PrimitiveType) to determine how to interpret the data in the vertex array. Fewer vertices are used to render the same number of primitives rendered earlier by the triangle list. |
| 290 | + |
| 291 | + > [!NOTE] |
| 292 | + > In the example code, the triangle strip is rendered by a series of red lines instead of the white lines used for the previous triangle list. The color change indicates a different primitive type was used to achieve the same result. |
| 293 | + |
| 294 | + ``` csharp |
| 295 | + GraphicsDevice.DrawUserIndexedPrimitives<VertexPositionColor>( |
| 296 | + PrimitiveType.TriangleStrip, |
| 297 | + primitiveList, |
| 298 | + 0, // vertex buffer offset to add to each element of the index buffer |
| 299 | + 8, // number of vertices to draw |
| 300 | + triangleStripIndices, |
| 301 | + 0, // first index element to read |
| 302 | + 6 // number of primitives to draw |
| 303 | + ); |
| 304 | + ``` |
| 305 | + |
| 306 | +## See Also |
| 307 | + |
| 308 | +- [How to enable Anti-aliasing](HowTo_Enable_Anti_Aliasing.md) |
| 309 | + |
| 310 | +### Concepts |
| 311 | + |
| 312 | +- [What Is 3D Rendering?](../../whatis/grasphics/WhatIs_3DRendering.md) |
| 313 | + |
| 314 | +### Reference |
| 315 | + |
| 316 | +- [GraphicsDevice](xref:Microsoft.Xna.Framework.Graphics.GraphicsDevice) |
| 317 | +- [DrawUserIndexedPrimitives](xref:Microsoft.Xna.Framework.Graphics.GraphicsDevice#Microsoft_Xna_Framework_Graphics_GraphicsDevice_DrawUserIndexedPrimitives__1_Microsoft_Xna_Framework_Graphics_PrimitiveType___0___System_Int32_System_Int32_System_Int32___System_Int32_System_Int32_) |
0 commit comments