aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/OpenSim/Region/CoreModules/World/Warp3DMap
diff options
context:
space:
mode:
authorDavid Walter Seikel2016-11-03 21:44:39 +1000
committerDavid Walter Seikel2016-11-03 21:44:39 +1000
commit134f86e8d5c414409631b25b8c6f0ee45fbd8631 (patch)
tree216b89d3fb89acfb81be1e440c25c41ab09fa96d /OpenSim/Region/CoreModules/World/Warp3DMap
parentMore changing to production grid. Double oops. (diff)
downloadopensim-SC_OLD-134f86e8d5c414409631b25b8c6f0ee45fbd8631.zip
opensim-SC_OLD-134f86e8d5c414409631b25b8c6f0ee45fbd8631.tar.gz
opensim-SC_OLD-134f86e8d5c414409631b25b8c6f0ee45fbd8631.tar.bz2
opensim-SC_OLD-134f86e8d5c414409631b25b8c6f0ee45fbd8631.tar.xz
Initial update to OpenSim 0.8.2.1 source code.
Diffstat (limited to 'OpenSim/Region/CoreModules/World/Warp3DMap')
-rw-r--r--OpenSim/Region/CoreModules/World/Warp3DMap/TerrainSplat.cs436
-rw-r--r--OpenSim/Region/CoreModules/World/Warp3DMap/Warp3DImageModule.cs260
2 files changed, 430 insertions, 266 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 5e0dfa7..5f2534b 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.PhysicsModules.SharedBase;
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
@@ -58,11 +62,22 @@ namespace OpenSim.Region.CoreModules.World.Warp3DMap
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);
60 64
65#pragma warning disable 414
66 private static string LogHeader = "[WARP 3D IMAGE MODULE]";
67#pragma warning restore 414
68
61 private Scene m_scene; 69 private Scene m_scene;
62 private IRendering m_primMesher; 70 private IRendering m_primMesher;
63 private IConfigSource m_config;
64 private Dictionary<UUID, Color4> m_colors = new Dictionary<UUID, Color4>(); 71 private Dictionary<UUID, Color4> m_colors = new Dictionary<UUID, Color4>();
65 private bool m_useAntiAliasing = false; // TODO: Make this a config option 72
73 private IConfigSource m_config;
74 private bool m_drawPrimVolume = true; // true if should render the prims on the tile
75 private bool m_textureTerrain = true; // true if to create terrain splatting texture
76 private bool m_texturePrims = true; // true if should texture the rendered prims
77 private float m_texturePrimSize = 48f; // size of prim before we consider texturing it
78 private bool m_renderMeshes = false; // true if to render meshes rather than just bounding boxes
79 private bool m_useAntiAliasing = false; // true if to anti-alias the rendered image
80
66 private bool m_Enabled = false; 81 private bool m_Enabled = false;
67 82
68 #region Region Module interface 83 #region Region Module interface
@@ -71,11 +86,27 @@ namespace OpenSim.Region.CoreModules.World.Warp3DMap
71 { 86 {
72 m_config = source; 87 m_config = source;
73 88
74 IConfig startupConfig = m_config.Configs["Startup"]; 89 string[] configSections = new string[] { "Map", "Startup" };
75 if (startupConfig.GetString("MapImageModule", "MapImageModule") != "Warp3DImageModule") 90
91 if (Util.GetConfigVarFromSections<string>(
92 m_config, "MapImageModule", configSections, "MapImageModule") != "Warp3DImageModule")
76 return; 93 return;
77 94
78 m_Enabled = true; 95 m_Enabled = true;
96
97 m_drawPrimVolume
98 = Util.GetConfigVarFromSections<bool>(m_config, "DrawPrimOnMapTile", configSections, m_drawPrimVolume);
99 m_textureTerrain
100 = Util.GetConfigVarFromSections<bool>(m_config, "TextureOnMapTile", configSections, m_textureTerrain);
101 m_texturePrims
102 = Util.GetConfigVarFromSections<bool>(m_config, "TexturePrims", configSections, m_texturePrims);
103 m_texturePrimSize
104 = Util.GetConfigVarFromSections<float>(m_config, "TexturePrimSize", configSections, m_texturePrimSize);
105 m_renderMeshes
106 = Util.GetConfigVarFromSections<bool>(m_config, "RenderMeshes", configSections, m_renderMeshes);
107 m_useAntiAliasing
108 = Util.GetConfigVarFromSections<bool>(m_config, "UseAntiAliasing", configSections, m_useAntiAliasing);
109
79 } 110 }
80 111
81 public void AddRegion(Scene scene) 112 public void AddRegion(Scene scene)
@@ -127,33 +158,28 @@ namespace OpenSim.Region.CoreModules.World.Warp3DMap
127 158
128 public Bitmap CreateMapTile() 159 public Bitmap CreateMapTile()
129 { 160 {
130 Vector3 camPos = new Vector3(127.5f, 127.5f, 221.7025033688163f); 161 // Vector3 camPos = new Vector3(127.5f, 127.5f, 221.7025033688163f);
131 Viewport viewport = new Viewport(camPos, -Vector3.UnitZ, 1024f, 0.1f, (int)Constants.RegionSize, (int)Constants.RegionSize, (float)Constants.RegionSize, (float)Constants.RegionSize); 162 // Camera above the middle of the region
163 Vector3 camPos = new Vector3(
164 m_scene.RegionInfo.RegionSizeX/2 - 0.5f,
165 m_scene.RegionInfo.RegionSizeY/2 - 0.5f,
166 221.7025033688163f);
167 // Viewport viewing down onto the region
168 Viewport viewport = new Viewport(camPos, -Vector3.UnitZ, 1024f, 0.1f,
169 (int)m_scene.RegionInfo.RegionSizeX, (int)m_scene.RegionInfo.RegionSizeY,
170 (float)m_scene.RegionInfo.RegionSizeX, (float)m_scene.RegionInfo.RegionSizeY );
171 // Fill the viewport and return the image
132 return CreateMapTile(viewport, false); 172 return CreateMapTile(viewport, false);
133 } 173 }
134 174
135 public Bitmap CreateViewImage(Vector3 camPos, Vector3 camDir, float fov, int width, int height, bool useTextures) 175 public Bitmap CreateViewImage(Vector3 camPos, Vector3 camDir, float fov, int width, int height, bool useTextures)
136 { 176 {
137 Viewport viewport = new Viewport(camPos, camDir, fov, (float)Constants.RegionSize, 0.1f, width, height); 177 Viewport viewport = new Viewport(camPos, camDir, fov, Constants.RegionSize, 0.1f, width, height);
138 return CreateMapTile(viewport, useTextures); 178 return CreateMapTile(viewport, useTextures);
139 } 179 }
140 180
141 public Bitmap CreateMapTile(Viewport viewport, bool useTextures) 181 public Bitmap CreateMapTile(Viewport viewport, bool useTextures)
142 { 182 {
143 bool drawPrimVolume = true;
144 bool textureTerrain = true;
145
146 try
147 {
148 IConfig startupConfig = m_config.Configs["Startup"];
149 drawPrimVolume = startupConfig.GetBoolean("DrawPrimOnMapTile", drawPrimVolume);
150 textureTerrain = startupConfig.GetBoolean("TextureOnMapTile", textureTerrain);
151 }
152 catch
153 {
154 m_log.Warn("[WARP 3D IMAGE MODULE]: Failed to load StartupConfig");
155 }
156
157 m_colors.Clear(); 183 m_colors.Clear();
158 184
159 int width = viewport.Width; 185 int width = viewport.Width;
@@ -166,6 +192,7 @@ namespace OpenSim.Region.CoreModules.World.Warp3DMap
166 } 192 }
167 193
168 WarpRenderer renderer = new WarpRenderer(); 194 WarpRenderer renderer = new WarpRenderer();
195
169 renderer.CreateScene(width, height); 196 renderer.CreateScene(width, height);
170 renderer.Scene.autoCalcNormals = false; 197 renderer.Scene.autoCalcNormals = false;
171 198
@@ -197,8 +224,8 @@ namespace OpenSim.Region.CoreModules.World.Warp3DMap
197 renderer.Scene.addLight("Light2", new warp_Light(new warp_Vector(-1f, -1f, 1f), 0xffffff, 0, 100, 40)); 224 renderer.Scene.addLight("Light2", new warp_Light(new warp_Vector(-1f, -1f, 1f), 0xffffff, 0, 100, 40));
198 225
199 CreateWater(renderer); 226 CreateWater(renderer);
200 CreateTerrain(renderer, textureTerrain); 227 CreateTerrain(renderer, m_textureTerrain);
201 if (drawPrimVolume) 228 if (m_drawPrimVolume)
202 CreateAllPrims(renderer, useTextures); 229 CreateAllPrims(renderer, useTextures);
203 230
204 renderer.Render(); 231 renderer.Render();
@@ -214,6 +241,18 @@ namespace OpenSim.Region.CoreModules.World.Warp3DMap
214 // afterwards. It's generally regarded as a bad idea to manually GC. If Warp3D is using lots of memory 241 // afterwards. It's generally regarded as a bad idea to manually GC. If Warp3D is using lots of memory
215 // then this may be some issue with the Warp3D code itself, though it's also quite possible that generating 242 // then this may be some issue with the Warp3D code itself, though it's also quite possible that generating
216 // this map tile simply takes a lot of memory. 243 // this map tile simply takes a lot of memory.
244 foreach (var o in renderer.Scene.objectData.Values)
245 {
246 warp_Object obj = (warp_Object)o;
247 obj.vertexData = null;
248 obj.triangleData = null;
249 }
250
251 renderer.Scene.removeAllObjects();
252 renderer = null;
253 viewport = null;
254
255 m_colors.Clear();
217 GC.Collect(); 256 GC.Collect();
218 m_log.Debug("[WARP 3D IMAGE MODULE]: GC.Collect()"); 257 m_log.Debug("[WARP 3D IMAGE MODULE]: GC.Collect()");
219 258
@@ -240,61 +279,74 @@ namespace OpenSim.Region.CoreModules.World.Warp3DMap
240 279
241 #region Rendering Methods 280 #region Rendering Methods
242 281
282 // Add a water plane to the renderer.
243 private void CreateWater(WarpRenderer renderer) 283 private void CreateWater(WarpRenderer renderer)
244 { 284 {
245 float waterHeight = (float)m_scene.RegionInfo.RegionSettings.WaterHeight; 285 float waterHeight = (float)m_scene.RegionInfo.RegionSettings.WaterHeight;
246 286
247 renderer.AddPlane("Water", 256f * 0.5f); 287 renderer.AddPlane("Water", m_scene.RegionInfo.RegionSizeX * 0.5f);
248 renderer.Scene.sceneobject("Water").setPos(127.5f, waterHeight, 127.5f); 288 renderer.Scene.sceneobject("Water").setPos(m_scene.RegionInfo.RegionSizeX/2 - 0.5f,
289 waterHeight,
290 m_scene.RegionInfo.RegionSizeY/2 - 0.5f );
249 291
250 renderer.AddMaterial("WaterColor", ConvertColor(WATER_COLOR)); 292 warp_Material waterColorMaterial = new warp_Material(ConvertColor(WATER_COLOR));
251 renderer.Scene.material("WaterColor").setReflectivity(0); // match water color with standard map module thanks lkalif 293 waterColorMaterial.setReflectivity(0); // match water color with standard map module thanks lkalif
252 renderer.Scene.material("WaterColor").setTransparency((byte)((1f - WATER_COLOR.A) * 255f)); 294 waterColorMaterial.setTransparency((byte)((1f - WATER_COLOR.A) * 255f));
295 renderer.Scene.addMaterial("WaterColor", waterColorMaterial);
253 renderer.SetObjectMaterial("Water", "WaterColor"); 296 renderer.SetObjectMaterial("Water", "WaterColor");
254 } 297 }
255 298
299 // Add a terrain to the renderer.
300 // Note that we create a 'low resolution' 256x256 vertex terrain rather than trying for
301 // full resolution. This saves a lot of memory especially for very large regions.
256 private void CreateTerrain(WarpRenderer renderer, bool textureTerrain) 302 private void CreateTerrain(WarpRenderer renderer, bool textureTerrain)
257 { 303 {
258 ITerrainChannel terrain = m_scene.Heightmap; 304 ITerrainChannel terrain = m_scene.Heightmap;
259 float[] heightmap = terrain.GetFloatsSerialised(); 305
306 // 'diff' is the difference in scale between the real region size and the size of terrain we're buiding
307 float diff = (float)m_scene.RegionInfo.RegionSizeX / 256f;
260 308
261 warp_Object obj = new warp_Object(256 * 256, 255 * 255 * 2); 309 warp_Object obj = new warp_Object(256 * 256, 255 * 255 * 2);
262 310
263 for (int y = 0; y < 256; y++) 311 // Create all the vertices for the terrain
312 for (float y = 0; y < m_scene.RegionInfo.RegionSizeY; y += diff)
264 { 313 {
265 for (int x = 0; x < 256; x++) 314 for (float x = 0; x < m_scene.RegionInfo.RegionSizeX; x += diff)
266 { 315 {
267 int v = y * 256 + x; 316 warp_Vector pos = ConvertVector(x, y, (float)terrain[(int)x, (int)y]);
268 float height = heightmap[v]; 317 obj.addVertex(new warp_Vertex(pos,
269 318 x / (float)m_scene.RegionInfo.RegionSizeX,
270 warp_Vector pos = ConvertVector(new Vector3(x, y, height)); 319 (((float)m_scene.RegionInfo.RegionSizeY) - y) / m_scene.RegionInfo.RegionSizeY) );
271 obj.addVertex(new warp_Vertex(pos, (float)x / 255f, (float)(255 - y) / 255f));
272 } 320 }
273 } 321 }
274 322
275 for (int y = 0; y < 256; y++) 323 // Now that we have all the vertices, make another pass and create
324 // the normals for each of the surface triangles and
325 // create the list of triangle indices.
326 for (float y = 0; y < m_scene.RegionInfo.RegionSizeY; y += diff)
276 { 327 {
277 for (int x = 0; x < 256; x++) 328 for (float x = 0; x < m_scene.RegionInfo.RegionSizeX; x += diff)
278 { 329 {
279 if (x < 255 && y < 255) 330 float newX = x / diff;
331 float newY = y / diff;
332 if (newX < 255 && newY < 255)
280 { 333 {
281 int v = y * 256 + x; 334 int v = (int)newY * 256 + (int)newX;
282 335
283 // Normal 336 // Normal for a triangle made up of three adjacent vertices
284 Vector3 v1 = new Vector3(x, y, heightmap[y * 256 + x]); 337 Vector3 v1 = new Vector3(newX, newY, (float)terrain[(int)x, (int)y]);
285 Vector3 v2 = new Vector3(x + 1, y, heightmap[y * 256 + x + 1]); 338 Vector3 v2 = new Vector3(newX + 1, newY, (float)terrain[(int)(x + 1), (int)y]);
286 Vector3 v3 = new Vector3(x, y + 1, heightmap[(y + 1) * 256 + x]); 339 Vector3 v3 = new Vector3(newX, newY + 1, (float)terrain[(int)x, ((int)(y + 1))]);
287 warp_Vector norm = ConvertVector(SurfaceNormal(v1, v2, v3)); 340 warp_Vector norm = ConvertVector(SurfaceNormal(v1, v2, v3));
288 norm = norm.reverse(); 341 norm = norm.reverse();
289 obj.vertex(v).n = norm; 342 obj.vertex(v).n = norm;
290 343
291 // Triangle 1 344 // Make two triangles for each of the squares in the grid of vertices
292 obj.addTriangle( 345 obj.addTriangle(
293 v, 346 v,
294 v + 1, 347 v + 1,
295 v + 256); 348 v + 256);
296 349
297 // Triangle 2
298 obj.addTriangle( 350 obj.addTriangle(
299 v + 256 + 1, 351 v + 256 + 1,
300 v + 256, 352 v + 256,
@@ -309,7 +361,7 @@ namespace OpenSim.Region.CoreModules.World.Warp3DMap
309 float[] startHeights = new float[4]; 361 float[] startHeights = new float[4];
310 float[] heightRanges = new float[4]; 362 float[] heightRanges = new float[4];
311 363
312 RegionSettings regionInfo = m_scene.RegionInfo.RegionSettings; 364 OpenSim.Framework.RegionSettings regionInfo = m_scene.RegionInfo.RegionSettings;
313 365
314 textureIDs[0] = regionInfo.TerrainTexture1; 366 textureIDs[0] = regionInfo.TerrainTexture1;
315 textureIDs[1] = regionInfo.TerrainTexture2; 367 textureIDs[1] = regionInfo.TerrainTexture2;
@@ -327,14 +379,12 @@ namespace OpenSim.Region.CoreModules.World.Warp3DMap
327 heightRanges[3] = (float)regionInfo.Elevation2NE; 379 heightRanges[3] = (float)regionInfo.Elevation2NE;
328 380
329 uint globalX, globalY; 381 uint globalX, globalY;
330 Utils.LongToUInts(m_scene.RegionInfo.RegionHandle, out globalX, out globalY); 382 Util.RegionHandleToWorldLoc(m_scene.RegionInfo.RegionHandle, out globalX, out globalY);
331 383
332 warp_Texture texture; 384 warp_Texture texture;
333
334 using ( 385 using (
335 Bitmap image 386 Bitmap image
336 = TerrainSplat.Splat( 387 = TerrainSplat.Splat(terrain, textureIDs, startHeights, heightRanges,
337 heightmap, textureIDs, startHeights, heightRanges,
338 new Vector3d(globalX, globalY, 0.0), m_scene.AssetService, textureTerrain)) 388 new Vector3d(globalX, globalY, 0.0), m_scene.AssetService, textureTerrain))
339 { 389 {
340 texture = new warp_Texture(image); 390 texture = new warp_Texture(image);
@@ -355,7 +405,6 @@ namespace OpenSim.Region.CoreModules.World.Warp3DMap
355 m_scene.ForEachSOG( 405 m_scene.ForEachSOG(
356 delegate(SceneObjectGroup group) 406 delegate(SceneObjectGroup group)
357 { 407 {
358 CreatePrim(renderer, group.RootPart, useTextures);
359 foreach (SceneObjectPart child in group.Parts) 408 foreach (SceneObjectPart child in group.Parts)
360 CreatePrim(renderer, child, useTextures); 409 CreatePrim(renderer, child, useTextures);
361 } 410 }
@@ -372,8 +421,48 @@ namespace OpenSim.Region.CoreModules.World.Warp3DMap
372 if (prim.Scale.LengthSquared() < MIN_SIZE * MIN_SIZE) 421 if (prim.Scale.LengthSquared() < MIN_SIZE * MIN_SIZE)
373 return; 422 return;
374 423
424 FacetedMesh renderMesh = null;
375 Primitive omvPrim = prim.Shape.ToOmvPrimitive(prim.OffsetPosition, prim.RotationOffset); 425 Primitive omvPrim = prim.Shape.ToOmvPrimitive(prim.OffsetPosition, prim.RotationOffset);
376 FacetedMesh renderMesh = m_primMesher.GenerateFacetedMesh(omvPrim, DetailLevel.Medium); 426
427 if (m_renderMeshes)
428 {
429 if (omvPrim.Sculpt != null && omvPrim.Sculpt.SculptTexture != UUID.Zero)
430 {
431 // Try fetchinng the asset
432 byte[] sculptAsset = m_scene.AssetService.GetData(omvPrim.Sculpt.SculptTexture.ToString());
433 if (sculptAsset != null)
434 {
435 // Is it a mesh?
436 if (omvPrim.Sculpt.Type == SculptType.Mesh)
437 {
438 AssetMesh meshAsset = new AssetMesh(omvPrim.Sculpt.SculptTexture, sculptAsset);
439 FacetedMesh.TryDecodeFromAsset(omvPrim, meshAsset, DetailLevel.Highest, out renderMesh);
440 meshAsset = null;
441 }
442 else // It's sculptie
443 {
444 IJ2KDecoder imgDecoder = m_scene.RequestModuleInterface<IJ2KDecoder>();
445 if (imgDecoder != null)
446 {
447 Image sculpt = imgDecoder.DecodeToImage(sculptAsset);
448 if (sculpt != null)
449 {
450 renderMesh = m_primMesher.GenerateFacetedSculptMesh(omvPrim, (Bitmap)sculpt,
451 DetailLevel.Medium);
452 sculpt.Dispose();
453 }
454 }
455 }
456 }
457 }
458 }
459
460 // If not a mesh or sculptie, try the regular mesher
461 if (renderMesh == null)
462 {
463 renderMesh = m_primMesher.GenerateFacetedMesh(omvPrim, DetailLevel.Medium);
464 }
465
377 if (renderMesh == null) 466 if (renderMesh == null)
378 return; 467 return;
379 468
@@ -432,7 +521,11 @@ namespace OpenSim.Region.CoreModules.World.Warp3DMap
432 521
433 Primitive.TextureEntryFace teFace = prim.Shape.Textures.GetFace((uint)i); 522 Primitive.TextureEntryFace teFace = prim.Shape.Textures.GetFace((uint)i);
434 Color4 faceColor = GetFaceColor(teFace); 523 Color4 faceColor = GetFaceColor(teFace);
435 string materialName = GetOrCreateMaterial(renderer, faceColor); 524 string materialName = String.Empty;
525 if (m_texturePrims && prim.Scale.LengthSquared() > m_texturePrimSize*m_texturePrimSize)
526 materialName = GetOrCreateMaterial(renderer, faceColor, teFace.TextureID);
527 else
528 materialName = GetOrCreateMaterial(renderer, faceColor);
436 529
437 faceObj.transform(m); 530 faceObj.transform(m);
438 faceObj.setPos(primPos); 531 faceObj.setPos(primPos);
@@ -521,10 +614,59 @@ namespace OpenSim.Region.CoreModules.World.Warp3DMap
521 return name; 614 return name;
522 } 615 }
523 616
617 public string GetOrCreateMaterial(WarpRenderer renderer, Color4 faceColor, UUID textureID)
618 {
619 string materialName = "Color-" + faceColor.ToString() + "-Texture-" + textureID.ToString();
620
621 if (renderer.Scene.material(materialName) == null)
622 {
623 renderer.AddMaterial(materialName, ConvertColor(faceColor));
624 if (faceColor.A < 1f)
625 {
626 renderer.Scene.material(materialName).setTransparency((byte) ((1f - faceColor.A)*255f));
627 }
628 warp_Texture texture = GetTexture(textureID);
629 if (texture != null)
630 renderer.Scene.material(materialName).setTexture(texture);
631 }
632
633 return materialName;
634 }
635
636 private warp_Texture GetTexture(UUID id)
637 {
638 warp_Texture ret = null;
639
640 byte[] asset = m_scene.AssetService.GetData(id.ToString());
641
642 if (asset != null)
643 {
644 IJ2KDecoder imgDecoder = m_scene.RequestModuleInterface<IJ2KDecoder>();
645
646 try
647 {
648 using (Bitmap img = (Bitmap)imgDecoder.DecodeToImage(asset))
649 ret = new warp_Texture(img);
650 }
651 catch (Exception e)
652 {
653 m_log.Warn(string.Format("[WARP 3D IMAGE MODULE]: Failed to decode asset {0}, exception ", id), e);
654 }
655 }
656
657 return ret;
658 }
659
524 #endregion Rendering Methods 660 #endregion Rendering Methods
525 661
526 #region Static Helpers 662 #region Static Helpers
527 663
664 // Note: axis change.
665 private static warp_Vector ConvertVector(float x, float y, float z)
666 {
667 return new warp_Vector(x, z, y);
668 }
669
528 private static warp_Vector ConvertVector(Vector3 vector) 670 private static warp_Vector ConvertVector(Vector3 vector)
529 { 671 {
530 return new warp_Vector(vector.X, vector.Z, vector.Y); 672 return new warp_Vector(vector.X, vector.Z, vector.Y);