コード例 #1
0
        public unsafe Bitmap TerrainToBitmap(Bitmap mapbmp)
        {
            BitmapData originalData = mapbmp.LockBits(
                new Rectangle(0, 0, mapbmp.Width, mapbmp.Height),
                ImageLockMode.ReadOnly, PixelFormat.Format24bppRgb);

            DateTime start = DateTime.Now;
            int tc = Environment.TickCount;
            m_log.Info("[MAPTILE]: Generating Maptile");
            //m_log.Info("[MAPTILE]: Generating Maptile Step 1: Terrain");

            // These textures should be in the AssetCache anyway, as every client conneting to this
            // region needs them. Except on start, when the map is recreated (before anyone connected),
            // and on change of the estate settings (textures and terrain values), when the map should
            // be recreated.
            RegionSettings settings = m_scene.RegionInfo.RegionSettings;

            // the four terrain colors as HSVs for interpolation
            HSV hsv1 = new HSV(computeAverageColor(settings.TerrainTexture1, defaultColor1));
            HSV hsv2 = new HSV(computeAverageColor(settings.TerrainTexture2, defaultColor2));
            HSV hsv3 = new HSV(computeAverageColor(settings.TerrainTexture3, defaultColor3));
            HSV hsv4 = new HSV(computeAverageColor(settings.TerrainTexture4, defaultColor4));

            float levelNElow = (float)settings.Elevation1NE;
            float levelNEhigh = (float)settings.Elevation2NE;

            float levelNWlow = (float)settings.Elevation1NW;
            float levelNWhigh = (float)settings.Elevation2NW;

            float levelSElow = (float)settings.Elevation1SE;
            float levelSEhigh = (float)settings.Elevation2SE;

            float levelSWlow = (float)settings.Elevation1SW;
            float levelSWhigh = (float)settings.Elevation2SW;

            float waterHeight = (float)settings.WaterHeight;

            double[,] hm = m_scene.Heightmap.GetDoubles();
            int pixelSize = 3;

            for (int y = 0; y < (int)Constants.RegionSize; y++)
            {
                float rowRatio = y / ((float)Constants.RegionSize - 1); // 0 - 1, for interpolation
                for (int x = 0; x < (int)Constants.RegionSize; x++)
                {
                    float columnRatio = x / ((float)Constants.RegionSize - 1); // 0 - 1, for interpolation

                    // Y flip the cordinates for the bitmap: hf origin is lower left, bm origin is upper left
                    int yr = ((int)Constants.RegionSize - 1) - y;

                    float heightvalue = getHeight(hm, x, y);
                    if (Single.IsInfinity(heightvalue) || Single.IsNaN(heightvalue))
                        heightvalue = 0;

                    if (heightvalue > waterHeight)
                    {
                        // add a bit noise for breaking up those flat colors:
                        // - a large-scale noise, for the "patches" (using an doubled s-curve for sharper contrast)
                        // - a small-scale noise, for bringing in some small scale variation
                        //float bigNoise = (float)TerrainUtil.InterpolatedNoise(x / 8.0, y / 8.0) * .5f + .5f; // map to 0.0 - 1.0
                        //float smallNoise = (float)TerrainUtil.InterpolatedNoise(x + 33, y + 43) * .5f + .5f;
                        //float hmod = heightvalue + smallNoise * 3f + S(S(bigNoise)) * 10f;
                        float hmod =
                            heightvalue +
                            (float)TerrainUtil.InterpolatedNoise(x + 33, y + 43) * 1.5f + 1.5f + // 0 - 3
                            S(S((float)TerrainUtil.InterpolatedNoise(x / 8.0, y / 8.0) * .5f + .5f)) * 10f; // 0 - 10

                        // find the low/high values for this point (interpolated bilinearily)
                        // (and remember, x=0,y=0 is SW)
                        float low = levelSWlow * (1f - rowRatio) * (1f - columnRatio) +
                            levelSElow * (1f - rowRatio) * columnRatio +
                            levelNWlow * rowRatio * (1f - columnRatio) +
                            levelNElow * rowRatio * columnRatio;
                        float high = levelSWhigh * (1f - rowRatio) * (1f - columnRatio) +
                            levelSEhigh * (1f - rowRatio) * columnRatio +
                            levelNWhigh * rowRatio * (1f - columnRatio) +
                            levelNEhigh * rowRatio * columnRatio;
                        if (high < low)
                        {
                            // someone tried to fool us. High value should be higher than low every time
                            float tmp = high;
                            high = low;
                            low = tmp;
                        }

                        HSV hsv;
                        if (hmod <= low) hsv = hsv1; // too low
                        else if (hmod >= high) hsv = hsv4; // too high
                        else
                        {
                            // HSV-interpolate along the colors
                            // first, rescale h to 0.0 - 1.0
                            hmod = (hmod - low) / (high - low);
                            // now we have to split: 0.00 => color1, 0.33 => color2, 0.67 => color3, 1.00 => color4
                            if (hmod < 1f / 3f) hsv = interpolateHSV(ref hsv1, ref hsv2, hmod * 3f);
                            else if (hmod < 2f / 3f) hsv = interpolateHSV(ref hsv2, ref hsv3, (hmod * 3f) - 1f);
                            else hsv = interpolateHSV(ref hsv3, ref hsv4, (hmod * 3f) - 2f);
                        }

                        // Shade the terrain for shadows
                        if (x < ((int)Constants.RegionSize - 1) && y < ((int)Constants.RegionSize - 1))
                        {
                            float hfvaluecompare = getHeight(hm, x + 1, y + 1); // light from north-east => look at land height there
                            if (Single.IsInfinity(hfvaluecompare) || Single.IsNaN(hfvaluecompare))
                                hfvaluecompare = 0f;

                            float hfdiff = heightvalue - hfvaluecompare;  // => positive if NE is lower, negative if here is lower
                            hfdiff *= 0.06f; // some random factor so "it looks good"
                            if (hfdiff > 0.02f)
                            {
                                float highlightfactor = 0.18f;
                                // NE is lower than here
                                // We have to desaturate and lighten the land at the same time
                                hsv.s = (hsv.s - (hfdiff * highlightfactor) > 0f) ? hsv.s - (hfdiff * highlightfactor) : 0f;
                                hsv.v = (hsv.v + (hfdiff * highlightfactor) < 1f) ? hsv.v + (hfdiff * highlightfactor) : 1f;
                            }
                            else if (hfdiff < -0.02f)
                            {
                                // here is lower than NE:
                                // We have to desaturate and blacken the land at the same time
                                hsv.s = (hsv.s + hfdiff > 0f) ? hsv.s + hfdiff : 0f;
                                hsv.v = (hsv.v + hfdiff > 0f) ? hsv.v + hfdiff : 0f;
                            }
                        }
                        //get the data from the original image
                        byte* Row = (byte*)originalData.Scan0 + (y * originalData.Stride);
                        Color hsvColor = hsv.toColor();
                        Row[x * pixelSize + 2] = hsvColor.R;
                        Row[x * pixelSize + 1] = hsvColor.G;
                        Row[x * pixelSize] = hsvColor.B;
                    }
                    else
                    {
                        // We're under the water level with the terrain, so paint water instead of land

                        heightvalue = waterHeight - heightvalue;
                        if (Single.IsInfinity(heightvalue) || Single.IsNaN(heightvalue))
                            heightvalue = 0f;
                        else if (heightvalue > 19f)
                            heightvalue = 19f;
                        else if (heightvalue < 0f)
                            heightvalue = 0f;

                        heightvalue = 100f - (heightvalue * 100f) / 19f;  // 0 - 19 => 100 - 0

                        //get the data from the original image
                        byte* Row = (byte*)originalData.Scan0 + (y * originalData.Stride);
                        Row[x * pixelSize + 2] = WATER_COLOR.R;
                        Row[x * pixelSize + 1] = WATER_COLOR.G;
                        Row[x * pixelSize] = WATER_COLOR.B;
                    }
                }
            }
            if (m_mapping != null)
                m_mapping.Clear();
            mapbmp.UnlockBits(originalData);
            //m_log.Info("[MAPTILE]: Generating Maptile Step 1: Done in " + (Environment.TickCount - tc) + " ms");
            return mapbmp;
        }
