public Bitmap CreateMapTile(Viewport viewport, bool useTextures)
        {
            m_colors.Clear();

            int width  = viewport.Width;
            int height = viewport.Height;

            if (m_useAntiAliasing)
            {
                width  *= 2;
                height *= 2;
            }

            WarpRenderer renderer = new WarpRenderer();

            renderer.CreateScene(width, height);
            renderer.Scene.autoCalcNormals = false;

            #region Camera

            warp_Vector pos = ConvertVector(viewport.Position);
            pos.z -= 0.001f; // Works around an issue with the Warp3D camera
            warp_Vector lookat = warp_Vector.add(ConvertVector(viewport.Position), ConvertVector(viewport.LookDirection));

            renderer.Scene.defaultCamera.setPos(pos);
            renderer.Scene.defaultCamera.lookAt(lookat);

            if (viewport.Orthographic)
            {
                renderer.Scene.defaultCamera.isOrthographic  = true;
                renderer.Scene.defaultCamera.orthoViewWidth  = viewport.OrthoWindowWidth;
                renderer.Scene.defaultCamera.orthoViewHeight = viewport.OrthoWindowHeight;
            }
            else
            {
                float fov = viewport.FieldOfView;
                fov *= 1.75f; // FIXME: ???
                renderer.Scene.defaultCamera.setFov(fov);
            }

            #endregion Camera

            renderer.Scene.addLight("Light1", new warp_Light(new warp_Vector(1.0f, 0.5f, 1f), 0xffffff, 0, 320, 40));
            renderer.Scene.addLight("Light2", new warp_Light(new warp_Vector(-1f, -1f, 1f), 0xffffff, 0, 100, 40));

            CreateWater(renderer);
            CreateTerrain(renderer, m_textureTerrain);
            if (m_drawPrimVolume)
            {
                CreateAllPrims(renderer, useTextures);
            }

            renderer.Render();
            Bitmap bitmap = renderer.Scene.getImage();

            if (m_useAntiAliasing)
            {
                using (Bitmap origBitmap = bitmap)
                {
                    bitmap = ImageUtils.ResizeImage(origBitmap, viewport.Width, viewport.Height);
                    origBitmap.Dispose();
                }
            }

            // XXX: It shouldn't really be necesary to force a GC here as one should occur anyway pretty shortly
            // afterwards.  It's generally regarded as a bad idea to manually GC.  If Warp3D is using lots of memory
            // then this may be some issue with the Warp3D code itself, though it's also quite possible that generating
            // this map tile simply takes a lot of memory.
            foreach (var o in renderer.Scene.objectData.Values)
            {
                warp_Object obj = (warp_Object)o;
                obj.vertexData   = null;
                obj.triangleData = null;
            }

            renderer.Scene.removeAllObjects();
            renderer = null;
            viewport = null;

            m_colors.Clear();
            GC.Collect();
            m_log.Debug("[WARP 3D IMAGE MODULE]: GC.Collect()");

            return(bitmap);
        }
