This repository was archived by the owner on Apr 21, 2019. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathPrimitiveBatch.cs
More file actions
executable file
·248 lines (205 loc) · 8.94 KB
/
PrimitiveBatch.cs
File metadata and controls
executable file
·248 lines (205 loc) · 8.94 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
#region File Description
//-----------------------------------------------------------------------------
// PrimitiveBatch.cs
//
// Microsoft XNA Community Game Platform
// Copyright (C) Microsoft Corporation. All rights reserved.
//-----------------------------------------------------------------------------
#endregion
#region Using Statements
using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.Input;
#endregion
namespace Othelis
{
// PrimitiveBatch is a class that handles efficient rendering automatically for its
// users, in a similar way to SpriteBatch. PrimitiveBatch can render lines, points,
// and triangles to the screen. In this sample, it is used to draw a spacewars
// retro scene.
public class PrimitiveBatch : IDisposable
{
#region Constants and Fields
// this constant controls how large the vertices buffer is. Larger buffers will
// require flushing less often, which can increase performance. However, having
// buffer that is unnecessarily large will waste memory.
const int DefaultBufferSize = 500;
// a block of vertices that calling AddVertex will fill. Flush will draw using
// this array, and will determine how many primitives to draw from
// positionInBuffer.
VertexPositionColor[] vertices = new VertexPositionColor[DefaultBufferSize];
// keeps track of how many vertices have been added. this value increases until
// we run out of space in the buffer, at which time Flush is automatically
// called.
int positionInBuffer = 0;
// a basic effect, which contains the shaders that we will use to draw our
// primitives.
BasicEffect basicEffect;
// the device that we will issue draw calls to.
GraphicsDevice device;
// this value is set by Begin, and is the type of primitives that we are
// drawing.
PrimitiveType primitiveType;
// how many verts does each of these primitives take up? points are 1,
// lines are 2, and triangles are 3.
int numVertsPerPrimitive;
// hasBegun is flipped to true once Begin is called, and is used to make
// sure users don't call End before Begin is called.
bool hasBegun = false;
bool isDisposed = false;
#endregion
// the constructor creates a new PrimitiveBatch and sets up all of the internals
// that PrimitiveBatch will need.
public PrimitiveBatch(GraphicsDevice graphicsDevice)
{
if (graphicsDevice == null)
{
throw new ArgumentNullException("graphicsDevice");
}
device = graphicsDevice;
// set up a new basic effect, and enable vertex colors.
basicEffect = new BasicEffect(graphicsDevice);
basicEffect.VertexColorEnabled = true;
// projection uses CreateOrthographicOffCenter to create 2d projection
// matrix with 0,0 in the upper left.
basicEffect.Projection = Matrix.CreateOrthographicOffCenter
(0, graphicsDevice.Viewport.Width,
graphicsDevice.Viewport.Height, 0,
0, 1);
this.basicEffect.World = Matrix.Identity;
this.basicEffect.View = Matrix.CreateLookAt(Vector3.Zero, Vector3.Forward,
Vector3.Up);
}
public void Dispose()
{
this.Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
if (disposing && !isDisposed)
{
if (basicEffect != null)
basicEffect.Dispose();
isDisposed = true;
}
}
// Begin is called to tell the PrimitiveBatch what kind of primitives will be
// drawn, and to prepare the graphics card to render those primitives.
public void Begin(PrimitiveType primitiveType)
{
if (hasBegun)
{
throw new InvalidOperationException
("End must be called before Begin can be called again.");
}
// these three types reuse vertices, so we can't flush properly without more
// complex logic. Since that's a bit too complicated for this sample, we'll
// simply disallow them.
if (primitiveType == PrimitiveType.LineStrip ||
primitiveType == PrimitiveType.TriangleStrip)
{
throw new NotSupportedException
("The specified primitiveType is not supported by PrimitiveBatch.");
}
this.primitiveType = primitiveType;
// how many verts will each of these primitives require?
this.numVertsPerPrimitive = NumVertsPerPrimitive(primitiveType);
//tell our basic effect to begin.
basicEffect.CurrentTechnique.Passes[0].Apply();
// flip the error checking boolean. It's now ok to call AddVertex, Flush,
// and End.
hasBegun = true;
}
// AddVertex is called to add another vertex to be rendered. To draw a point,
// AddVertex must be called once. for lines, twice, and for triangles 3 times.
// this function can only be called once begin has been called.
// if there is not enough room in the vertices buffer, Flush is called
// automatically.
public void AddVertex(Vector2 vertex, Color color)
{
if (!hasBegun)
{
throw new InvalidOperationException
("Begin must be called before AddVertex can be called.");
}
// are we starting a new primitive? if so, and there will not be enough room
// for a whole primitive, flush.
bool newPrimitive = ((positionInBuffer % numVertsPerPrimitive) == 0);
if (newPrimitive &&
(positionInBuffer + numVertsPerPrimitive) >= vertices.Length)
{
Flush();
}
// once we know there's enough room, set the vertex in the buffer,
// and increase position.
vertices[positionInBuffer].Position = new Vector3(vertex, 0);
vertices[positionInBuffer].Color = color;
positionInBuffer++;
}
// End is called once all the primitives have been drawn using AddVertex.
// it will call Flush to actually submit the draw call to the graphics card, and
// then tell the basic effect to end.
public void End()
{
if (!hasBegun)
{
throw new InvalidOperationException
("Begin must be called before End can be called.");
}
// Draw whatever the user wanted us to draw
Flush();
hasBegun = false;
}
// Flush is called to issue the draw call to the graphics card. Once the draw
// call is made, positionInBuffer is reset, so that AddVertex can start over
// at the beginning. End will call this to draw the primitives that the user
// requested, and AddVertex will call this if there is not enough room in the
// buffer.
private void Flush()
{
if (!hasBegun)
{
throw new InvalidOperationException
("Begin must be called before Flush can be called.");
}
// no work to do
if (positionInBuffer == 0)
{
return;
}
// how many primitives will we draw?
int primitiveCount = positionInBuffer / numVertsPerPrimitive;
// submit the draw call to the graphics card
device.DrawUserPrimitives<VertexPositionColor>(primitiveType, vertices, 0,
primitiveCount);
// now that we've drawn, it's ok to reset positionInBuffer back to zero,
// and write over any vertices that may have been set previously.
positionInBuffer = 0;
}
#region Helper functions
// NumVertsPerPrimitive is a boring helper function that tells how many vertices
// it will take to draw each kind of primitive.
static private int NumVertsPerPrimitive(PrimitiveType primitive)
{
int numVertsPerPrimitive;
switch (primitive)
{
case PrimitiveType.LineList:
numVertsPerPrimitive = 2;
break;
case PrimitiveType.TriangleList:
numVertsPerPrimitive = 3;
break;
default:
throw new InvalidOperationException("primitive is not valid");
}
return numVertsPerPrimitive;
}
#endregion
}
}