コード例 #2
0
        // interpolate two colors in HSV space and return the resulting color
        private HSV interpolateHSV(ref HSV c1, ref HSV c2, float ratio) {
            if (ratio <= 0f) return c1;
            if (ratio >= 1f) return c2;

            // make sure we are on the same side on the hue-circle for interpolation
            // We change the hue of the parameters here, but we don't change the color
            // represented by that value
            if (c1.h - c2.h > 180f) c1.h -= 360f;
            else if (c2.h - c1.h > 180f) c1.h += 360f;

            return new HSV(c1.h * (1f - ratio) + c2.h * ratio,
                           c1.s * (1f - ratio) + c2.s * ratio,
                           c1.v * (1f - ratio) + c2.v * ratio);
        }
コード例 #3
0
        public unsafe Bitmap TerrainToBitmap(Bitmap mapbmp)
        {
            BitmapProcessing.FastBitmap unsafeBMP = new BitmapProcessing.FastBitmap(mapbmp);
            unsafeBMP.LockBitmap();
            //DateTime start = DateTime.Now;
            //m_log.Info("[MAPTILE]: Generating Maptile Step 1: Terrain");

            // These textures should be in the AssetCache anyway, as every client conneting to this
            // region needs them. Except on start, when the map is recreated (before anyone connected),
            // and on change of the estate settings (textures and terrain values), when the map should
            // be recreated.
            RegionSettings settings = m_scene.RegionInfo.RegionSettings;

            // the four terrain colors as HSVs for interpolation
            HSV hsv1 = new HSV(computeAverageColor(settings.TerrainTexture1, defaultColor1));
            HSV hsv2 = new HSV(computeAverageColor(settings.TerrainTexture2, defaultColor2));
            HSV hsv3 = new HSV(computeAverageColor(settings.TerrainTexture3, defaultColor3));
            HSV hsv4 = new HSV(computeAverageColor(settings.TerrainTexture4, defaultColor4));

            float levelNElow = (float)settings.Elevation1NE;
            float levelNEhigh = (float)settings.Elevation2NE;

            float levelNWlow = (float)settings.Elevation1NW;
            float levelNWhigh = (float)settings.Elevation2NW;

            float levelSElow = (float)settings.Elevation1SE;
            float levelSEhigh = (float)settings.Elevation2SE;

            float levelSWlow = (float)settings.Elevation1SW;
            float levelSWhigh = (float)settings.Elevation2SW;

            float waterHeight = (float)settings.WaterHeight;

            ITerrainChannel heightmap = m_scene.RequestModuleInterface<ITerrainChannel>();
            float sizeRatio = (float)m_scene.RegionInfo.RegionSizeX / (float)Constants.RegionSize;
            for (float y = 0; y < m_scene.RegionInfo.RegionSizeY; y += sizeRatio)
            {
                float rowRatio = y / (m_scene.RegionInfo.RegionSizeY - 1); // 0 - 1, for interpolation
                for (float x = 0; x < m_scene.RegionInfo.RegionSizeX; x += sizeRatio)
                {
                    float columnRatio = x / (m_scene.RegionInfo.RegionSizeX - 1); // 0 - 1, for interpolation

                    float heightvalue = getHeight (heightmap, (int)x, (int)y);

                    if (heightvalue > waterHeight)
                    {
                        // add a bit noise for breaking up those flat colors:
                        // - a large-scale noise, for the "patches" (using an doubled s-curve for sharper contrast)
                        // - a small-scale noise, for bringing in some small scale variation
                        //float bigNoise = (float)TerrainUtil.InterpolatedNoise(x / 8.0, y / 8.0) * .5f + .5f; // map to 0.0 - 1.0
                        //float smallNoise = (float)TerrainUtil.InterpolatedNoise(x + 33, y + 43) * .5f + .5f;
                        //float hmod = heightvalue + smallNoise * 3f + S(S(bigNoise)) * 10f;
                        float hmod =
                            heightvalue; // 0 - 10

                        // find the low/high values for this point (interpolated bilinearily)
                        // (and remember, x=0,y=0 is SW)
                        float low = levelSWlow * (1f - rowRatio) * (1f - columnRatio) +
                            levelSElow * (1f - rowRatio) * columnRatio +
                            levelNWlow * rowRatio * (1f - columnRatio) +
                            levelNElow * rowRatio * columnRatio;
                        float high = levelSWhigh * (1f - rowRatio) * (1f - columnRatio) +
                            levelSEhigh * (1f - rowRatio) * columnRatio +
                            levelNWhigh * rowRatio * (1f - columnRatio) +
                            levelNEhigh * rowRatio * columnRatio;
                        if (high < low)
                        {
                            // someone tried to fool us. High value should be higher than low every time
                            float tmp = high;
                            high = low;
                            low = tmp;
                        }

                        HSV hsv;
                        if (hmod <= low) hsv = hsv1; // too low
                        else if (hmod >= high) hsv = hsv4; // too high
                        else
                        {
                            // HSV-interpolate along the colors
                            // first, rescale h to 0.0 - 1.0
                            hmod = (hmod - low) / (high - low);
                            // now we have to split: 0.00 => color1, 0.33 => color2, 0.67 => color3, 1.00 => color4
                            if (hmod < 1f / 3f) hsv = interpolateHSV (ref hsv1, ref hsv2, hmod * 3f);
                            else if (hmod < 2f / 3f) hsv = interpolateHSV (ref hsv2, ref hsv3, (hmod * 3f) - 1f);
                            else hsv = interpolateHSV (ref hsv3, ref hsv4, (hmod * 3f) - 2f);
                        }
                        //get the data from the original image
                        Color hsvColor = hsv.toColor ();
                        unsafeBMP.SetPixel ((int)(x / sizeRatio),(int)(((m_scene.RegionInfo.RegionSizeY - 1) - y) / sizeRatio), hsvColor);
                    }
                    else
                    {
                        // We're under the water level with the terrain, so paint water instead of land
                        unsafeBMP.SetPixel ((int)(x / sizeRatio), (int)(((m_scene.RegionInfo.RegionSizeY - 1) - y) / sizeRatio), WATER_COLOR);
                    }
                }
            }
            if (m_mapping != null)
            {
                SaveCache ();
                m_mapping.Clear ();
            }
            unsafeBMP.UnlockBitmap();
            //m_log.Info("[MAPTILE]: Generating Maptile Step 1: Done in " + (DateTime.Now - start).TotalSeconds + " ms");
            return unsafeBMP.Bitmap();
        }
