/// <summary>
        /// Fully build a terrain node - used for root nodes
        /// </summary>
        /// <param name="item">Contains data to manage building the node</param>
        public static void BuildNode(TerrainNodeSplitItem item)
        {
            //// preserve current depth buffer and detach it from the device
              //DepthStencilBuffer previousDepth = game.GraphicsDevice.DepthStencilBuffer;
              //game.GraphicsDevice.DepthStencilBuffer = null;

              ///// geometry map /////
              GenerateGeometryMap(item);
              item.GeometrySurface.ResolveHeightData();
              item.CopyHeightData(ref item.GeometrySurface.HeightData);

              if (!item.IsSphere)
              {
            ///// height map /////
            GenerateHeightmap(item);

            ///// normal texture /////
            GenerateNormalTexture(item);

            ///// diffuse texture /////
            GenerateDiffuseTexture(item);
              }

              //// re-attach the normal depth buffer
              //game.GraphicsDevice.DepthStencilBuffer = previousDepth;
        }
 public static void QueueTerrainNodeSplit(TerrainNodeSplitItem item)
 {
     lock (splitQueue)
       {
     splitQueue.Enqueue(item);
     dataAvailableEvent.Set();
       }
 }
        /// <summary>
        /// Construct a terrain node instance from a pre-generated geometry map
        /// </summary>
        /// <param name="item"></param>
        /// <param name="level"></param>
        /// <param name="radius"></param>
        /// <param name="createTerrainNodeVertexBuffer"></param>
        public TerrainNode(TerrainNodeSplitItem item, int level, double radius, CreateTerrainNodeVertexBufferDelegate createTerrainNodeVertexBuffer, bool isSphere)
        {
            this.bounds = item.Bounds;
              this.level = level;
              this.radius = radius;
              this.createPosition = null;
              this.createTerrainNodeVertexBuffer = createTerrainNodeVertexBuffer;
              this.isSphere = isSphere;

              this.diffuseSurface = item.DiffuseSurface;
              this.normalSurface = item.NormalSurface;

              Initialize(item);
        }
        public void Save(TerrainNodeSplitItem item)
        {
            float lowest = 999999;
              float highest = -9999999;

              // normalize heights
              for (int i = 0; i < width * height; i++)
              {
            if (HeightData[i] > highest) highest = HeightData[i];
            if (HeightData[i] < lowest) lowest = HeightData[i];
              }

              float range = highest - lowest;

              highest = 100;

              Color[] data = new Color[width * height];

              for (int i = 0; i < width * height; i++)
              {
            // float c = (heightMap[i] - lowest) / range;     // map lowest value to 0
            float c = HeightData[i] / highest;                 // negative values will show as black, below sea level
            data[i] = new Color(c, c, c);
              }

            //#if !XBOX
            //      using (FileStream stream = new FileStream(String.Format(@"screenshots\height_{0}_{1}.png", Globals.NormalMapNumber++, item.ChildIndex), FileMode.Create))
            //      {
            //        Texture2D tex = new Texture2D(device, width, height, false, SurfaceFormat.Color);
            //        tex.SetData<Color>(data);
            //        tex.SaveAsPng(stream, tex.Width, tex.Height);
            //      }
            //#endif
        }
