Rendering Bounding Volumes with VBO

From XNAWiki
Jump to: navigation, search

This is bounding volume render game component for rendering Bounding Box, Bounding Sphere and Bounding volume with VBO support. You can add and remove bounding volumes, which will be drawn when Draw(GameTime gameTime) is called.

In the constructor, you need to pass a Camera object, which provide the game component world, view, project matrices. Also, a line render effect is needed, which can be simpily replaced with BasicEffect.

using System;
using System.Collections.Generic;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Storage;
using Microsoft.Xna.Framework.Content;
 
using IndexType = System.Int16;
 
 
/// <summary>
/// This is a game component that implements IUpdateable.
/// </summary>
public class BoundingVolumeRenderer : Microsoft.Xna.Framework.DrawableGameComponent
{
    #region constructors
 
    public BoundingVolumeRenderer(Game game, Camera camera)
        : base(game)
    {
        // TODO: Construct any child components here
        ActiveCamera = camera;
        decl = new VertexDeclaration(Game.GraphicsDevice, VertexPositionColor.VertexElements);
 
    }
    #endregion
 
    #region overrided functions
    /// <summary>
    /// Allows the game component to perform any initialization it needs to before starting
    /// to run.  This is where it can query for any required services and load content.
    /// </summary>
    public override void Initialize()
    {
        // TODO: Add your initialization code here
 
        BBoxVerts = new VertexPositionColor[MAX_BBOX_COUNT * NUM_BOX_VERT];
 
        BBoxNames = new Dictionary<string, int>();
 
        BSphereVerts = new VertexPositionColor[MAX_BSPHERE_COUNT * NUM_BSPHERE_VERT];
 
        BSphereNames = new Dictionary<string, int>();
 
        BFrustumVerts = new VertexPositionColor[MAX_BFRUSTUM_COUNT * NUM_BFRUSTUM_VERT];
 
        BFrustumNames = new Dictionary<string, int>();
 
        base.Initialize();
    }
 
    /// <summary>
    /// Allows the game component to update itself.
    /// </summary>
    /// <param name="gameTime">Provides a snapshot of timing values.</param>
    public override void Update(GameTime gameTime)
    {
 
        //ok, copy the current bbox vertex buffer to the main vbo
        if (NeedSwitchBBoxVBO == false && FinishedSwitchBBoxVBO == true)
        {
 
            if (numDirtyBBox > 1)
            {
                BBoxVertexBuffer.SetData<VertexPositionColor>(0, BBoxVerts, 0,
    numBBox * NUM_BOX_VERT, VERTEX_SIZE, SetDataOptions.NoOverwrite);
            }
            else
            {
                BBoxVertexBuffer.SetData<VertexPositionColor>(UpdateBBoxBlockStartIndex * VERTEX_SIZE, BBoxVerts, UpdateBBoxBlockStartIndex,
                NUM_BOX_VERT, VERTEX_SIZE, SetDataOptions.NoOverwrite);
            }
 
            numDirtyBBox = 0;
            FinishedSwitchBBoxVBO = false;
        }
 
        //ok, copy the current bsphere vertex buffer to the main vbo
        if (NeedSwitchBSphereVBO == false && FinishedSwitchBSphereVBO == true)
        {
 
            if (numDirtyBSphere > 1)
            {
                BSphereVertexBuffer.SetData<VertexPositionColor>(0, BSphereVerts, 0,
numBSphere * NUM_BSPHERE_VERT, VERTEX_SIZE, SetDataOptions.NoOverwrite);
            }
            else
            {
                BSphereVertexBuffer.SetData<VertexPositionColor>(UpdateBSphereBlockStartIndex * VERTEX_SIZE, BSphereVerts, UpdateBSphereBlockStartIndex,
                NUM_BSPHERE_VERT, VERTEX_SIZE, SetDataOptions.NoOverwrite);
            }
 
            FinishedSwitchBSphereVBO = false;
        }
 
        //ok, copy the current bfrustrum vertex buffer to the main vbo
        if (NeedSwitchBFrustumVBO == false && FinishedSwitchBFrustumVBO == true)
        {
            if (numDirtyBFrustum > 1)
            {
                BFrustumVertexBuffer.SetData<VertexPositionColor>(0, BFrustumVerts, 0,
numBFrustum * NUM_BFRUSTUM_VERT, VERTEX_SIZE, SetDataOptions.NoOverwrite);
            }
            else
            {
                BFrustumVertexBuffer.SetData<VertexPositionColor>(VERTEX_SIZE * UpdateBFrustumBlockStartIndex, BFrustumVerts, UpdateBFrustumBlockStartIndex,
NUM_BFRUSTUM_VERT, VERTEX_SIZE, SetDataOptions.NoOverwrite);
            }
 
            numDirtyBFrustum = 0;
            FinishedSwitchBFrustumVBO = false;
        }
 
        base.Update(gameTime);
    }
 
