Search code examples
modelxnabufferdraw

How to draw millions of cubes without idle , model instancing in XNA?


I work for a project in the style of game "Minecraft".

I started using the "Model Instancing" in order to generate a large number of cubes possessing an identical model. So far so good. My problem is that if I increase the size of my matrix to draw from [300-1-300] (90,000 cubic meters) of [500-1-500] (250,000 cubic meters) my program slows down tremendously. It goes from 60fps to 20 ...

I do not really understand why. But I use the technique correctly "Hardware Instancing". I also noticed on forums that this technique allows XNA to draw up to 7 million cubic!! Do you have any idea from where my problem ?

Thanks a lots

Here is my function that draws the model instantiated:

// Draw the 3D map (world) of game 
public void drawWorld(GameTime gameTime)
{
    / * Draw all the structures that make up the world with the instancing system * /
    Array.Resize(ref instanceTransforms, smallListInstance.Count);

    for (int i = 0; i < ListInstance.Count; i++)
    {
        instanceTransforms[i] = ListInstance[i].Transform;
    }

    DrawModelHardwareInstancing(myModel, myTexture2D,instancedModelBones,instanceTransforms, arcadia.camera.View, arcadia.camera.Projection);


}
// ### end function drawWorld

Here is my function [DrawModelHardwareInstancing] which draws models with the method [Hardware Instancing] used in the sample from microsoft.

// Function that will draw all the models instantiated in the list
void DrawModelHardwareInstancing(Model model,Texture2D texture, Matrix[] modelBones,
                                 Matrix[] instances, Matrix view, Matrix projection)
{
    Game.GraphicsDevice.SamplerStates[0] = SamplerState.LinearWrap;
    if (instances.Length == 0)
        return;

    // If we have more instances than room in our vertex buffer, grow it to the neccessary size.
    if ((instanceVertexBuffer == null) ||
        (instances.Length > instanceVertexBuffer.VertexCount))
    {
        if (instanceVertexBuffer != null)
            instanceVertexBuffer.Dispose();

        instanceVertexBuffer = new DynamicVertexBuffer(Game.GraphicsDevice, instanceVertexDeclaration,
                                                       instances.Length, BufferUsage.WriteOnly);
    }

    // Transfer the latest instance transform matrices into the instanceVertexBuffer.
    instanceVertexBuffer.SetData(instances, 0, instances.Length, SetDataOptions.Discard);

    foreach (ModelMesh mesh in model.Meshes)
    {
        foreach (ModelMeshPart meshPart in mesh.MeshParts)
        {
            // Tell the GPU to read from both the model vertex buffer plus our instanceVertexBuffer.
            Game.GraphicsDevice.SetVertexBuffers(
                new VertexBufferBinding(meshPart.VertexBuffer, meshPart.VertexOffset, 0),
                new VertexBufferBinding(instanceVertexBuffer, 0, 1)
            );

            Game.GraphicsDevice.Indices = meshPart.IndexBuffer;


            // Set up the instance rendering effect.
            Effect effect = meshPart.Effect;
            //effect.CurrentTechnique = effect.Techniques["HardwareInstancing"];
            effect.Parameters["World"].SetValue(modelBones[mesh.ParentBone.Index]);
            effect.Parameters["View"].SetValue(view);
            effect.Parameters["Projection"].SetValue(projection);
            effect.Parameters["Texture"].SetValue(texture);


            // Draw all the instance copies in a single call.
            foreach (EffectPass pass in effect.CurrentTechnique.Passes)
            {
                pass.Apply();

                Game.GraphicsDevice.DrawInstancedPrimitives(PrimitiveType.TriangleList, 0, 0,
                                                       meshPart.NumVertices, meshPart.StartIndex,
                                                       meshPart.PrimitiveCount, instances.Length);
            }
        }



    }
}
// ### end function DrawModelHardwareInstancing

Solution

  • Thank you very much! I finally solved my problem thanks to your advice;-)!

    I draw only the models that are in front of my field of vision 'camera'. Updates the models only when needed 'edit', 'new position etc. ..'

    My last step is to draw only 3 of 6 cube face to improve performences.

    Code sample to draw only the models that are in front

    public BoundingFrustum Frustum { get; private set; }

    //Initialize 
    Matrix viewProjection = View * Projection; 
    Frustum = new BoundingFrustum(viewProjection); 
    
    //Update 
    Matrix viewProjection = View * Projection; 
    Frustum.Matrix = viewProjection; 
    
    //Check instance and make the instanceTransformation for draw 
    foreach (var object in ListInstance.Where(m => Frustum.Contains(m.BoundingBox) != ContaintmentType.Disjoint) 
    {
        instanceTransforms[index].Add(smallListInstance[i].Transform); 
    
    } 
    // And now draw ...