Example #1
0
        protected override TerrainMeshData GenerateForLod(IntensityImage image, int downsample)
        {
            // Downsampling rate must be a power of 2.
            if (!MathUtils.IsPowerOfTwo(downsample))
            {
                throw new Exception($"Downsample rate of {downsample} is not a power of 2.");
            }

            // Calculate image bounds based on UV bounds.
            int imageStartX = Mathf.RoundToInt(_uvBounds.U1 * image.Width / downsample);
            int imageEndX   = Mathf.RoundToInt(_uvBounds.U2 * (image.Width / downsample - 1));
            int imageStartY = Mathf.RoundToInt(_uvBounds.V1 * image.Height / downsample);
            int imageEndY   = Mathf.RoundToInt(_uvBounds.V2 * (image.Height / downsample - 1));

            // Each pixel in the selected area of the downsampled image represents a vertex.
            int lonVertCount = imageEndX - imageStartX + 1;
            int latVertCount = imageEndY - imageStartY + 1;

            float latIncrement = _boundingBox.LatSwing / (latVertCount - 1);
            float lonIncrement = _boundingBox.LonSwing / (lonVertCount - 1);

            Vector3[] verts     = new Vector3[latVertCount * lonVertCount];
            Vector2[] uvs       = new Vector2[latVertCount * lonVertCount];
            Vector3[] edgeVerts = new Vector3[4 * (latVertCount + lonVertCount - 2) + 2];

            Vector2 latLongOffset = BoundingBoxUtils.MedianLatLon(_boundingBox);

            Vector3 min = new Vector3(float.PositiveInfinity, 0, 0);

            int yIndex = 0, vertexIndex = 0;

            for (float vy = _boundingBox.LatStart; yIndex < latVertCount; vy += latIncrement)
            {
                // The y-coordinate on the image that corresponds to the current row of vertices.
                // Note this is actually inverted since we are traversing from bottom up.
                int y = (latVertCount - yIndex - 1 + imageStartY) * downsample;

                // Create a new vertex using the latitude angle. The coordinates of this vertex
                // will serve as a base for all the other vertices of the same latitude.
                Vector3 baseLatVertex = GenerateBaseLatitudeVertex(vy);

                int xIndex = 0;
                for (float vx = _boundingBox.LonStart; xIndex < lonVertCount; vx += lonIncrement)
                {
                    // The x-coordinate on the image that corresponds to the current vertex.
                    int x = (xIndex + imageStartX) * downsample;

                    // Get the raw intensity value from the image.
                    float value = downsample == 1 ?
                                  image.GetPixel(x, y) :
                                  image.GetCenteredAverage(x, y, downsample + 1);

                    // Scale the intensity value by the height scale, and
                    // then add it to the radius to get the final "height".
                    float height = value * _metadata.HeightScale + _metadata.Radius;

                    Vector3 vertex = GenerateVertex(height * baseLatVertex, vx, latLongOffset, _metadata.Radius);

                    // Keep track of minimum; this will be used later to position the terrain on the table-top.
                    if (vertex.x < min.x)
                    {
                        min = vertex;
                    }

                    // Add to edge vertices
                    if (yIndex == 0)
                    {
                        edgeVerts[xIndex] = vertex;
                    }
                    else if (xIndex == lonVertCount - 1)
                    {
                        edgeVerts[lonVertCount + yIndex - 1] = vertex;
                    }
                    else if (yIndex == latVertCount - 1)
                    {
                        edgeVerts[2 * (lonVertCount - 1) + latVertCount - xIndex - 1] = vertex;
                    }
                    else if (xIndex == 0)
                    {
                        edgeVerts[2 * (lonVertCount + latVertCount - 2) - yIndex] = vertex;
                    }

                    verts[vertexIndex] = vertex;
                    uvs[vertexIndex]   = GenerateUVCoord(xIndex, yIndex, lonVertCount, latVertCount, _uvBounds);

                    xIndex++;
                    vertexIndex++;
                }

                yIndex++;
            }

            // Finish generating the data for the terrain edge.
            ProcessEdgeVertices(edgeVerts, min.x);

            return(new TerrainMeshData()
            {
                Vertices = verts,
                TexCoords = uvs,
                Triangles = GenerateTriangles(lonVertCount, latVertCount),
                ExtraVertices = edgeVerts,
                ExtraTriangles = GenerateTriangles(edgeVerts.Length / 2, 2, true),
                MinimumVertex = min
            });
        }
