Example #1
0
        /// <summary>
        ///     Initializes a new instance of the <see cref="QuadNode" /> class.
        /// </summary>
        /// <param name="type">Type of node.</param>
        /// <param name="size">Width/Height of node (# of vertices across - 1).</param>
        /// <param name="depth">Depth of current node</param>
        /// <param name="parent">Parent QuadNode</param>
        /// <param name="parentTree">Top level Tree.</param>
        /// <param name="positionIndex">Index of top left Vertex in the parent tree Vertices array</param>
        public QuadNode(
            NodeType type,
            int size,
            int depth,
            QuadNode parent,
            Tanks3DFPP.Terrain.QuadTree parentTree,
            int positionIndex)
        {
            Type = type;
            this.size = size;
            this.depth = depth;

            parentNode = parent;
            this.parentTree = parentTree;

            this.positionIndex = positionIndex;

            AddVertices();

            /*
             * The bounding box is used to determine where the tree
             * should be split and where to look for nodes that are within view.
             *
             * In a production system it would make more sense to use the maximum height value of the tree as a guide to define the bounds of the bounding box.
             * Even the maximum height within each individual quad may be used to define the bounds which would make culling away invisible quads much more efficient when standing on a mountain.
             * In fact, a more efficient method would be to use bounding shapes instead of volumes, using a rectangle as the bounding object.
             * The view frustrum could then be projected onto the terrain as a 2D shape.
             * Intersection checks for LOD would be much more efficient.
             */
            Bounds = new BoundingBox(
                this.parentTree.Vertices[VertexTopLeft.Index].Position,
                this.parentTree.Vertices[VertexBottomRight.Index].Position)
                {
                    Min = { Y = 0 },
                    Max = { Y = limY }
                };

            //If node does not belong to the highest LOD
            if (this.size >= minSizeToContainChildren)
            {
                AddChildren();
            }

            // If this is the root node
            if (this.depth == 1)
            {
                /*
                 * Create the neighbors.
                 * This is essentially the bootstrap point for the whole tree.
                 */

                AddNeighbors();

                VertexTopLeft.Activated = true;
                VertexTopRight.Activated = true;
                VertexCenter.Activated = true;
                VertexBottomLeft.Activated = true;
                VertexBottomRight.Activated = true;

                VertexTop.Activated = true;
                VertexLeft.Activated = true;
                VertexRight.Activated = true;
                VertexBottom.Activated = true;
            }
        }
Example #2
0
 /*
  * Each quad looks like this:
  * (V - QuadNodeVertex)
  *
  *      V------V------V
  *      |      |      |
  *      |      |      |
  *      V------V------V
  *      |      |      |
  *      |      |      |
  *      V------V------V
  *
  *  Since we are operating on triangles.
  *  Thus the need for 9 vertices per node.
  *  The order of activating vertices is as follows:
  *      1) Top-left
  *      2) Top-right
  *      3) Center
  *      4) Bottom-left
  *      5) Bottom-right
  *  Only after splitting are the Top, Left, Right and Bottom (in that order) vertices activated.
  *
  * Now let's say that quad has a neighbor to the right.
  * Since we've activated the "Right" vertex on this node by splitting it
  * we have to also activate the "Left" node on the neighbor to the right
  * otherwise we'll have a split node up against an unsplit node which
  * will create a visible seam.
  * Having the extra vertices at the quad level allows us to activate
  * only the necessary vertices without completely splitting
  * the neighboring quad.
  */
 /// <summary>
 ///     Adds the children quad nodes.
 /// </summary>
 private void AddChildren()
 {
     int halfSize = size / 2,
         nextDepth = depth + 1;
     ChildTopLeft = new QuadNode(NodeType.TopLeft, halfSize, nextDepth, this, parentTree, VertexTopLeft.Index);
     ChildTopRight = new QuadNode(NodeType.TopRight, halfSize, nextDepth, this, parentTree, VertexTop.Index);
     ChildBottomLeft = new QuadNode(NodeType.BottomLeft, halfSize, nextDepth, this, parentTree,
                                    VertexBottomLeft.Index);
     ChildBottomRight = new QuadNode(NodeType.BottomRight, halfSize, nextDepth, this, parentTree,
                                     VertexCenter.Index);
     hasChildren = true;
 }
