///
/// This class stores the root of an octree spatial subdivision. It serves as
/// an encapsulator to create, update, render and manipulate the octree.
///
public class Octree : SceneItem
{
///
/// primitive shader for drawing the octree boxes
///
protected ShaderCustom primitiveShader;
///
/// the root (top) level node of the octree
///
protected OctreeNode root;
///
/// the hierarchical depth of octree
/// nodes to draw during a render call
///
protected uint treeDisplayDepth;
///
/// the depth of the octree
///
protected uint treeDepth;
///
/// the volume of the smallest
/// leaf nodes in the tree
///
protected float leafVolume;
///
/// the color used to render the
/// boundaries of the octree
///
protected Color treeColor;
///
/// the number of leaf nodes in the tree
///
protected int leafNodeCount;
/*
* CONSTRUCTORS
*/
///
/// constructor for an octree with parameters for controlling automatic subdivision
///
/// the game object
/// a name for this octree
/// the top level box that contains the entire tree
/// the maximum depth to divide the tree
/// a threshold that will prevent the trees leaf nodes
/// from being subdivided smaller than this volume, or zero to ignore this feature.
public Octree(TwelveCylinderGame newGame, string newName, Bounds box, uint maxDepth, float minLeafVolume)
: base(newName, NodeType.SceneItem, true, newGame)
{
DateTime start = DateTime.Now;
leafNodeCount = 0;
//determine maximum tree depth
treeDepth = GetNewMaxDepth(box.Volume, minLeafVolume, maxDepth);
root = new OctreeNode(this, null, curGame, box, 0, treeDepth);
init();
DateTime end = DateTime.Now;
//printTreeData(end - start);
}
//additional constructors omitted
///
/// init common parameters for the tree
///
private void init()
{
treeColor = Color.White;
this.Visible = false;
treeDisplayDepth = 0;
if (curGame != null)
primitiveShader = curGame.ShaderManager.Primitive3DShader;
}
///
/// prints info about the new octree to the log file
///
/// the time span used to create the tree definition
protected virtual void printTreeData(TimeSpan time)
{
curGame.Log(this.Name + " Max depth: " + treeDepth);
curGame.Log("Creation Time: " + time.TotalMilliseconds + " ms");
curGame.Log("leaf volume: " + leafVolume);
}
public override void unload(bool disposeResources)
{
base.unload(disposeResources);
if (root != null)
root.unload(disposeResources);
if (primitiveShader != null)
primitiveShader.unload(disposeResources);
root = null;
primitiveShader = null;
}
/*
* METHODS
*/
///
/// callback to add a leaf node to this tree
///
///
public virtual int addLeafNode(OctreeNode leaf)
{
return leafNodeCount++;
}
///
/// gets a list of octree leaf nodes which intersect with
/// the specified bounding box
///
/// a bounding box to test
/// a list of octree leaf nodes
public virtual List GetIntersectedLeafNodes(Bounds b)
{
List leafNodes = new List(12);
root.GetIntersectedLeafNodes(b, ref leafNodes);
return leafNodes;
}
///
/// Recursive function to find the octree leaf node
/// that contains the specified world position.
///
/// a world space coordinate
/// the containing leaf node
public virtual OctreeNode GetContainingLeaf(Vector3 worldPos)
{
return root.getContainingLeaf(worldPos);
}
///
/// Calculates the maximum tree depth that should be subdivied to.
/// This is calculated from a minimum leaf volume and an arbitrary
/// maximum depth value.
///
/// the volume of the top level bounds of the tree
/// the minimum leaf volume
/// the maximum depth of the tree
/// the new maximum depth of the tree
protected uint GetNewMaxDepth(float topVolume, float minLeafVolume, uint maxDepth)
{
uint newTreeDepth = 0;
float newLeafVolume = topVolume;
if (minLeafVolume > 0)
{
while (true)
{
newLeafVolume /= 8;
newTreeDepth++;
//reached arbitrary maximum
if(newTreeDepth == maxDepth)
break;
if (newLeafVolume < minLeafVolume)
{
//back one step
newLeafVolume *= 8;
newTreeDepth--;
break;
}
}
}
else
{
//negative volume indicates to ignore
newTreeDepth = maxDepth;
}
//limit to 0
if(newTreeDepth < 0)
newTreeDepth = 0;
return newTreeDepth;
}
///
/// calculates the tree depth and leaf volume
/// properties from the current tree hierarchy
///
protected virtual void updateTreeData()
{
treeDepth = root.getMaximumDepth();
leafVolume = root.Box.Volume / (float)Math.Pow(OctreeNode.NUM_TREE_CHILDREN, treeDepth);
}
//encapsulators omitted
/*
* UPDATE & RENDER
*/
public override void update(GameTime gameTime, TwelveCylinder.GameCamera.Camera curCam, Matrix projMatrix, Matrix viewProj)
{
base.update(gameTime, curCam, projMatrix, viewProj);
//update shader if visibility is on
if (this.Visible)
primitiveShader.WVP = viewProj;
//update the tree nodes
root.update(gameTime, curCam, projMatrix, viewProj);
}
///
/// renders the bounds of the branches of this octree
///
///
public override void Render(TwelveCylinder.StageManagement.RenderMode rm)
{
if (curGame == null || primitiveShader == null ||
!primitiveShader.EffectLoaded)
return; //not capable of rendering
//sets vertex decleration to VertexPositionColor
curGame.beginPrimitiveVertexDecleration();
//use single pass primitive shader
primitiveShader.Begin();
primitiveShader.BeginPass(0);
RenderTreeBounds();
primitiveShader.EndPass(0);
primitiveShader.End();
}
protected virtual void RenderTreeBounds()
{
root.DrawTreeBounds(treeDisplayDepth);
}
}