    /// <summary>
    /// Loads any component specific content
    /// </summary>
    protected override void LoadContent()
    {
 
        BBoxVertexBuffer = new DynamicVertexBuffer(Game.GraphicsDevice, BBoxVBOSizeInByte, BufferUsage.WriteOnly);
 
        BBoxVertexBufferTemp = new DynamicVertexBuffer(Game.GraphicsDevice, BBoxVBOSizeInByte, BufferUsage.WriteOnly);
 
        if (sizeof(IndexType) == sizeof(Int16))
            BBoxIndexBuffer = new DynamicIndexBuffer(Game.GraphicsDevice, BBoxIBOSizeInByte, BufferUsage.WriteOnly, IndexElementSize.SixteenBits);
        else
            BBoxIndexBuffer = new DynamicIndexBuffer(Game.GraphicsDevice, BBoxIBOSizeInByte, BufferUsage.WriteOnly, IndexElementSize.ThirtyTwoBits);
 
        BSphereVertexBuffer = new DynamicVertexBuffer(Game.GraphicsDevice, BSphereVBOSizeInByte, BufferUsage.WriteOnly);
 
        BSphereVertexBufferTemp = new DynamicVertexBuffer(Game.GraphicsDevice, BSphereVBOSizeInByte, BufferUsage.WriteOnly);
 
        if (sizeof(IndexType) == sizeof(Int16))
            BSphereIndexBuffer = new DynamicIndexBuffer(Game.GraphicsDevice, BSphereIBOSizeInByte, BufferUsage.WriteOnly, IndexElementSize.SixteenBits);
        else
            BSphereIndexBuffer = new DynamicIndexBuffer(Game.GraphicsDevice, BSphereIBOSizeInByte, BufferUsage.WriteOnly, IndexElementSize.ThirtyTwoBits);
 
        BFrustumVertexBuffer = new DynamicVertexBuffer(Game.GraphicsDevice, BFrustumVBOSizeInByte, BufferUsage.WriteOnly);
 
        BFrustumVertexBufferTemp = new DynamicVertexBuffer(Game.GraphicsDevice, BFrustumVBOSizeInByte, BufferUsage.WriteOnly);
 
        if (sizeof(IndexType) == sizeof(Int16))
            BFrustumIndexBuffer = new DynamicIndexBuffer(Game.GraphicsDevice, BFrustumIBOSizeInByte, BufferUsage.WriteOnly, IndexElementSize.SixteenBits);
        else
            BFrustumIndexBuffer = new DynamicIndexBuffer(Game.GraphicsDevice, BFrustumIBOSizeInByte, BufferUsage.WriteOnly, IndexElementSize.ThirtyTwoBits);
 
        VolumeEffect = Game.Content.Load<Effect>("LineRender");
 
        base.LoadContent();
    }
 
    /// <summary>
    /// Allows the game component to draw itself.
    /// </summary>
    /// <param name="gameTime">Provides a snapshot of timing values.</param>
    public override void Draw(GameTime gameTime)
    {
        ActiveRenderState();
 
        VolumeEffect.Parameters["World"].SetValue(ActiveCamera.World);
        VolumeEffect.Parameters["View"].SetValue(ActiveCamera.View);
        VolumeEffect.Parameters["Projection"].SetValue(ActiveCamera.Projection);
 
        VolumeEffect.Begin();
        VolumeEffect.CurrentTechnique.Passes[0].Begin();
 
        //update vbo if necessary
        if (NeedBuildBBoxVBO == true)
        {
            UpdateBBoxVBO();
        }
 
        //draw only there is sth in buffers
        if (isBBoxesVisible && numBBoxGPU > 0)
        {
 
            Game.GraphicsDevice.Indices = BBoxIndexBuffer;
 
            //switch vbo when it is dirty
            if (NeedSwitchBBoxVBO)
            {
                Game.GraphicsDevice.Vertices[0].SetSource(
    BBoxVertexBufferTemp, 0, VERTEX_SIZE);
                FinishedSwitchBBoxVBO = true;
                NeedSwitchBBoxVBO = false;
 
            }
            else
            {
                Game.GraphicsDevice.Vertices[0].SetSource(
                    BBoxVertexBuffer, 0, VERTEX_SIZE);
            }
 
            Game.GraphicsDevice.DrawIndexedPrimitives(PrimitiveType.LineList, 0, 0, numBBoxGPU * NUM_BOX_VERT, 0, numBBoxGPU * NUM_BOX_INDEX / 2);
 
        }
 
        //update vbo if necessary
        if (NeedBuildBSphereVBO == true)
        {
            UpdateBSphereVBO();
        }
 
        //draw only there is sth in buffers
        if (isBSpheresVisible && numBSphereGPU > 0)
        {
 
            Game.GraphicsDevice.Indices = BSphereIndexBuffer;
 
            //switch vbo when it is dirty
            if (NeedSwitchBSphereVBO)
            {
                Game.GraphicsDevice.Vertices[0].SetSource(
    BSphereVertexBufferTemp, 0, VERTEX_SIZE);
                FinishedSwitchBSphereVBO = true;
                NeedSwitchBSphereVBO = false;
 
            }
            else
            {
                Game.GraphicsDevice.Vertices[0].SetSource(
                    BSphereVertexBuffer, 0, VERTEX_SIZE);
            }
 
            Game.GraphicsDevice.DrawIndexedPrimitives(PrimitiveType.LineList, 0, 0, numBSphereGPU * NUM_BSPHERE_VERT, 0, numBSphereGPU * NUM_BSPHERE_INDEX / 2);
 
        }
 
        //update vbo if necessary
        if (NeedBuildBFrustumVBO == true)
        {
            UpdateBFrustumVBO();
        }
 
        //draw only there is sth in buffers
        if (isBFrustumesVisible && numBFrustumGPU > 0)
        {
            //draw all bounding Frustums
 
            Game.GraphicsDevice.Indices = BFrustumIndexBuffer;
 
            //switch vbo when it is dirty
            if (NeedSwitchBFrustumVBO)
            {
                Game.GraphicsDevice.Vertices[0].SetSource(
    BFrustumVertexBufferTemp, 0, VERTEX_SIZE);
                FinishedSwitchBFrustumVBO = true;
                NeedSwitchBFrustumVBO = false;
 
            }
            else
            {
                Game.GraphicsDevice.Vertices[0].SetSource(
                    BFrustumVertexBuffer, 0, VERTEX_SIZE);
            }
 
            Game.GraphicsDevice.DrawIndexedPrimitives(PrimitiveType.LineList, 0, 0, numBFrustumGPU * NUM_BFRUSTUM_VERT, 0, numBFrustumGPU * NUM_BFRUSTUM_INDEX / 2);
 
        }
 
        VolumeEffect.CurrentTechnique.Passes[0].End();
        VolumeEffect.End();
 
        RestoreRenderState();
 
 
        base.Draw(gameTime);
    }
 