Example #3
0
        /// <summary>
        ///     Adds the neighboring quad nodes.
        /// </summary>
        private void AddNeighbors()
        {
            switch (Type)
            {
                case NodeType.TopLeft:

                    #region The direct neighbors that DO NOT belong within the current node's parent:

                    // In case this is not the topmost quad node of the whole tree
                    if (Parent.NeighborTop != null)
                    {
                        // The parent's top neighbor's next-level bottom-left quad node.
                        // This is effectively the adjacent quad node of the same size as this one.
                        NeighborTop = Parent.NeighborTop.ChildBottomLeft;
                    }

                    // In case this is not the leftmost quad node of the whole tree
                    if (Parent.NeighborLeft != null)
                    {
                        // The parent's left neighbor's next-level top-right quad node.
                        // This is effectively the adjacent quad node of the same size as this one.
                        NeighborLeft = Parent.NeighborLeft.ChildTopRight;
                    }

                    #endregion

                    #region The direct neighbors within the same parent:

                    NeighborRight = Parent.ChildTopRight;
                    NeighborBottom = Parent.ChildBottomLeft;

                    #endregion

                    break;
                case NodeType.TopRight:

                    #region The direct neighbors that DO NOT belong within the current node's parent:

                    if (Parent.NeighborTop != null)
                    {
                        NeighborTop = Parent.NeighborTop.ChildBottomRight;
                    }

                    if (Parent.NeighborRight != null)
                    {
                        NeighborRight = Parent.NeighborRight.ChildTopLeft;
                    }

                    #endregion

                    #region The direct neighbors within the same parent:

                    NeighborLeft = Parent.ChildTopLeft;
                    NeighborBottom = Parent.ChildBottomRight;

                    #endregion

                    break;
                case NodeType.BottomLeft:

                    #region The direct neighbors that DO NOT belong within the current node's parent:

                    if (Parent.NeighborBottom != null)
                    {
                        NeighborBottom = Parent.NeighborBottom.ChildTopLeft;
                    }

                    if (Parent.NeighborLeft != null)
                    {
                        NeighborLeft = Parent.NeighborLeft.ChildBottomRight;
                    }

                    #endregion

                    #region The direct neighbors within the same parent:

                    NeighborRight = Parent.ChildBottomRight;
                    NeighborTop = Parent.ChildTopLeft;

                    #endregion

                    break;
                case NodeType.BottomRight:

                    #region The direct neighbors that DO NOT belong within the current node's parent:

                    if (Parent.NeighborBottom != null)
                    {
                        NeighborBottom = Parent.NeighborBottom.ChildTopRight;
                    }

                    if (Parent.NeighborRight != null)
                    {
                        NeighborRight = Parent.NeighborRight.ChildBottomLeft;
                    }

                    #endregion

                    #region The direct neighbors within the same parent:

                    NeighborLeft = Parent.ChildBottomLeft;
                    NeighborTop = Parent.ChildTopRight;

                    #endregion

                    break;
            }

            if (hasChildren)
            {
                // recursively add neighbors for all children.
                ChildTopLeft.AddNeighbors();
                ChildTopRight.AddNeighbors();
                ChildBottomLeft.AddNeighbors();
                ChildBottomRight.AddNeighbors();
            }
        }
Example #4
0
 private static void EnsureNeighborParentSplit(QuadNode neighbor)
 {
     if (neighbor != null && neighbor.Parent != null && !neighbor.Parent.IsSplit)
     {
         neighbor.Parent.Split();
     }
 }
Example #5
0
        public void Update(ICamera camera)
        {
            // Checking camera position is not enough - terrain has to update also while changing the angle.
            if (camera.Frustum != lastCameraFrustum)
            {
                Effect.Parameters["xView"].SetValue(camera.View);

                lastCameraFrustum = camera.Frustum;
                IndexCount = 0;

                root.Merge();
                root.EnforceMinimumDepth();
                active = root.DeepestNodeContainingPoint(camera.LookAt);
                if (active != null)
                {
                    active.Split();
                }

                root.SetActiveVertices();

                buffers.UpdateIndexBuffer(Indices, IndexCount);
                buffers.SwapBuffer();
            }

            this.RotateLight(.2f);
        }
Example #6
0
 public void Initialize(IHeightMap heightMap)
 {
     Thread t = new Thread(() =>
         {
             topNodeSize = heightMap.Width - 1;
             vertices = new TreeVertexCollection(this.position, heightMap, Game1.GameParameters.MapScale);
             buffers = new BufferManager(vertices.Vertices, GraphicsDevice);
             root = new QuadNode(NodeType.FullNode, topNodeSize, 1, null, this, 0);
             Indices = new int[(heightMap.Width + 1) * (heightMap.Height + 1) * 3];
             this.FireReady(this);
         });
     t.Start();
 }
