Sign in to follow this  
  • entries
    177
  • comments
    531
  • views
    163648

Batching 101

Sign in to follow this  

111 views

Alright here it is, my code to batch all the objects in id.Net, along with the long ass explanation of how it works.


using System;
using Drawing = System.Drawing;
using Collections = System.Collections.Generic;
using DirectX = Microsoft.DirectX;
using Direct3D = Microsoft.DirectX.Direct3D;

namespace id
{

public class Scene
{

private class Batch
{

public Collections.List IndexList = new Collections.List();

public int MinimumIndex = int.MaxValue;

public int Offset = 0;

public int Vertices = 0;

public Batch()
{
return;
}

public void Initialize()
{
int MaximimIndex = int.MinValue;
for( int i = 0; i < IndexList.Count; i++ )
{
if( IndexList < MinimumIndex )
{
MinimumIndex = (int)IndexList;
}
if( IndexList > MaximimIndex )
{
MaximimIndex = (int)IndexList;
}
}
Vertices = ( MaximimIndex - MinimumIndex ) + 1;
return;
}

}

public class Object
{

public int BufferOffset = 0;

public Graphic Graphic = null;

public uint[] IndexArray = null;

public Direct3D.CustomVertex.PositionTextured[] VertexArray = null;

public Object()
{
return;
}

public Object( Graphic GraphicReference )
{
Graphic = GraphicReference;
return;
}

}

private Direct3D.IndexBuffer IndexBuffer = null;

private Collections.Dictionary BatchList = new Collections.Dictionary();

public static DirectX.GraphicsStream GraphicsStream = null;

private Collections.Dictionary> ObjectList = new Collections.Dictionary>();

private static Collections.List VertexList = new Collections.List();

private static Direct3D.VertexBuffer VertexBuffer = null;

static Scene()
{
return;
}

public void Add( Object ObjectReference )
{
if( ObjectReference == null )
{
return;
}
else if( ( ObjectReference.Graphic == null ) || ( ObjectReference.VertexArray == null ) || ( ObjectReference.VertexArray.Length == 0 ) || ( ObjectReference.IndexArray == null ) || ( ObjectReference.IndexArray.Length == 0 ) )
{
return;
}
else if( ObjectList.ContainsKey( ObjectReference.Graphic ) == false )
{
ObjectList.Add( ObjectReference.Graphic, new Collections.List() );
BatchList.Add( ObjectReference.Graphic, new Batch() );
}
ObjectList[ObjectReference.Graphic].Add( ObjectReference );
return;
}

public void Build()
{
int Indices = 0;
int BaseIndex = 0;
Collections.IEnumerator>> ObjectListEnumerator = ObjectList.GetEnumerator();
do
{
BaseIndex = 0;
if( ( ObjectListEnumerator.Current.Key != null ) && ( ObjectListEnumerator.Current.Value != null ) )
{
BatchList[ObjectListEnumerator.Current.Key].Offset = VertexList.Count;
for( int i = 0; i < ObjectListEnumerator.Current.Value.Count; i++ )
{
for( int j = 0; j < ObjectListEnumerator.Current.Value.IndexArray.Length; j++ )
{
ObjectListEnumerator.Current.Value.IndexArray[j] += (uint)BaseIndex;
}
Indices += ObjectListEnumerator.Current.Value.IndexArray.Length;
BaseIndex += ObjectListEnumerator.Current.Value.VertexArray.Length;
ObjectListEnumerator.Current.Value.BufferOffset = VertexList.Count;
VertexList.AddRange( ObjectListEnumerator.Current.Value.VertexArray );
BatchList[ObjectListEnumerator.Current.Key].IndexList.AddRange( ObjectListEnumerator.Current.Value.IndexArray );
}
}
}
while( ObjectListEnumerator.MoveNext() );
if( Indices > 0 )
{
int IndexBufferOffset = 0;
IndexBuffer = new Direct3D.IndexBuffer( typeof( uint ), Indices, Game.Device, Direct3D.Usage.None, Direct3D.Pool.Managed );
Collections.IEnumerator> BatchListEnumerator = BatchList.GetEnumerator();
do
{
if( ( BatchListEnumerator.Current.Key != null ) && ( BatchListEnumerator.Current.Value != null ) )
{
IndexBuffer.SetData( BatchListEnumerator.Current.Value.IndexList.ToArray(), IndexBufferOffset, Direct3D.LockFlags.None );
IndexBufferOffset += BatchListEnumerator.Current.Value.IndexList.Count * sizeof( uint );
BatchListEnumerator.Current.Value.Initialize();
}
}
while( BatchListEnumerator.MoveNext() );
}
return;
}

public static void BuildBuffer()
{
VertexBuffer = new Direct3D.VertexBuffer( typeof( Direct3D.CustomVertex.PositionTextured ), VertexList.Count, Game.Device, Direct3D.Usage.SoftwareProcessing, Direct3D.CustomVertex.PositionTextured.Format, Direct3D.Pool.Managed );
VertexBuffer.SetData( VertexList.ToArray(), 0, Direct3D.LockFlags.None );
VertexList.Clear();
return;
}

public static void Lock()
{
GraphicsStream = VertexBuffer.Lock( 0, 0, Direct3D.LockFlags.None );
return;
}

public void Render()
{
int Position = 0;
Game.Device.VertexFormat = Direct3D.CustomVertex.PositionTextured.Format;
Game.Device.Indices = IndexBuffer;
Game.Device.SetStreamSource( 0, VertexBuffer, 0 );
Collections.IEnumerator> BatchListEnumerator = BatchList.GetEnumerator();
do
{
if( ( BatchListEnumerator.Current.Key != null ) && ( BatchListEnumerator.Current.Value != null ) )
{
BatchListEnumerator.Current.Key.Apply();
Game.Device.DrawIndexedPrimitives( Direct3D.PrimitiveType.TriangleList, BatchListEnumerator.Current.Value.Offset, BatchListEnumerator.Current.Value.MinimumIndex, BatchListEnumerator.Current.Value.Vertices, Position, BatchListEnumerator.Current.Value.IndexList.Count / 3 );
Position += BatchListEnumerator.Current.Value.IndexList.Count;
}
}
while( BatchListEnumerator.MoveNext() );
return;
}

public static void Unlock()
{
VertexBuffer.Unlock();
return;
}

}

}