    private void ActiveRenderState()
    {
        previousRenderState = Game.GraphicsDevice.RenderState;
        Game.GraphicsDevice.RenderState.AlphaBlendEnable = true;
        Game.GraphicsDevice.RenderState.SourceBlend = Blend.SourceAlpha;
        Game.GraphicsDevice.RenderState.DestinationBlend = Blend.InverseSourceAlpha;
        Game.GraphicsDevice.RenderState.DepthBufferEnable = true;
        Game.GraphicsDevice.VertexDeclaration = decl;
 
    }
 
    private void RestoreRenderState()
    {
        Game.GraphicsDevice.RenderState.AlphaBlendEnable = previousRenderState.AlphaBlendEnable;
        Game.GraphicsDevice.RenderState.SourceBlend = previousRenderState.SourceBlend;
        Game.GraphicsDevice.RenderState.DestinationBlend = previousRenderState.DestinationBlend;
        Game.GraphicsDevice.RenderState.DepthBufferEnable = previousRenderState.DepthBufferEnable;
    }
 
    #endregion
 
    #region Logic functions
    /// <summary>
    /// Add a new Bounding box
    /// </summary>
    /// <param name="newBoundingBox"></param>
    /// <param name="name"></param>
    /// <param name="boxColor"></param>
    public void AddBBox(BoundingBox newBoundingBox, string name, Color boxColor)
    {
        //if the number is still in limit
        if (numBBox < MAX_BBOX_COUNT && !BBoxNames.ContainsKey(name))
        {
            Vector3[] corners = newBoundingBox.GetCorners();
            for (int i = 0; i < NUM_BOX_VERT; i++)
            {
                BBoxVerts[CurrentBBoxVertexIndex + i].Position = corners[i];
                BBoxVerts[CurrentBBoxVertexIndex + i].Color = boxColor;
            }
 
            BBoxNames[name] = numBBox;
            numBBox++;
 
            CurrentBBoxVertexIndex += NUM_BOX_VERT;
 
            NeedBuildBBoxVBO = true;
        }
    }
 
    /// <summary>
    /// Add an annomous bbox
    /// </summary>
    /// <param name="newBoundingBox"></param>
    /// <param name="boxColor"></param>
    public void AddBBox(BoundingBox newBoundingBox, Color boxColor)
    {
        AddBBox(newBoundingBox, numBBox.ToString(), boxColor);
    }
 
    /// <summary>
    /// Add a new Bounding Frustum
    /// </summary>
    /// <param name="newBoundingFrustum"></param>
    /// <param name="name"></param>
    /// <param name="FrustumColor"></param>
    public void AddBFrustum(BoundingFrustum newBoundingFrustum, string name, Color FrustumColor)
    {
        //if the number is still in limit
        if (numBFrustum < MAX_BFRUSTUM_COUNT && !BFrustumNames.ContainsKey(name))
        {
            Vector3[] corners = newBoundingFrustum.GetCorners();
            for (int i = 0; i < NUM_BFRUSTUM_VERT; i++)
            {
                BFrustumVerts[CurrentBFrustumVertexIndex + i].Position = corners[i];
                BFrustumVerts[CurrentBFrustumVertexIndex + i].Color = FrustumColor;
            }
 
            BFrustumNames[name] = numBFrustum;
            numBFrustum++;
 
            CurrentBFrustumVertexIndex += NUM_BFRUSTUM_VERT;
 
            NeedBuildBFrustumVBO = true;
        }
    }
 
    public void AddBSphere(BoundingSphere newBoundingSphere, string name, Color sphereColor)
    {
        if (numBSphere < MAX_BSPHERE_COUNT && !BSphereNames.ContainsKey(name))
        {
            //create sphere for three axises
 
            Matrix sphereMatrix = Matrix.CreateScale(newBoundingSphere.Radius) * Matrix.CreateTranslation(newBoundingSphere.Center);
 
            //create the loop on the XY plane first
            for (float a = 0f; a <= MathHelper.TwoPi; a += BSPHERE_STEP)
            {
                Vector3 position =
                    new Vector3((float)Math.Cos(a), (float)Math.Sin(a), 0f);
                position = Vector3.Transform(position, sphereMatrix);
 
                BSphereVerts[CurrentBSphereVertexIndex++] = new VertexPositionColor(position, sphereColor);
            }
 
            //next on the XZ plane
            for (float a = 0f; a <= MathHelper.TwoPi; a += BSPHERE_STEP)
            {
                Vector3 position =
                    new Vector3((float)Math.Cos(a), 0f, (float)Math.Sin(a));
                position = Vector3.Transform(position, sphereMatrix);
 
                BSphereVerts[CurrentBSphereVertexIndex++] = new VertexPositionColor(position, sphereColor);
            }
 
            //finally on the YZ plane
            for (float a = 0f; a <= MathHelper.TwoPi; a += BSPHERE_STEP)
            {
                Vector3 position =
                    new Vector3(0f, (float)Math.Cos(a), (float)Math.Sin(a));
                position = Vector3.Transform(position, sphereMatrix);
 
                BSphereVerts[CurrentBSphereVertexIndex++] = new VertexPositionColor(position, sphereColor);
            }
 
            BSphereNames[name] = numBSphere;
 
            numBSphere++;
 
            NeedBuildBSphereVBO = true;
 
        }
    }
 
    /// <summary>
    /// Remove the box from array (need to rearrange the vertex buffer and index buffer)
    /// </summary>
    /// <param name="name"></param>
    public void RemoveBBox(string name)
    {
        if (numBBox > 0 && BBoxNames.ContainsKey(name))
        {
            int removeIndex = BBoxNames[name];
 
            //first, swap remove vertex block with the last vertex block in vertex array
            int RemoveBlockStartIndex = removeIndex * NUM_BOX_VERT;
            int LastBlockStartIndex = (numBBox - 1) * NUM_BOX_VERT;
            for (int moveIndex = 0; moveIndex < NUM_BOX_VERT; moveIndex++)
            {
                VertexPositionColor tempVert = BBoxVerts[RemoveBlockStartIndex + moveIndex];
                BBoxVerts[RemoveBlockStartIndex + moveIndex] = BBoxVerts[LastBlockStartIndex + moveIndex];
                BBoxVerts[LastBlockStartIndex + moveIndex] = tempVert;
            }
 
            //second, delete the name in name list
            BBoxNames.Remove(name);
 
            //set back the current indices
            numBBox--;
 
            CurrentBBoxVertexIndex = LastBlockStartIndex;
 
            //BBoxDrawOffset = BBoxVBOTempOffset;
 
            NeedBuildBBoxVBO = true;
 
        }
    }
 