Пример #2
0
        /// <summary>
        /// Builds a composited terrain texture given the region texture
        /// and heightmap settings
        /// </summary>
        /// <param name="terrain">Terrain heightmap</param>
        /// <param name="regionInfo">Region information including terrain texture parameters</param>
        /// <returns>A 256x256 square RGB texture ready for rendering</returns>
        /// <remarks>Based on the algorithm described at http://opensimulator.org/wiki/Terrain_Splatting
        /// Note we create a 256x256 dimension texture even if the actual terrain is larger.
        /// </remarks>

        public static Bitmap Splat(ITerrainChannel terrain,
                                   UUID[] textureIDs, float[] startHeights, float[] heightRanges,
                                   Vector3d regionPosition, IAssetService assetService, bool textureTerrain)
        {
            Debug.Assert(textureIDs.Length == 4);
            Debug.Assert(startHeights.Length == 4);
            Debug.Assert(heightRanges.Length == 4);

            Bitmap[] detailTexture = new Bitmap[4];

            if (textureTerrain)
            {
                // Swap empty terrain textureIDs with default IDs
                for (int i = 0; i < textureIDs.Length; i++)
                {
                    if (textureIDs[i] == UUID.Zero)
                    {
                        textureIDs[i] = DEFAULT_TERRAIN_DETAIL[i];
                    }
                }

                #region Texture Fetching

                if (assetService != null)
                {
                    for (int i = 0; i < 4; i++)
                    {
                        AssetBase asset;
                        UUID      cacheID = UUID.Combine(TERRAIN_CACHE_MAGIC, textureIDs[i]);

                        // Try to fetch a cached copy of the decoded/resized version of this texture
                        asset = assetService.GetCached(cacheID.ToString());
                        if (asset != null)
                        {
                            try
                            {
                                using (System.IO.MemoryStream stream = new System.IO.MemoryStream(asset.Data))
                                    detailTexture[i] = (Bitmap)Image.FromStream(stream);
                            }
                            catch (Exception ex)
                            {
                                m_log.Warn("Failed to decode cached terrain texture " + cacheID +
                                           " (textureID: " + textureIDs[i] + "): " + ex.Message);
                            }
                        }

                        if (detailTexture[i] == null)
                        {
                            // Try to fetch the original JPEG2000 texture, resize if needed, and cache as PNG
                            asset = assetService.Get(textureIDs[i].ToString());
                            if (asset != null)
                            {
                                //                                    m_log.DebugFormat(
                                //                                        "[TERRAIN SPLAT]: Got cached original JPEG2000 terrain texture {0} {1}", i, asset.ID);

                                try { detailTexture[i] = (Bitmap)CSJ2K.J2kImage.FromBytes(asset.Data); }
                                catch (Exception ex)
                                {
                                    m_log.Warn("Failed to decode terrain texture " + asset.ID + ": " + ex.Message);
                                }
                            }

                            if (detailTexture[i] != null)
                            {
                                // Make sure this texture is the correct size, otherwise resize
                                if (detailTexture[i].Width != 256 || detailTexture[i].Height != 256)
                                {
                                    using (Bitmap origBitmap = detailTexture[i])
                                    {
                                        detailTexture[i] = ImageUtils.ResizeImage(origBitmap, 256, 256);
                                    }
                                }

                                // Save the decoded and resized texture to the cache
                                byte[] data;
                                using (System.IO.MemoryStream stream = new System.IO.MemoryStream())
                                {
                                    detailTexture[i].Save(stream, ImageFormat.Png);
                                    data = stream.ToArray();
                                }

                                // Cache a PNG copy of this terrain texture
                                AssetBase newAsset = new AssetBase
                                {
                                    Data        = data,
                                    Description = "PNG",
                                    Flags       = AssetFlags.Collectable,
                                    FullID      = cacheID,
                                    ID          = cacheID.ToString(),
                                    Local       = true,
                                    Name        = String.Empty,
                                    Temporary   = true,
                                    Type        = (sbyte)AssetType.Unknown
                                };
                                newAsset.Metadata.ContentType = "image/png";
                                assetService.Store(newAsset);
                            }
                        }
                    }
                }

                #endregion Texture Fetching
            }

            // Fill in any missing textures with a solid color
            for (int i = 0; i < 4; i++)
            {
                if (detailTexture[i] == null)
                {
                    m_log.DebugFormat("{0} Missing terrain texture for layer {1}. Filling with solid default color",
                                      LogHeader, i);
                    // Create a solid color texture for this layer
                    detailTexture[i] = new Bitmap(256, 256, PixelFormat.Format24bppRgb);
                    using (Graphics gfx = Graphics.FromImage(detailTexture[i]))
                    {
                        using (SolidBrush brush = new SolidBrush(DEFAULT_TERRAIN_COLOR[i]))
                            gfx.FillRectangle(brush, 0, 0, 256, 256);
                    }
                }
                else
                {
                    if (detailTexture[i].Width != 256 || detailTexture[i].Height != 256)
                    {
                        detailTexture[i] = ResizeBitmap(detailTexture[i], 256, 256);
                    }
                }
            }

            #region Layer Map

            float[,] layermap = new float[256, 256];

            // Scale difference between actual region size and the 256 texture being created
            int xFactor = terrain.Width / 256;
            int yFactor = terrain.Height / 256;

            // Create 'layermap' where each value is the fractional layer number to place
            //    at that point. For instance, a value of 1.345 gives the blending of
            //    layer 1 and layer 2 for that point.
            for (int y = 0; y < 256; y++)
            {
                for (int x = 0; x < 256; x++)
                {
                    float height = (float)terrain[x * xFactor, y *yFactor];

                    float pctX = (float)x / 255f;
                    float pctY = (float)y / 255f;

                    // Use bilinear interpolation between the four corners of start height and
                    // height range to select the current values at this position
                    float startHeight = ImageUtils.Bilinear(
                        startHeights[0],
                        startHeights[2],
                        startHeights[1],
                        startHeights[3],
                        pctX, pctY);
                    startHeight = Utils.Clamp(startHeight, 0f, 255f);

                    float heightRange = ImageUtils.Bilinear(
                        heightRanges[0],
                        heightRanges[2],
                        heightRanges[1],
                        heightRanges[3],
                        pctX, pctY);
                    heightRange = Utils.Clamp(heightRange, 0f, 255f);

                    // Generate two frequencies of perlin noise based on our global position
                    // The magic values were taken from http://opensimulator.org/wiki/Terrain_Splatting
                    Vector3 vec = new Vector3
                                  (
                        ((float)regionPosition.X + (x * xFactor)) * 0.20319f,
                        ((float)regionPosition.Y + (y * yFactor)) * 0.20319f,
                        height * 0.25f
                                  );

                    float lowFreq  = Perlin.noise2(vec.X * 0.222222f, vec.Y * 0.222222f) * 6.5f;
                    float highFreq = Perlin.turbulence2(vec.X, vec.Y, 2f) * 2.25f;
                    float noise    = (lowFreq + highFreq) * 2f;

                    // Combine the current height, generated noise, start height, and height range parameters, then scale all of it
                    float layer = ((height + noise - startHeight) / heightRange) * 4f;
                    if (Single.IsNaN(layer))
                    {
                        layer = 0f;
                    }
                    layermap[x, y] = Utils.Clamp(layer, 0f, 3f);
                }
            }

            #endregion Layer Map

            #region Texture Compositing

            Bitmap     output     = new Bitmap(256, 256, PixelFormat.Format24bppRgb);
            BitmapData outputData = output.LockBits(new Rectangle(0, 0, 256, 256), ImageLockMode.WriteOnly, PixelFormat.Format24bppRgb);

            // Unsafe work as we lock down the source textures for quicker access and access the
            //    pixel data directly
            unsafe
            {
                // Get handles to all of the texture data arrays
                BitmapData[] datas = new BitmapData[]
                {
                    detailTexture[0].LockBits(new Rectangle(0, 0, 256, 256), ImageLockMode.ReadOnly, detailTexture[0].PixelFormat),
                    detailTexture[1].LockBits(new Rectangle(0, 0, 256, 256), ImageLockMode.ReadOnly, detailTexture[1].PixelFormat),
                    detailTexture[2].LockBits(new Rectangle(0, 0, 256, 256), ImageLockMode.ReadOnly, detailTexture[2].PixelFormat),
                    detailTexture[3].LockBits(new Rectangle(0, 0, 256, 256), ImageLockMode.ReadOnly, detailTexture[3].PixelFormat)
                };

                // Compute size of each pixel data (used to address into the pixel data array)
                int[] comps = new int[]
                {
                    (datas[0].PixelFormat == PixelFormat.Format32bppArgb) ? 4 : 3,
                    (datas[1].PixelFormat == PixelFormat.Format32bppArgb) ? 4 : 3,
                    (datas[2].PixelFormat == PixelFormat.Format32bppArgb) ? 4 : 3,
                    (datas[3].PixelFormat == PixelFormat.Format32bppArgb) ? 4 : 3
                };

                for (int y = 0; y < 256; y++)
                {
                    for (int x = 0; x < 256; x++)
                    {
                        float layer = layermap[x, y];

                        // Select two textures
                        int l0 = (int)Math.Floor(layer);
                        int l1 = Math.Min(l0 + 1, 3);

                        byte *ptrA = (byte *)datas[l0].Scan0 + y * datas[l0].Stride + x * comps[l0];
                        byte *ptrB = (byte *)datas[l1].Scan0 + y * datas[l1].Stride + x * comps[l1];
                        byte *ptrO = (byte *)outputData.Scan0 + y * outputData.Stride + x * 3;

                        float aB = *(ptrA + 0);
                        float aG = *(ptrA + 1);
                        float aR = *(ptrA + 2);

                        float bB = *(ptrB + 0);
                        float bG = *(ptrB + 1);
                        float bR = *(ptrB + 2);

                        float layerDiff = layer - l0;

                        // Interpolate between the two selected textures
                        *(ptrO + 0) = (byte)Math.Floor(aB + layerDiff * (bB - aB));
                        *(ptrO + 1) = (byte)Math.Floor(aG + layerDiff * (bG - aG));
                        *(ptrO + 2) = (byte)Math.Floor(aR + layerDiff * (bR - aR));
                    }
                }

                for (int i = 0; i < detailTexture.Length; i++)
                {
                    detailTexture[i].UnlockBits(datas[i]);
                }
            }

            for (int i = 0; i < detailTexture.Length; i++)
            {
                if (detailTexture[i] != null)
                {
                    detailTexture[i].Dispose();
                }
            }

            output.UnlockBits(outputData);

            // We generated the texture upside down, so flip it
            output.RotateFlip(RotateFlipType.RotateNoneFlipY);

            #endregion Texture Compositing

            return(output);
        }