Classes:


Object - This is inherited by anything that has to be added to a
scene, which is walls and floor/ceiling polygons.

BufferOffset - This is where it's vertices got added to the
VertexBuffer, used so moving sectors can be
rebuilt easily.

Graphic - The texture or animation used for is object, for
sorting.

IndexArray - Indices into the VertexBuffer. Each object only
needs to start as though it was from 0, the scene
builder will find and assign the actually indices
when built. These need to be in order though for
batching to work(you can't have 0,2,3,5).

VertexArray - The vertices.

Batch - This is used to store the information needed for the call to
DrawIndexedPrimitives. There is one batch for evey different
texture in a scene.

IndexList - Contains all the indices used for this batch. Used
to find the other values.

MinimumIndex - The lowest index out of all the indices that make
up this batch, for MinIndex.

Offset - The value used for BaseVertexIndex, takes the number of
vertics in the level at the start of being "built".

Vertices - The number of different vertices, for NumVertices;

Scene - The actual scene class. Inherited by each sector, but also
stores the vertex data for the whole level.

IndexBuffer - The index buffer for this sector.

MinimumIndex - List of batches, one batch per texture in the
sector.

GraphicsStream - Walls use this to add their new vertices to the
VertexBuffer when being rebuilt.

ObjectList - List of objects(walls,polygons) arranged by
texture.

VertexList - Stores the vertices before building the
VertexBuffer.

VertexBuffer - Vertex buffer for the whole level.

Add - Adds an object to ObjectList if it's not in there already.
Also adds a batch based on the objects texture if one
doesn't already exist.

Build - This starts off by going through all the different
textures in the sector. The batch for that texture has
it's offset assigned to the length of VertexIndex(if the
fisrt batch of objects contains 100 vertices, then this
becomes 100 for the second batch). Then there's a loop
that goes through all the objects that have this
texture. The object's have their indices and offset into
the vertex buffer set to the proper values. Then the
object's have their vertices added to VertexList and
their indices added into the current batch.
Next after all that is done and the batches have all
their indices, a loop adds those indices to the index
buffer. Now the index buffer is arranged by texture.

BuildBuffer - This is the last functioned called before doing
any rendering, it loads all the vertices from
VertexList into VertexBuffer.

Render - Goes through all the batches in this scene, setting the
right texture and rendering based on the indices.

Putting it all together:

1. When loading a level, the sectors are loaded first, although at this
time they don't contain much information(only that of the WAD).

2. When loading the lines each checks what sector it belongs in(based on
which side falls into "the void"). The line and it's
walls(lower,middle,upper) are then added to that sector(Scene.Add).

3. Once the level has all the data from the Doom WAD file, it loops
through all the sectors calling Build.

4. After all the sectors are "built", Scene.BuildBuffer is called




That's it for now. Hopefully someone will find it somewhat usefull, it sure took a long time to write. I have tested it with some moving sectors and all the values do come out to be the right value.
Sign in to follow this  


0 Comments


Recommended Comments

There are no comments to display.

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Important Information

By using GameDev.net, you agree to our Terms of Use and Guidelines.

Create an account with GameDev.net and join the game development conversation on our forums, blogs, articles, and more!

Sign me up!