    public void UpdateBBox(string name, BoundingBox updatedBBox)
    {
        if (numBBox > 0 && BBoxNames.ContainsKey(name))
        {
            int updateIndex = BBoxNames[name];
 
            UpdateBBoxBlockStartIndex = updateIndex * NUM_BOX_VERT;
 
            Vector3[] corners = updatedBBox.GetCorners();
 
            for (int moveIndex = 0; moveIndex < NUM_BOX_VERT; moveIndex++)
            {
                BBoxVerts[UpdateBBoxBlockStartIndex + moveIndex].Position = corners[moveIndex];
            }
 
            numDirtyBBox++;
            NeedBuildBBoxVBO = true;
        }
    }
 
    /// <summary>
    /// Remove the Frustum from array (need to rearrange the vertex buffer and index buffer)
    /// </summary>
    /// <param name="name"></param>
    public void RemoveBFrustum(string name)
    {
        if (numBFrustum > 0 && BFrustumNames.ContainsKey(name))
        {
            int removeIndex = BFrustumNames[name];
 
            //first, swap remove vertex block with the last vertex block in vertex array
            int RemoveBlockStartIndex = removeIndex * NUM_BFRUSTUM_VERT;
            int LastBlockStartIndex = (numBFrustum - 1) * NUM_BFRUSTUM_VERT;
            for (int moveIndex = 0; moveIndex < NUM_BFRUSTUM_VERT; moveIndex++)
            {
                VertexPositionColor tempVert = BFrustumVerts[RemoveBlockStartIndex + moveIndex];
                BFrustumVerts[RemoveBlockStartIndex + moveIndex] = BFrustumVerts[LastBlockStartIndex + moveIndex];
                BFrustumVerts[LastBlockStartIndex + moveIndex] = tempVert;
            }
 
            //second, delete the name in name list
            BFrustumNames.Remove(name);
 
            //set back the current indices
            numBFrustum--;
 
            CurrentBFrustumVertexIndex = LastBlockStartIndex;
 
            NeedBuildBFrustumVBO = true;
 
        }
    }
 
    public void UpdateBFrustum(string name, BoundingFrustum updatedFrustum)
    {
        if (numBFrustum > 0 && BFrustumNames.ContainsKey(name))
        {
            int updateIndex = BFrustumNames[name];
 
            UpdateBFrustumBlockStartIndex = updateIndex * NUM_BFRUSTUM_VERT;
 
            Vector3[] corners = updatedFrustum.GetCorners();
 
            for (int moveIndex = 0; moveIndex < NUM_BFRUSTUM_VERT; moveIndex++)
            {
                BFrustumVerts[UpdateBFrustumBlockStartIndex + moveIndex].Position = corners[moveIndex];
            }
 
            numDirtyBFrustum++;
            NeedBuildBFrustumVBO = true;
        }
    }
 
    public void RemoveBSphere(string name)
    {
        if (numBSphere > 0 && BSphereNames.ContainsKey(name))
        {
            int removeIndex = BSphereNames[name];
 
            //first, swap remove vertex block with the last vertex block in vertex array
            int RemoveBlockStartIndex = removeIndex * NUM_BSPHERE_VERT;
            int LastBlockStartIndex = (numBSphere - 1) * NUM_BSPHERE_VERT;
            for (int moveIndex = 0; moveIndex < NUM_BSPHERE_VERT; moveIndex++)
            {
                VertexPositionColor tempVert = BSphereVerts[RemoveBlockStartIndex + moveIndex];
                BSphereVerts[RemoveBlockStartIndex + moveIndex] = BSphereVerts[LastBlockStartIndex + moveIndex];
                BSphereVerts[LastBlockStartIndex + moveIndex] = tempVert;
            }
 
            //second, delete the name in name list
            BSphereNames.Remove(name);
 
            //set back the current indices
            numBSphere--;
 
            CurrentBSphereVertexIndex = LastBlockStartIndex;
 
            //BSphereDrawOffset = BSphereVBOTempOffset;
 
            NeedBuildBSphereVBO = true;
 
        }
    }
 
    public void UpdateBSphere(string name, BoundingSphere newBSphere)
    {
        if (numBSphere > 0 && BSphereNames.ContainsKey(name))
        {
            int updateIndex = BSphereNames[name];
 
            UpdateBSphereBlockStartIndex = updateIndex * NUM_BSPHERE_VERT;
 
            int tempUpdateIndex = UpdateBSphereBlockStartIndex;
 
            Matrix sphereMatrix = Matrix.CreateScale(newBSphere.Radius) * Matrix.CreateTranslation(newBSphere.Center);
 
            //create the loop on the XY plane first
            for (float a = 0f; a <= MathHelper.TwoPi; a += BSPHERE_STEP)
            {
                Vector3 position =
                    new Vector3((float)Math.Cos(a), (float)Math.Sin(a), 0f);
                position = Vector3.Transform(position, sphereMatrix);
 
                BSphereVerts[tempUpdateIndex++].Position = position;
            }
 
            //next on the XZ plane
            for (float a = 0f; a <= MathHelper.TwoPi; a += BSPHERE_STEP)
            {
                Vector3 position =
                    new Vector3((float)Math.Cos(a), 0f, (float)Math.Sin(a));
                position = Vector3.Transform(position, sphereMatrix);
 
                BSphereVerts[tempUpdateIndex++].Position = position;
            }
 
            //finally on the YZ plane
            for (float a = 0f; a <= MathHelper.TwoPi; a += BSPHERE_STEP)
            {
                Vector3 position =
                    new Vector3(0f, (float)Math.Cos(a), (float)Math.Sin(a));
                position = Vector3.Transform(position, sphereMatrix);
 
                BSphereVerts[tempUpdateIndex++].Position = position;
            }
 
            numDirtyBSphere++;
            NeedBuildBSphereVBO = true;
        }
    }
 
