//////////////////////////////////////////////////////////////////// /// <summary> /// Yet another recursive function. This function takes the position of the camera, and statuses all the children nodes of the /// specified quadnode based on their distance. It then recurses down the tree, statusing as it goes. /// In essence, this function is what applies our LOD /// /// Addition: I've integrated the frustrum checks into this algorithm, to improve performance. /// </summary> /// <param name="qNode">The root node.</param> /// <param name="cameraPoint">The camera's position</param> /// <param name="LODLevel">The LOD Distance to apply. 3.5 is the recommended minimum, because of stitching issues. Less than 2 may cause an unhandled exception.</param> private void RecursiveChildStatus(QuadNode qNode, Vector3 cameraPoint, float LODLevel, BoundingFrustum CameraFrustrum) { //Determine the squared distance of the camera from the specified node, taking into account the size of the node and the scale of //the terrain. //This value doesn't undergo a Sqrt to save processing power: instead, the opposite side is sqared qNode.distanceFromCamera = (float)(Math.Pow(Math.Abs(cameraPoint.X - ((qNode.XPosition + (qNode.NodeScale / 2)) * Scale)), 2) + Math.Pow(Math.Abs(cameraPoint.Y - ((0 + (qNode.NodeScale / 2)) * Scale)), 2) * LODHeightImpact + Math.Pow(Math.Abs(cameraPoint.Z - ((qNode.YPosition + (qNode.NodeScale / 2)) * Scale)), 2)); ///////////////////////////////////////////////// //Staus this node as 1... qNode.status = 1; //...then, if node depth is not too deep... if (qNode.NodeDepth < maxNodeDepth) { float leftUpLOD = qNode.leftUpNode.NodeScale * Scale * LODLevel; float leftDownLOD = qNode.leftDownNode.NodeScale * Scale * LODLevel; float rightUpLOD = qNode.rightUpNode.NodeScale * Scale * LODLevel; float rightDownLOD = qNode.rightDownNode.NodeScale * Scale * LODLevel; leftUpLOD *= leftUpLOD; leftDownLOD *= leftDownLOD; rightUpLOD *= rightUpLOD; rightDownLOD *= rightDownLOD; //...determine whether or not to recurse onto the nodes children. if (qNode.distanceFromCamera < leftUpLOD) { if (CameraFrustrum.Intersects(qNode.leftUpNode.boundBox)) { qNode.status = 2; RecursiveChildStatus(qNode.leftUpNode, cameraPoint, LODLevel, CameraFrustrum); } else { qNode.leftUpNode.inView = false; qNode.status = 2; } } if (qNode.distanceFromCamera < leftDownLOD) { if (CameraFrustrum.Intersects(qNode.leftDownNode.boundBox)) { qNode.status = 2; RecursiveChildStatus(qNode.leftDownNode, cameraPoint, LODLevel, CameraFrustrum); } else { qNode.leftDownNode.inView = false; qNode.status = 2; } } if (qNode.distanceFromCamera < rightUpLOD) { if (CameraFrustrum.Intersects(qNode.rightUpNode.boundBox)) { qNode.status = 2; RecursiveChildStatus(qNode.rightUpNode, cameraPoint, LODLevel, CameraFrustrum); } else { qNode.rightUpNode.inView = false; qNode.status = 2; } } if (qNode.distanceFromCamera < rightDownLOD) { if (CameraFrustrum.Intersects(qNode.rightDownNode.boundBox)) { qNode.status = 2; RecursiveChildStatus(qNode.rightDownNode, cameraPoint, LODLevel, CameraFrustrum); } else { qNode.rightDownNode.inView = false; qNode.status = 2; } } } }
/// <summary> /// This recursive function takes a point and sinks into the quadtree until it finds the active /// Quadnode which currently covers that point. /// </summary> private QuadNode GetQuadNodeFromPoint(float xPos, float yPos, QuadNode rootNode) { if (rootNode.status != 1 && rootNode.NodeDepth < this.maxNodeDepth) { if (xPos < rootNode.XPosition * this.Scale + rootNode.NodeScale * this.Scale / 2) { if (yPos < rootNode.YPosition * this.Scale + rootNode.NodeScale * this.Scale / 2) { return GetQuadNodeFromPoint(xPos, yPos, rootNode.leftUpNode); } else { return GetQuadNodeFromPoint(xPos, yPos, rootNode.rightUpNode); } } else { if (yPos < rootNode.YPosition * this.Scale + rootNode.NodeScale * this.Scale / 2) { return GetQuadNodeFromPoint(xPos, yPos, rootNode.leftDownNode); } else { return GetQuadNodeFromPoint(xPos, yPos, rootNode.rightDownNode); } } } else { return rootNode; } }
////////////////////////////////////////////////// /// <summary> /// Another recursive Function, this was previously used to set the status of a node and then recurse up the tree, setting /// the status of all it's parent nodes. /// /// Now depricated, because this is clearly a retarded way to do things (the whole point of a quad tree in to recurse *down*, this /// function is kept because it may contain useful code I can draw on later. /// </summary> /// <param name="qNode">A leaf node</param> private void RecursiveParentStatus(QuadNode qNode) { if (qNode.NodeDepth > 0) { short tempStatus1 = qNode.parent.leftUpNode.status; short tempStatus2 = qNode.parent.leftDownNode.status; short tempStatus3 = qNode.parent.rightUpNode.status; short tempStatus4 = qNode.parent.rightDownNode.status; qNode.parent.status = 0; if (qNode.status == 1) { if (qNode.leftUpNode == null) { qNode.parent.leftUpNode.status = 1; qNode.parent.rightUpNode.status = 1; qNode.parent.leftDownNode.status = 1; qNode.parent.rightDownNode.status = 1; qNode.parent.status = 2; } else { #if WINDOWS System.Diagnostics.Debug.Fail("debug problem1"); #else throw new Exception("debug problem1"); #endif } } else { if (qNode.status == 2) { if (qNode.parent.leftUpNode.status == 0) { qNode.parent.leftUpNode.status = 1; } if (qNode.parent.leftDownNode.status == 0) { qNode.parent.leftDownNode.status = 1; } if (qNode.parent.rightUpNode.status == 0) { qNode.parent.rightUpNode.status = 1; } if (qNode.parent.rightDownNode.status == 0) { qNode.parent.rightDownNode.status = 1; } qNode.parent.status = 2; } else { #if WINDOWS System.Diagnostics.Debug.Fail("debug problem2"); #else throw new Exception("debug problem2"); #endif } } RecursiveParentStatus(qNode.parent); } }
////////////////////////////////////////////// /// <summary> /// This function creates a quad nodes four component children. These child nodes then have this function applied to them. /// This results in a recursion down the quad tree, creating nodes until maxNodeDepth is reached. /// </summary> /// <param name="rootNode">The node to start the recursion at.</param> private void RecursiveCreateQuad(QuadNode rootNode) { //NOW WITH ANNOYING FRUSTRUMS! //^Now that I've got them working, they're not annoying anymore. But they're still frustrums ^-^ //Generate LeftUp child Node, set it's parent, and add it to the Quadnode list. rootNode.leftUpNode = new QuadNode(HeightMap, normStore, SquareSize, VBsize, Scale, rootNode.NodeDepth + 1, rootNode.XPosition, rootNode.YPosition, rootNode, allVertices); rootNode.leftUpNode.parent = rootNode; allQuadNodes.Add(rootNode.leftUpNode); //Generate leftDown child Node, set it's parent, and add it to the Quadnode list. rootNode.leftDownNode = new QuadNode(HeightMap, normStore, SquareSize, VBsize, Scale, rootNode.NodeDepth + 1, rootNode.XPosition + rootNode.NodeScale / 2, rootNode.YPosition, rootNode, allVertices); rootNode.leftDownNode.parent = rootNode; allQuadNodes.Add(rootNode.leftDownNode); //Generate rightUp child Node, set it's parent, and add it to the Quadnode list. rootNode.rightUpNode = new QuadNode(HeightMap, normStore, SquareSize, VBsize, Scale, rootNode.NodeDepth + 1, rootNode.XPosition, rootNode.YPosition + rootNode.NodeScale / 2, rootNode, allVertices); rootNode.rightUpNode.parent = rootNode; allQuadNodes.Add(rootNode.rightUpNode); //Generate rightDown child Node, set it's parent, and add it to the Quadnode list. rootNode.rightDownNode = new QuadNode(HeightMap, normStore, SquareSize, VBsize, Scale, rootNode.NodeDepth + 1, rootNode.XPosition + rootNode.NodeScale / 2, rootNode.YPosition + rootNode.NodeScale / 2, rootNode, allVertices); rootNode.rightDownNode.parent = rootNode; allQuadNodes.Add(rootNode.rightDownNode); //Check that this isn't as deep as the recursion is allowed to go... if (rootNode.NodeDepth < maxNodeDepth - 1) { //And recurse on all 4 nodes. RecursiveCreateQuad(rootNode.leftUpNode); RecursiveCreateQuad(rootNode.leftDownNode); RecursiveCreateQuad(rootNode.rightUpNode); RecursiveCreateQuad(rootNode.rightDownNode); } }
//heightMap must be 2^n+1, squareSize must be 2^n+1, both are constants of the Quadtree. //nodedepth will not go below a level that will make square size impossible given the height field. /// <summary> /// This constructs a single complete quadNode in the Quadtree. /// </summary> /// <param name="heightMap">This should be a reference to the Terrains heightmap. Inherited from Quadterrain.</param> /// <param name="squareSize">This is the number of vertices along the edge of the quadnode. Inherited from Quadterrain.</param> /// <param name="VertBuffSize">This is the number of vertices along the edge of a Vertex buffer. Inherited from Quadterrain.</param> /// <param name="terrainScale">The scale of the terrain. Inherited from Quadterrain.</param> /// <param name="nodeDepth">The depth of this individual node. Vitally important for generating many properties of the node.</param> /// <param name="xPosition">The X origin of the node on the heightmap.</param> /// <param name="yPosition">The Y origin of the node on the heightmap.</param> /// <param name="parentFullQuad">The parent node of this node in the Tree.</param> /// <param name="Identifier">An ID number. I can't actually remember what I used it for... :?</param> /// <param name="Vertices">This should be a reference to the Vertex Buffers. Inherited from Quadterrain.</param> public QuadNode(Texture2D heightMap, Vector3[,] normStore, int squareSize, int VertBuffSize, float terrainScale, int nodeDepth, int xPosition, int yPosition, QuadNode parentFullQuad, VertexPosition[][] Vertices) { stitchedSides = 0; //Initialise x and y position variables. XPosition = xPosition; YPosition = yPosition; //Initialise parent variable parent = parentFullQuad; //Initialise status status = 0; //Get number of Vertex Buffers int numberOfVBs = (int)Math.Pow((heightMap.Height - 1) / (VertBuffSize - 1), 2); int sqrtNumberOfVBs = (int)Math.Sqrt(numberOfVBs); ///////////////////////////////////////////////////// // For: // SquareSize = 5 // Height = 257 // NodeDepth, NodeLevel, NodeScale, stepSize // 0, 1, 257, 64 // 1, 2, 129, 32 // 2, 4, 65, 16 // 3, 8, 33, 8 // 4, 16, 19, 4 // 5, 32, 9, 2 // 6, 64, 5, 1 //////////////////////////////////////////////////// int stitching = 0; NodeLevel = (int)Math.Pow(2, (nodeDepth)); NodeScale = ((heightMap.Height - 1) / NodeLevel) + 1; stepSize = (NodeScale - 1) / (squareSize - 1); indices = new int[9][]; //////////////////////////////////////////////////// //Gather the vertex buffer of this node VBuffer = (int)(Math.Floor(((float)(xPosition + 1) / heightMap.Width) * sqrtNumberOfVBs) + (Math.Floor(((float)(yPosition + 1) / heightMap.Height) * sqrtNumberOfVBs) * sqrtNumberOfVBs)) + 1; int VBufferRootScale = 1; //Set this nodes buffer to the root buffer if the Node is too big if (NodeScale > VertBuffSize) { VBuffer = 0; VBufferRootScale = (heightMap.Width - 1) / (VertBuffSize - 1); // ~~ HeightMapWidth/VBufferSize ~~ VBXOffset = 0; VBYOffset = 0; } else { VBXOffset = ((VBuffer - 1) % (int)sqrtNumberOfVBs) * (VertBuffSize - 1); VBYOffset = ((VBuffer - 1) / (int)sqrtNumberOfVBs) * (VertBuffSize - 1); } //Initialised Bounding Box variables float minHeight = 10000; float maxHeight = 0; //indices[0] = none //indices[1] = LEFT //indices[2] = TOP //indices[3] = RIGHT //indices[4] = DOWN //indices[5] = LEFT-TOP //indices[6] = TOP-RIGHT //indices[7] = RIGHT-DOWN //indices[8] = DOWN-LEFT //Generate Index array for this node. for (stitching = 0; stitching < 9; stitching++) { List<int> tempIndices = new List<int>(); //These variables are used for creating flatstrips bool addingFlats = false; int xOffset = 0; Vector3 oldNormal = new Vector3(); //Build Index arrays for each stitching type for (int y = yPosition; y < yPosition + ((NodeScale - 1) - stepSize + 1); y += stepSize) { for (int x = xPosition; x < xPosition + ((NodeScale - 1) - stepSize + 1); x += stepSize) { // Test if (y < heightMap.Width - stepSize && x < heightMap.Height - stepSize) { //Edge stitching: // // //1 Lrge stitch // /\ // /__\ // // //2 Sml stitches // ___ ___ // | / \ | // |/ \| bool standard = false; if (Vertices[VBuffer][Math.Abs((x - VBXOffset) + (y - VBYOffset) * VertBuffSize) / VBufferRootScale].Position.Y > maxHeight) { maxHeight = Vertices[VBuffer][Math.Abs((x - VBXOffset) + (y - VBYOffset) * VertBuffSize) / VBufferRootScale].Position.Y; } else { if (Vertices[VBuffer][Math.Abs((x - VBXOffset) + (y - VBYOffset) * VertBuffSize) / VBufferRootScale].Position.Y < minHeight) { minHeight = Vertices[VBuffer][Math.Abs((x - VBXOffset) + (y - VBYOffset) * VertBuffSize) / VBufferRootScale].Position.Y; } } ///////////////////////////////////////////////////// //STITCH BOTTOM if (x <= xPosition) { if (stitching == 4 || stitching == 7 || stitching == 8) { if (y % (stepSize * 2) == 0) { standard = false; //1 Lrge stitch tempIndices.Add(Math.Abs((x - VBXOffset) + ((y - VBYOffset) + stepSize * 2) * VertBuffSize) / VBufferRootScale); tempIndices.Add(Math.Abs(((x - VBXOffset) + stepSize) + ((y - VBYOffset) + stepSize) * VertBuffSize) / VBufferRootScale); tempIndices.Add(Math.Abs((x - VBXOffset) + (y - VBYOffset) * VertBuffSize) / VBufferRootScale); numberOfIndices += 3; //2 Sml stitches (Don't do edges) if (y != yPosition || stitching == 8 || stitching == 4) { tempIndices.Add(Math.Abs(((x - VBXOffset) + stepSize) + ((y - VBYOffset) + stepSize) * VertBuffSize) / VBufferRootScale); tempIndices.Add(Math.Abs(((x - VBXOffset) + stepSize) + ((y - VBYOffset)) * VertBuffSize) / VBufferRootScale); tempIndices.Add(Math.Abs(((x - VBXOffset)) + ((y - VBYOffset)) * VertBuffSize) / VBufferRootScale); } if (y != yPosition + (NodeScale - 1) - stepSize * 2 || stitching == 7 || stitching == 4) { tempIndices.Add(Math.Abs(((x - VBXOffset) + stepSize) + ((y - VBYOffset) + stepSize) * VertBuffSize) / VBufferRootScale); tempIndices.Add(Math.Abs((x - VBXOffset) + ((y - VBYOffset) + stepSize * 2) * VertBuffSize) / VBufferRootScale); tempIndices.Add(Math.Abs(((x - VBXOffset) + stepSize) + ((y - VBYOffset) + stepSize * 2) * VertBuffSize) / VBufferRootScale); } } } else { standard = true; } } ////////////////////////////////////////// //STITCH TOP if (x == xPosition + ((NodeScale - 1) - stepSize)) { if (stitching == 2 || stitching == 5 || stitching == 6) { if (y % (stepSize * 2) == 0) { standard = false; //1 Lrg Stich tempIndices.Add(Math.Abs(((x - VBXOffset)) + ((y - VBYOffset) + stepSize) * VertBuffSize) / VBufferRootScale); tempIndices.Add(Math.Abs(((x - VBXOffset) + stepSize) + ((y - VBYOffset) + stepSize * 2) * VertBuffSize) / VBufferRootScale); tempIndices.Add(Math.Abs(((x - VBXOffset) + stepSize) + (y - VBYOffset) * VertBuffSize) / VBufferRootScale); //2 Smll Stitches (don't do edges) if (y != yPosition || stitching == 2 || stitching == 5) { tempIndices.Add(Math.Abs(((x - VBXOffset)) + ((y - VBYOffset)) * VertBuffSize) / VBufferRootScale); tempIndices.Add(Math.Abs(((x - VBXOffset)) + ((y - VBYOffset) + stepSize) * VertBuffSize) / VBufferRootScale); tempIndices.Add(Math.Abs(((x - VBXOffset) + stepSize) + ((y - VBYOffset)) * VertBuffSize) / VBufferRootScale); } if (y != yPosition + (NodeScale - 1) - stepSize * 2 || stitching == 2 || stitching == 6) { tempIndices.Add(Math.Abs(((x - VBXOffset) + stepSize) + ((y - VBYOffset) + stepSize * 2) * VertBuffSize) / VBufferRootScale); tempIndices.Add(Math.Abs(((x - VBXOffset)) + ((y - VBYOffset) + stepSize) * VertBuffSize) / VBufferRootScale); tempIndices.Add(Math.Abs(((x - VBXOffset)) + ((y - VBYOffset) + stepSize * 2) * VertBuffSize) / VBufferRootScale); } } } else { standard = true; } } //////////////////////////////// //STITCH RIGHT if (y <= yPosition) { if (stitching == 3 || stitching == 6 || stitching == 7) { if (x % (stepSize * 2) == 0) { standard = false; tempIndices.Add(Math.Abs(((x - VBXOffset) + stepSize) + ((y - VBYOffset) + stepSize) * VertBuffSize) / VBufferRootScale); tempIndices.Add(Math.Abs(((x - VBXOffset) + stepSize * 2) + ((y - VBYOffset)) * VertBuffSize) / VBufferRootScale); tempIndices.Add(Math.Abs((x - VBXOffset) + (y - VBYOffset) * VertBuffSize) / VBufferRootScale); if (x != xPosition || stitching == 3 || stitching == 6) { tempIndices.Add(Math.Abs(((x - VBXOffset)) + ((y - VBYOffset) + stepSize) * VertBuffSize) / VBufferRootScale); tempIndices.Add(Math.Abs(((x - VBXOffset) + stepSize) + ((y - VBYOffset) + stepSize) * VertBuffSize) / VBufferRootScale); tempIndices.Add(Math.Abs(((x - VBXOffset)) + ((y - VBYOffset)) * VertBuffSize) / VBufferRootScale); } if (x != xPosition + (NodeScale - 1) - stepSize * 2 || stitching == 3 || stitching == 7) { tempIndices.Add(Math.Abs(((x - VBXOffset) + stepSize * 2) + ((y - VBYOffset)) * VertBuffSize) / VBufferRootScale); tempIndices.Add(Math.Abs(((x - VBXOffset) + stepSize) + ((y - VBYOffset) + stepSize) * VertBuffSize) / VBufferRootScale); tempIndices.Add(Math.Abs(((x - VBXOffset) + stepSize * 2) + ((y - VBYOffset) + stepSize) * VertBuffSize) / VBufferRootScale); } } } else { standard = true; } } ////////////////////////////////////////// //STITCH LEFT if (y == yPosition + ((NodeScale - 1) - stepSize)) { if (stitching == 1 || stitching == 5 || stitching == 8) { if (x % (stepSize * 2) == 0) { standard = false; //1 Lrg Stich tempIndices.Add(Math.Abs(((x - VBXOffset) + stepSize * 2) + ((y - VBYOffset) + stepSize) * VertBuffSize) / VBufferRootScale); tempIndices.Add(Math.Abs(((x - VBXOffset) + stepSize) + ((y - VBYOffset)) * VertBuffSize) / VBufferRootScale); tempIndices.Add(Math.Abs(((x - VBXOffset)) + ((y - VBYOffset) + stepSize) * VertBuffSize) / VBufferRootScale); //2 Smll Stitches (don't do edges) if (x != xPosition || stitching == 1 || stitching == 5) { tempIndices.Add(Math.Abs(((x - VBXOffset) + stepSize) + ((y - VBYOffset)) * VertBuffSize) / VBufferRootScale); tempIndices.Add(Math.Abs(((x - VBXOffset)) + ((y - VBYOffset)) * VertBuffSize) / VBufferRootScale); tempIndices.Add(Math.Abs(((x - VBXOffset)) + ((y - VBYOffset) + stepSize) * VertBuffSize) / VBufferRootScale); } if (x != xPosition + (NodeScale - 1) - stepSize * 2 || stitching == 1 || stitching == 8) { tempIndices.Add(Math.Abs(((x - VBXOffset) + stepSize) + ((y - VBYOffset)) * VertBuffSize) / VBufferRootScale); tempIndices.Add(Math.Abs(((x - VBXOffset) + stepSize * 2) + ((y - VBYOffset) + stepSize) * VertBuffSize) / VBufferRootScale); tempIndices.Add(Math.Abs(((x - VBXOffset) + stepSize * 2) + ((y - VBYOffset)) * VertBuffSize) / VBufferRootScale); } } } else { standard = true; } } if (x > xPosition && y > yPosition && x != xPosition + ((NodeScale - 1) - stepSize) && y != yPosition + ((NodeScale - 1) - stepSize)) { standard = true; } //NO STITCHING (Centre piece) if (standard == true) { #region depricated //indices[stitching][i++] = Math.Abs((x - VBXOffset) + ((y - VBYOffset) + stepSize) * VertBuffSize) / VBufferRootScale; //indices[stitching][i++] = Math.Abs(((x - VBXOffset) + stepSize) + (y - VBYOffset) * VertBuffSize) / VBufferRootScale; //indices[stitching][i++] = Math.Abs((x - VBXOffset) + (y - VBYOffset) * VertBuffSize) / VBufferRootScale; //indices[stitching][i++] = Math.Abs((x - VBXOffset) + ((y - VBYOffset) + stepSize) * VertBuffSize) / VBufferRootScale; //indices[stitching][i++] = Math.Abs(((x - VBXOffset) + stepSize) + ((y - VBYOffset) + stepSize) * VertBuffSize) / VBufferRootScale; //indices[stitching][i++] = Math.Abs(((x - VBXOffset) + stepSize) + (y - VBYOffset) * VertBuffSize) / VBufferRootScale; #endregion ///////////////////////////////////////////////////////////////////// //This section is used to cut the number of polygons by removing detail on flat planes Vector3 next1Normal = normStore[x, y]; Vector3 next2Normal = normStore[x, y + stepSize]; Vector3 next1Position = Vertices[VBuffer][Math.Abs((x - VBXOffset) + (y - VBYOffset) * VertBuffSize) / VBufferRootScale].Position; Vector3 next2Position = Vertices[VBuffer][Math.Abs((x - VBXOffset) + (y + stepSize - VBYOffset) * VertBuffSize) / VBufferRootScale].Position; Vector3 nn = Vertices[VBuffer][Math.Abs((x + stepSize - VBXOffset) + (y - VBYOffset) * VertBuffSize) / VBufferRootScale].Position - next1Position; nn.Normalize(); Vector3 nn2 = Vertices[VBuffer][Math.Abs((x + stepSize - VBXOffset) + (y + stepSize - VBYOffset) * VertBuffSize) / VBufferRootScale].Position - next2Position; nn2.Normalize(); Vector3 next1Normalised = Vector3.Cross(next1Normal, Vector3.UnitZ); next1Normalised.Normalize(); Vector3 next2Normalised = Vector3.Cross(next2Normal, Vector3.UnitZ); next2Normalised.Normalize(); //MessageBox.Show("nn: "+ nn +", next2Position: " + next2Position.ToString() + ", next1Position: " + next1Position.ToString() + ",n1normalised: " + next1Normalised.ToString() + ", Formula: " + Vector3.Add(next1Position, Vector3.Multiply(next1Normalised, terrainScale * stepSize))); //if (Math.Abs(oldNormal.X - next1Normal.X) < 0.000001 && Math.Abs(oldNormal.X - next2Normal.X) < 0.000001 && Math.Abs(oldNormal.Y - next1Normal.Y) < 0.000001 && Math.Abs(oldNormal.Y - next2Normal.Y) < 0.000001 && x < xPosition + ((NodeScale - 1) - stepSize * 2)) //if (oldNormal == next1Normal && oldNormal == next2Normal && Math.Abs((nn.Y) - (next1Normalised.Y)) < .00001 && Math.Abs((nn2.Y) - (next2Normalised.Y)) < .00001 && x < xPosition + ((NodeScale - 1) - stepSize * 2) || oldNormal == next1Normal && oldNormal == next2Normal && float.IsNaN(Math.Abs(Math.Abs(nn.Z) - Math.Abs(next1Normalised.Z))) && x < xPosition + ((NodeScale - 1) - stepSize * 2)) if (oldNormal == next1Normal && oldNormal == next2Normal && nn == next1Normalised && nn2 == next2Normalised && x < xPosition + ((NodeScale - 1) - stepSize * 2) || oldNormal == next1Normal && oldNormal == next2Normal && float.IsNaN((next1Normalised.Z)) && nn.X == 1 && nn2.X == 1 && x < xPosition + ((NodeScale - 1) - stepSize * 2)) { //MessageBox.Show(nn+" "+next1Normalised); if (addingFlats == false) { //Activates when we enter a flatstrip addingFlats = true; xOffset = x; oldNormal = normStore[x, y + stepSize]; } else { //In the middle of a flatstip, we do nothing } } else { if (addingFlats == true) { //Activaes when we leave a flatstrip addingFlats = false; tempIndices.Add(Math.Abs(((xOffset - VBXOffset)) + (y - VBYOffset) * VertBuffSize) / VBufferRootScale); tempIndices.Add(Math.Abs((x - stepSize - VBXOffset) + ((y - VBYOffset) + stepSize) * VertBuffSize) / VBufferRootScale); tempIndices.Add(Math.Abs((x - stepSize - VBXOffset) + (y - VBYOffset) * VertBuffSize) / VBufferRootScale); tempIndices.Add(Math.Abs(((xOffset - VBXOffset)) + ((y - VBYOffset) + stepSize) * VertBuffSize) / VBufferRootScale); tempIndices.Add(Math.Abs((x - stepSize - VBXOffset) + ((y - VBYOffset) + stepSize) * VertBuffSize) / VBufferRootScale); tempIndices.Add(Math.Abs(((xOffset - VBXOffset)) + (y - VBYOffset) * VertBuffSize) / VBufferRootScale); ///////////////////////////////////////////////////////////////////// //This makes cliffs look better by reorientating the diagonal, and thus removing "cliff feet". VertexPosition v0 = Vertices[VBuffer][Math.Abs((x - VBXOffset) + ((y - VBYOffset)) * VertBuffSize) / VBufferRootScale]; VertexPosition vX = Vertices[VBuffer][Math.Abs((x - VBXOffset + stepSize) + ((y - VBYOffset)) * VertBuffSize) / VBufferRootScale]; VertexPosition vY = Vertices[VBuffer][Math.Abs((x - VBXOffset) + ((y - VBYOffset) + stepSize) * VertBuffSize) / VBufferRootScale]; VertexPosition vXY = Vertices[VBuffer][Math.Abs((x - VBXOffset + stepSize) + ((y - VBYOffset) + stepSize) * VertBuffSize) / VBufferRootScale]; float rightDiagDifference = Math.Abs(v0.Position.Y - vXY.Position.Y); float leftDiagDifference = Math.Abs(vX.Position.Y - vY.Position.Y); if (rightDiagDifference < leftDiagDifference) { tempIndices.Add(Math.Abs((x - VBXOffset) + (y - VBYOffset) * VertBuffSize) / VBufferRootScale); tempIndices.Add(Math.Abs(((x - VBXOffset) + stepSize) + ((y - VBYOffset) + stepSize) * VertBuffSize) / VBufferRootScale); tempIndices.Add(Math.Abs(((x - VBXOffset) + stepSize) + (y - VBYOffset) * VertBuffSize) / VBufferRootScale); tempIndices.Add(Math.Abs((x - VBXOffset) + ((y - VBYOffset) + stepSize) * VertBuffSize) / VBufferRootScale); tempIndices.Add(Math.Abs(((x - VBXOffset) + stepSize) + ((y - VBYOffset) + stepSize) * VertBuffSize) / VBufferRootScale); tempIndices.Add(Math.Abs(((x - VBXOffset)) + (y - VBYOffset) * VertBuffSize) / VBufferRootScale); } else { tempIndices.Add(Math.Abs((x - VBXOffset) + ((y - VBYOffset) + stepSize) * VertBuffSize) / VBufferRootScale); tempIndices.Add(Math.Abs(((x - VBXOffset) + stepSize) + (y - VBYOffset) * VertBuffSize) / VBufferRootScale); tempIndices.Add(Math.Abs((x - VBXOffset) + (y - VBYOffset) * VertBuffSize) / VBufferRootScale); tempIndices.Add(Math.Abs((x - VBXOffset) + ((y - VBYOffset) + stepSize) * VertBuffSize) / VBufferRootScale); tempIndices.Add(Math.Abs(((x - VBXOffset) + stepSize) + ((y - VBYOffset) + stepSize) * VertBuffSize) / VBufferRootScale); tempIndices.Add(Math.Abs(((x - VBXOffset) + stepSize) + (y - VBYOffset) * VertBuffSize) / VBufferRootScale); } tempIndices.Add(Math.Abs((x - stepSize - VBXOffset) + ((y - VBYOffset) + stepSize) * VertBuffSize) / VBufferRootScale); tempIndices.Add(Math.Abs(((x - stepSize - VBXOffset) + stepSize) + (y - VBYOffset) * VertBuffSize) / VBufferRootScale); tempIndices.Add(Math.Abs((x - stepSize - VBXOffset) + (y - VBYOffset) * VertBuffSize) / VBufferRootScale); tempIndices.Add(Math.Abs((x - stepSize - VBXOffset) + ((y - VBYOffset) + stepSize) * VertBuffSize) / VBufferRootScale); tempIndices.Add(Math.Abs(((x - stepSize - VBXOffset) + stepSize) + ((y - VBYOffset) + stepSize) * VertBuffSize) / VBufferRootScale); tempIndices.Add(Math.Abs(((x - stepSize - VBXOffset) + stepSize) + (y - VBYOffset) * VertBuffSize) / VBufferRootScale); oldNormal = normStore[x, y + stepSize]; ///////////////////////////////////////////////////////////////////// } else { ///////////////////////////////////////////////////////////////////// //Generate a normal square: nothing special, except for removing cliff feet oldNormal = normStore[x, y + stepSize]; //Make cliffs look better by reorientating the diagonal VertexPosition v0 = Vertices[VBuffer][Math.Abs((x - VBXOffset) + ((y - VBYOffset)) * VertBuffSize) / VBufferRootScale]; VertexPosition vX = Vertices[VBuffer][Math.Abs((x - VBXOffset + stepSize) + ((y - VBYOffset)) * VertBuffSize) / VBufferRootScale]; VertexPosition vY = Vertices[VBuffer][Math.Abs((x - VBXOffset) + ((y - VBYOffset) + stepSize) * VertBuffSize) / VBufferRootScale]; VertexPosition vXY = Vertices[VBuffer][Math.Abs((x - VBXOffset + stepSize) + ((y - VBYOffset) + stepSize) * VertBuffSize) / VBufferRootScale]; float rightDiagDifference = Math.Abs(v0.Position.Y - vXY.Position.Y); float leftDiagDifference = Math.Abs(vX.Position.Y - vY.Position.Y); if (rightDiagDifference < leftDiagDifference) { tempIndices.Add(Math.Abs((x - VBXOffset) + (y - VBYOffset) * VertBuffSize) / VBufferRootScale); tempIndices.Add(Math.Abs(((x - VBXOffset) + stepSize) + ((y - VBYOffset) + stepSize) * VertBuffSize) / VBufferRootScale); tempIndices.Add(Math.Abs(((x - VBXOffset) + stepSize) + (y - VBYOffset) * VertBuffSize) / VBufferRootScale); tempIndices.Add(Math.Abs((x - VBXOffset) + ((y - VBYOffset) + stepSize) * VertBuffSize) / VBufferRootScale); tempIndices.Add(Math.Abs(((x - VBXOffset) + stepSize) + ((y - VBYOffset) + stepSize) * VertBuffSize) / VBufferRootScale); tempIndices.Add(Math.Abs(((x - VBXOffset)) + (y - VBYOffset) * VertBuffSize) / VBufferRootScale); } else { tempIndices.Add(Math.Abs((x - VBXOffset) + ((y - VBYOffset) + stepSize) * VertBuffSize) / VBufferRootScale); tempIndices.Add(Math.Abs(((x - VBXOffset) + stepSize) + (y - VBYOffset) * VertBuffSize) / VBufferRootScale); tempIndices.Add(Math.Abs((x - VBXOffset) + (y - VBYOffset) * VertBuffSize) / VBufferRootScale); tempIndices.Add(Math.Abs((x - VBXOffset) + ((y - VBYOffset) + stepSize) * VertBuffSize) / VBufferRootScale); tempIndices.Add(Math.Abs(((x - VBXOffset) + stepSize) + ((y - VBYOffset) + stepSize) * VertBuffSize) / VBufferRootScale); tempIndices.Add(Math.Abs(((x - VBXOffset) + stepSize) + (y - VBYOffset) * VertBuffSize) / VBufferRootScale); } } } } } } } ///////////////////////////////////////////////////////////////////// //Keep track of the number of indices. indices[stitching] = new int[tempIndices.Count]; if (numberOfIndices < tempIndices.Count) numberOfIndices = tempIndices.Count; indices[stitching] = tempIndices.ToArray(); } //////////////////////////////////////////////// //Create Nodes Bounding Box. boundBox = new BoundingBox(new Vector3((float)(xPosition) * terrainScale, minHeight, (float)(yPosition) * terrainScale), new Vector3((float)(xPosition + NodeScale) * terrainScale, maxHeight, (float)(yPosition + NodeScale) * terrainScale)); ///////////////////////////////////////////////////////////////////// //Set node Depth. NodeDepth = nodeDepth; }
/// <summary> /// Initialises a complete QuadTerrain. ///This is the Constructor method. As you might have guessed, it constructs a quad terrain. /// Quasar. /// </summary> /// <param name="factory">The factory.</param> /// <param name="heightMap">The Texture2D to use as a heightmap. Must have square dimensions of (2^n)+1, where n is an integer.</param> /// <param name="squareSize">The edge size of each individual LOD square. Lower values increase CPU Load, decrease GPU Load, and increase Loading times. Must be (2^n)+1, and larger than 5.</param> /// <param name="vertexBufferSize">The size of Vertex buffer to use. Lower Values increase the number of draw calls by splitting the terrain into several Vertex Buffers. Must be (2^n)+1, and larger than squareSize.</param> /// <param name="scale">The XZ scale to multiply the terrain by.</param> /// <param name="height">The Y scale to multiply the terrain by.</param> public QuadTerrain(GraphicFactory factory ,Texture2D heightMap, int squareSize, int vertexBufferSize, float scale, float height) { this.factory = factory; //I'm not entirely sure what this does, but it is used in updateTerrain. //I think it is used to prevent the Vertex Buffers being filled during the initialisation UpdateTerrain call. first = true; LODHeightImpact = 1.0f; //Set terrain width and height from heightmap. terrainHeight = heightMap.Height; terrainWidth = heightMap.Width; //Set some obvious public variables HeightMap = heightMap; SquareSize = squareSize; Scale = scale; HeightScale = height; VBsize = vertexBufferSize; //Copy the heightmap from a Texture to an array of colours... Color[] heightMapColors = new Color[terrainWidth * terrainHeight]; heightMap.GetData(heightMapColors); //Initialise the HeightStore and heightStore = new float[heightMap.Width, heightMap.Height]; normStore = new Vector3[heightMap.Width, heightMap.Height]; //this is the Normal texture for the entire terrain. It is used within the shader to prevent normal popup. normalTexture = factory.CreateTexture2D(heightMap.Width, heightMap.Height, true, SurfaceFormat.Color); Color[] normalData = new Color[heightMap.Width * heightMap.Height]; #region depricated /* int NodeDepth= 0; int NodeLevel = (int)Math.Pow(2, (NodeDepth)); int NodeScale = ((heightMap.Height - 1) / NodeLevel) + 1; int stepSize = (NodeScale - 1) / (squareSize - 1); */ //////////////////////////////////////////////////// // For: // SquareSize = 9 // Height = 257 // NodeDepth, NodeLevel, NodeScale, stepSize // 0, 1, 257, 64 // 1, 2, 129, 32 // 2, 4, 65, 16 // 3, 8, 33, 8 // 4, 16, 19, 4 // 5, 32, 9, 2 // 6, 64, 5, 1 //////////////////////////////////////////////////// //Get node depth: //int NodeScale = stepSize*(squareSize-1)+1; //int NodeLevel = (NodeScale - 1) * (heightMap.Height - 1); //int maxNodeDepth = (int)Math.Log(NodeLevel, 2); //int maxNodeDepth; #endregion //Determine Maximum Node Depth from Square Size and height of heightmap int NodeScale = 1 * (squareSize - 1) + 1; int NodeLevel = (heightMap.Height - 1) / (NodeScale - 1); maxNodeDepth = (int)Math.Log(NodeLevel, 2); //Work out number of vertex arrays needed: //Math.Pow(HeightMap.HEIGHT / 512, 2))+1; numberOfVBs = (int)Math.Pow((terrainHeight - 1) / (VBsize - 1), 2); sqrtNumberOfVBs = (int)Math.Sqrt(numberOfVBs); //Initialise the Array of Vertex Arrays, accompanying Array of Vertex Buffers and the Array of Integer Index Arrays. allVertices = new VertexPosition[numberOfVBs + 1][]; allVBs = new VertexBuffer[numberOfVBs + 1]; allIndices = new int[numberOfVBs + 1][]; //And as if that wasn't confusing enough... //Now I initialise each Array in the Array of Vertex Arrays, each corresponding Buffer in the Array of Vertex Buffers, //and each Integer Index Array in the Array of Integer Index Arrays. for (int i = 0; i < numberOfVBs + 1; i++) { allVertices[i] = new VertexPosition[(VBsize + 1) * (VBsize + 1)]; allVBs[i] = factory.CreateVertexBuffer(VertexPosition.VertexDeclaration, (VBsize + 1) * (VBsize + 1), BufferUsage.WriteOnly); allIndices[i] = new int[VBsize * VBsize * 6]; } //Array. //This list is used to sort the Quadnodes to be drawn by distance to save on Overdraw. qnl = new List<QuadNode>(); //Create a 2D array of floats from the heightmap colour array. for (int y = 0; y < terrainHeight; y++) { for (int x = 0; x < terrainWidth; x++) { //heightstore Array heightStore[x, y] = heightMapColors[x + y * terrainWidth].R; } } //Define the 'renderedIndices' array, which keeps track of the number of indices from each vertex buffer each frame. renderedindices = new int[numberOfVBs + 1]; ///////////////////////////////////////////////////// //Generate Vertex Positions and normals int PlusXVBIndex; int PlusYVBIndex; int PlusXYVBIndex; int PlusVBIndex; for (int y = 0; y < terrainHeight; y++) { for (int x = 0; x < terrainWidth; x++) { PlusXYVBIndex = (int)(Math.Floor(((float)(x + 1) / terrainWidth) * sqrtNumberOfVBs) + (Math.Floor(((float)(y + 1) / terrainWidth) * sqrtNumberOfVBs) * sqrtNumberOfVBs)); PlusXVBIndex = (int)(Math.Floor(((float)(x + 1) / terrainWidth) * sqrtNumberOfVBs) + (Math.Floor(((float)(y) / terrainWidth) * sqrtNumberOfVBs) * sqrtNumberOfVBs)); PlusYVBIndex = (int)(Math.Floor(((float)(x) / terrainWidth) * sqrtNumberOfVBs) + (Math.Floor(((float)(y + 1) / terrainWidth) * sqrtNumberOfVBs) * sqrtNumberOfVBs)); PlusVBIndex = (int)(Math.Floor(((float)(x) / terrainWidth) * sqrtNumberOfVBs) + (Math.Floor(((float)(y) / terrainWidth) * sqrtNumberOfVBs) * sqrtNumberOfVBs)); Vector3 normX = Vector3.Zero; Vector3 normY = Vector3.Zero; Vector3 normalVector = new Vector3(); if (x > 0 && y > 0 && x < terrainWidth - 1 && y < terrainHeight - 1) { normX = new Vector3((heightStore[x - 1, y] - heightStore[x + 1, y]) / 2 * height, 0, scale); normY = new Vector3(0, (heightStore[x, y - 1] - heightStore[x, y + 1]) / 2 * height, scale); normalVector = normX + normY; normalVector.Normalize(); Vector3 texVector = new Vector3(); texVector.X = (normalVector.X + 1) / 2f; texVector.Y = (normalVector.Y + 1) / 2f; texVector.Z = (normalVector.Z + 1) / 2f; normalData[x + y * terrainHeight] = new Color(texVector); //MessageBox.Show(normalVector.ToString() + " "+new Color(normalVector)); } else { normX = new Vector3(0, 0, scale); normY = new Vector3(0, 0, scale); normalVector = normX + normY; normalVector.Normalize(); Vector3 texVector = new Vector3(); texVector.X = (normalVector.X + 1) / 2f; texVector.Y = (normalVector.Y + 1) / 2f; texVector.Z = (normalVector.Z + 1) / 2f; normalData[x + y * terrainHeight] = new Color(texVector); } normStore[x, y] = normalVector; ///////////////////////////////////////////// //Fill Vertex Arrays //Foreach vertex array... for (int i = 0; i < numberOfVBs; i++) { //Vertex Buffers // 0, 1, 2, 3... What about root 0? // 4, 5, 6, 7 // 8, 9,10,11 //12,13,14,15 //Change to... // 1, 2, 3, 4... root=0 // 5, 6, 7, 8 // 9,10,11,12 //13,14,15,16 //This works to sort the VB's. //It works to align the x,y and VBi values: only if an x/y coord is in an i VB. Uses 1st VB chart. Just use i+1 /////////////////////////////////////////////////////////// //^^ If you understood the above comment, you're doing better than me. ^^ /////////////////////////////////////////////////////////// //This is the 'multiple vertex buffers' algorithm, which puts vertices into their own VBuffers. Anything involving //it won't be very well documented, because I can only vaguely remember writing it in the first place. //I'm not entirely sure what that means, but I suspect I was either drunk or asleep at the time. Possibly both. if (PlusXYVBIndex == i) { //allVertices[i+1][(x % VBsize) + (y % VBsize) * VBsize].Position = new Vector3(x * scale, heightStore[x, y] * height, -y * scale); if (x < terrainHeight - 1) { //MessageBox.Show(x + " " + y + " " + x % (VBsize - 1) + " " + y % (VBsize - 1) + " " + i); allVertices[i + 1][x % (VBsize - 1) + y % (VBsize - 1) * VBsize].Position = new Vector3(x * scale, heightStore[x, y] * height, y * scale); //allVertices[i + 1][x % (VBsize - 1) + y % (VBsize - 1) * VBsize].Normal = normalVector; //Add this vertex to VB i+1, at location (x % (VBsize-1) + y % (VBsize-1) * VBsize) } } else { //Bottom or left if (PlusVBIndex == i) { if (PlusYVBIndex == i) { //MessageBox.Show(x + "BotLeft&Y " + y + " " + (x % (VBsize - 1) + (VBsize - 1)) + " " + y % (VBsize - 1) + " " + i); allVertices[i + 1][(x % (VBsize - 1) + (VBsize - 1)) + (y % (VBsize - 1)) * VBsize].Position = new Vector3(x * scale, heightStore[x, y] * height, y * scale); //allVertices[i + 1][(x % (VBsize - 1) + (VBsize - 1)) + (y % (VBsize - 1)) * VBsize].Normal = normalVector; //Add this vertex to VB i+1, at location (x % (VBsize - 1) + (VBsize - 1)) + (y % (VBsize - 1)) * VBsize) } else { if (PlusXVBIndex == i) { //MessageBox.Show(x + "BotLeft&X " + y + " " + x % (VBsize - 1) + " " + (y % (VBsize - 1) + (VBsize - 1)) + " " + i); allVertices[i + 1][x % (VBsize - 1) + (y % (VBsize - 1) + (VBsize - 1)) * VBsize].Position = new Vector3(x * scale, heightStore[x, y] * height, y * scale); //allVertices[i + 1][x % (VBsize - 1) + (y % (VBsize - 1) + (VBsize - 1)) * VBsize].Normal = normalVector; //Add this vertex to VB i+1, at location (x % (VBsize - 1)) + (y % (VBsize - 1) + (VBsize - 1)) * VBsize) } else { //MessageBox.Show(x + "BotLeft " + y + " " + (x % (VBsize - 1) + (VBsize - 1)) + " " + (y % (VBsize - 1) + (VBsize - 1)) + " " + i); allVertices[i + 1][x % (VBsize - 1) + (VBsize - 1) + (y % (VBsize - 1) + (VBsize - 1)) * VBsize].Position = new Vector3(x * scale, heightStore[x, y] * height, y * scale); //allVertices[i + 1][x % (VBsize - 1) + (VBsize - 1) + (y % (VBsize - 1) + (VBsize - 1)) * VBsize].Normal = normalVector; //Add this vertex to VB i+1, at location (x % (VBsize - 1) + (VBsize - 1)) + (y % (VBsize - 1) + (VBsize - 1)) * VBsize) } } } else { //Corner Left if (PlusYVBIndex == i) { if (y < terrainHeight - 1) { //MessageBox.Show(x + "Left " + y + " " + (x % (VBsize - 1) + (VBsize - 1)) + " " + y % (VBsize - 1) + " " + i); allVertices[i + 1][(x % (VBsize - 1) + (VBsize - 1) + (y % (VBsize - 1)) * VBsize)].Position = new Vector3(x * scale, heightStore[x, y] * height, y * scale); //allVertices[i + 1][(x % (VBsize - 1) + (VBsize - 1) + (y % (VBsize - 1)) * VBsize)].Normal = normalVector; //Add this vertex to VB i+1, at location ((x % (VBsize - 1) + (VBsize - 1)) + (y % (VBsize - 1)) * VBsize) } } //Corner Bottom if (PlusXVBIndex == i) { if (x < terrainHeight - 1) { //MessageBox.Show(x + "Bot " + y + " " + x % (VBsize - 1) + " " + (y % (VBsize - 1) + (VBsize - 1)) + " " + i); allVertices[i + 1][(x % (VBsize - 1)) + ((y % (VBsize - 1) + (VBsize - 1)) * VBsize)].Position = new Vector3(x * scale, heightStore[x, y] * height, y * scale); //allVertices[i + 1][(x % (VBsize - 1)) + ((y % (VBsize - 1) + (VBsize - 1)) * VBsize)].Normal = normalVector; //Add this vertex to VB i+1, at location ((x % (VBsize - 1)) + ((y % (VBsize - 1) + (VBsize - 1)) * VBsize) } } } } if (i == 0) { int stepSize = (terrainWidth - 1) / (vertexBufferSize - 1); int numberOfSteps = (terrainWidth - 1) / stepSize; if (x % stepSize == 0 && y % stepSize == 0) { allVertices[0][(x / stepSize) + (y / stepSize) * VBsize].Position = new Vector3(x * scale, heightStore[x, y] * height, y * scale); //allVertices[0][(x / stepSize) + (y / stepSize) * VBsize].Normal = normalVector; //Add this vertex to VB 0, at location ((x/stepSize) + (y/stepSize) * VBsize) } } } } } //Generate the normal texture from the normal data generated a second ago. normalTexture.SetData<Color>(normalData); factory.MipMapTexture(ref normalTexture); //////////////////////////////////////////////////////////// //Add to vertex buffer for (int j = 0; j < numberOfVBs + 1; j++) { allVBs[j].SetData<VertexPosition>(allVertices[j]); } //////////////////////////////////////////////////////////// #region depricated // int[] nodeStatsArray = new int[6,6]; //for (i=HeightMap.Height-1;i> // rootNode = new QuadNode(HeightMap, SquareSize, 4, 0, 0); //NodeDepth Nodes Total Nodes //0 1 1 //1 4 5 //2 16 21 //3 64 85 //4 256 341 //5 1024 1365 //allQuadNodes = new List<QuadNode>(); //int NodeDepth = 0; //NodeLevel = (int)Math.Pow(2, (NodeDepth)); //NodeScale = ((heightMap.Height - 1) / NodeLevel) + 1; //int stepSize = (NodeScale - 1) / (squareSize - 1); //QuadNode qNode; //QuadNode[] parentNode = new QuadNode[1]; //int xPosition = 0; //int yPosition = 0; //qNode = new QuadNode(HeightMap, SquareSize, NodeDepth, xPosition, yPosition); //NodeDepth++; //while (xPosition < terrainWidth) //{ // while (yPosition < terrainHeight) // { // while (NodeDepth < maxNodeDepth - 5) // { // NodeLevel = (int)Math.Pow(2, (NodeDepth)); // NodeScale = ((heightMap.Height - 1) / NodeLevel) + 1; // qNode = new QuadNode(HeightMap, SquareSize, NodeDepth, xPosition, yPosition); // allQuadNodes.Add(qNode); // qNode = new QuadNode(HeightMap, SquareSize, NodeDepth, xPosition + NodeScale - 1, yPosition); // allQuadNodes.Add(qNode); // qNode = new QuadNode(HeightMap, SquareSize, NodeDepth, xPosition, yPosition + NodeScale - 1); // allQuadNodes.Add(qNode); // qNode = new QuadNode(HeightMap, SquareSize, NodeDepth, xPosition + NodeScale - 1, yPosition + NodeScale - 1); // allQuadNodes.Add(qNode); // parentNode[0] = qNode; // NodeDepth++; // } // NodeDepth--; // NodeLevel = (int)Math.Pow(2, (NodeDepth-1)); // NodeScale = ((heightMap.Height - 1) / NodeLevel) + 1; // //NodeDepth--; // yPosition += NodeScale - 1; // } // xPosition += NodeScale - 1; // yPosition = 0; //} //NodeDepth++; //int numQuadNodes = allQuadNodes.Count; //int[] indexBufferArray = new int[numQuadNodes * ((squareSize - 1) * (squareSize - 1) * 6)]; #endregion //Define Quadnode List allQuadNodes = new List<QuadNode>(); //Create RootNode RootNode = new QuadNode(HeightMap, normStore, SquareSize, VBsize, Scale, 0, 0, 0, null, allVertices); allQuadNodes.Add(RootNode); //////////////////////////////////////////////// //Creates all quadnodes in the entire quadtree // //This is a recursive function. It breaks the rootnode into 4 new quadnodes, then applies itself to each of //these children nodes, in turn breaking them. See it's definition for a more complete explanation. RecursiveCreateQuad(RootNode); //////////////////////////////////////////////// //Set Adjacent Nodes for each Quadnode, for stitching purposes. for (int i = 0; i < allQuadNodes.Count; i++) { if (allQuadNodes[i].NodeDepth > 0) { foreach (QuadNode qNode in allQuadNodes) { if (qNode.XPosition == allQuadNodes[i].XPosition + allQuadNodes[i].NodeScale - 1 && qNode.NodeDepth == allQuadNodes[i].NodeDepth && qNode.YPosition == allQuadNodes[i].YPosition) { allQuadNodes[i].adjacentNorthQuad = qNode; } if (qNode.YPosition == allQuadNodes[i].YPosition + allQuadNodes[i].NodeScale - 1 && qNode.NodeDepth == allQuadNodes[i].NodeDepth && qNode.XPosition == allQuadNodes[i].XPosition) { allQuadNodes[i].adjacentEastQuad = qNode; } if (qNode.XPosition == allQuadNodes[i].XPosition - allQuadNodes[i].NodeScale + 1 && qNode.NodeDepth == allQuadNodes[i].NodeDepth && qNode.YPosition == allQuadNodes[i].YPosition) { allQuadNodes[i].adjacentSouthQuad = qNode; } if (qNode.YPosition == allQuadNodes[i].YPosition - allQuadNodes[i].NodeScale + 1 && qNode.NodeDepth == allQuadNodes[i].NodeDepth && qNode.XPosition == allQuadNodes[i].XPosition) { allQuadNodes[i].adjacentWestQuad = qNode; } } } } /////////////////////////////////////////////// //Run an update on the terrain for it's initial state. See the update method. //I'm not entirely sure why this is necessary, but I'm sure there's a good reason. Matrix viewMatrix = Matrix.CreateLookAt(Vector3.Zero, Vector3.Forward, Vector3.Up); Matrix projectionMatrix = Matrix.CreatePerspectiveFieldOfView(MathHelper.ToRadians(45), 10f, 1, 100); BoundingFrustum startFrustrum = new BoundingFrustum(viewMatrix * projectionMatrix); #if DEBUG debugTimer = new Stopwatch(); #endif UpdateTerrain(new Vector3(), startFrustrum, 3.5f); allIBs = new DynamicIndexBuffer[allIndices.Length]; for (int l = 0; l < allIndices.Length; l++) { allIBs[l] = factory.CreateDynamicIndexBuffer(IndexElementSize.ThirtyTwoBits, (allVertices[l].Length), BufferUsage.WriteOnly); } }
/// <summary> /// This simple function compares two quadnode by distance. Used for sorting to save overdraw. /// </summary> /// <param name="x"></param> /// <param name="y"></param> /// <returns></returns> private static int compareQuadNodesByDistance(QuadNode x, QuadNode y) { int retval = x.distanceFromCamera.CompareTo(y.distanceFromCamera); if (retval < 0) { return -1; } else { if (retval > 0) { return 1; } else { return 0; } } }