/// /// 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); } }