    private void UpdateBBoxVBO()
    {
        if (numBBox > MAX_BBOX_COUNT)//numBBox == 0 || 
            return;
 
        //OK, continue update
 
        Int32 numBoxCopy = numBBox - numBBoxGPU;   //num of box to copy
        int vboOffset = numBBoxGPU * NUM_BOX_VERT * VERTEX_SIZE;
        int iboOffset = numBBoxGPU * NUM_BOX_INDEX * sizeof(Int32);
 
        //we are handling the adding vertex condition
        if (numBoxCopy > 0)
        {
            //copy the indices to index buffer
            IndexType[] copyIndices = new IndexType[numBoxCopy * NUM_BOX_INDEX];
            for (int copyIndex = 0; copyIndex < numBoxCopy; copyIndex++)
            {
                for (int index = 0; index < NUM_BOX_INDEX; index++)
                {
                    copyIndices[copyIndex * NUM_BOX_INDEX + index] = (IndexType)(BBoxIndices[index] + (numBBoxGPU + copyIndex) * NUM_BOX_VERT);
                }
            }
 
            BBoxVertexBuffer.SetData<VertexPositionColor>(vboOffset, BBoxVerts,
                numBBoxGPU * NUM_BOX_VERT, numBoxCopy * NUM_BOX_VERT, VERTEX_SIZE, SetDataOptions.NoOverwrite);
 
            BBoxIndexBuffer.SetData<IndexType>(iboOffset, copyIndices, 0, numBoxCopy * NUM_BOX_INDEX, SetDataOptions.NoOverwrite);
        }
 
        //well, we need to take care of remove vertex condition here
        else
        {
            if (numBBox > 0)
            {
                if (numDirtyBBox > 1)
                {
                    BBoxVertexBufferTemp.SetData<VertexPositionColor>(0, BBoxVerts, 0,
                    numBBox * NUM_BOX_VERT, VERTEX_SIZE, SetDataOptions.NoOverwrite);
                }
                else
                {
                    BBoxVertexBufferTemp.SetData<VertexPositionColor>(UpdateBBoxBlockStartIndex * VERTEX_SIZE, BBoxVerts, UpdateBBoxBlockStartIndex,
                    NUM_BOX_VERT, VERTEX_SIZE, SetDataOptions.NoOverwrite);
                }
 
 
                NeedSwitchBBoxVBO = true;
                FinishedSwitchBBoxVBO = false;
 
            }
 
        }
 
        numBBoxGPU = numBBox;
 
        NeedBuildBBoxVBO = false;
 
 
    }
 
    private void UpdateBFrustumVBO()
    {
        if (numBFrustum > MAX_BFRUSTUM_COUNT)//numBFrustum == 0 || 
            return;
 
        //OK, continue update
 
        Int32 numFrustumCopy = numBFrustum - numBFrustumGPU;   //num of Frustum to copy
        int vboOffset = numBFrustumGPU * NUM_BFRUSTUM_VERT * VERTEX_SIZE;
        int iboOffset = numBFrustumGPU * NUM_BFRUSTUM_INDEX * sizeof(Int32);
 
        //we are handling the adding vertex condition
        if (numFrustumCopy > 0)
        {
            //copy the indices to index buffer
            IndexType[] copyIndices = new IndexType[numFrustumCopy * NUM_BFRUSTUM_INDEX];
            for (int copyIndex = 0; copyIndex < numFrustumCopy; copyIndex++)
            {
                for (int index = 0; index < NUM_BFRUSTUM_INDEX; index++)
                {
                    copyIndices[copyIndex * NUM_BFRUSTUM_INDEX + index] = (IndexType)(BFrustumIndices[index] + (numBFrustumGPU + copyIndex) * NUM_BFRUSTUM_VERT);
                }
            }
 
            BFrustumVertexBuffer.SetData<VertexPositionColor>(vboOffset, BFrustumVerts,
                numBFrustumGPU * NUM_BFRUSTUM_VERT, numFrustumCopy * NUM_BFRUSTUM_VERT, VERTEX_SIZE, SetDataOptions.NoOverwrite);
            BFrustumVertexBufferTemp.SetData<VertexPositionColor>(vboOffset, BFrustumVerts,
                numBFrustumGPU * NUM_BFRUSTUM_VERT, numFrustumCopy * NUM_BFRUSTUM_VERT, VERTEX_SIZE, SetDataOptions.NoOverwrite);
 
            BFrustumIndexBuffer.SetData<IndexType>(iboOffset, copyIndices, 0, numFrustumCopy * NUM_BFRUSTUM_INDEX, SetDataOptions.NoOverwrite);
        }
 
        //well, we need to take care of remove/update vertex condition here
        else
        {
            if (numBFrustum > 0)
            {
                if (numDirtyBFrustum > 1)  //update entire vbo
                {
                    BFrustumVertexBufferTemp.SetData<VertexPositionColor>(0, BFrustumVerts, 0,
                        numBFrustum * NUM_BFRUSTUM_VERT, VERTEX_SIZE, SetDataOptions.NoOverwrite);
                }
                else   //update the dirty section in vbo
                {
                    BFrustumVertexBufferTemp.SetData<VertexPositionColor>(VERTEX_SIZE * UpdateBFrustumBlockStartIndex, BFrustumVerts, UpdateBFrustumBlockStartIndex,
    NUM_BFRUSTUM_VERT, VERTEX_SIZE, SetDataOptions.NoOverwrite);
                }
            }
            NeedSwitchBFrustumVBO = true;
            FinishedSwitchBFrustumVBO = false;
        }
 
        numBFrustumGPU = numBFrustum;
 
        NeedBuildBFrustumVBO = false;
 
 
    }
 
