Skip to content

Commit f6e9587

Browse files
Added custom vertex article
1 parent 7387095 commit f6e9587

File tree

5 files changed

+294
-0
lines changed

5 files changed

+294
-0
lines changed
Lines changed: 231 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,231 @@
1+
---
2+
title: How to create a Custom Vertex Declaration
3+
description: Demonstrates how to create a custom vertex declaration and use it to render a 3D object.
4+
requireMSLicense: true
5+
---
6+
7+
## Overview
8+
9+
MonoGame includes a few predefined classes for common vertex buffer declarations such as [VertexPositionColor](xref:Microsoft.Xna.Framework.Graphics.VertexPositionColor) and [VertexPositionColorTexture](xref:Microsoft.Xna.Framework.Graphics.VertexPositionColorTexture). If you need to create a vertex buffer declaration that includes additional user-defined types, create a custom vertex declaration.
10+
11+
![Custom Vertex Example](images/graphics_custom_vertex.png)
12+
13+
A custom vertex declaration is a class that implements fully customizable per-vertex data. Furthermore, if you derive the class from [IVertexType](xref:Microsoft.Xna.Framework.Graphics.IVertexType), you will not need to create a vertex declaration when creating your vertex buffer or drawing the geometry.
14+
15+
## Requirements
16+
17+
The following texture will be used to render to the screen.
18+
19+
![MonoGame Logo](images/logo.png)
20+
21+
Save it to your content project and name it "**logo**" (this name will used to reference it in the project). Make sure to [add it to your content project](../content_pipeline/HowTo_GameContent_Add.md).
22+
23+
## Starting from the [Basic Effect](HowTo_Create_a_BasicEffect.md) tutorial
24+
25+
1. Follow the steps of [How to create a Basic Effect](HowTo_Create_a_BasicEffect.md).
26+
27+
This gives us a starting project that is rendering a 3D scene with a [Basic Effect](xref:Microsoft.Xna.Framework.Graphics.BasicEffect).
28+
29+
## Create a custom vertex declaration
30+
31+
1. Create a new class file called `CustomVertex1.cs`
32+
33+
1. Add the required `using` statements to the top of the file.
34+
35+
```csharp
36+
using Microsoft.Xna.Framework;
37+
using Microsoft.Xna.Framework.Graphics;
38+
```
39+
40+
1. Declare a structure that derives from [IVertexType](xref:Microsoft.Xna.Framework.Graphics.IVertexType).
41+
42+
```csharp
43+
public struct CustomVertex1 : IVertexType
44+
```
45+
46+
1. Add members to the struct that describe the per-vertex data.
47+
48+
This example uses position as a [Vector3 Structure](xref:Microsoft.Xna.Framework.Vector3) type, a texture coordinate using a [Vector2 Structure](xref:Microsoft.Xna.Framework.Vector2) type, and a vertex declaration using the [VertexDeclaration](xref:Microsoft.Xna.Framework.Graphics.VertexDeclaration) type.
49+
50+
```csharp
51+
private Vector3 vertexPosition;
52+
private Vector2 vertexTextureCoordinate;
53+
54+
public readonly static VertexDeclaration VertexDeclaration = new VertexDeclaration
55+
(
56+
new VertexElement(0, VertexElementFormat.Vector3, VertexElementUsage.Position, 0),
57+
new VertexElement(12, VertexElementFormat.Vector2, VertexElementUsage.TextureCoordinate, 0)
58+
);
59+
```
60+
61+
1. Implement the constructor and public accessor methods.
62+
63+
```csharp
64+
//The constructor for the custom vertex. This allows similar
65+
//initialization of custom vertex arrays as compared to arrays of a
66+
//standard vertex type, such as VertexPositionColor.
67+
public CustomVertex1(Vector3 pos, Vector2 textureCoordinate)
68+
{
69+
vertexPosition = pos;
70+
vertexTextureCoordinate = textureCoordinate;
71+
}
72+
73+
//Public methods for accessing the components of the custom vertex.
74+
public Vector3 Position
75+
{
76+
get { return vertexPosition; }
77+
set { vertexPosition = value; }
78+
}
79+
80+
public Vector2 TextureCoordinate
81+
{
82+
get { return vertexTextureCoordinate; }
83+
set { vertexTextureCoordinate = value; }
84+
}
85+
```
86+
87+
1. Implement a non-public method for accessing the vertex declaration.
88+
89+
```csharp
90+
VertexDeclaration IVertexType.VertexDeclaration
91+
{
92+
get { return VertexDeclaration; }
93+
}
94+
```
95+
96+
## Using the Custom Vertex Buffer
97+
98+
Using the Custom Vertex buffer, we will render a cube that is textured with the logo texture.
99+
100+
1. Declare some variables at the top of your Game class.
101+
102+
```csharp
103+
private CustomVertex1[] cubeVertices;
104+
private VertexBuffer vertexBuffer;
105+
private Texture2D logoTexture;
106+
```
107+
108+
This gives us the necessary data in order to draw our cube.
109+
110+
1. Create a new method called `SetupDrawingCube()` and we will start with initializing the vertex buffer, passing in the **typeof(CustomVertex1)** instead of a vertex declaration to describe the vertex buffer data.
111+
112+
```csharp
113+
public void SetupDrawingCube()
114+
{
115+
vertexBuffer = new VertexBuffer(
116+
GraphicsDevice,
117+
typeof(CustomVertex1),
118+
36,
119+
BufferUsage.None
120+
);
121+
```
122+
123+
1. Create the per-vertex data; this shows a portion of the code.
124+
125+
[!code-csharp[](./files/cubevertexdata.cs)]
126+
127+
> For a triangle list, you need three vertices for a triangle and two triangles to make the front face of a cube.
128+
129+
1. Finally, set the data into the vertex buffer data by calling [VertexBuffer.SetData](xref:Microsoft.Xna.Framework.Graphics.VertexBuffer) and set the vertex buffer to the device by calling [GraphicsDevice.SetVertexBuffer](xref:Microsoft.Xna.Framework.Graphics.GraphicsDevice).
130+
131+
```csharp
132+
vertexBuffer.SetData<CustomVertex1>(cubeVertices);
133+
134+
graphics.GraphicsDevice.SetVertexBuffer(vertexBuffer);
135+
}
136+
```
137+
138+
1. In `LoadContent` we need to Load the texture we are going to draw, as well as call the new method we defined to setup the primitive Cube.
139+
140+
```csharp
141+
logoTexture = Content.Load<Texture2D>("logo");
142+
SetupDrawingCube();
143+
```
144+
145+
1. At the moment the `BasicEffect` setup from the previous tutorial only draws Vertex Colors, so we need to change that to pass it the [texture](xref:Microsoft.Xna.Framework.Graphics.BasicEffect#Microsoft_Xna_Framework_Graphics_BasicEffect_Texture) we just loaded, as well as enabling [Texture drawing](xref:Microsoft.Xna.Framework.Graphics.BasicEffect#Microsoft_Xna_Framework_Graphics_BasicEffect_TextureEnabled)
146+
147+
Replace:
148+
149+
```csharp
150+
basicEffect.VertexColorEnabled = true;
151+
```
152+
153+
With the following
154+
155+
```csharp
156+
// Enable Texture Drawing - VERY IMPORTANT!!
157+
basicEffect.TextureEnabled = true;
158+
159+
// Set the texture we loaded (does nothing without the above setting)
160+
basicEffect.Texture = logoTexture;
161+
```
162+
163+
1. Finally, Draw the object by calling [GraphicsDevice.DrawPrimitives](xref:Microsoft.Xna.Framework.Graphics.GraphicsDevice).
164+
165+
```csharp
166+
foreach (EffectPass pass in basicEffect.CurrentTechnique.Passes)
167+
{
168+
pass.Apply();
169+
170+
graphics.GraphicsDevice.DrawPrimitives(
171+
PrimitiveType.TriangleList,
172+
0, // start vertex
173+
12 // number of primitives to draw, 2 vertexes per side of the cube
174+
);
175+
}
176+
```
177+
178+
## Extra credit, make it spin
179+
180+
At the moment all you can see is the front face of the cube, which is not very exciting. Though [Matrix Transforms](HowTo_TransformPoint.md) however, we make our cube spin.
181+
182+
1. First define a variable to manage the current rotation.
183+
184+
```csharp
185+
float rotation = 0f;
186+
```
187+
188+
1. Next, we will update this rotation each frame in the `Update` method.
189+
190+
```csharp
191+
// Update rotation angle
192+
float deltaTime = (float)gameTime.ElapsedGameTime.TotalSeconds;
193+
rotation += deltaTime;
194+
```
195+
196+
1. Then finally, in the `Draw` method, we need to calculate the rotation matrix and the translation matrix to rotate the cube by and then apply that to the basic effect drawing the cube.
197+
198+
Add the following to the `Draw` call **BEFORE** the effect drawing loop.
199+
200+
```csharp
201+
// Create rotation matrices
202+
Matrix rotationMatrix = Matrix.CreateRotationX(rotation) *
203+
Matrix.CreateRotationY(rotation) *
204+
Matrix.CreateRotationZ(rotation);
205+
// Apply rotation to the world matrix
206+
Matrix worldMatrix = rotationMatrix * Matrix.CreateTranslation(new Vector3(0, 0, 0));
207+
// Update the world matrix in the effect
208+
basicEffect.World = worldMatrix;
209+
```
210+
211+
> [!NOTE]
212+
> This is a very basic example of applying rotation, just for reference.
213+
214+
With the changes in place, your cube will look a little more snazzy and rotate in the world.
215+
216+
![Spinning Cube](images/HowTo_CustomVertex_Final.gif)
217+
218+
## See Also
219+
220+
- [How to create a Basic Effect](HowTo_Create_a_BasicEffect.md)
221+
- [How to transform a Point](HowTo_TransformPoint.md)
222+
223+
### Concepts
224+
225+
- [What Is 3D Rendering?](../../whatis/graphics/WhatIs_3DRendering.md)
226+
227+
### Reference
228+
229+
- [IVertexType](xref:Microsoft.Xna.Framework.Graphics.IVertexType)
230+
- [VertexDeclaration](xref:Microsoft.Xna.Framework.Graphics.VertexDeclaration)
231+
- [Matrix](xref:Microsoft.Xna.Framework.Matrix)
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
Vector3 LeftTopFront = new Vector3(-1.0f, 1.0f, 1.0f);
2+
Vector3 LeftBottomFront = new Vector3(-1.0f, -1.0f, 1.0f);
3+
Vector3 LeftTopBack = new Vector3(-1.0f, 1.0f, -1.0f);
4+
Vector3 LeftBottomBack = new Vector3(-1.0f, -1.0f, -1.0f);
5+
6+
Vector3 RightTopFront = new Vector3(1.0f, 1.0f, 1.0f);
7+
Vector3 RightBottomFront = new Vector3(1.0f, -1.0f, 1.0f);
8+
Vector3 RightTopBack = new Vector3(1.0f, 1.0f, -1.0f);
9+
Vector3 RightBottomBack = new Vector3(1.0f, -1.0f, -1.0f);
10+
11+
Vector2 textureLeftTop = new Vector2(0.0f, 0.0f);
12+
Vector2 textureLeftBottom = new Vector2(0.0f, 1.0f);
13+
Vector2 textureRightTop = new Vector2(1.0f, 0.0f);
14+
Vector2 textureRightBottom = new Vector2(1.0f, 1.0f);
15+
16+
// Front face.
17+
cubeVertices = new CustomVertex1[36];
18+
cubeVertices[0] = new CustomVertex1(LeftTopFront, textureLeftTop);
19+
cubeVertices[1] = new CustomVertex1(LeftBottomFront, textureLeftBottom);
20+
cubeVertices[2] = new CustomVertex1(RightTopFront, textureRightTop);
21+
cubeVertices[3] = new CustomVertex1(LeftBottomFront, textureLeftBottom);
22+
cubeVertices[4] = new CustomVertex1(RightBottomFront, textureRightBottom);
23+
cubeVertices[5] = new CustomVertex1(RightTopFront, textureRightTop);
24+
25+
// Add the vertices for the BACK face.
26+
cubeVertices[6] = new CustomVertex1(LeftTopBack, textureRightTop);
27+
cubeVertices[7] = new CustomVertex1(RightTopBack, textureLeftTop);
28+
cubeVertices[8] = new CustomVertex1(LeftBottomBack, textureRightBottom);
29+
cubeVertices[9] = new CustomVertex1(LeftBottomBack, textureRightBottom);
30+
cubeVertices[10] = new CustomVertex1(RightTopBack, textureLeftTop);
31+
cubeVertices[11] = new CustomVertex1(RightBottomBack, textureLeftBottom);
32+
33+
// Add the vertices for the TOP face.
34+
cubeVertices[12] = new CustomVertex1(LeftTopFront, textureLeftBottom);
35+
cubeVertices[13] = new CustomVertex1(RightTopBack, textureRightTop);
36+
cubeVertices[14] = new CustomVertex1(LeftTopBack, textureLeftTop);
37+
cubeVertices[15] = new CustomVertex1(LeftTopFront, textureLeftBottom);
38+
cubeVertices[16] = new CustomVertex1(RightTopFront, textureRightBottom);
39+
cubeVertices[17] = new CustomVertex1(RightTopBack, textureRightTop);
40+
41+
// Add the vertices for the BOTTOM face.
42+
cubeVertices[18] = new CustomVertex1(LeftBottomFront, textureLeftTop);
43+
cubeVertices[19] = new CustomVertex1(LeftBottomBack, textureLeftBottom);
44+
cubeVertices[20] = new CustomVertex1(RightBottomBack, textureRightBottom);
45+
cubeVertices[21] = new CustomVertex1(LeftBottomFront, textureLeftTop);
46+
cubeVertices[22] = new CustomVertex1(RightBottomBack, textureRightBottom);
47+
cubeVertices[23] = new CustomVertex1(RightBottomFront, textureRightTop);
48+
49+
// Add the vertices for the LEFT face.
50+
cubeVertices[24] = new CustomVertex1(LeftTopFront, textureRightTop);
51+
cubeVertices[25] = new CustomVertex1(LeftBottomBack, textureLeftBottom);
52+
cubeVertices[26] = new CustomVertex1(LeftBottomFront, textureRightBottom);
53+
cubeVertices[27] = new CustomVertex1(LeftTopBack, textureLeftTop);
54+
cubeVertices[28] = new CustomVertex1(LeftBottomBack, textureLeftBottom);
55+
cubeVertices[29] = new CustomVertex1(LeftTopFront, textureRightTop);
56+
57+
// Add the vertices for the RIGHT face.
58+
cubeVertices[30] = new CustomVertex1(RightTopFront, textureLeftTop);
59+
cubeVertices[31] = new CustomVertex1(RightBottomFront, textureLeftBottom);
60+
cubeVertices[32] = new CustomVertex1(RightBottomBack, textureRightBottom);
61+
cubeVertices[33] = new CustomVertex1(RightTopBack, textureRightTop);
62+
cubeVertices[34] = new CustomVertex1(RightTopFront, textureLeftTop);
63+
cubeVertices[35] = new CustomVertex1(RightBottomBack, textureRightBottom);
Loading
Loading
Loading

0 commit comments

Comments
 (0)