This has been a reoccurring problem for me and I gave up for a while. I tried fixing it again with no luck. What I did figure out is that I'm getting 320,000 draw calls a second, while drawing 40,000 cubes. I have no idea why this is happening either. I only need one draw call to draw my map, but it keeps getting redrawn for some reason.
This class is a list which stores the cubes from an array. The draw method is what I use in my main game class to draw the cubes.
public class DrawableList<T> : DrawableGameComponent
{
private BasicEffect effect;
private ThirdPersonCam camera;
public int numberOfCalls = 0;
private class Entity
{
public Vector3 Position { get; set; }
public Matrix Orientation { get; set; }
public Texture2D Texture { get; set; }
}
private Cube cube;
private List<Entity> entities = new List<Entity>();
public DrawableList(Game game, ThirdPersonCam camera, BasicEffect effect)
: base(game)
{
this.effect = effect;
cube = new Cube(game.GraphicsDevice);
this.camera = camera;
}
public void Add(Vector3 position, Matrix orientation, Texture2D texture)
{
entities.Add(new Entity()
{
Position = position,
Orientation = orientation,
Texture = texture
});
}
public override void Draw(GameTime gameTime)
{
foreach (var item in entities)
{
effect.VertexColorEnabled = false;
effect.TextureEnabled = true;
effect.Texture = item.Texture;
Matrix center = Matrix.CreateTranslation(new Vector3(-0.5f, -0.5f, -0.5f));
Matrix scale = Matrix.CreateScale(1f);
Matrix translate = Matrix.CreateTranslation(item.Position);
effect.World = center * scale * translate;
effect.View = camera.view;
effect.Projection = camera.proj;
effect.FogEnabled = true;
effect.FogColor = Color.CornflowerBlue.ToVector3();
effect.FogStart = 1.0f;
effect.FogEnd = 50.0f;
cube.Draw(effect);
numberOfCalls++;
}
base.Draw(gameTime);
}
}
This is my cube class. Pretty self explanatory.
public GraphicsDevice device;
public VertexBuffer cubeVertexBuffer;
public Cube(GraphicsDevice graphicsDevice)
{
device = graphicsDevice;
var vertices = new List<VertexPositionTexture>();
BuildFace(vertices, new Vector3(0, 0, 0), new Vector3(0, 1, 1));
BuildFace(vertices, new Vector3(0, 0, 1), new Vector3(1, 1, 1));
BuildFace(vertices, new Vector3(1, 0, 1), new Vector3(1, 1, 0));
BuildFace(vertices, new Vector3(1, 0, 0), new Vector3(0, 1, 0));
BuildFaceHorizontal(vertices, new Vector3(0, 1, 0), new Vector3(1, 1, 1));
BuildFaceHorizontal(vertices, new Vector3(0, 0, 1), new Vector3(1, 0, 0));
cubeVertexBuffer = new VertexBuffer(device, VertexPositionTexture.VertexDeclaration, vertices.Count, BufferUsage.WriteOnly);
cubeVertexBuffer.SetData<VertexPositionTexture>(vertices.ToArray());
}
private void BuildFace(List<VertexPositionTexture> vertices, Vector3 p1, Vector3 p2)
{
vertices.Add(BuildVertex(p1.X, p1.Y, p1.Z, 1, 0));
vertices.Add(BuildVertex(p1.X, p2.Y, p1.Z, 1, 1));
vertices.Add(BuildVertex(p2.X, p2.Y, p2.Z, 0, 1));
vertices.Add(BuildVertex(p2.X, p2.Y, p2.Z, 0, 1));
vertices.Add(BuildVertex(p2.X, p1.Y, p2.Z, 0, 0));
vertices.Add(BuildVertex(p1.X, p1.Y, p1.Z, 1, 0));
}
private void BuildFaceHorizontal(List<VertexPositionTexture> vertices, Vector3 p1, Vector3 p2)
{
vertices.Add(BuildVertex(p1.X, p1.Y, p1.Z, 0, 1));
vertices.Add(BuildVertex(p2.X, p1.Y, p1.Z, 1, 1));
vertices.Add(BuildVertex(p2.X, p2.Y, p2.Z, 1, 0));
vertices.Add(BuildVertex(p1.X, p1.Y, p1.Z, 0, 1));
vertices.Add(BuildVertex(p2.X, p2.Y, p2.Z, 1, 0));
vertices.Add(BuildVertex(p1.X, p1.Y, p2.Z, 0, 0));
}
private VertexPositionTexture BuildVertex(float x, float y, float z, float u, float v)
{
return new VertexPositionTexture(new Vector3(x, y, z), new Vector2(u, v));
}
public void Draw(BasicEffect effect)
{
foreach (EffectPass pass in effect.CurrentTechnique.Passes)
{
pass.Apply();
device.SetVertexBuffer(cubeVertexBuffer);
device.DrawPrimitives(PrimitiveType.TriangleList, 0, cubeVertexBuffer.VertexCount / 3);
}
}
Now, I realize that having two draw methods may be odd, but I didn't know how else to get this to work. I also realize that because the SetVertexBuffer is in my draw method, it's going to get called every time draw is called. What I don't understand is how to create one vertex buffer and set it just ONCE, at initialization, so that my fps isn't at 15.
Eventually, my goal is to have each 50x50 chunk being drawn only while it's in view of the camera, and this means storing each chunk into its own vertex buffer. Another thing I have no clue how to accomplish lol.
Another option would be to only draw the faces that are being shown, but again I don't know how this is done and it'd be useless to do until I can figure out I'm getting so many draw calls -.-