コード例 #4
0
        public void TerrainToBitmap(Bitmap mapbmp)
        {
            int tc = Environment.TickCount;

            m_log.Info("[MAPTILE]: Generating Maptile Step 1: Terrain");

            // These textures should be in the AssetCache anyway, as every client conneting to this
            // region needs them. Except on start, when the map is recreated (before anyone connected),
            // and on change of the estate settings (textures and terrain values), when the map should
            // be recreated.
            RegionSettings settings = m_scene.RegionInfo.RegionSettings;

            // the four terrain colors as HSVs for interpolation
            HSV hsv1 = new HSV(computeAverageColor(settings.TerrainTexture1, defaultColor1));
            HSV hsv2 = new HSV(computeAverageColor(settings.TerrainTexture2, defaultColor2));
            HSV hsv3 = new HSV(computeAverageColor(settings.TerrainTexture3, defaultColor3));
            HSV hsv4 = new HSV(computeAverageColor(settings.TerrainTexture4, defaultColor4));

            float levelNElow  = (float)settings.Elevation1NE;
            float levelNEhigh = (float)settings.Elevation2NE;

            float levelNWlow  = (float)settings.Elevation1NW;
            float levelNWhigh = (float)settings.Elevation2NW;

            float levelSElow  = (float)settings.Elevation1SE;
            float levelSEhigh = (float)settings.Elevation2SE;

            float levelSWlow  = (float)settings.Elevation1SW;
            float levelSWhigh = (float)settings.Elevation2SW;

            float waterHeight = (float)settings.WaterHeight;

            double[,] hm = m_scene.Heightmap.GetDoubles();

            for (int x = 0; x < (int)Constants.RegionSize; x++)
            {
                float columnRatio = x / ((float)Constants.RegionSize - 1); // 0 - 1, for interpolation
                for (int y = 0; y < (int)Constants.RegionSize; y++)
                {
                    float rowRatio = y / ((float)Constants.RegionSize - 1); // 0 - 1, for interpolation

                    // Y flip the cordinates for the bitmap: hf origin is lower left, bm origin is upper left
                    int yr = ((int)Constants.RegionSize - 1) - y;

                    float heightvalue = getHeight(hm, x, y);
                    if (Single.IsInfinity(heightvalue) || Single.IsNaN(heightvalue))
                    {
                        heightvalue = 0;
                    }

                    if (heightvalue > waterHeight)
                    {
                        // add a bit noise for breaking up those flat colors:
                        // - a large-scale noise, for the "patches" (using an doubled s-curve for sharper contrast)
                        // - a small-scale noise, for bringing in some small scale variation
                        //float bigNoise = (float)TerrainUtil.InterpolatedNoise(x / 8.0, y / 8.0) * .5f + .5f; // map to 0.0 - 1.0
                        //float smallNoise = (float)TerrainUtil.InterpolatedNoise(x + 33, y + 43) * .5f + .5f;
                        //float hmod = heightvalue + smallNoise * 3f + S(S(bigNoise)) * 10f;
                        float hmod =
                            heightvalue +
                            (float)TerrainUtil.InterpolatedNoise(x + 33, y + 43) * 1.5f + 1.5f +            // 0 - 3
                            S(S((float)TerrainUtil.InterpolatedNoise(x / 8.0, y / 8.0) * .5f + .5f)) * 10f; // 0 - 10

                        // find the low/high values for this point (interpolated bilinearily)
                        // (and remember, x=0,y=0 is SW)
                        float low = levelSWlow * (1f - rowRatio) * (1f - columnRatio) +
                                    levelSElow * (1f - rowRatio) * columnRatio +
                                    levelNWlow * rowRatio * (1f - columnRatio) +
                                    levelNElow * rowRatio * columnRatio;
                        float high = levelSWhigh * (1f - rowRatio) * (1f - columnRatio) +
                                     levelSEhigh * (1f - rowRatio) * columnRatio +
                                     levelNWhigh * rowRatio * (1f - columnRatio) +
                                     levelNEhigh * rowRatio * columnRatio;
                        if (high < low)
                        {
                            // someone tried to fool us. High value should be higher than low every time
                            float tmp = high;
                            high = low;
                            low  = tmp;
                        }

                        HSV hsv;
                        if (hmod <= low)
                        {
                            hsv = hsv1;              // too low
                        }
                        else if (hmod >= high)
                        {
                            hsv = hsv4;                    // too high
                        }
                        else
                        {
                            // HSV-interpolate along the colors
                            // first, rescale h to 0.0 - 1.0
                            hmod = (hmod - low) / (high - low);
                            // now we have to split: 0.00 => color1, 0.33 => color2, 0.67 => color3, 1.00 => color4
                            if (hmod < 1f / 3f)
                            {
                                hsv = interpolateHSV(ref hsv1, ref hsv2, hmod * 3f);
                            }
                            else if (hmod < 2f / 3f)
                            {
                                hsv = interpolateHSV(ref hsv2, ref hsv3, (hmod * 3f) - 1f);
                            }
                            else
                            {
                                hsv = interpolateHSV(ref hsv3, ref hsv4, (hmod * 3f) - 2f);
                            }
                        }

                        // Shade the terrain for shadows
                        if (x < ((int)Constants.RegionSize - 1) && y < ((int)Constants.RegionSize - 1))
                        {
                            float hfvaluecompare = getHeight(hm, x + 1, y + 1); // light from north-east => look at land height there
                            if (Single.IsInfinity(hfvaluecompare) || Single.IsNaN(hfvaluecompare))
                            {
                                hfvaluecompare = 0f;
                            }

                            float hfdiff = heightvalue - hfvaluecompare; // => positive if NE is lower, negative if here is lower
                            hfdiff *= 0.06f;                             // some random factor so "it looks good"
                            if (hfdiff > 0.02f)
                            {
                                float highlightfactor = 0.18f;
                                // NE is lower than here
                                // We have to desaturate and lighten the land at the same time
                                hsv.s = (hsv.s - (hfdiff * highlightfactor) > 0f) ? hsv.s - (hfdiff * highlightfactor) : 0f;
                                hsv.v = (hsv.v + (hfdiff * highlightfactor) < 1f) ? hsv.v + (hfdiff * highlightfactor) : 1f;
                            }
                            else if (hfdiff < -0.02f)
                            {
                                // here is lower than NE:
                                // We have to desaturate and blacken the land at the same time
                                hsv.s = (hsv.s + hfdiff > 0f) ? hsv.s + hfdiff : 0f;
                                hsv.v = (hsv.v + hfdiff > 0f) ? hsv.v + hfdiff : 0f;
                            }
                        }
                        mapbmp.SetPixel(x, yr, hsv.toColor());
                    }
                    else
                    {
                        // We're under the water level with the terrain, so paint water instead of land

                        heightvalue = waterHeight - heightvalue;
                        if (Single.IsInfinity(heightvalue) || Single.IsNaN(heightvalue))
                        {
                            heightvalue = 0f;
                        }
                        else if (heightvalue > 19f)
                        {
                            heightvalue = 19f;
                        }
                        else if (heightvalue < 0f)
                        {
                            heightvalue = 0f;
                        }

                        heightvalue = 100f - (heightvalue * 100f) / 19f;  // 0 - 19 => 100 - 0

                        mapbmp.SetPixel(x, yr, WATER_COLOR);
                    }
                }
            }
            m_log.Info("[MAPTILE]: Generating Maptile Step 1: Done in " + (Environment.TickCount - tc) + " ms");
        }
