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; }
// 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); }
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(); }
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"); }
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()); }