    private void UpdateBSphereVBO()
    {
        if (numBSphere > MAX_BSPHERE_COUNT)//numBSphere == 0 || 
            return;
 
        //OK, continue update
 
        Int32 numSphereCopy = numBSphere - numBSphereGPU;   //num of Sphere to copy
        int vboOffset = numBSphereGPU * NUM_BSPHERE_VERT * VERTEX_SIZE;
        int iboOffset = numBSphereGPU * NUM_BSPHERE_INDEX * sizeof(Int32);
 
        //we are handling the adding vertex condition
        if (numSphereCopy > 0)
        {
            //copy the indices to index buffer
            IndexType[] copyIndices = new IndexType[numSphereCopy * NUM_BSPHERE_INDEX];
            for (int copyIndex = 0; copyIndex < numSphereCopy; copyIndex++)
            {
                int index = 0;
                int iindex = 0;
 
                for (int axisIndex = 0; axisIndex < NUM_SPHERE_AXIS; axisIndex++)
                {
                    while (index < BSPHERE_RESOLUTION * (axisIndex + 1) * 2)
                    {
                        copyIndices[copyIndex * NUM_BSPHERE_INDEX + index++] = (IndexType)(iindex + (numBSphereGPU + copyIndex) * NUM_BSPHERE_VERT);
                        ++iindex;
                        copyIndices[copyIndex * NUM_BSPHERE_INDEX + index++] = (IndexType)(iindex + (numBSphereGPU + copyIndex) * NUM_BSPHERE_VERT);
                    }
                    ++iindex;
                }
            }
 
            BSphereVertexBuffer.SetData<VertexPositionColor>(vboOffset, BSphereVerts,
                numBSphereGPU * NUM_BSPHERE_VERT, numSphereCopy * NUM_BSPHERE_VERT, VERTEX_SIZE, SetDataOptions.NoOverwrite);
 
            BSphereIndexBuffer.SetData<IndexType>(iboOffset, copyIndices, 0, numSphereCopy * NUM_BSPHERE_INDEX, SetDataOptions.NoOverwrite);
        }
 
        //well, we need to take care of remove vertex condition here
        else
        {
            if (numBSphere > 0)
            {
                if (numDirtyBSphere > 1)
                {
                    BSphereVertexBufferTemp.SetData<VertexPositionColor>(0, BSphereVerts, 0,
                    numBSphere * NUM_BSPHERE_VERT, VERTEX_SIZE, SetDataOptions.NoOverwrite);
                }
                else
                {
                    BSphereVertexBufferTemp.SetData<VertexPositionColor>(UpdateBSphereBlockStartIndex * VERTEX_SIZE, BSphereVerts, UpdateBSphereBlockStartIndex,
                    NUM_BSPHERE_VERT, VERTEX_SIZE, SetDataOptions.NoOverwrite);
                }
 
                NeedSwitchBSphereVBO = true;
                FinishedSwitchBSphereVBO = false;
 
            }
 
        }
 
        numBSphereGPU = numBSphere;
 
        NeedBuildBSphereVBO = false;
    }
    #endregion
 
    #region Properties
    public System.Int32 NumBBoxGPU
    {
        get { return numBBoxGPU; }
        set { numBBoxGPU = value; }
    }
 
    public bool IsBBoxesVisible
    {
        get { return isBBoxesVisible; }
        set { isBBoxesVisible = value; }
    }
 
    public System.Int32 NumBSphereGPU
    {
        get { return numBSphereGPU; }
        set { numBSphereGPU = value; }
    }
 
    public bool IsBSpheresVisible
    {
        get { return isBSpheresVisible; }
        set { isBSpheresVisible = value; }
    }
 
    public Camera ControlCamera
    {
        get { return ActiveCamera; }
        set { ActiveCamera = value; }
    }
    #endregion
 
    #region Constants
 
    private static readonly int VERTEX_SIZE = VertexPositionColor.SizeInBytes;
 
    /// <summary>
    /// Max Video memory cost for Int16-IBO BBox is 0.724 MB
    /// </summary>
    #region BBox
    private const Int32 MAX_BBOX_COUNT = 2500; //Int16 limit : 2730
 
    private const Int32 NUM_BOX_VERT = 8;
 
    private const Int32 NUM_BOX_INDEX = 24;
 
    private readonly int BBoxVBOSizeInByte = MAX_BBOX_COUNT * NUM_BOX_VERT * VERTEX_SIZE;
 
    private readonly int BBoxIBOSizeInByte = MAX_BBOX_COUNT * NUM_BOX_INDEX * sizeof(IndexType);
 
    private readonly IndexType[] BBoxIndices = new IndexType[]
        {
            0, 1,
            1, 2,
            2, 3,
            3, 0,
            0, 4,
            1, 5,
            2, 6,
            3, 7,
            4, 5,
            5, 6,
            6, 7,
            7, 4,
        };
    #endregion
 
    /// <summary>
    /// Max Video memory cost for Int16-IBO BFrustum is 0.724 MB
    /// </summary>
    #region BFrustum
    private const Int32 MAX_BFRUSTUM_COUNT = 2500; //Int16 limit : 2730
 
    private const Int32 NUM_BFRUSTUM_VERT = 8;
 
    private const Int32 NUM_BFRUSTUM_INDEX = 24;
 
    private readonly int BFrustumVBOSizeInByte = MAX_BFRUSTUM_COUNT * NUM_BFRUSTUM_VERT * VERTEX_SIZE;
 
    private readonly int BFrustumIBOSizeInByte = MAX_BFRUSTUM_COUNT * NUM_BFRUSTUM_INDEX * sizeof(IndexType);
 