Exemple #5
0
        private TerrainNode CreateRootTerrainNode(Face face)
        {
            // generate geometry map
              TerrainNodeSplitItem item = new TerrainNodeSplitItem(null, 0, new TerrainNodeBounds(-1.0, -1.0, 2.0, 2.0, TerrainNodeBounds.NodeQuadrant.None, face, 0), isSphere);
              TerrainNodeSplitManager.BuildNode(item);

              // build terrain node using geometry map
              return new TerrainNode(item, 0, radius, createTerrainNodeVertexBuffer, isSphere);
        }
        /// <summary>
        /// Initialize the terrain node
        /// </summary>
        private void Initialize(TerrainNodeSplitItem item)
        {
            geometricError = Constants.ErrorFactor * (float)Math.Pow(0.5, level + 1);
              geometricError *= (float)(radius / Constants.EarthRadius);    // TODO : adjust error based on earth-sized planet settings

              CalculatePatchPosition(item);
              GenerateMesh(item);
        }
        /// <summary>
        /// Calculate the sphere-space position of the patch by determining where the center vertex lies on the sphere
        /// </summary>
        private void CalculatePatchPosition(TerrainNodeSplitItem item)
        {
            // find the cube position of the center vertex
              double x = bounds.Left + bounds.Width / 2;
              double y = bounds.Bottom + bounds.Height / 2;

              // get the matrix used to rotate the a position to the appropriate cube face
              Matrix faceMatrix = CubeFaces.GetFace(bounds.Face).FaceMatrix;

              // create the vertex position in cube-space and rotate it to the appropriate face
              Position3 cubePosition = new Position3(x, y, 1);
              cubePosition.Transform(ref faceMatrix);

              // get the vertex position on the unit sphere and rotate it to the appropriate face
              Position3 spherePosition = Tools.CubeToSphereMapping(x, y, 1);
              spherePosition.Transform(ref faceMatrix);
              spherePosition.Normalize();

              Vector2 patchPosition = new Vector2(Constants.PatchWidth / 2, Constants.PatchHeight / 2);

              if (item == null)
              {
            // transform the vertex based on the user defined delegate - this returns the height value of the point on the sphere
            createPosition(ref spherePosition, ref cubePosition, ref patchPosition, radius, out position);
              }
              else
              {
            double h = item.HeightData[(int)((patchPosition.Y + 1) * (Constants.PatchWidth + 2) + (patchPosition.X + 1))];
            if (h < 0) h = 0;
            position = spherePosition * (radius + h);
              }
        }
        /// <summary>
        /// Generate terrain mesh for this node
        /// </summary>
        public void GenerateMesh(TerrainNodeSplitItem item)
        {
            // the minus one here is correct: e.g. 3x3 vertices labeled 0, 1, 2: v0.x = 0, v1.x = 0.5, v2.x = 1.  The increment = 1 / (3 - 1) = 0.5
              double horizontalStep = bounds.Width / (Constants.PatchWidth - 1);
              double verticalStep = bounds.Height / (Constants.PatchHeight - 1);
              float horizontalTextureStep = 1.0f / (Constants.PatchWidth - 1);
              float verticalTextureStep = 1.0f / (Constants.PatchHeight - 1);

              float MinX, MinY, MinZ;
              float MaxX, MaxY, MaxZ;

              // initialize min and max vertex tracking
              MinX = MinY = MinZ = 999999;
              MaxX = MaxY = MaxZ = -999999;

              // get matrix used to transform vertices to the proper cube face
              Matrix faceMatrix = CubeFaces.GetFace(bounds.Face).FaceMatrix;

              // create vertex storage using user supplied delegate
              vertexBuffer = createTerrainNodeVertexBuffer(item.HeightData.Length);

              //int cubeVertexIndex = 0;
              //CubeVertices = new Vector2[item.HeightData.Length];

              hasMeshBorder = false;
              int vertexIndex = 0;
              int Rows = Constants.PatchHeight;
              int Columns = Constants.PatchWidth;

              if (item.HeightData.Length > Constants.PatchWidth * Constants.PatchHeight)
              {
            hasMeshBorder = true;
            Rows += 2;
            Columns += 2;
              }

              patchRows = Rows;
              patchColumns = Columns;

              float v = 0;
              double y = bounds.Bottom;

              if (hasMeshBorder)
              {
            v -= verticalTextureStep;
            y -= verticalStep;
              }

              for (int hy = 0; hy < Rows; hy++)
              {
            float u = 0;
            double x = bounds.Left;

            if (hasMeshBorder)
            {
              u -= horizontalTextureStep;
              x -= horizontalStep;
            }

            for (int hx = 0; hx < Columns; hx++)
            {
              // create the vertex position and rotate it to the appropriate face
              Position3 cubePosition = new Position3(x, y, 1);

              Position3 spherePosition = Tools.CubeToSphereMapping(x, y, 1);
              spherePosition.Transform(ref faceMatrix);
              cubePosition.Transform(ref faceMatrix);

              // transform the vertex based on the user defined delegate
              double height;
              Position3 finalPosition;
              Vector2 patchPosition = new Vector2(hx, hy);

              if (item == null)
            CreatePosition(ref spherePosition, ref cubePosition, ref patchPosition, out finalPosition, out height);
              else
              {
            height = item.HeightData[hy * Columns + hx];
            if (height < 0) height = 0;
            finalPosition = spherePosition * (radius + height);
            TranslateToPatchSpace(ref finalPosition);
              }

              VertexPositionNormalTextureHeight vertex = new VertexPositionNormalTextureHeight();

              vertex.Position = (Vector3)finalPosition;
              vertex.Height = (float)height;
              vertex.Normal = (Vector3)spherePosition;
              vertex.TextureCoordinate = new Vector2(u, v);
              vertex.Tangent = Vector4.Zero;
              vertexBuffer.Vertices[vertexIndex++] = vertex;

              // track min and max coordinates, but only  for the vertices that will be in the final mesh
              if (hx >= 1 && hx < Columns - 1 && hy >= 1 && hy < Rows - 1)
              {
            if (vertex.Position.X < MinX) MinX = vertex.Position.X;
            if (vertex.Position.Y < MinY) MinY = vertex.Position.Y;
            if (vertex.Position.Z < MinZ) MinZ = vertex.Position.Z;

            if (vertex.Position.X > MaxX) MaxX = vertex.Position.X;
            if (vertex.Position.Y > MaxY) MaxY = vertex.Position.Y;
            if (vertex.Position.Z > MaxZ) MaxZ = vertex.Position.Z;
              }

              x += horizontalStep;
              u += horizontalTextureStep;
            }

            y += verticalStep;
            v += verticalTextureStep;
              }

              // create min and max bounding vertices, in patch-space
              minVertex = new Vector3(MinX, MinY, MinZ);
              maxVertex = new Vector3(MaxX, MaxY, MaxZ);

              // calculate normals and tangents
              CalculateNormals();
              CalculateTangents();

              // save vertex buffer changes - this effectively copies the raw data to a vertex buffer on the device
              vertexBuffer.CommitChanges(Constants.PatchWidth, Constants.PatchHeight);
        }
        /// <summary>
        /// Executed inside a worker thread to handle generating a single child node
        /// </summary>
        /// <param name="item"></param>
        public void GenerateChild(TerrainNodeSplitItem item)
        {
            if (!CancelSplitting)
              {
            if (workingChildren == null)
              workingChildren = new TerrainNode[4];

            workingChildren[item.ChildIndex] = new TerrainNode(item, level + 1, radius, createTerrainNodeVertexBuffer, item.IsSphere);
              }

              // if this was the last child then we're done!
              if (item.ChildIndex == 3)
              {
            if (!CancelSplitting)
            {
              // transfer working children over to actual children
              if (children == null)
            children = new TerrainNode[4];

              for (int i = 0; i < 4; i++)
            children[i] = workingChildren[i];

              ClearWorkingChildren();
            }
            else
            {
              ClearWorkingChildren();
            }

            CancelSplitting = false;
            Splitting = false;
              }
        }
        private static void GenerateNormalTexture(TerrainNodeSplitItem item)
        {
            if (!Constants.DisableNormalMapGeneration)
              {
            // get normal surface from pool
            item.NormalSurface = normalSurfacePool.New();

            // generate normal map based on heightmap
            normalGenerator.Execute(item.HeightmapSurface, item.NormalSurface, item.Bounds.Level);
              }
        }
        private static void GenerateHeightmap(TerrainNodeSplitItem item)
        {
            if (Constants.ForceHeightmapGeneration || !Constants.DisableDiffuseTextureGeneration || !Constants.DisableNormalMapGeneration)
              {
            // get surface from pool
            item.HeightmapSurface = heightmapSurfacePool.New();

            // generate the heightmap on the gpu
            heightmapGenerator.Execute(item.HeightmapSurface, item.Bounds);
              }
        }
        private static void GenerateGeometryMap(TerrainNodeSplitItem item)
        {
            // get surface from pool
              item.GeometrySurface = geometrySurfacePool.New();

              // generate the geometry map on the gpu
              if (item.IsSphere)
            terrainNodeSphereGenerator.Execute(item.GeometrySurface, item.Bounds);
              else
            terrainNodeGenerator.Execute(item.GeometrySurface, item.Bounds);
        }
        private static void GenerateDiffuseTexture(TerrainNodeSplitItem item)
        {
            if (!Constants.DisableDiffuseTextureGeneration)
              {
            // get diffuse surface from pool
            item.DiffuseSurface = textureSurfacePool.New();

            // generate diffuse texture based on heightmap
            textureGenerator.Execute(item.HeightmapSurface, item.NormalSurface, item.DiffuseSurface, 1.0f / 135.0f); //  135.0f);
              }
        }
 public static void QueueNodeSplit(TerrainNodeSplitItem item)
 {
     geometryQueue.Enqueue(item);
 }