Example #7
0
        /// <summary>
        ///     Initializes a new instance of the <see cref="QuadNode" /> class.
        /// </summary>
        /// <param name="type">Type of node.</param>
        /// <param name="size">Width/Height of node (# of vertices across - 1).</param>
        /// <param name="depth">Depth of current node</param>
        /// <param name="parent">Parent QuadNode</param>
        /// <param name="parentTree">Top level Tree.</param>
        /// <param name="positionIndex">Index of top left Vertex in the parent tree Vertices array</param>
        public QuadNode(
            NodeType type,
            int size,
            int depth,
            QuadNode parent,
            Tanks3DFPP.Terrain.QuadTree parentTree,
            int positionIndex)
        {
            Type       = type;
            this.size  = size;
            this.depth = depth;

            parentNode      = parent;
            this.parentTree = parentTree;

            this.positionIndex = positionIndex;


            AddVertices();

            /*
             * The bounding box is used to determine where the tree
             * should be split and where to look for nodes that are within view.
             *
             * In a production system it would make more sense to use the maximum height value of the tree as a guide to define the bounds of the bounding box.
             * Even the maximum height within each individual quad may be used to define the bounds which would make culling away invisible quads much more efficient when standing on a mountain.
             * In fact, a more efficient method would be to use bounding shapes instead of volumes, using a rectangle as the bounding object.
             * The view frustrum could then be projected onto the terrain as a 2D shape.
             * Intersection checks for LOD would be much more efficient.
             */
            Bounds = new BoundingBox(
                this.parentTree.Vertices[VertexTopLeft.Index].Position,
                this.parentTree.Vertices[VertexBottomRight.Index].Position)
            {
                Min = { Y = 0 },
                Max = { Y = limY }
            };

            //If node does not belong to the highest LOD
            if (this.size >= minSizeToContainChildren)
            {
                AddChildren();
            }

            // If this is the root node
            if (this.depth == 1)
            {
                /*
                 * Create the neighbors.
                 * This is essentially the bootstrap point for the whole tree.
                 */

                AddNeighbors();

                VertexTopLeft.Activated     = true;
                VertexTopRight.Activated    = true;
                VertexCenter.Activated      = true;
                VertexBottomLeft.Activated  = true;
                VertexBottomRight.Activated = true;

                VertexTop.Activated    = true;
                VertexLeft.Activated   = true;
                VertexRight.Activated  = true;
                VertexBottom.Activated = true;
            }
        }
Example #8
0
        /// <summary>
        ///     Adds the neighboring quad nodes.
        /// </summary>
        private void AddNeighbors()
        {
            switch (Type)
            {
            case NodeType.TopLeft:

                #region The direct neighbors that DO NOT belong within the current node's parent:

                // In case this is not the topmost quad node of the whole tree
                if (Parent.NeighborTop != null)
                {
                    // The parent's top neighbor's next-level bottom-left quad node.
                    // This is effectively the adjacent quad node of the same size as this one.
                    NeighborTop = Parent.NeighborTop.ChildBottomLeft;
                }

                // In case this is not the leftmost quad node of the whole tree
                if (Parent.NeighborLeft != null)
                {
                    // The parent's left neighbor's next-level top-right quad node.
                    // This is effectively the adjacent quad node of the same size as this one.
                    NeighborLeft = Parent.NeighborLeft.ChildTopRight;
                }

                #endregion

                #region The direct neighbors within the same parent:

                NeighborRight  = Parent.ChildTopRight;
                NeighborBottom = Parent.ChildBottomLeft;

                #endregion

                break;

            case NodeType.TopRight:

                #region The direct neighbors that DO NOT belong within the current node's parent:

                if (Parent.NeighborTop != null)
                {
                    NeighborTop = Parent.NeighborTop.ChildBottomRight;
                }

                if (Parent.NeighborRight != null)
                {
                    NeighborRight = Parent.NeighborRight.ChildTopLeft;
                }

                #endregion

                #region The direct neighbors within the same parent:

                NeighborLeft   = Parent.ChildTopLeft;
                NeighborBottom = Parent.ChildBottomRight;

                #endregion

                break;

            case NodeType.BottomLeft:

                #region The direct neighbors that DO NOT belong within the current node's parent:

                if (Parent.NeighborBottom != null)
                {
                    NeighborBottom = Parent.NeighborBottom.ChildTopLeft;
                }

                if (Parent.NeighborLeft != null)
                {
                    NeighborLeft = Parent.NeighborLeft.ChildBottomRight;
                }

                #endregion

                #region The direct neighbors within the same parent:

                NeighborRight = Parent.ChildBottomRight;
                NeighborTop   = Parent.ChildTopLeft;

                #endregion

                break;

            case NodeType.BottomRight:

                #region The direct neighbors that DO NOT belong within the current node's parent:

                if (Parent.NeighborBottom != null)
                {
                    NeighborBottom = Parent.NeighborBottom.ChildTopRight;
                }

                if (Parent.NeighborRight != null)
                {
                    NeighborRight = Parent.NeighborRight.ChildBottomLeft;
                }

                #endregion

                #region The direct neighbors within the same parent:

                NeighborLeft = Parent.ChildBottomLeft;
                NeighborTop  = Parent.ChildTopRight;

                #endregion

                break;
            }

            if (hasChildren)
            {
                // recursively add neighbors for all children.
                ChildTopLeft.AddNeighbors();
                ChildTopRight.AddNeighbors();
                ChildBottomLeft.AddNeighbors();
                ChildBottomRight.AddNeighbors();
            }
        }