aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/OpenSim/Region/CoreModules/World/Warp3DMap/TerrainSplat.cs
diff options
context:
space:
mode:
authorMelanie2010-10-03 16:34:55 +0100
committerMelanie2010-10-03 16:34:55 +0100
commit27340f616edfc2c6c199cb9d550b0ca3f67d2c6f (patch)
tree2c77b3e286970a6984eaa4d04eb7ca316462baed /OpenSim/Region/CoreModules/World/Warp3DMap/TerrainSplat.cs
parentInitial port of the Warp3D map tile renderer (diff)
downloadopensim-SC_OLD-27340f616edfc2c6c199cb9d550b0ca3f67d2c6f.zip
opensim-SC_OLD-27340f616edfc2c6c199cb9d550b0ca3f67d2c6f.tar.gz
opensim-SC_OLD-27340f616edfc2c6c199cb9d550b0ca3f67d2c6f.tar.bz2
opensim-SC_OLD-27340f616edfc2c6c199cb9d550b0ca3f67d2c6f.tar.xz
Addign the new / renamed files for previous commit
Diffstat (limited to 'OpenSim/Region/CoreModules/World/Warp3DMap/TerrainSplat.cs')
-rw-r--r--OpenSim/Region/CoreModules/World/Warp3DMap/TerrainSplat.cs343
1 files changed, 343 insertions, 0 deletions
diff --git a/OpenSim/Region/CoreModules/World/Warp3DMap/TerrainSplat.cs b/OpenSim/Region/CoreModules/World/Warp3DMap/TerrainSplat.cs
new file mode 100644
index 0000000..7bf675d
--- /dev/null
+++ b/OpenSim/Region/CoreModules/World/Warp3DMap/TerrainSplat.cs
@@ -0,0 +1,343 @@
1/*
2 * Copyright (c) Contributors, http://opensimulator.org/
3 * See CONTRIBUTORS.TXT for a full list of copyright holders.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 * * Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * * Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 * * Neither the name of the OpenSimulator Project nor the
13 * names of its contributors may be used to endorse or promote products
14 * derived from this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
17 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19 * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
20 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27
28using System;
29using System.Diagnostics;
30using System.Drawing;
31using System.Drawing.Imaging;
32using log4net;
33using OpenMetaverse;
34using OpenSim.Framework;
35using OpenSim.Services.Interfaces;
36
37namespace OpenSim.Region.CoreModules.World.Warp3DMap
38{
39 public static class TerrainSplat
40 {
41 #region Constants
42
43 private static readonly UUID DIRT_DETAIL = new UUID("0bc58228-74a0-7e83-89bc-5c23464bcec5");
44 private static readonly UUID GRASS_DETAIL = new UUID("63338ede-0037-c4fd-855b-015d77112fc8");
45 private static readonly UUID MOUNTAIN_DETAIL = new UUID("303cd381-8560-7579-23f1-f0a880799740");
46 private static readonly UUID ROCK_DETAIL = new UUID("53a2f406-4895-1d13-d541-d2e3b86bc19c");
47
48 private static readonly UUID[] DEFAULT_TERRAIN_DETAIL = new UUID[]
49 {
50 DIRT_DETAIL,
51 GRASS_DETAIL,
52 MOUNTAIN_DETAIL,
53 ROCK_DETAIL
54 };
55
56 private static readonly Color[] DEFAULT_TERRAIN_COLOR = new Color[]
57 {
58 Color.FromArgb(255, 164, 136, 117),
59 Color.FromArgb(255, 65, 87, 47),
60 Color.FromArgb(255, 157, 145, 131),
61 Color.FromArgb(255, 125, 128, 130)
62 };
63
64 private static readonly UUID TERRAIN_CACHE_MAGIC = new UUID("2c0c7ef2-56be-4eb8-aacb-76712c535b4b");
65
66 #endregion Constants
67
68 private static readonly ILog m_log = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType.Name);
69
70 /// <summary>
71 /// Builds a composited terrain texture given the region texture
72 /// and heightmap settings
73 /// </summary>
74 /// <param name="heightmap">Terrain heightmap</param>
75 /// <param name="regionInfo">Region information including terrain texture parameters</param>
76 /// <returns>A composited 256x256 RGB texture ready for rendering</returns>
77 /// <remarks>Based on the algorithm described at http://opensimulator.org/wiki/Terrain_Splatting
78 /// </remarks>
79 public static Bitmap Splat(float[] heightmap, UUID[] textureIDs, float[] startHeights, float[] heightRanges, Vector3d regionPosition, IAssetService assetService, bool textureTerrain)
80 {
81 Debug.Assert(heightmap.Length == 256 * 256);
82 Debug.Assert(textureIDs.Length == 4);
83 Debug.Assert(startHeights.Length == 4);
84 Debug.Assert(heightRanges.Length == 4);
85
86 Bitmap[] detailTexture = new Bitmap[4];
87
88 if (textureTerrain)
89 {
90 // Swap empty terrain textureIDs with default IDs
91 for (int i = 0; i < textureIDs.Length; i++)
92 {
93 if (textureIDs[i] == UUID.Zero)
94 textureIDs[i] = DEFAULT_TERRAIN_DETAIL[i];
95 }
96
97 #region Texture Fetching
98
99 if (assetService != null)
100 {
101 for (int i = 0; i < 4; i++)
102 {
103 AssetBase asset;
104 UUID cacheID = UUID.Combine(TERRAIN_CACHE_MAGIC, textureIDs[i]);
105
106 // Try to fetch a cached copy of the decoded/resized version of this texture
107 asset = assetService.GetCached(cacheID.ToString());
108 if (asset != null)
109 {
110 try
111 {
112 using (System.IO.MemoryStream stream = new System.IO.MemoryStream(asset.Data))
113 detailTexture[i] = (Bitmap)Image.FromStream(stream);
114 }
115 catch (Exception ex)
116 {
117 m_log.Warn("Failed to decode cached terrain texture " + cacheID +
118 " (textureID: " + textureIDs[i] + "): " + ex.Message);
119 }
120 }
121
122 if (detailTexture[i] == null)
123 {
124 // Try to fetch the original JPEG2000 texture, resize if needed, and cache as PNG
125 asset = assetService.Get(textureIDs[i].ToString());
126 if (asset != null)
127 {
128 try { detailTexture[i] = (Bitmap)CSJ2K.J2kImage.FromBytes(asset.Data); }
129 catch (Exception ex)
130 {
131 m_log.Warn("Failed to decode terrain texture " + asset.ID + ": " + ex.Message);
132 }
133 }
134
135 if (detailTexture[i] != null)
136 {
137 Bitmap bitmap = detailTexture[i];
138
139 // Make sure this texture is the correct size, otherwise resize
140 if (bitmap.Width != 256 || bitmap.Height != 256)
141 bitmap = ImageUtils.ResizeImage(bitmap, 256, 256);
142
143 // Save the decoded and resized texture to the cache
144 byte[] data;
145 using (System.IO.MemoryStream stream = new System.IO.MemoryStream())
146 {
147 bitmap.Save(stream, ImageFormat.Png);
148 data = stream.ToArray();
149 }
150
151 // Cache a PNG copy of this terrain texture
152 AssetBase newAsset = new AssetBase
153 {
154 Data = data,
155 Description = "PNG",
156 Flags = AssetFlags.Collectable,
157 FullID = cacheID,
158 ID = cacheID.ToString(),
159 Local = true,
160 Name = String.Empty,
161 Temporary = true,
162 Type = (sbyte)AssetType.Unknown
163 };
164 newAsset.Metadata.ContentType = "image/png";
165 assetService.Store(newAsset);
166 }
167 }
168 }
169 }
170
171 #endregion Texture Fetching
172 }
173
174 // Fill in any missing textures with a solid color
175 for (int i = 0; i < 4; i++)
176 {
177 if (detailTexture[i] == null)
178 {
179 // Create a solid color texture for this layer
180 detailTexture[i] = new Bitmap(256, 256, PixelFormat.Format24bppRgb);
181 using (Graphics gfx = Graphics.FromImage(detailTexture[i]))
182 {
183 using (SolidBrush brush = new SolidBrush(DEFAULT_TERRAIN_COLOR[i]))
184 gfx.FillRectangle(brush, 0, 0, 256, 256);
185 }
186 }
187 }
188
189 #region Layer Map
190
191 float[] layermap = new float[256 * 256];
192
193 for (int y = 0; y < 256; y++)
194 {
195 for (int x = 0; x < 256; x++)
196 {
197 float height = heightmap[y * 256 + x];
198
199 float pctX = (float)x / 255f;
200 float pctY = (float)y / 255f;
201
202 // Use bilinear interpolation between the four corners of start height and
203 // height range to select the current values at this position
204 float startHeight = ImageUtils.Bilinear(
205 startHeights[0],
206 startHeights[2],
207 startHeights[1],
208 startHeights[3],
209 pctX, pctY);
210 startHeight = Utils.Clamp(startHeight, 0f, 255f);
211
212 float heightRange = ImageUtils.Bilinear(
213 heightRanges[0],
214 heightRanges[2],
215 heightRanges[1],
216 heightRanges[3],
217 pctX, pctY);
218 heightRange = Utils.Clamp(heightRange, 0f, 255f);
219
220 // Generate two frequencies of perlin noise based on our global position
221 // The magic values were taken from http://opensimulator.org/wiki/Terrain_Splatting
222 Vector3 vec = new Vector3
223 (
224 ((float)regionPosition.X + x) * 0.20319f,
225 ((float)regionPosition.Y + y) * 0.20319f,
226 height * 0.25f
227 );
228
229 float lowFreq = Perlin.noise2(vec.X * 0.222222f, vec.Y * 0.222222f) * 6.5f;
230 float highFreq = Perlin.turbulence2(vec.X, vec.Y, 2f) * 2.25f;
231 float noise = (lowFreq + highFreq) * 2f;
232
233 // Combine the current height, generated noise, start height, and height range parameters, then scale all of it
234 float layer = ((height + noise - startHeight) / heightRange) * 4f;
235 if (Single.IsNaN(layer)) layer = 0f;
236 layermap[y * 256 + x] = Utils.Clamp(layer, 0f, 3f);
237 }
238 }
239
240 #endregion Layer Map
241
242 #region Texture Compositing
243
244 Bitmap output = new Bitmap(256, 256, PixelFormat.Format24bppRgb);
245 BitmapData outputData = output.LockBits(new Rectangle(0, 0, 256, 256), ImageLockMode.WriteOnly, PixelFormat.Format24bppRgb);
246
247 unsafe
248 {
249 // Get handles to all of the texture data arrays
250 BitmapData[] datas = new BitmapData[]
251 {
252 detailTexture[0].LockBits(new Rectangle(0, 0, 256, 256), ImageLockMode.ReadOnly, detailTexture[0].PixelFormat),
253 detailTexture[1].LockBits(new Rectangle(0, 0, 256, 256), ImageLockMode.ReadOnly, detailTexture[1].PixelFormat),
254 detailTexture[2].LockBits(new Rectangle(0, 0, 256, 256), ImageLockMode.ReadOnly, detailTexture[2].PixelFormat),
255 detailTexture[3].LockBits(new Rectangle(0, 0, 256, 256), ImageLockMode.ReadOnly, detailTexture[3].PixelFormat)
256 };
257
258 int[] comps = new int[]
259 {
260 (datas[0].PixelFormat == PixelFormat.Format32bppArgb) ? 4 : 3,
261 (datas[1].PixelFormat == PixelFormat.Format32bppArgb) ? 4 : 3,
262 (datas[2].PixelFormat == PixelFormat.Format32bppArgb) ? 4 : 3,
263 (datas[3].PixelFormat == PixelFormat.Format32bppArgb) ? 4 : 3
264 };
265
266 for (int y = 0; y < 256; y++)
267 {
268 for (int x = 0; x < 256; x++)
269 {
270 float layer = layermap[y * 256 + x];
271
272 // Select two textures
273 int l0 = (int)Math.Floor(layer);
274 int l1 = Math.Min(l0 + 1, 3);
275
276 byte* ptrA = (byte*)datas[l0].Scan0 + y * datas[l0].Stride + x * comps[l0];
277 byte* ptrB = (byte*)datas[l1].Scan0 + y * datas[l1].Stride + x * comps[l1];
278 byte* ptrO = (byte*)outputData.Scan0 + y * outputData.Stride + x * 3;
279
280 float aB = *(ptrA + 0);
281 float aG = *(ptrA + 1);
282 float aR = *(ptrA + 2);
283
284 float bB = *(ptrB + 0);
285 float bG = *(ptrB + 1);
286 float bR = *(ptrB + 2);
287
288 float layerDiff = layer - l0;
289
290 // Interpolate between the two selected textures
291 *(ptrO + 0) = (byte)Math.Floor(aB + layerDiff * (bB - aB));
292 *(ptrO + 1) = (byte)Math.Floor(aG + layerDiff * (bG - aG));
293 *(ptrO + 2) = (byte)Math.Floor(aR + layerDiff * (bR - aR));
294 }
295 }
296
297 for (int i = 0; i < 4; i++)
298 detailTexture[i].UnlockBits(datas[i]);
299 }
300
301 output.UnlockBits(outputData);
302
303 // We generated the texture upside down, so flip it
304 output.RotateFlip(RotateFlipType.RotateNoneFlipY);
305
306 #endregion Texture Compositing
307
308 return output;
309 }
310
311 public static Bitmap SplatSimple(float[] heightmap)
312 {
313 const float BASE_HSV_H = 93f / 360f;
314 const float BASE_HSV_S = 44f / 100f;
315 const float BASE_HSV_V = 34f / 100f;
316
317 Bitmap img = new Bitmap(256, 256);
318 BitmapData bitmapData = img.LockBits(new Rectangle(0, 0, 256, 256), ImageLockMode.WriteOnly, PixelFormat.Format24bppRgb);
319
320 unsafe
321 {
322 for (int y = 255; y >= 0; y--)
323 {
324 for (int x = 0; x < 256; x++)
325 {
326 float normHeight = heightmap[y * 256 + x] / 255f;
327 normHeight = Utils.Clamp(normHeight, BASE_HSV_V, 1.0f);
328
329 Color4 color = Color4.FromHSV(BASE_HSV_H, BASE_HSV_S, normHeight);
330
331 byte* ptr = (byte*)bitmapData.Scan0 + y * bitmapData.Stride + x * 3;
332 *(ptr + 0) = (byte)(color.B * 255f);
333 *(ptr + 1) = (byte)(color.G * 255f);
334 *(ptr + 2) = (byte)(color.R * 255f);
335 }
336 }
337 }
338
339 img.UnlockBits(bitmapData);
340 return img;
341 }
342 }
343}