Example #2
0
        protected override TerrainMeshData GenerateForLod(IntensityImage image, int downsample = 1)
        {
            // Downsampling rate must be a power of 2.
            if (!MathUtils.IsPowerOfTwo(downsample))
            {
                throw new Exception($"Downsample rate of {downsample} is not a power of 2.");
            }

            // Vertex count for the latitude is the same as the downsampled texture height.
            // However, we need to generate an extra set of vertices in the longitude
            // direction to complete the loop around. We cannot simply reuse the first
            // vertices of of the loop, due to the start and end having different UV
            // coordinates despite having same world coordinates.
            int lonVertCount = image.Width / downsample + 1;
            int latVertCount = image.Height / downsample;

            Debug.Log(lonVertCount + ", " + latVertCount);

            Vector3[] verts = new Vector3[lonVertCount * latVertCount];
            Vector2[] uvs   = new Vector2[lonVertCount * latVertCount];

            // Vertex counter
            int vertexIndex = 0;

            // Calculate the incretmental step sizes of the latitude
            // and longitude here for potential performance increase.
            float latStepSize = Mathf.PI / (latVertCount - 1);
            float lonStepSize = 360.0f / (lonVertCount - 1);

            // Iterate through the rows of vertices.
            for (int vy = 0; vy < latVertCount; vy++)
            {
                // The y-coordinate on the image that corresponds to the current row of vertices.
                int y = vy * downsample;

                // Iterate through each vertex in the row of verticies.
                // Calculate the actual angle of the latitude.
                float latAng = latStepSize * vy + Mathf.PI / 2;

                // Create a new vertex using the latitude angle. The coordinates of this
                // vertex will serve as a base for all the other vertices in this latitude.
                Vector3 baseLatVertex = new Vector3(Mathf.Cos(latAng), Mathf.Sin(latAng), 0);

                // Iterate through each vertex in the row of verticies.
                // Traverse backwards in order to get correct orientation of texture and normals.
                for (int vx = lonVertCount - 1; vx >= 0; vx--)
                {
                    // The x-coordinate on the image that corresponds to the current vertex.
                    int x = vx * downsample;

                    // Get the raw intensity value from the image.
                    float value = downsample == 1 ?
                                  image.GetPixel(x, y) :
                                  image.GetCenteredAverage(x, y, downsample + 1);

                    // Scale the intensity value by the height scale, and
                    // then add it to the radius to get the final "height".
                    float height = value * _metadata.HeightScale + _metadata.Radius;

                    // Longitude is offset by 90 degrees so that the foward vector is at 0,0 lat and long.
                    verts[vertexIndex] = Quaternion.Euler(0, -90 - vx * lonStepSize, 0) * (height * baseLatVertex);
                    uvs[vertexIndex]   = MeshGenerationUtils.GenerateUVCoord(vx, vy, lonVertCount, latVertCount);
                    vertexIndex++;
                }
            }

            return(new TerrainMeshData()
            {
                Vertices = verts,
                TexCoords = uvs,
                Triangles = MeshGenerationUtils.GenerateTriangles(lonVertCount, latVertCount)
            });
        }
Example #3
0
        public static IntensityImage ToIntensityImage(TiffImage tiff)
        {
            TiffMetadata metadata = tiff.Metadata;

            // Check image format.
            if (metadata.SamplesPerPixel != 1)
            {
                throw new Exception("Color source cannot be converted into intensity image. Use ToRGBAImage() instead.");
            }

            // Create an Image object to store the result.
            IntensityImage result = new IntensityImage(metadata.Width, metadata.Height);

            // Tiled encoding...
            if (metadata.Tiled)
            {
                Vector2Int tilesAcrossImage = new Vector2Int(
                    Mathf.CeilToInt(metadata.Width / (float)metadata.TileWidth),
                    Mathf.CeilToInt(metadata.Height / (float)metadata.TileHeight)
                    );

                // Byte array for buffering the bytes read from each tile.
                byte[] tileBytes = new byte[metadata.TileSize];

                // Float array for buffering the intensity values of each tile.
                float[] values = new float[metadata.TileWidth * metadata.TileHeight];

                // Iterate through each tile.
                for (int ty = 0; ty < tilesAcrossImage.y; ty++)
                {
                    // The y-coordinate of the tile's top row of pixels on the image.
                    int y = ty * metadata.TileHeight;

                    for (int tx = 0; tx < tilesAcrossImage.x; tx++)
                    {
                        // The x-coordinate of the tile's left column of pixels on the image.
                        int x = tx * metadata.TileWidth;

                        // Read bytes from tile and convert them to pixel values.
                        tiff.ReadTile(tileBytes, 0, x, y, 0, 0);

                        TiffUtils.BytesToFloat(tileBytes, values);

                        // Iterate through the intensity values in the tile.
                        for (int i = 0; i < values.Length; i++)
                        {
                            // Calculate the x and y coordinate relative to the tile
                            // based on the index of the pixel within the tile.
                            Vector2Int tilePixel = ImageUtils.IndexToCoordinates(i, metadata.TileWidth);

                            // Update the Image object with the pixel value.
                            result.SetPixel(tilePixel.x + x, tilePixel.y + y, values[i]);
                        }
                    }
                }
            }

            // Scanline encoding...
            else
            {
                // Byte array for buffering the bytes read from each scanline.
                byte[] scanlineBytes = new byte[metadata.ScanlineSize];

                // Float array for buffering the intensity values of each pixels in a scanline.
                float[] values = new float[metadata.Width];

                // Iterate through all the scanlines.
                for (int y = 0; y < metadata.Height; y++)
                {
                    // Read bytes from scanline and convert them to pixel values.
                    tiff.ReadScanline(scanlineBytes, y);
                    TiffUtils.BytesToFloat(scanlineBytes, values);

                    // Iterate through all the pixel values in the scanline.
                    for (int x = 0; x < metadata.Width; x++)
                    {
                        // Update the Image object with the pixel value.
                        result.SetPixel(x, y, values[x]);
                    }
                }
            }

            return(result);
        }
 /*
  * TODO Modify the implementations of the function such that
  * they generate all the LODs at the same time, instead of
  * having to read the image data once for each LOD.
  */
 protected abstract TerrainMeshData GenerateForLod(IntensityImage image, int downsample);