aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/OpenSim/Region/CoreModules/World
diff options
context:
space:
mode:
Diffstat (limited to 'OpenSim/Region/CoreModules/World')
-rw-r--r--OpenSim/Region/CoreModules/World/Warp3DMap/TerrainSplat.cs436
-rw-r--r--OpenSim/Region/CoreModules/World/Warp3DMap/Warp3DImageModule.cs241
2 files changed, 417 insertions, 260 deletions
diff --git a/OpenSim/Region/CoreModules/World/Warp3DMap/TerrainSplat.cs b/OpenSim/Region/CoreModules/World/Warp3DMap/TerrainSplat.cs
index df5ac92..9534ad3 100644
--- a/OpenSim/Region/CoreModules/World/Warp3DMap/TerrainSplat.cs
+++ b/OpenSim/Region/CoreModules/World/Warp3DMap/TerrainSplat.cs
@@ -1,4 +1,4 @@
1/* 1/*
2 * Copyright (c) Contributors, http://opensimulator.org/ 2 * Copyright (c) Contributors, http://opensimulator.org/
3 * See CONTRIBUTORS.TXT for a full list of copyright holders. 3 * See CONTRIBUTORS.TXT for a full list of copyright holders.
4 * 4 *
@@ -32,6 +32,7 @@ using System.Drawing.Imaging;
32using log4net; 32using log4net;
33using OpenMetaverse; 33using OpenMetaverse;
34using OpenSim.Framework; 34using OpenSim.Framework;
35using OpenSim.Region.Framework.Interfaces;
35using OpenSim.Services.Interfaces; 36using OpenSim.Services.Interfaces;
36 37
37namespace OpenSim.Region.CoreModules.World.Warp3DMap 38namespace OpenSim.Region.CoreModules.World.Warp3DMap
@@ -66,261 +67,271 @@ namespace OpenSim.Region.CoreModules.World.Warp3DMap
66 #endregion Constants 67 #endregion Constants
67 68
68 private static readonly ILog m_log = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType.Name); 69 private static readonly ILog m_log = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType.Name);
70 private static string LogHeader = "[WARP3D TERRAIN SPLAT]";
69 71
70 /// <summary> 72 /// <summary>
71 /// Builds a composited terrain texture given the region texture 73 /// Builds a composited terrain texture given the region texture
72 /// and heightmap settings 74 /// and heightmap settings
73 /// </summary> 75 /// </summary>
74 /// <param name="heightmap">Terrain heightmap</param> 76 /// <param name="terrain">Terrain heightmap</param>
75 /// <param name="regionInfo">Region information including terrain texture parameters</param> 77 /// <param name="regionInfo">Region information including terrain texture parameters</param>
76 /// <returns>A composited 256x256 RGB texture ready for rendering</returns> 78 /// <returns>A 256x256 square RGB texture ready for rendering</returns>
77 /// <remarks>Based on the algorithm described at http://opensimulator.org/wiki/Terrain_Splatting 79 /// <remarks>Based on the algorithm described at http://opensimulator.org/wiki/Terrain_Splatting
80 /// Note we create a 256x256 dimension texture even if the actual terrain is larger.
78 /// </remarks> 81 /// </remarks>
79 public static Bitmap Splat(float[] heightmap, UUID[] textureIDs, float[] startHeights, float[] heightRanges, Vector3d regionPosition, IAssetService assetService, bool textureTerrain) 82 public static Bitmap Splat(ITerrainChannel terrain,
83 UUID[] textureIDs, float[] startHeights, float[] heightRanges,
84 Vector3d regionPosition, IAssetService assetService, bool textureTerrain)
80 { 85 {
81 Debug.Assert(heightmap.Length == 256 * 256);
82 Debug.Assert(textureIDs.Length == 4); 86 Debug.Assert(textureIDs.Length == 4);
83 Debug.Assert(startHeights.Length == 4); 87 Debug.Assert(startHeights.Length == 4);
84 Debug.Assert(heightRanges.Length == 4); 88 Debug.Assert(heightRanges.Length == 4);
85 89
86 Bitmap[] detailTexture = new Bitmap[4]; 90 Bitmap[] detailTexture = new Bitmap[4];
87 Bitmap output = null;
88 BitmapData outputData = null;
89 91
90 try 92 if (textureTerrain)
91 { 93 {
92 if (textureTerrain) 94 // Swap empty terrain textureIDs with default IDs
95 for (int i = 0; i < textureIDs.Length; i++)
93 { 96 {
94 // Swap empty terrain textureIDs with default IDs 97 if (textureIDs[i] == UUID.Zero)
95 for (int i = 0; i < textureIDs.Length; i++) 98 textureIDs[i] = DEFAULT_TERRAIN_DETAIL[i];
96 { 99 }
97 if (textureIDs[i] == UUID.Zero) 100
98 textureIDs[i] = DEFAULT_TERRAIN_DETAIL[i]; 101 #region Texture Fetching
99 } 102
100 103 if (assetService != null)
101 #region Texture Fetching 104 {
102 105 for (int i = 0; i < 4; i++)
103 if (assetService != null)
104 { 106 {
105 for (int i = 0; i < 4; i++) 107 AssetBase asset;
108 UUID cacheID = UUID.Combine(TERRAIN_CACHE_MAGIC, textureIDs[i]);
109
110 // Try to fetch a cached copy of the decoded/resized version of this texture
111 asset = assetService.GetCached(cacheID.ToString());
112 if (asset != null)
113 {
114 try
115 {
116 using (System.IO.MemoryStream stream = new System.IO.MemoryStream(asset.Data))
117 detailTexture[i] = (Bitmap)Image.FromStream(stream);
118 }
119 catch (Exception ex)
120 {
121 m_log.Warn("Failed to decode cached terrain texture " + cacheID +
122 " (textureID: " + textureIDs[i] + "): " + ex.Message);
123 }
124 }
125
126 if (detailTexture[i] == null)
106 { 127 {
107 AssetBase asset; 128 // Try to fetch the original JPEG2000 texture, resize if needed, and cache as PNG
108 UUID cacheID = UUID.Combine(TERRAIN_CACHE_MAGIC, textureIDs[i]); 129 asset = assetService.Get(textureIDs[i].ToString());
109
110 // Try to fetch a cached copy of the decoded/resized version of this texture
111 asset = assetService.GetCached(cacheID.ToString());
112 if (asset != null) 130 if (asset != null)
113 { 131 {
114// m_log.DebugFormat( 132// m_log.DebugFormat(
115// "[TERRAIN SPLAT]: Got asset service cached terrain texture {0} {1}", i, asset.ID); 133// "[TERRAIN SPLAT]: Got cached original JPEG2000 terrain texture {0} {1}", i, asset.ID);
116 134
117 try 135 try { detailTexture[i] = (Bitmap)CSJ2K.J2kImage.FromBytes(asset.Data); }
118 {
119 using (System.IO.MemoryStream stream = new System.IO.MemoryStream(asset.Data))
120 detailTexture[i] = (Bitmap)Image.FromStream(stream);
121 }
122 catch (Exception ex) 136 catch (Exception ex)
123 { 137 {
124 m_log.Warn("Failed to decode cached terrain texture " + cacheID + 138 m_log.Warn("Failed to decode terrain texture " + asset.ID + ": " + ex.Message);
125 " (textureID: " + textureIDs[i] + "): " + ex.Message);
126 } 139 }
127 } 140 }
128
129 if (detailTexture[i] == null)
130 {
131 // Try to fetch the original JPEG2000 texture, resize if needed, and cache as PNG
132 asset = assetService.Get(textureIDs[i].ToString());
133 if (asset != null)
134 {
135// m_log.DebugFormat(
136// "[TERRAIN SPLAT]: Got cached original JPEG2000 terrain texture {0} {1}", i, asset.ID);
137 141
138 try { detailTexture[i] = (Bitmap)CSJ2K.J2kImage.FromBytes(asset.Data); } 142 if (detailTexture[i] != null)
139 catch (Exception ex) 143 {
144 // Make sure this texture is the correct size, otherwise resize
145 if (detailTexture[i].Width != 256 || detailTexture[i].Height != 256)
146 {
147 using (Bitmap origBitmap = detailTexture[i])
140 { 148 {
141 m_log.Warn("Failed to decode terrain texture " + asset.ID + ": " + ex.Message); 149 detailTexture[i] = ImageUtils.ResizeImage(origBitmap, 256, 256);
142 } 150 }
143 } 151 }
144 152
145 if (detailTexture[i] != null) 153 // Save the decoded and resized texture to the cache
146 { 154 byte[] data;
147 // Make sure this texture is the correct size, otherwise resize 155 using (System.IO.MemoryStream stream = new System.IO.MemoryStream())
148 if (detailTexture[i].Width != 256 || detailTexture[i].Height != 256) 156 {
149 { 157 detailTexture[i].Save(stream, ImageFormat.Png);
150 using (Bitmap origBitmap = detailTexture[i]) 158 data = stream.ToArray();
151 {
152 detailTexture[i] = ImageUtils.ResizeImage(origBitmap, 256, 256);
153 }
154 }
155
156 // Save the decoded and resized texture to the cache
157 byte[] data;
158 using (System.IO.MemoryStream stream = new System.IO.MemoryStream())
159 {
160 detailTexture[i].Save(stream, ImageFormat.Png);
161 data = stream.ToArray();
162 }
163
164 // Cache a PNG copy of this terrain texture
165 AssetBase newAsset = new AssetBase
166 {
167 Data = data,
168 Description = "PNG",
169 Flags = AssetFlags.Collectable,
170 FullID = cacheID,
171 ID = cacheID.ToString(),
172 Local = true,
173 Name = String.Empty,
174 Temporary = true,
175 Type = (sbyte)AssetType.Unknown
176 };
177 newAsset.Metadata.ContentType = "image/png";
178 assetService.Store(newAsset);
179 } 159 }
160
161 // Cache a PNG copy of this terrain texture
162 AssetBase newAsset = new AssetBase
163 {
164 Data = data,
165 Description = "PNG",
166 Flags = AssetFlags.Collectable,
167 FullID = cacheID,
168 ID = cacheID.ToString(),
169 Local = true,
170 Name = String.Empty,
171 Temporary = true,
172 Type = (sbyte)AssetType.Unknown
173 };
174 newAsset.Metadata.ContentType = "image/png";
175 assetService.Store(newAsset);
180 } 176 }
181 } 177 }
182 } 178 }
183
184 #endregion Texture Fetching
185 } 179 }
186 180
187 // Fill in any missing textures with a solid color 181 #endregion Texture Fetching
188 for (int i = 0; i < 4; i++) 182 }
183
184 // Fill in any missing textures with a solid color
185 for (int i = 0; i < 4; i++)
186 {
187 if (detailTexture[i] == null)
189 { 188 {
190 if (detailTexture[i] == null) 189 m_log.DebugFormat("{0} Missing terrain texture for layer {1}. Filling with solid default color",
190 LogHeader, i);
191 // Create a solid color texture for this layer
192 detailTexture[i] = new Bitmap(256, 256, PixelFormat.Format24bppRgb);
193 using (Graphics gfx = Graphics.FromImage(detailTexture[i]))
191 { 194 {
192// m_log.DebugFormat( 195 using (SolidBrush brush = new SolidBrush(DEFAULT_TERRAIN_COLOR[i]))
193// "[TERRAIN SPLAT]: Generating solid colour for missing texture {0}", i); 196 gfx.FillRectangle(brush, 0, 0, 256, 256);
194
195 // Create a solid color texture for this layer
196 detailTexture[i] = new Bitmap(256, 256, PixelFormat.Format24bppRgb);
197 using (Graphics gfx = Graphics.FromImage(detailTexture[i]))
198 {
199 using (SolidBrush brush = new SolidBrush(DEFAULT_TERRAIN_COLOR[i]))
200 gfx.FillRectangle(brush, 0, 0, 256, 256);
201 }
202 } 197 }
203 } 198 }
204 199 else
205 #region Layer Map
206
207 float[] layermap = new float[256 * 256];
208
209 for (int y = 0; y < 256; y++)
210 { 200 {
211 for (int x = 0; x < 256; x++) 201 if (detailTexture[i].Width != 256 || detailTexture[i].Height != 256)
212 { 202 {
213 float height = heightmap[y * 256 + x]; 203 detailTexture[i] = ResizeBitmap(detailTexture[i], 256, 256);
214
215 float pctX = (float)x / 255f;
216 float pctY = (float)y / 255f;
217
218 // Use bilinear interpolation between the four corners of start height and
219 // height range to select the current values at this position
220 float startHeight = ImageUtils.Bilinear(
221 startHeights[0],
222 startHeights[2],
223 startHeights[1],
224 startHeights[3],
225 pctX, pctY);
226 startHeight = Utils.Clamp(startHeight, 0f, 255f);
227
228 float heightRange = ImageUtils.Bilinear(
229 heightRanges[0],
230 heightRanges[2],
231 heightRanges[1],
232 heightRanges[3],
233 pctX, pctY);
234 heightRange = Utils.Clamp(heightRange, 0f, 255f);
235
236 // Generate two frequencies of perlin noise based on our global position
237 // The magic values were taken from http://opensimulator.org/wiki/Terrain_Splatting
238 Vector3 vec = new Vector3
239 (
240 ((float)regionPosition.X + x) * 0.20319f,
241 ((float)regionPosition.Y + y) * 0.20319f,
242 height * 0.25f
243 );
244
245 float lowFreq = Perlin.noise2(vec.X * 0.222222f, vec.Y * 0.222222f) * 6.5f;
246 float highFreq = Perlin.turbulence2(vec.X, vec.Y, 2f) * 2.25f;
247 float noise = (lowFreq + highFreq) * 2f;
248
249 // Combine the current height, generated noise, start height, and height range parameters, then scale all of it
250 float layer = ((height + noise - startHeight) / heightRange) * 4f;
251 if (Single.IsNaN(layer)) layer = 0f;
252 layermap[y * 256 + x] = Utils.Clamp(layer, 0f, 3f);
253 } 204 }
254 } 205 }
255 206 }
256 #endregion Layer Map 207
257 208 #region Layer Map
258 #region Texture Compositing 209
259 210 float[,] layermap = new float[256, 256];
260 output = new Bitmap(256, 256, PixelFormat.Format24bppRgb); 211
261 outputData = output.LockBits(new Rectangle(0, 0, 256, 256), ImageLockMode.WriteOnly, PixelFormat.Format24bppRgb); 212 // Scale difference between actual region size and the 256 texture being created
262 213 int xFactor = terrain.Width / 256;
263 unsafe 214 int yFactor = terrain.Height / 256;
215
216 // Create 'layermap' where each value is the fractional layer number to place
217 // at that point. For instance, a value of 1.345 gives the blending of
218 // layer 1 and layer 2 for that point.
219 for (int y = 0; y < 256; y++)
220 {
221 for (int x = 0; x < 256; x++)
264 { 222 {
265 // Get handles to all of the texture data arrays 223 float height = (float)terrain[x * xFactor, y * yFactor];
266 BitmapData[] datas = new BitmapData[] 224
267 { 225 float pctX = (float)x / 255f;
268 detailTexture[0].LockBits(new Rectangle(0, 0, 256, 256), ImageLockMode.ReadOnly, detailTexture[0].PixelFormat), 226 float pctY = (float)y / 255f;
269 detailTexture[1].LockBits(new Rectangle(0, 0, 256, 256), ImageLockMode.ReadOnly, detailTexture[1].PixelFormat), 227
270 detailTexture[2].LockBits(new Rectangle(0, 0, 256, 256), ImageLockMode.ReadOnly, detailTexture[2].PixelFormat), 228 // Use bilinear interpolation between the four corners of start height and
271 detailTexture[3].LockBits(new Rectangle(0, 0, 256, 256), ImageLockMode.ReadOnly, detailTexture[3].PixelFormat) 229 // height range to select the current values at this position
272 }; 230 float startHeight = ImageUtils.Bilinear(
273 231 startHeights[0],
274 int[] comps = new int[] 232 startHeights[2],
275 { 233 startHeights[1],
276 (datas[0].PixelFormat == PixelFormat.Format32bppArgb) ? 4 : 3, 234 startHeights[3],
277 (datas[1].PixelFormat == PixelFormat.Format32bppArgb) ? 4 : 3, 235 pctX, pctY);
278 (datas[2].PixelFormat == PixelFormat.Format32bppArgb) ? 4 : 3, 236 startHeight = Utils.Clamp(startHeight, 0f, 255f);
279 (datas[3].PixelFormat == PixelFormat.Format32bppArgb) ? 4 : 3 237
280 }; 238 float heightRange = ImageUtils.Bilinear(
281 239 heightRanges[0],
282 for (int y = 0; y < 256; y++) 240 heightRanges[2],
283 { 241 heightRanges[1],
284 for (int x = 0; x < 256; x++) 242 heightRanges[3],
285 { 243 pctX, pctY);
286 float layer = layermap[y * 256 + x]; 244 heightRange = Utils.Clamp(heightRange, 0f, 255f);
287 245
288 // Select two textures 246 // Generate two frequencies of perlin noise based on our global position
289 int l0 = (int)Math.Floor(layer); 247 // The magic values were taken from http://opensimulator.org/wiki/Terrain_Splatting
290 int l1 = Math.Min(l0 + 1, 3); 248 Vector3 vec = new Vector3
291 249 (
292 byte* ptrA = (byte*)datas[l0].Scan0 + y * datas[l0].Stride + x * comps[l0]; 250 ((float)regionPosition.X + (x * xFactor)) * 0.20319f,
293 byte* ptrB = (byte*)datas[l1].Scan0 + y * datas[l1].Stride + x * comps[l1]; 251 ((float)regionPosition.Y + (y * yFactor)) * 0.20319f,
294 byte* ptrO = (byte*)outputData.Scan0 + y * outputData.Stride + x * 3; 252 height * 0.25f
295 253 );
296 float aB = *(ptrA + 0); 254
297 float aG = *(ptrA + 1); 255 float lowFreq = Perlin.noise2(vec.X * 0.222222f, vec.Y * 0.222222f) * 6.5f;
298 float aR = *(ptrA + 2); 256 float highFreq = Perlin.turbulence2(vec.X, vec.Y, 2f) * 2.25f;
299 257 float noise = (lowFreq + highFreq) * 2f;
300 float bB = *(ptrB + 0); 258
301 float bG = *(ptrB + 1); 259 // Combine the current height, generated noise, start height, and height range parameters, then scale all of it
302 float bR = *(ptrB + 2); 260 float layer = ((height + noise - startHeight) / heightRange) * 4f;
303 261 if (Single.IsNaN(layer))
304 float layerDiff = layer - l0; 262 layer = 0f;
305 263 layermap[x, y] = Utils.Clamp(layer, 0f, 3f);
306 // Interpolate between the two selected textures
307 *(ptrO + 0) = (byte)Math.Floor(aB + layerDiff * (bB - aB));
308 *(ptrO + 1) = (byte)Math.Floor(aG + layerDiff * (bG - aG));
309 *(ptrO + 2) = (byte)Math.Floor(aR + layerDiff * (bR - aR));
310 }
311 }
312
313 for (int i = 0; i < 4; i++)
314 detailTexture[i].UnlockBits(datas[i]);
315 } 264 }
316 } 265 }
317 finally 266
267 #endregion Layer Map
268
269 #region Texture Compositing
270
271 Bitmap output = new Bitmap(256, 256, PixelFormat.Format24bppRgb);
272 BitmapData outputData = output.LockBits(new Rectangle(0, 0, 256, 256), ImageLockMode.WriteOnly, PixelFormat.Format24bppRgb);
273
274 // Unsafe work as we lock down the source textures for quicker access and access the
275 // pixel data directly
276 unsafe
318 { 277 {
319 for (int i = 0; i < 4; i++) 278 // Get handles to all of the texture data arrays
320 if (detailTexture[i] != null) 279 BitmapData[] datas = new BitmapData[]
321 detailTexture[i].Dispose(); 280 {
281 detailTexture[0].LockBits(new Rectangle(0, 0, 256, 256), ImageLockMode.ReadOnly, detailTexture[0].PixelFormat),
282 detailTexture[1].LockBits(new Rectangle(0, 0, 256, 256), ImageLockMode.ReadOnly, detailTexture[1].PixelFormat),
283 detailTexture[2].LockBits(new Rectangle(0, 0, 256, 256), ImageLockMode.ReadOnly, detailTexture[2].PixelFormat),
284 detailTexture[3].LockBits(new Rectangle(0, 0, 256, 256), ImageLockMode.ReadOnly, detailTexture[3].PixelFormat)
285 };
286
287 // Compute size of each pixel data (used to address into the pixel data array)
288 int[] comps = new int[]
289 {
290 (datas[0].PixelFormat == PixelFormat.Format32bppArgb) ? 4 : 3,
291 (datas[1].PixelFormat == PixelFormat.Format32bppArgb) ? 4 : 3,
292 (datas[2].PixelFormat == PixelFormat.Format32bppArgb) ? 4 : 3,
293 (datas[3].PixelFormat == PixelFormat.Format32bppArgb) ? 4 : 3
294 };
295
296 for (int y = 0; y < 256; y++)
297 {
298 for (int x = 0; x < 256; x++)
299 {
300 float layer = layermap[x, y];
301
302 // Select two textures
303 int l0 = (int)Math.Floor(layer);
304 int l1 = Math.Min(l0 + 1, 3);
305
306 byte* ptrA = (byte*)datas[l0].Scan0 + y * datas[l0].Stride + x * comps[l0];
307 byte* ptrB = (byte*)datas[l1].Scan0 + y * datas[l1].Stride + x * comps[l1];
308 byte* ptrO = (byte*)outputData.Scan0 + y * outputData.Stride + x * 3;
309
310 float aB = *(ptrA + 0);
311 float aG = *(ptrA + 1);
312 float aR = *(ptrA + 2);
313
314 float bB = *(ptrB + 0);
315 float bG = *(ptrB + 1);
316 float bR = *(ptrB + 2);
317
318 float layerDiff = layer - l0;
319
320 // Interpolate between the two selected textures
321 *(ptrO + 0) = (byte)Math.Floor(aB + layerDiff * (bB - aB));
322 *(ptrO + 1) = (byte)Math.Floor(aG + layerDiff * (bG - aG));
323 *(ptrO + 2) = (byte)Math.Floor(aR + layerDiff * (bR - aR));
324 }
325 }
326
327 for (int i = 0; i < detailTexture.Length; i++)
328 detailTexture[i].UnlockBits(datas[i]);
322 } 329 }
323 330
331 for (int i = 0; i < detailTexture.Length; i++)
332 if (detailTexture[i] != null)
333 detailTexture[i].Dispose();
334
324 output.UnlockBits(outputData); 335 output.UnlockBits(outputData);
325 336
326 // We generated the texture upside down, so flip it 337 // We generated the texture upside down, so flip it
@@ -331,6 +342,17 @@ namespace OpenSim.Region.CoreModules.World.Warp3DMap
331 return output; 342 return output;
332 } 343 }
333 344
345 public static Bitmap ResizeBitmap(Bitmap b, int nWidth, int nHeight)
346 {
347 m_log.DebugFormat("{0} ResizeBitmap. From <{1},{2}> to <{3},{4}>",
348 LogHeader, b.Width, b.Height, nWidth, nHeight);
349 Bitmap result = new Bitmap(nWidth, nHeight);
350 using (Graphics g = Graphics.FromImage(result))
351 g.DrawImage(b, 0, 0, nWidth, nHeight);
352 b.Dispose();
353 return result;
354 }
355
334 public static Bitmap SplatSimple(float[] heightmap) 356 public static Bitmap SplatSimple(float[] heightmap)
335 { 357 {
336 const float BASE_HSV_H = 93f / 360f; 358 const float BASE_HSV_H = 93f / 360f;
diff --git a/OpenSim/Region/CoreModules/World/Warp3DMap/Warp3DImageModule.cs b/OpenSim/Region/CoreModules/World/Warp3DMap/Warp3DImageModule.cs
index 6d9e31b..f072268 100644
--- a/OpenSim/Region/CoreModules/World/Warp3DMap/Warp3DImageModule.cs
+++ b/OpenSim/Region/CoreModules/World/Warp3DMap/Warp3DImageModule.cs
@@ -31,21 +31,25 @@ using System.Drawing;
31using System.Drawing.Imaging; 31using System.Drawing.Imaging;
32using System.IO; 32using System.IO;
33using System.Reflection; 33using System.Reflection;
34
34using CSJ2K; 35using CSJ2K;
35using Nini.Config; 36using Nini.Config;
36using log4net; 37using log4net;
37using Rednettle.Warp3D; 38using Rednettle.Warp3D;
38using Mono.Addins; 39using Mono.Addins;
39using OpenMetaverse; 40
40using OpenMetaverse.Imaging;
41using OpenMetaverse.Rendering;
42using OpenMetaverse.StructuredData;
43using OpenSim.Framework; 41using OpenSim.Framework;
44using OpenSim.Region.Framework.Interfaces; 42using OpenSim.Region.Framework.Interfaces;
45using OpenSim.Region.Framework.Scenes; 43using OpenSim.Region.Framework.Scenes;
46using OpenSim.Region.Physics.Manager; 44using OpenSim.Region.Physics.Manager;
47using OpenSim.Services.Interfaces; 45using OpenSim.Services.Interfaces;
48 46
47using OpenMetaverse;
48using OpenMetaverse.Assets;
49using OpenMetaverse.Imaging;
50using OpenMetaverse.Rendering;
51using OpenMetaverse.StructuredData;
52
49using WarpRenderer = global::Warp3D.Warp3D; 53using WarpRenderer = global::Warp3D.Warp3D;
50 54
51namespace OpenSim.Region.CoreModules.World.Warp3DMap 55namespace OpenSim.Region.CoreModules.World.Warp3DMap
@@ -57,12 +61,20 @@ namespace OpenSim.Region.CoreModules.World.Warp3DMap
57 private static readonly Color4 WATER_COLOR = new Color4(29, 72, 96, 216); 61 private static readonly Color4 WATER_COLOR = new Color4(29, 72, 96, 216);
58 62
59 private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); 63 private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
64 private static string LogHeader = "[WARP 3D IMAGE MODULE]";
60 65
61 private Scene m_scene; 66 private Scene m_scene;
62 private IRendering m_primMesher; 67 private IRendering m_primMesher;
63 private IConfigSource m_config;
64 private Dictionary<UUID, Color4> m_colors = new Dictionary<UUID, Color4>(); 68 private Dictionary<UUID, Color4> m_colors = new Dictionary<UUID, Color4>();
65 private bool m_useAntiAliasing = false; // TODO: Make this a config option 69
70 private IConfigSource m_config;
71 private bool m_drawPrimVolume = true; // true if should render the prims on the tile
72 private bool m_textureTerrain = true; // true if to create terrain splatting texture
73 private bool m_texturePrims = true; // true if should texture the rendered prims
74 private float m_texturePrimSize = 48f; // size of prim before we consider texturing it
75 private bool m_renderMeshes = false; // true if to render meshes rather than just bounding boxes
76 private bool m_useAntiAliasing = false; // true if to anti-alias the rendered image
77
66 private bool m_Enabled = false; 78 private bool m_Enabled = false;
67 79
68 #region Region Module interface 80 #region Region Module interface
@@ -71,11 +83,27 @@ namespace OpenSim.Region.CoreModules.World.Warp3DMap
71 { 83 {
72 m_config = source; 84 m_config = source;
73 85
86 string[] configSections = new string[] { "Map", "Startup" };
87
74 if (Util.GetConfigVarFromSections<string>( 88 if (Util.GetConfigVarFromSections<string>(
75 m_config, "MapImageModule", new string[] { "Startup", "Map" }, "MapImageModule") != "Warp3DImageModule") 89 m_config, "MapImageModule", configSections, "MapImageModule") != "Warp3DImageModule")
76 return; 90 return;
77 91
78 m_Enabled = true; 92 m_Enabled = true;
93
94 m_drawPrimVolume
95 = Util.GetConfigVarFromSections<bool>(m_config, "DrawPrimOnMapTile", configSections, m_drawPrimVolume);
96 m_textureTerrain
97 = Util.GetConfigVarFromSections<bool>(m_config, "TextureOnMapTile", configSections, m_textureTerrain);
98 m_texturePrims
99 = Util.GetConfigVarFromSections<bool>(m_config, "TexturePrims", configSections, m_texturePrims);
100 m_texturePrimSize
101 = Util.GetConfigVarFromSections<float>(m_config, "TexturePrimSize", configSections, m_texturePrimSize);
102 m_renderMeshes
103 = Util.GetConfigVarFromSections<bool>(m_config, "RenderMeshes", configSections, m_renderMeshes);
104 m_useAntiAliasing
105 = Util.GetConfigVarFromSections<bool>(m_config, "UseAntiAliasing", configSections, m_useAntiAliasing);
106
79 } 107 }
80 108
81 public void AddRegion(Scene scene) 109 public void AddRegion(Scene scene)
@@ -127,31 +155,28 @@ namespace OpenSim.Region.CoreModules.World.Warp3DMap
127 155
128 public Bitmap CreateMapTile() 156 public Bitmap CreateMapTile()
129 { 157 {
130 Vector3 camPos = new Vector3(127.5f, 127.5f, 221.7025033688163f); 158 // Vector3 camPos = new Vector3(127.5f, 127.5f, 221.7025033688163f);
159 // Camera above the middle of the region
160 Vector3 camPos = new Vector3(
161 m_scene.RegionInfo.RegionSizeX/2 - 0.5f,
162 m_scene.RegionInfo.RegionSizeY/2 - 0.5f,
163 221.7025033688163f);
164 // Viewport viewing down onto the region
131 Viewport viewport = new Viewport(camPos, -Vector3.UnitZ, 1024f, 0.1f, 165 Viewport viewport = new Viewport(camPos, -Vector3.UnitZ, 1024f, 0.1f,
132 (int)Constants.RegionSize, (int)Constants.RegionSize, 166 (int)m_scene.RegionInfo.RegionSizeX, (int)m_scene.RegionInfo.RegionSizeY,
133 (float)Constants.RegionSize, (float)Constants.RegionSize); 167 (float)m_scene.RegionInfo.RegionSizeX, (float)m_scene.RegionInfo.RegionSizeY );
168 // Fill the viewport and return the image
134 return CreateMapTile(viewport, false); 169 return CreateMapTile(viewport, false);
135 } 170 }
136 171
137 public Bitmap CreateViewImage(Vector3 camPos, Vector3 camDir, float fov, int width, int height, bool useTextures) 172 public Bitmap CreateViewImage(Vector3 camPos, Vector3 camDir, float fov, int width, int height, bool useTextures)
138 { 173 {
139 Viewport viewport = new Viewport(camPos, camDir, fov, (float)Constants.RegionSize, 0.1f, width, height); 174 Viewport viewport = new Viewport(camPos, camDir, fov, Constants.RegionSize, 0.1f, width, height);
140 return CreateMapTile(viewport, useTextures); 175 return CreateMapTile(viewport, useTextures);
141 } 176 }
142 177
143 public Bitmap CreateMapTile(Viewport viewport, bool useTextures) 178 public Bitmap CreateMapTile(Viewport viewport, bool useTextures)
144 { 179 {
145 bool drawPrimVolume = true;
146 bool textureTerrain = true;
147
148 string[] configSections = new string[] { "Map", "Startup" };
149
150 drawPrimVolume
151 = Util.GetConfigVarFromSections<bool>(m_config, "DrawPrimOnMapTile", configSections, drawPrimVolume);
152 textureTerrain
153 = Util.GetConfigVarFromSections<bool>(m_config, "TextureOnMapTile", configSections, textureTerrain);
154
155 m_colors.Clear(); 180 m_colors.Clear();
156 181
157 int width = viewport.Width; 182 int width = viewport.Width;
@@ -195,8 +220,8 @@ namespace OpenSim.Region.CoreModules.World.Warp3DMap
195 renderer.Scene.addLight("Light2", new warp_Light(new warp_Vector(-1f, -1f, 1f), 0xffffff, 0, 100, 40)); 220 renderer.Scene.addLight("Light2", new warp_Light(new warp_Vector(-1f, -1f, 1f), 0xffffff, 0, 100, 40));
196 221
197 CreateWater(renderer); 222 CreateWater(renderer);
198 CreateTerrain(renderer, textureTerrain); 223 CreateTerrain(renderer, m_textureTerrain);
199 if (drawPrimVolume) 224 if (m_drawPrimVolume)
200 CreateAllPrims(renderer, useTextures); 225 CreateAllPrims(renderer, useTextures);
201 226
202 renderer.Render(); 227 renderer.Render();
@@ -212,6 +237,16 @@ namespace OpenSim.Region.CoreModules.World.Warp3DMap
212 // afterwards. It's generally regarded as a bad idea to manually GC. If Warp3D is using lots of memory 237 // afterwards. It's generally regarded as a bad idea to manually GC. If Warp3D is using lots of memory
213 // then this may be some issue with the Warp3D code itself, though it's also quite possible that generating 238 // then this may be some issue with the Warp3D code itself, though it's also quite possible that generating
214 // this map tile simply takes a lot of memory. 239 // this map tile simply takes a lot of memory.
240 foreach (var o in renderer.Scene.objectData.Values)
241 {
242 warp_Object obj = (warp_Object)o;
243 obj.vertexData = null;
244 obj.triangleData = null;
245 }
246 renderer.Scene.removeAllObjects();
247 renderer = null;
248 viewport = null;
249 m_colors.Clear();
215 GC.Collect(); 250 GC.Collect();
216 m_log.Debug("[WARP 3D IMAGE MODULE]: GC.Collect()"); 251 m_log.Debug("[WARP 3D IMAGE MODULE]: GC.Collect()");
217 252
@@ -238,61 +273,74 @@ namespace OpenSim.Region.CoreModules.World.Warp3DMap
238 273
239 #region Rendering Methods 274 #region Rendering Methods
240 275
276 // Add a water plane to the renderer.
241 private void CreateWater(WarpRenderer renderer) 277 private void CreateWater(WarpRenderer renderer)
242 { 278 {
243 float waterHeight = (float)m_scene.RegionInfo.RegionSettings.WaterHeight; 279 float waterHeight = (float)m_scene.RegionInfo.RegionSettings.WaterHeight;
244 280
245 renderer.AddPlane("Water", 256f * 0.5f); 281 renderer.AddPlane("Water", m_scene.RegionInfo.RegionSizeX * 0.5f);
246 renderer.Scene.sceneobject("Water").setPos(127.5f, waterHeight, 127.5f); 282 renderer.Scene.sceneobject("Water").setPos(m_scene.RegionInfo.RegionSizeX/2 - 0.5f,
283 waterHeight,
284 m_scene.RegionInfo.RegionSizeY/2 - 0.5f );
247 285
248 renderer.AddMaterial("WaterColor", ConvertColor(WATER_COLOR)); 286 warp_Material waterColorMaterial = new warp_Material(ConvertColor(WATER_COLOR));
249 renderer.Scene.material("WaterColor").setReflectivity(0); // match water color with standard map module thanks lkalif 287 waterColorMaterial.setReflectivity(0); // match water color with standard map module thanks lkalif
250 renderer.Scene.material("WaterColor").setTransparency((byte)((1f - WATER_COLOR.A) * 255f)); 288 waterColorMaterial.setTransparency((byte)((1f - WATER_COLOR.A) * 255f));
289 renderer.Scene.addMaterial("WaterColor", waterColorMaterial);
251 renderer.SetObjectMaterial("Water", "WaterColor"); 290 renderer.SetObjectMaterial("Water", "WaterColor");
252 } 291 }
253 292
293 // Add a terrain to the renderer.
294 // Note that we create a 'low resolution' 256x256 vertex terrain rather than trying for
295 // full resolution. This saves a lot of memory especially for very large regions.
254 private void CreateTerrain(WarpRenderer renderer, bool textureTerrain) 296 private void CreateTerrain(WarpRenderer renderer, bool textureTerrain)
255 { 297 {
256 ITerrainChannel terrain = m_scene.Heightmap; 298 ITerrainChannel terrain = m_scene.Heightmap;
257 float[] heightmap = terrain.GetFloatsSerialised(); 299
300 // 'diff' is the difference in scale between the real region size and the size of terrain we're buiding
301 float diff = (float)m_scene.RegionInfo.RegionSizeX / 256f;
258 302
259 warp_Object obj = new warp_Object(256 * 256, 255 * 255 * 2); 303 warp_Object obj = new warp_Object(256 * 256, 255 * 255 * 2);
260 304
261 for (int y = 0; y < 256; y++) 305 // Create all the vertices for the terrain
306 for (float y = 0; y < m_scene.RegionInfo.RegionSizeY; y += diff)
262 { 307 {
263 for (int x = 0; x < 256; x++) 308 for (float x = 0; x < m_scene.RegionInfo.RegionSizeX; x += diff)
264 { 309 {
265 int v = y * 256 + x; 310 warp_Vector pos = ConvertVector(x, y, (float)terrain[(int)x, (int)y]);
266 float height = heightmap[v]; 311 obj.addVertex(new warp_Vertex(pos,
267 312 x / (float)m_scene.RegionInfo.RegionSizeX,
268 warp_Vector pos = ConvertVector(new Vector3(x, y, height)); 313 (((float)m_scene.RegionInfo.RegionSizeY) - y) / m_scene.RegionInfo.RegionSizeY) );
269 obj.addVertex(new warp_Vertex(pos, (float)x / 255f, (float)(255 - y) / 255f));
270 } 314 }
271 } 315 }
272 316
273 for (int y = 0; y < 256; y++) 317 // Now that we have all the vertices, make another pass and create
318 // the normals for each of the surface triangles and
319 // create the list of triangle indices.
320 for (float y = 0; y < m_scene.RegionInfo.RegionSizeY; y += diff)
274 { 321 {
275 for (int x = 0; x < 256; x++) 322 for (float x = 0; x < m_scene.RegionInfo.RegionSizeX; x += diff)
276 { 323 {
277 if (x < 255 && y < 255) 324 float newX = x / diff;
325 float newY = y / diff;
326 if (newX < 255 && newY < 255)
278 { 327 {
279 int v = y * 256 + x; 328 int v = (int)newY * 256 + (int)newX;
280 329
281 // Normal 330 // Normal for a triangle made up of three adjacent vertices
282 Vector3 v1 = new Vector3(x, y, heightmap[y * 256 + x]); 331 Vector3 v1 = new Vector3(newX, newY, (float)terrain[(int)x, (int)y]);
283 Vector3 v2 = new Vector3(x + 1, y, heightmap[y * 256 + x + 1]); 332 Vector3 v2 = new Vector3(newX + 1, newY, (float)terrain[(int)(x + 1), (int)y]);
284 Vector3 v3 = new Vector3(x, y + 1, heightmap[(y + 1) * 256 + x]); 333 Vector3 v3 = new Vector3(newX, newY + 1, (float)terrain[(int)x, ((int)(y + 1))]);
285 warp_Vector norm = ConvertVector(SurfaceNormal(v1, v2, v3)); 334 warp_Vector norm = ConvertVector(SurfaceNormal(v1, v2, v3));
286 norm = norm.reverse(); 335 norm = norm.reverse();
287 obj.vertex(v).n = norm; 336 obj.vertex(v).n = norm;
288 337
289 // Triangle 1 338 // Make two triangles for each of the squares in the grid of vertices
290 obj.addTriangle( 339 obj.addTriangle(
291 v, 340 v,
292 v + 1, 341 v + 1,
293 v + 256); 342 v + 256);
294 343
295 // Triangle 2
296 obj.addTriangle( 344 obj.addTriangle(
297 v + 256 + 1, 345 v + 256 + 1,
298 v + 256, 346 v + 256,
@@ -307,7 +355,7 @@ namespace OpenSim.Region.CoreModules.World.Warp3DMap
307 float[] startHeights = new float[4]; 355 float[] startHeights = new float[4];
308 float[] heightRanges = new float[4]; 356 float[] heightRanges = new float[4];
309 357
310 RegionSettings regionInfo = m_scene.RegionInfo.RegionSettings; 358 OpenSim.Framework.RegionSettings regionInfo = m_scene.RegionInfo.RegionSettings;
311 359
312 textureIDs[0] = regionInfo.TerrainTexture1; 360 textureIDs[0] = regionInfo.TerrainTexture1;
313 textureIDs[1] = regionInfo.TerrainTexture2; 361 textureIDs[1] = regionInfo.TerrainTexture2;
@@ -325,16 +373,18 @@ namespace OpenSim.Region.CoreModules.World.Warp3DMap
325 heightRanges[3] = (float)regionInfo.Elevation2NE; 373 heightRanges[3] = (float)regionInfo.Elevation2NE;
326 374
327 uint globalX, globalY; 375 uint globalX, globalY;
328 Utils.LongToUInts(m_scene.RegionInfo.RegionHandle, out globalX, out globalY); 376 Util.RegionHandleToWorldLoc(m_scene.RegionInfo.RegionHandle, out globalX, out globalY);
329 377
330 warp_Texture texture; 378 warp_Texture texture;
331
332 using ( 379 using (
333 Bitmap image 380 Bitmap image
334 = TerrainSplat.Splat( 381 = TerrainSplat.Splat(terrain, textureIDs, startHeights, heightRanges,
335 heightmap, textureIDs, startHeights, heightRanges,
336 new Vector3d(globalX, globalY, 0.0), m_scene.AssetService, textureTerrain)) 382 new Vector3d(globalX, globalY, 0.0), m_scene.AssetService, textureTerrain))
337 { 383 {
384 image.Save( // DEBUG DEBUG
385 String.Format("maptiles/splat-{0}-{1}-{2}.jpg", m_scene.RegionInfo.RegionName,
386 m_scene.RegionInfo.RegionLocX, m_scene.RegionInfo.RegionLocY),
387 ImageFormat.Jpeg);
338 texture = new warp_Texture(image); 388 texture = new warp_Texture(image);
339 } 389 }
340 390
@@ -370,8 +420,48 @@ namespace OpenSim.Region.CoreModules.World.Warp3DMap
370 if (prim.Scale.LengthSquared() < MIN_SIZE * MIN_SIZE) 420 if (prim.Scale.LengthSquared() < MIN_SIZE * MIN_SIZE)
371 return; 421 return;
372 422
423 FacetedMesh renderMesh = null;
373 Primitive omvPrim = prim.Shape.ToOmvPrimitive(prim.OffsetPosition, prim.RotationOffset); 424 Primitive omvPrim = prim.Shape.ToOmvPrimitive(prim.OffsetPosition, prim.RotationOffset);
374 FacetedMesh renderMesh = m_primMesher.GenerateFacetedMesh(omvPrim, DetailLevel.Medium); 425
426 if (m_renderMeshes)
427 {
428 if (omvPrim.Sculpt != null && omvPrim.Sculpt.SculptTexture != UUID.Zero)
429 {
430 // Try fetchinng the asset
431 byte[] sculptAsset = m_scene.AssetService.GetData(omvPrim.Sculpt.SculptTexture.ToString());
432 if (sculptAsset != null)
433 {
434 // Is it a mesh?
435 if (omvPrim.Sculpt.Type == SculptType.Mesh)
436 {
437 AssetMesh meshAsset = new AssetMesh(omvPrim.Sculpt.SculptTexture, sculptAsset);
438 FacetedMesh.TryDecodeFromAsset(omvPrim, meshAsset, DetailLevel.Highest, out renderMesh);
439 meshAsset = null;
440 }
441 else // It's sculptie
442 {
443 IJ2KDecoder imgDecoder = m_scene.RequestModuleInterface<IJ2KDecoder>();
444 if (imgDecoder != null)
445 {
446 Image sculpt = imgDecoder.DecodeToImage(sculptAsset);
447 if (sculpt != null)
448 {
449 renderMesh = m_primMesher.GenerateFacetedSculptMesh(omvPrim, (Bitmap)sculpt,
450 DetailLevel.Medium);
451 sculpt.Dispose();
452 }
453 }
454 }
455 }
456 }
457 }
458
459 // If not a mesh or sculptie, try the regular mesher
460 if (renderMesh == null)
461 {
462 renderMesh = m_primMesher.GenerateFacetedMesh(omvPrim, DetailLevel.Medium);
463 }
464
375 if (renderMesh == null) 465 if (renderMesh == null)
376 return; 466 return;
377 467
@@ -430,7 +520,11 @@ namespace OpenSim.Region.CoreModules.World.Warp3DMap
430 520
431 Primitive.TextureEntryFace teFace = prim.Shape.Textures.GetFace((uint)i); 521 Primitive.TextureEntryFace teFace = prim.Shape.Textures.GetFace((uint)i);
432 Color4 faceColor = GetFaceColor(teFace); 522 Color4 faceColor = GetFaceColor(teFace);
433 string materialName = GetOrCreateMaterial(renderer, faceColor); 523 string materialName = String.Empty;
524 if (m_texturePrims && prim.Scale.LengthSquared() > m_texturePrimSize*m_texturePrimSize)
525 materialName = GetOrCreateMaterial(renderer, faceColor, teFace.TextureID);
526 else
527 materialName = GetOrCreateMaterial(renderer, faceColor);
434 528
435 faceObj.transform(m); 529 faceObj.transform(m);
436 faceObj.setPos(primPos); 530 faceObj.setPos(primPos);
@@ -519,10 +613,51 @@ namespace OpenSim.Region.CoreModules.World.Warp3DMap
519 return name; 613 return name;
520 } 614 }
521 615
616 public string GetOrCreateMaterial(WarpRenderer renderer, Color4 faceColor, UUID textureID)
617 {
618 string materialName = "Color-" + faceColor.ToString() + "-Texture-" + textureID.ToString();
619
620 if (renderer.Scene.material(materialName) == null)
621 {
622 renderer.AddMaterial(materialName, ConvertColor(faceColor));
623 if (faceColor.A < 1f)
624 {
625 renderer.Scene.material(materialName).setTransparency((byte) ((1f - faceColor.A)*255f));
626 }
627 warp_Texture texture = GetTexture(textureID);
628 if (texture != null)
629 renderer.Scene.material(materialName).setTexture(texture);
630 }
631
632 return materialName;
633 }
634
635 private warp_Texture GetTexture(UUID id)
636 {
637 warp_Texture ret = null;
638 byte[] asset = m_scene.AssetService.GetData(id.ToString());
639 if (asset != null)
640 {
641 IJ2KDecoder imgDecoder = m_scene.RequestModuleInterface<IJ2KDecoder>();
642 Bitmap img = (Bitmap) imgDecoder.DecodeToImage(asset);
643 if (img != null)
644 {
645 return new warp_Texture(img);
646 }
647 }
648 return ret;
649 }
650
522 #endregion Rendering Methods 651 #endregion Rendering Methods
523 652
524 #region Static Helpers 653 #region Static Helpers
525 654
655 // Note: axis change.
656 private static warp_Vector ConvertVector(float x, float y, float z)
657 {
658 return new warp_Vector(x, z, y);
659 }
660
526 private static warp_Vector ConvertVector(Vector3 vector) 661 private static warp_Vector ConvertVector(Vector3 vector)
527 { 662 {
528 return new warp_Vector(vector.X, vector.Z, vector.Y); 663 return new warp_Vector(vector.X, vector.Z, vector.Y);