コード例 #5
0
        public unsafe Bitmap TerrainToBitmap(Bitmap mapbmp)
        {
            BitmapProcessing.FastBitmap unsafeBMP = new BitmapProcessing.FastBitmap(mapbmp);
            unsafeBMP.LockBitmap();
            //DateTime start = DateTime.Now;
            //m_log.Info("[MAPTILE]: Generating Maptile Step 1: Terrain");

            // These textures should be in the AssetCache anyway, as every client conneting to this
            // region needs them. Except on start, when the map is recreated (before anyone connected),
            // and on change of the estate settings (textures and terrain values), when the map should
            // be recreated.
            RegionSettings settings = m_scene.RegionInfo.RegionSettings;

            // the four terrain colors as HSVs for interpolation
            HSV hsv1 = new HSV(computeAverageColor(settings.TerrainTexture1, defaultColor1));
            HSV hsv2 = new HSV(computeAverageColor(settings.TerrainTexture2, defaultColor2));
            HSV hsv3 = new HSV(computeAverageColor(settings.TerrainTexture3, defaultColor3));
            HSV hsv4 = new HSV(computeAverageColor(settings.TerrainTexture4, defaultColor4));

            float levelNElow  = (float)settings.Elevation1NE;
            float levelNEhigh = (float)settings.Elevation2NE;

            float levelNWlow  = (float)settings.Elevation1NW;
            float levelNWhigh = (float)settings.Elevation2NW;

            float levelSElow  = (float)settings.Elevation1SE;
            float levelSEhigh = (float)settings.Elevation2SE;

            float levelSWlow  = (float)settings.Elevation1SW;
            float levelSWhigh = (float)settings.Elevation2SW;

            float waterHeight = (float)settings.WaterHeight;

            ITerrainChannel heightmap = m_scene.RequestModuleInterface <ITerrainChannel>();
            float           sizeRatio = (float)m_scene.RegionInfo.RegionSizeX / (float)Constants.RegionSize;

            for (float y = 0; y < m_scene.RegionInfo.RegionSizeY; y += sizeRatio)
            {
                float rowRatio = y / (m_scene.RegionInfo.RegionSizeY - 1); // 0 - 1, for interpolation
                for (float x = 0; x < m_scene.RegionInfo.RegionSizeX; x += sizeRatio)
                {
                    float columnRatio = x / (m_scene.RegionInfo.RegionSizeX - 1); // 0 - 1, for interpolation

                    float heightvalue = getHeight(heightmap, (int)x, (int)y);

                    if (heightvalue > waterHeight)
                    {
                        // add a bit noise for breaking up those flat colors:
                        // - a large-scale noise, for the "patches" (using an doubled s-curve for sharper contrast)
                        // - a small-scale noise, for bringing in some small scale variation
                        //float bigNoise = (float)TerrainUtil.InterpolatedNoise(x / 8.0, y / 8.0) * .5f + .5f; // map to 0.0 - 1.0
                        //float smallNoise = (float)TerrainUtil.InterpolatedNoise(x + 33, y + 43) * .5f + .5f;
                        //float hmod = heightvalue + smallNoise * 3f + S(S(bigNoise)) * 10f;
                        float hmod =
                            heightvalue; // 0 - 10

                        // find the low/high values for this point (interpolated bilinearily)
                        // (and remember, x=0,y=0 is SW)
                        float low = levelSWlow * (1f - rowRatio) * (1f - columnRatio) +
                                    levelSElow * (1f - rowRatio) * columnRatio +
                                    levelNWlow * rowRatio * (1f - columnRatio) +
                                    levelNElow * rowRatio * columnRatio;
                        float high = levelSWhigh * (1f - rowRatio) * (1f - columnRatio) +
                                     levelSEhigh * (1f - rowRatio) * columnRatio +
                                     levelNWhigh * rowRatio * (1f - columnRatio) +
                                     levelNEhigh * rowRatio * columnRatio;
                        if (high < low)
                        {
                            // someone tried to fool us. High value should be higher than low every time
                            float tmp = high;
                            high = low;
                            low  = tmp;
                        }

                        HSV hsv;
                        if (hmod <= low)
                        {
                            hsv = hsv1;              // too low
                        }
                        else if (hmod >= high)
                        {
                            hsv = hsv4;                    // too high
                        }
                        else
                        {
                            // HSV-interpolate along the colors
                            // first, rescale h to 0.0 - 1.0
                            hmod = (hmod - low) / (high - low);
                            // now we have to split: 0.00 => color1, 0.33 => color2, 0.67 => color3, 1.00 => color4
                            if (hmod < 1f / 3f)
                            {
                                hsv = interpolateHSV(ref hsv1, ref hsv2, hmod * 3f);
                            }
                            else if (hmod < 2f / 3f)
                            {
                                hsv = interpolateHSV(ref hsv2, ref hsv3, (hmod * 3f) - 1f);
                            }
                            else
                            {
                                hsv = interpolateHSV(ref hsv3, ref hsv4, (hmod * 3f) - 2f);
                            }
                        }
                        //get the data from the original image
                        Color hsvColor = hsv.toColor();
                        unsafeBMP.SetPixel((int)(x / sizeRatio), (int)(((m_scene.RegionInfo.RegionSizeY - 1) - y) / sizeRatio), hsvColor);
                    }
                    else
                    {
                        // We're under the water level with the terrain, so paint water instead of land
                        unsafeBMP.SetPixel((int)(x / sizeRatio), (int)(((m_scene.RegionInfo.RegionSizeY - 1) - y) / sizeRatio), WATER_COLOR);
                    }
                }
            }
            if (m_mapping != null)
            {
                m_mapping.Clear();
            }
            unsafeBMP.UnlockBitmap();
            //m_log.Info("[MAPTILE]: Generating Maptile Step 1: Done in " + (DateTime.Now - start).TotalSeconds + " ms");
            return(unsafeBMP.Bitmap());
        }