    private readonly IndexType[] BFrustumIndices = new IndexType[]
        {
            0, 1,
            1, 2,
            2, 3,
            3, 0,
            0, 4,
            1, 5,
            2, 6,
            3, 7,
            4, 5,
            5, 6,
            6, 7,
            7, 4,
        };
    #endregion
 
    /// <summary>
    /// Max Video memory cost for Int16-IBO BSphere is 7.95 MB !
    /// </summary>
    #region BSphere
    private const int MAX_BSPHERE_COUNT = 2500;//Int16 limit : 364;
 
    private const int NUM_SPHERE_AXIS = 3;
 
    private const int BSPHERE_RESOLUTION = 30;
 
    private const float BSPHERE_STEP = MathHelper.TwoPi / (float)BSPHERE_RESOLUTION;
 
    private const int NUM_BSPHERE_VERT = (BSPHERE_RESOLUTION + 1) * NUM_SPHERE_AXIS;
 
    private const int NUM_BSPHERE_INDEX = BSPHERE_RESOLUTION * NUM_SPHERE_AXIS * 2;
 
    private readonly int BSphereVBOSizeInByte = MAX_BSPHERE_COUNT * NUM_BSPHERE_VERT * VERTEX_SIZE;
 
    private readonly int BSphereIBOSizeInByte = MAX_BSPHERE_COUNT * NUM_BSPHERE_INDEX * sizeof(IndexType);
    #endregion
 
    #endregion
 
    #region Variables
 
    #region BBOX
 
    /// <summary>
    /// Bounding box vertices
    /// </summary>
    private VertexPositionColor[] BBoxVerts;
 
    /// <summary>
    /// Bounding box name
    /// </summary>
    private Dictionary<string, int> BBoxNames;
 
    /// <summary>
    /// Current valid index of Bbox vertices
    /// </summary>
    private int CurrentBBoxVertexIndex = 0;
 
    /// <summary>
    /// Bounding box Vertex buffer (separate with two parts: 1, for rendering, 2, for modification
    /// </summary>
    private DynamicVertexBuffer BBoxVertexBuffer = null;
 
    private DynamicVertexBuffer BBoxVertexBufferTemp = null;
 
    /// <summary>
    /// Bounding box index buffer
    /// </summary>
    private DynamicIndexBuffer BBoxIndexBuffer = null;
 
    /// <summary>
    /// Flag for rebuild vbo
    /// </summary>
    private bool NeedBuildBBoxVBO = false;
 
    /// <summary>
    /// Number of bounding boxes added
    /// </summary>
    private Int32 numBBox = 0;
 
    /// <summary>
    /// Number of bounding boxes in GPU
    /// </summary>
    private Int32 numBBoxGPU = 0;
 
    /// <summary>
    /// Switch the bbox vbo
    /// </summary>
    private bool NeedSwitchBBoxVBO = false;
 
    /// <summary>
    /// Flag of finishing switching bbox vbo
    /// </summary>
    private bool FinishedSwitchBBoxVBO = false;
 
    private bool isBBoxesVisible = true;
 
    private int numDirtyBBox = 0;
 
    private int UpdateBBoxBlockStartIndex = 0;
 
    #endregion
 
    #region BSphere
 
    /// <summary>
    /// Vertices for bounding sphere
    /// </summary>
    private VertexPositionColor[] BSphereVerts;
 
    /// <summary>
    /// Bounding Sphere name
    /// </summary>
    private Dictionary<string, int> BSphereNames;
 
    /// <summary>
    /// Current valid index of BSphere vertices
    /// </summary>
    private int CurrentBSphereVertexIndex = 0;
 
    /// <summary>
    /// Bounding Sphere Vertex buffer (separate with two parts: 1, for rendering, 2, for modification
    /// </summary>
    private DynamicVertexBuffer BSphereVertexBuffer = null;
 
    private DynamicVertexBuffer BSphereVertexBufferTemp = null;
 
    /// <summary>
    /// Bounding Sphere index buffer
    /// </summary>
    private DynamicIndexBuffer BSphereIndexBuffer = null;
 
    /// <summary>
    /// Flag for rebuild vbo
    /// </summary>
    private bool NeedBuildBSphereVBO = false;
 
    /// <summary>
    /// Number of bounding Spheres added
    /// </summary>
    private Int32 numBSphere = 0;
 
    /// <summary>
    /// Number of bounding Spheres in GPU
    /// </summary>
    private Int32 numBSphereGPU = 0;
 
    /// <summary>
    /// Switch the BSphere vbo
    /// </summary>
    private bool NeedSwitchBSphereVBO = false;
 
    /// <summary>
    /// Flag of finishing switching bSphere vbo
    /// </summary>
    private bool FinishedSwitchBSphereVBO = false;
 
    /// <summary>
    ///  Visible flag of bounding spheres
    /// </summary>
    private bool isBSpheresVisible = true;
 
 
    //update bounding sphere dirty section
    private int numDirtyBSphere = 0;
 
    private int UpdateBSphereBlockStartIndex = 0;
 
    #endregion
 
    #region BFrustum
 
    /// <summary>
    /// Bounding Frustum vertices
    /// </summary>
    private VertexPositionColor[] BFrustumVerts;
 
    /// <summary>
    /// Bounding Frustum name
    /// </summary>
    private Dictionary<string, int> BFrustumNames;
 
    /// <summary>
    /// Current valid index of BFrustum vertices
    /// </summary>
    private int CurrentBFrustumVertexIndex = 0;
 
    /// <summary>
    /// Bounding Frustum Vertex buffer (separate with two parts: 1, for rendering, 2, for modification
    /// </summary>
    private DynamicVertexBuffer BFrustumVertexBuffer = null;
 
    private DynamicVertexBuffer BFrustumVertexBufferTemp = null;
 
    /// <summary>
    /// Bounding Frustum index buffer
    /// </summary>
    private DynamicIndexBuffer BFrustumIndexBuffer = null;
 
    /// <summary>
    /// Flag for rebuild vbo
    /// </summary>
    private bool NeedBuildBFrustumVBO = false;
 
    /// <summary>
    /// Number of bounding Frustumes added
    /// </summary>
    private Int32 numBFrustum = 0;
 
    /// <summary>
    /// Number of bounding Frustumes in GPU
    /// </summary>
    private Int32 numBFrustumGPU = 0;
 
    /// <summary>
    /// Switch the bFrustum vbo
    /// </summary>
    private bool NeedSwitchBFrustumVBO = false;
 
    /// <summary>
    /// Flag of finishing switching bFrustum vbo
    /// </summary>
    private bool FinishedSwitchBFrustumVBO = false;
 
    private bool isBFrustumesVisible = true;
 
    /// <summary>
    /// Update frustum number
    /// </summary>
    private int numDirtyBFrustum = 0;
 
    private int UpdateBFrustumBlockStartIndex = 0;
 
    #endregion
 
    #region Render stuffs
 
    private Effect VolumeEffect;
 
    private Camera ActiveCamera;
 
    private VertexDeclaration decl;
 
    private RenderState previousRenderState;
 
    #endregion
 
    #endregion
 
}

Camera class:

#region Using Directives
using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
#endregion
 
 
/// <summary>
/// Camera base class
/// </summary>
public class Camera : GameComponent
{
    #region Variables
 
    //matrices
    protected Matrix mWorldMatrix;
    protected Matrix mViewMatrix;
    protected Matrix mProjectionMatrix;
 
    //camera settings
    protected Vector3 mPosition;
    protected Vector3 mInitPosition;
    protected Vector3 mTarget;
    protected Vector3 mInitTarget;
    protected float mAspectRatio;
    protected float mNearPlane;
    protected float mFarPlane;
 
    //game settings
    protected Game mGame; 
 
    #endregion
 
    #region Propeties
    public float FarPlane
    {
        get { return mFarPlane; }
        set
        {
            mFarPlane = value;
            UpdateCamera();
        }
    }
 
    public float NearPlane
    {
        get { return mNearPlane; }
        set
        {
            mNearPlane = value;
            UpdateCamera();
        }
    }
 
    public float AspectRatio
    {
        get { return mAspectRatio; }
        set
        {
            mAspectRatio = value;
            UpdateCamera();
        }
    }
 
    public Vector3 Lookat
    {
        get { return mTarget; }
        set
        {
            mTarget = value;
            UpdateCamera();
        }
    }
 
    public Vector3 Position
    {
        get { return mPosition; }
        set { mPosition = value;}
    }
 
    public Matrix Projection
    {
        get { return mProjectionMatrix; }
        set { mProjectionMatrix = value; }
    }
 
    public Matrix View
    {
        get { return mViewMatrix; }
        set { mViewMatrix = value; }
    }
 
 
    public Matrix World
    {
        get { return mWorldMatrix; }
        set { mWorldMatrix = value; }
    }
 
    public Matrix WoldViewProj
    {
        get { return (mWorldMatrix * mViewMatrix * mProjectionMatrix); }
    }
 
    public BoundingFrustum ViewFrustum
    {
        get { return new BoundingFrustum(mWorldMatrix * mViewMatrix * mProjectionMatrix); }
    }
    #endregion
 
    #region Constructors
    public Camera(Game pGame, Vector3 pPosition)
        : this(pGame, pPosition, Vector3.Zero)
    {
    }
 
    public Camera(Game pGame, Vector3 pPosition, Vector3 pTarget):
        base(pGame)
    {
        mPosition = pPosition;
        mTarget = pTarget;
        mInitPosition = pPosition;
        mInitTarget = pTarget;
        mGame = pGame;
        mAspectRatio = mGame.GraphicsDevice.Viewport.AspectRatio;
        mNearPlane = 1f;
        mFarPlane = 1000f;
        UpdateCamera();
    }
    #endregion
 
	#region Initialize
	public override void Initialize()
	{
		base.Initialize();
	} // Initialize
	#endregion
 
    #region Update
    /// <summary>
    /// Update the camera with new settings
    /// </summary>
    private void UpdateCamera()
    {
        this.mViewMatrix = Matrix.CreateLookAt(mPosition, mTarget, Vector3.Up);
        this.mProjectionMatrix = Matrix.CreatePerspectiveFieldOfView((float)Math.PI / 4, mAspectRatio, mNearPlane, mFarPlane);
        this.mWorldMatrix = Matrix.Identity;
    }
 
    public void Reset()
    {
        mPosition = mInitPosition;
        mTarget = mInitTarget;
        UpdateCamera();
    }
 
    //public override void Update(GameTime gameTime)
    //{
    //}
    #endregion
}

LineRender.fx:

float4x4 World;
float4x4 View;
float4x4 Projection;
 
struct VertexShaderInput
{
    float4 Position		: POSITION0;
	float4 Color		: COLOR0;
};
 
struct VertexShaderOutput
{
    float4 Position : POSITION0;
    float4 Color		: COLOR0;
};
 
//------------------------------------
// Vertex Shader
//------------------------------------
 
VertexShaderOutput LineRenderingVS(VertexShaderInput input)
{
    VertexShaderOutput output;
 
    float4 worldPosition = mul(input.Position, World);
    float4 viewPosition = mul(worldPosition, View);
    output.Position = mul(viewPosition, Projection);
 
    output.Color = input.Color;
 
    return output;
}
 
//------------------------------------
// Pixel Shader
//------------------------------------
 
float4 LineRenderingPS(VertexShaderOutput input) : COLOR0
{
    return input.Color;
}
 
//------------------------------------
// Technique
//------------------------------------
 
technique VolumeRendering
{
	pass PassFor3D
	{
		VertexShader = compile vs_1_1 LineRenderingVS();
		PixelShader = compile ps_1_1 LineRenderingPS();
	}
}