aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/addon-modules/OpenSim.Modules.Warp3DCachedImageModule/src
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--addon-modules/OpenSim.Modules.Warp3DCachedImageModule/src/Perlin.cs264
-rw-r--r--addon-modules/OpenSim.Modules.Warp3DCachedImageModule/src/TerrainSplat.cs470
-rw-r--r--addon-modules/OpenSim.Modules.Warp3DCachedImageModule/src/Warp3DImageModule.cs1073
3 files changed, 1807 insertions, 0 deletions
diff --git a/addon-modules/OpenSim.Modules.Warp3DCachedImageModule/src/Perlin.cs b/addon-modules/OpenSim.Modules.Warp3DCachedImageModule/src/Perlin.cs
new file mode 100644
index 0000000..2279b76
--- /dev/null
+++ b/addon-modules/OpenSim.Modules.Warp3DCachedImageModule/src/Perlin.cs
@@ -0,0 +1,264 @@
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 OpenMetaverse;
30
31namespace OpenSim.Region.CoreModules.World.Warp3DMap
32{
33 public static class Perlin
34 {
35 // We use a hardcoded seed to keep the noise generation consistent between runs
36 private const int SEED = 42;
37
38 private const int SAMPLE_SIZE = 1024;
39 private const int B = SAMPLE_SIZE;
40 private const int BM = SAMPLE_SIZE - 1;
41 private const int N = 0x1000;
42
43 private static readonly int[] p = new int[SAMPLE_SIZE + SAMPLE_SIZE + 2];
44 private static readonly float[,] g3 = new float[SAMPLE_SIZE + SAMPLE_SIZE + 2, 3];
45 private static readonly float[,] g2 = new float[SAMPLE_SIZE + SAMPLE_SIZE + 2, 2];
46 private static readonly float[] g1 = new float[SAMPLE_SIZE + SAMPLE_SIZE + 2];
47
48 static Perlin()
49 {
50 Random rng = new Random(SEED);
51 int i, j, k;
52
53 for (i = 0; i < B; i++)
54 {
55 p[i] = i;
56 g1[i] = (float)((rng.Next() % (B + B)) - B) / B;
57
58 for (j = 0; j < 2; j++)
59 g2[i, j] = (float)((rng.Next() % (B + B)) - B) / B;
60 normalize2(g2, i);
61
62 for (j = 0; j < 3; j++)
63 g3[i, j] = (float)((rng.Next() % (B + B)) - B) / B;
64 normalize3(g3, i);
65 }
66
67 while (--i > 0)
68 {
69 k = p[i];
70 p[i] = p[j = rng.Next() % B];
71 p[j] = k;
72 }
73
74 for (i = 0; i < B + 2; i++)
75 {
76 p[B + i] = p[i];
77 g1[B + i] = g1[i];
78 for (j = 0; j < 2; j++)
79 g2[B + i, j] = g2[i, j];
80 for (j = 0; j < 3; j++)
81 g3[B + i, j] = g3[i, j];
82 }
83 }
84
85 public static float noise1(float arg)
86 {
87 int bx0, bx1;
88 float rx0, rx1, sx, t, u, v;
89
90 t = arg + N;
91 bx0 = ((int)t) & BM;
92 bx1 = (bx0 + 1) & BM;
93 rx0 = t - (int)t;
94 rx1 = rx0 - 1f;
95
96 sx = s_curve(rx0);
97
98 u = rx0 * g1[p[bx0]];
99 v = rx1 * g1[p[bx1]];
100
101 return Utils.Lerp(u, v, sx);
102 }
103
104 public static float noise2(float x, float y)
105 {
106 int bx, by, b00, b10, b01, b11;
107 float rx0, rx1, ry0, ry1, sx, sy, a, b, t, u, v;
108 int i, j;
109
110 t = x + N;
111 rx0 = t - (int)t;
112 bx = ((int)t) & BM;
113 i = p[bx];
114 bx = (bx + 1) & BM;
115 j = p[bx];
116
117 t = y + N;
118 ry0 = t - (int)t;
119 by = ((int)t) & BM;
120 b00 = p[i + by];
121 b10 = p[j + by];
122
123 by = (by + 1) & BM;
124 b01 = p[i + by];
125 b11 = p[j + by];
126
127 sx = s_curve(rx0);
128 u = rx0 * g2[b00, 0] + ry0 * g2[b00, 1];
129 rx1 = rx0 - 1f;
130 v = rx1 * g2[b10, 0] + ry0 * g2[b10, 1];
131 a = Utils.Lerp(u, v, sx);
132
133 ry1 = ry0 - 1f;
134 u = rx0 * g2[b01, 0] + ry1 * g2[b01, 1];
135 v = rx1 * g2[b11, 0] + ry1 * g2[b11, 1];
136 b = Utils.Lerp(u, v, sx);
137
138 sy = s_curve(ry0);
139 return Utils.Lerp(a, b, sy);
140 }
141
142 public static float noise3(float x, float y, float z)
143 {
144 int bx0, bx1, by0, by1, bz0, bz1, b00, b10, b01, b11;
145 float rx0, rx1, ry0, ry1, rz0, rz1, sy, sz, a, b, c, d, t, u, v;
146 int i, j;
147
148 t = x + N;
149 bx0 = ((int)t) & BM;
150 bx1 = (bx0 + 1) & BM;
151 rx0 = t - (int)t;
152 rx1 = rx0 - 1f;
153
154 t = y + N;
155 by0 = ((int)t) & BM;
156 by1 = (by0 + 1) & BM;
157 ry0 = t - (int)t;
158 ry1 = ry0 - 1f;
159
160 t = z + N;
161 bz0 = ((int)t) & BM;
162 bz1 = (bz0 + 1) & BM;
163 rz0 = t - (int)t;
164 rz1 = rz0 - 1f;
165
166 i = p[bx0];
167 j = p[bx1];
168
169 b00 = p[i + by0];
170 b10 = p[j + by0];
171 b01 = p[i + by1];
172 b11 = p[j + by1];
173
174 t = s_curve(rx0);
175 sy = s_curve(ry0);
176 sz = s_curve(rz0);
177
178 u = rx0 * g3[b00 + bz0, 0] + ry0 * g3[b00 + bz0, 1] + rz0 * g3[b00 + bz0, 2];
179 v = rx1 * g3[b10 + bz0, 0] + ry0 * g3[b10 + bz0, 1] + rz0 * g3[b10 + bz0, 2];
180 a = Utils.Lerp(u, v, t);
181
182 u = rx0 * g3[b01 + bz0, 0] + ry1 * g3[b01 + bz0, 1] + rz0 * g3[b01 + bz0, 2];
183 v = rx1 * g3[b11 + bz0, 0] + ry1 * g3[b11 + bz0, 1] + rz0 * g3[b11 + bz0, 2];
184 b = Utils.Lerp(u, v, t);
185
186 c = Utils.Lerp(a, b, sy);
187
188 u = rx0 * g3[b00 + bz1, 0] + ry0 * g3[b00 + bz1, 1] + rz1 * g3[b00 + bz1, 2];
189 v = rx1 * g3[b10 + bz1, 0] + ry0 * g3[b10 + bz1, 1] + rz1 * g3[b10 + bz1, 2];
190 a = Utils.Lerp(u, v, t);
191
192 u = rx0 * g3[b01 + bz1, 0] + ry1 * g3[b01 + bz1, 1] + rz1 * g3[b01 + bz1, 2];
193 v = rx1 * g3[b11 + bz1, 0] + ry1 * g3[b11 + bz1, 1] + rz1 * g3[b11 + bz1, 2];
194 b = Utils.Lerp(u, v, t);
195
196 d = Utils.Lerp(a, b, sy);
197 return Utils.Lerp(c, d, sz);
198 }
199
200 public static float turbulence1(float x, float freq)
201 {
202 float t;
203
204 for (t = 0f; freq >= 1f; freq *= 0.5f)
205 {
206 t += noise1(freq * x) / freq;
207 }
208 return t;
209 }
210
211 public static float turbulence2(float x, float y, float freq)
212 {
213 float t;
214
215 for (t = 0f; freq >= 1f; freq *= 0.5f)
216 t += noise2(freq * x, freq * y) / freq;
217
218 return t;
219 }
220
221 public static float turbulence3(float x, float y, float z, float freq)
222 {
223 float t;
224
225 for (t = 0f; freq >= 1f; freq *= 0.5f)
226 {
227 t += noise3(freq * x, freq * y, freq * z) / freq;
228 }
229 return t;
230 }
231
232 private static void normalize2(float[,] v, int i)
233 {
234 float s;
235 float a = v[i, 0];
236 float b = v[i, 1];
237
238 s = (float)Math.Sqrt(a * a + b * b);
239 s = 1.0f / s;
240 v[i, 0] = a * s;
241 v[i, 1] = b * s;
242 }
243
244 private static void normalize3(float[,] v, int i)
245 {
246 float s;
247 float a = v[i, 0];
248 float b = v[i, 1];
249 float c = v[i, 2];
250
251 s = (float)Math.Sqrt(a * a + b * b + c * c);
252 s = 1.0f / s;
253
254 v[i, 0] = a * s;
255 v[i, 1] = b * s;
256 v[i, 2] = c * s;
257 }
258
259 private static float s_curve(float t)
260 {
261 return t * t * (3f - 2f * t);
262 }
263 }
264}
diff --git a/addon-modules/OpenSim.Modules.Warp3DCachedImageModule/src/TerrainSplat.cs b/addon-modules/OpenSim.Modules.Warp3DCachedImageModule/src/TerrainSplat.cs
new file mode 100644
index 0000000..f60beaf
--- /dev/null
+++ b/addon-modules/OpenSim.Modules.Warp3DCachedImageModule/src/TerrainSplat.cs
@@ -0,0 +1,470 @@
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.Region.Framework.Interfaces;
36using OpenSim.Services.Interfaces;
37
38namespace OpenSim.Region.CoreModules.World.Warp3DMap
39{
40 public static class TerrainSplat
41 {
42 #region Constants
43
44 private static readonly UUID DIRT_DETAIL = new UUID("0bc58228-74a0-7e83-89bc-5c23464bcec5");
45 private static readonly UUID GRASS_DETAIL = new UUID("63338ede-0037-c4fd-855b-015d77112fc8");
46 private static readonly UUID MOUNTAIN_DETAIL = new UUID("303cd381-8560-7579-23f1-f0a880799740");
47 private static readonly UUID ROCK_DETAIL = new UUID("53a2f406-4895-1d13-d541-d2e3b86bc19c");
48
49 private static readonly UUID[] DEFAULT_TERRAIN_DETAIL = new UUID[]
50 {
51 DIRT_DETAIL,
52 GRASS_DETAIL,
53 MOUNTAIN_DETAIL,
54 ROCK_DETAIL
55 };
56
57 private static readonly Color[] DEFAULT_TERRAIN_COLOR = new Color[]
58 {
59 Color.FromArgb(255, 164, 136, 117),
60 Color.FromArgb(255, 65, 87, 47),
61 Color.FromArgb(255, 157, 145, 131),
62 Color.FromArgb(255, 125, 128, 130)
63 };
64
65 private static readonly UUID TERRAIN_CACHE_MAGIC = new UUID("2c0c7ef2-56be-4eb8-aacb-76712c535b4b");
66
67 #endregion Constants
68
69 private static readonly ILog m_log = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType.Name);
70 private static string LogHeader = "[WARP3D TERRAIN SPLAT]";
71
72 /// <summary>
73 /// Builds a composited terrain texture given the region texture
74 /// and heightmap settings
75 /// </summary>
76 /// <param name="terrain">Terrain heightmap</param>
77 /// <param name="regionInfo">Region information including terrain texture parameters</param>
78 /// <returns>A 256x256 square RGB texture ready for rendering</returns>
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.
81 /// </remarks>
82
83 public static Bitmap Splat(ITerrainChannel terrain, UUID[] textureIDs,
84 float[] startHeights, float[] heightRanges,
85 uint regionPositionX, uint regionPositionY,
86 IAssetService assetService, IJ2KDecoder decoder,
87 bool textureTerrain, bool averagetextureTerrain,
88 int twidth, int theight)
89 {
90 Debug.Assert(textureIDs.Length == 4);
91 Debug.Assert(startHeights.Length == 4);
92 Debug.Assert(heightRanges.Length == 4);
93
94 Bitmap[] detailTexture = new Bitmap[4];
95
96 byte[] mapColorsRed = new byte[4];
97 byte[] mapColorsGreen = new byte[4];
98 byte[] mapColorsBlue = new byte[4];
99
100 bool usecolors = false;
101
102 if (textureTerrain)
103 {
104 // Swap empty terrain textureIDs with default IDs
105 for(int i = 0; i < textureIDs.Length; i++)
106 {
107 if(textureIDs[i] == UUID.Zero)
108 textureIDs[i] = DEFAULT_TERRAIN_DETAIL[i];
109 }
110
111 #region Texture Fetching
112
113 if(assetService != null)
114 {
115 for(int i = 0; i < 4; i++)
116 {
117 AssetBase asset = null;
118
119 // asset cache indexes are strings
120 string cacheName ="MAP-Patch" + textureIDs[i].ToString();
121
122 // Try to fetch a cached copy of the decoded/resized version of this texture
123 asset = assetService.GetCached(cacheName);
124 if(asset != null)
125 {
126 try
127 {
128 using(System.IO.MemoryStream stream = new System.IO.MemoryStream(asset.Data))
129 detailTexture[i] = (Bitmap)Image.FromStream(stream);
130
131 if(detailTexture[i].PixelFormat != PixelFormat.Format24bppRgb ||
132 detailTexture[i].Width != 16 || detailTexture[i].Height != 16)
133 {
134 detailTexture[i].Dispose();
135 detailTexture[i] = null;
136 }
137 }
138 catch(Exception ex)
139 {
140 m_log.Warn("Failed to decode cached terrain patch texture" + textureIDs[i] + "): " + ex.Message);
141 }
142 }
143
144 if(detailTexture[i] == null)
145 {
146 // Try to fetch the original JPEG2000 texture, resize if needed, and cache as PNG
147 asset = assetService.Get(textureIDs[i].ToString());
148 if(asset != null)
149 {
150 try
151 {
152 detailTexture[i] = (Bitmap)decoder.DecodeToImage(asset.Data);
153 }
154 catch(Exception ex)
155 {
156 m_log.Warn("Failed to decode terrain texture " + asset.ID + ": " + ex.Message);
157 }
158 }
159
160 if(detailTexture[i] != null)
161 {
162 if(detailTexture[i].PixelFormat != PixelFormat.Format24bppRgb ||
163 detailTexture[i].Width != 16 || detailTexture[i].Height != 16)
164 using(Bitmap origBitmap = detailTexture[i])
165 detailTexture[i] = Util.ResizeImageSolid(origBitmap, 16, 16);
166
167 // Save the decoded and resized texture to the cache
168 byte[] data;
169 using(System.IO.MemoryStream stream = new System.IO.MemoryStream())
170 {
171 detailTexture[i].Save(stream, ImageFormat.Png);
172 data = stream.ToArray();
173 }
174
175 // Cache a PNG copy of this terrain texture
176 AssetBase newAsset = new AssetBase
177 {
178 Data = data,
179 Description = "PNG",
180 Flags = AssetFlags.Collectable,
181 FullID = UUID.Zero,
182 ID = cacheName,
183 Local = true,
184 Name = String.Empty,
185 Temporary = true,
186 Type = (sbyte)AssetType.Unknown
187 };
188 newAsset.Metadata.ContentType = "image/png";
189 assetService.Store(newAsset);
190 }
191 }
192 }
193 }
194
195 #endregion Texture Fetching
196 if(averagetextureTerrain)
197 {
198 for(int t = 0; t < 4; t++)
199 {
200 usecolors = true;
201 if(detailTexture[t] == null)
202 {
203 mapColorsRed[t] = DEFAULT_TERRAIN_COLOR[t].R;
204 mapColorsGreen[t] = DEFAULT_TERRAIN_COLOR[t].G;
205 mapColorsBlue[t] = DEFAULT_TERRAIN_COLOR[t].B;
206 continue;
207 }
208
209 int npixeis = 0;
210 int cR = 0;
211 int cG = 0;
212 int cB = 0;
213
214 BitmapData bmdata = detailTexture[t].LockBits(new Rectangle(0, 0, 16, 16),
215 ImageLockMode.ReadOnly, detailTexture[t].PixelFormat);
216
217 npixeis = bmdata.Height * bmdata.Width;
218 int ylen = bmdata.Height * bmdata.Stride;
219
220 unsafe
221 {
222 for(int y = 0; y < ylen; y += bmdata.Stride)
223 {
224 byte* ptrc = (byte*)bmdata.Scan0 + y;
225 for(int x = 0 ; x < bmdata.Width; ++x)
226 {
227 cR += *(ptrc++);
228 cG += *(ptrc++);
229 cB += *(ptrc++);
230 }
231 }
232
233 }
234 detailTexture[t].UnlockBits(bmdata);
235 detailTexture[t].Dispose();
236
237 mapColorsRed[t] = (byte)Util.Clamp(cR / npixeis, 0 , 255);
238 mapColorsGreen[t] = (byte)Util.Clamp(cG / npixeis, 0 , 255);
239 mapColorsBlue[t] = (byte)Util.Clamp(cB / npixeis, 0 , 255);
240 }
241 }
242 else
243 {
244 // Fill in any missing textures with a solid color
245 for(int i = 0; i < 4; i++)
246 {
247 if(detailTexture[i] == null)
248 {
249 m_log.DebugFormat("{0} Missing terrain texture for layer {1}. Filling with solid default color", LogHeader, i);
250
251 // Create a solid color texture for this layer
252 detailTexture[i] = new Bitmap(16, 16, PixelFormat.Format24bppRgb);
253 using(Graphics gfx = Graphics.FromImage(detailTexture[i]))
254 {
255 using(SolidBrush brush = new SolidBrush(DEFAULT_TERRAIN_COLOR[i]))
256 gfx.FillRectangle(brush, 0, 0, 16, 16);
257 }
258 }
259 else
260 {
261 if(detailTexture[i].Width != 16 || detailTexture[i].Height != 16)
262 {
263 using(Bitmap origBitmap = detailTexture[i])
264 detailTexture[i] = Util.ResizeImageSolid(origBitmap, 16, 16);
265 }
266 }
267 }
268 }
269 }
270 else
271 {
272 usecolors = true;
273 for(int t = 0; t < 4; t++)
274 {
275 mapColorsRed[t] = DEFAULT_TERRAIN_COLOR[t].R;
276 mapColorsGreen[t] = DEFAULT_TERRAIN_COLOR[t].G;
277 mapColorsBlue[t] = DEFAULT_TERRAIN_COLOR[t].B;
278 }
279 }
280
281 #region Layer Map
282
283 float xFactor = terrain.Width / twidth;
284 float yFactor = terrain.Height / theight;
285
286 #endregion Layer Map
287
288 #region Texture Compositing
289
290 Bitmap output = new Bitmap(twidth, theight, PixelFormat.Format24bppRgb);
291 BitmapData outputData = output.LockBits(new Rectangle(0, 0, twidth, theight), ImageLockMode.WriteOnly, PixelFormat.Format24bppRgb);
292
293 // Unsafe work as we lock down the source textures for quicker access and access the
294 // pixel data directly
295 float invtwitdthMinus1 = 1.0f / (twidth - 1);
296 float invtheightMinus1 = 1.0f / (theight - 1);
297 int ty;
298 int tx;
299 float pctx;
300 float pcty;
301 float height;
302 float layer;
303 float layerDiff;
304 int l0;
305 int l1;
306 uint yglobalpos;
307
308 if(usecolors)
309 {
310 float a;
311 float b;
312 unsafe
313 {
314 byte* ptrO;
315 for(int y = 0; y < theight; ++y)
316 {
317 pcty = y * invtheightMinus1;
318 ptrO = (byte*)outputData.Scan0 + y * outputData.Stride;
319 ty = (int)(y * yFactor);
320 yglobalpos = (uint)ty + regionPositionY;
321
322 for(int x = 0; x < twidth; ++x)
323 {
324 tx = (int)(x * xFactor);
325 pctx = x * invtwitdthMinus1;
326 height = (float)terrain[tx, ty];
327 layer = getLayerTex(height, pctx, pcty,
328 (uint)tx + regionPositionX, yglobalpos,
329 startHeights, heightRanges);
330
331 // Select two textures
332 l0 = (int)layer;
333 l1 = Math.Min(l0 + 1, 3);
334
335 layerDiff = layer - l0;
336
337 a = mapColorsRed[l0];
338 b = mapColorsRed[l1];
339 *(ptrO++) = (byte)(a + layerDiff * (b - a));
340
341 a = mapColorsGreen[l0];
342 b = mapColorsGreen[l1];
343 *(ptrO++) = (byte)(a + layerDiff * (b - a));
344
345 a = mapColorsBlue[l0];
346 b = mapColorsBlue[l1];
347 *(ptrO++) = (byte)(a + layerDiff * (b - a));
348 }
349 }
350 }
351 }
352 else
353 {
354 float aB;
355 float aG;
356 float aR;
357 float bB;
358 float bG;
359 float bR;
360
361 unsafe
362 {
363 // Get handles to all of the texture data arrays
364 BitmapData[] datas = new BitmapData[]
365 {
366 detailTexture[0].LockBits(new Rectangle(0, 0, 16, 16), ImageLockMode.ReadOnly, detailTexture[0].PixelFormat),
367 detailTexture[1].LockBits(new Rectangle(0, 0, 16, 16), ImageLockMode.ReadOnly, detailTexture[1].PixelFormat),
368 detailTexture[2].LockBits(new Rectangle(0, 0, 16, 16), ImageLockMode.ReadOnly, detailTexture[2].PixelFormat),
369 detailTexture[3].LockBits(new Rectangle(0, 0, 16, 16), ImageLockMode.ReadOnly, detailTexture[3].PixelFormat)
370 };
371
372 byte* ptr;
373 byte* ptrO;
374 for(int y = 0; y < theight; y++)
375 {
376 pcty = y * invtheightMinus1;
377 int ypatch = ((int)(y * yFactor) & 0x0f) * datas[0].Stride;
378 ptrO = (byte*)outputData.Scan0 + y * outputData.Stride;
379 ty = (int)(y * yFactor);
380 yglobalpos = (uint)ty + regionPositionY;
381
382 for(int x = 0; x < twidth; x++)
383 {
384 tx = (int)(x * xFactor);
385 pctx = x * invtwitdthMinus1;
386 height = (float)terrain[tx, ty];
387 layer = getLayerTex(height, pctx, pcty,
388 (uint)tx + regionPositionX, yglobalpos,
389 startHeights, heightRanges);
390
391 // Select two textures
392 l0 = (int)layer;
393 layerDiff = layer - l0;
394
395 int patchOffset = (tx & 0x0f) * 3 + ypatch;
396
397 ptr = (byte*)datas[l0].Scan0 + patchOffset;
398 aB = *(ptr++);
399 aG = *(ptr++);
400 aR = *(ptr);
401
402 l1 = Math.Min(l0 + 1, 3);
403 ptr = (byte*)datas[l1].Scan0 + patchOffset;
404 bB = *(ptr++);
405 bG = *(ptr++);
406 bR = *(ptr);
407
408
409 // Interpolate between the two selected textures
410 *(ptrO++) = (byte)(aB + layerDiff * (bB - aB));
411 *(ptrO++) = (byte)(aG + layerDiff * (bG - aG));
412 *(ptrO++) = (byte)(aR + layerDiff * (bR - aR));
413 }
414 }
415
416 for(int i = 0; i < detailTexture.Length; i++)
417 detailTexture[i].UnlockBits(datas[i]);
418 }
419
420 for(int i = 0; i < detailTexture.Length; i++)
421 if(detailTexture[i] != null)
422 detailTexture[i].Dispose();
423 }
424
425 output.UnlockBits(outputData);
426
427//output.Save("terr.png",ImageFormat.Png);
428
429 #endregion Texture Compositing
430
431 return output;
432 }
433
434 [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
435 private static float getLayerTex(float height, float pctX, float pctY, uint X, uint Y,
436 float[] startHeights, float[] heightRanges)
437 {
438 // Use bilinear interpolation between the four corners of start height and
439 // height range to select the current values at this position
440 float startHeight = ImageUtils.Bilinear(
441 startHeights[0], startHeights[2],
442 startHeights[1], startHeights[3],
443 pctX, pctY);
444 if (float.IsNaN(startHeight))
445 return 0;
446
447 startHeight = Utils.Clamp(startHeight, 0f, 255f);
448
449 float heightRange = ImageUtils.Bilinear(
450 heightRanges[0], heightRanges[2],
451 heightRanges[1], heightRanges[3],
452 pctX, pctY);
453 heightRange = Utils.Clamp(heightRange, 0f, 255f);
454 if(heightRange == 0f || float.IsNaN(heightRange))
455 return 0;
456
457 // Generate two frequencies of perlin noise based on our global position
458 // The magic values were taken from http://opensimulator.org/wiki/Terrain_Splatting
459 float sX = X * 0.20319f;
460 float sY = Y * 0.20319f;
461
462 float noise = Perlin.noise2(sX * 0.222222f, sY * 0.222222f) * 13.0f;
463 noise += Perlin.turbulence2(sX, sY, 2f) * 4.5f;
464
465 // Combine the current height, generated noise, start height, and height range parameters, then scale all of it
466 float layer = ((height + noise - startHeight) / heightRange) * 4f;
467 return Utils.Clamp(layer, 0f, 3f);
468 }
469 }
470}
diff --git a/addon-modules/OpenSim.Modules.Warp3DCachedImageModule/src/Warp3DImageModule.cs b/addon-modules/OpenSim.Modules.Warp3DCachedImageModule/src/Warp3DImageModule.cs
new file mode 100644
index 0000000..8e50a16
--- /dev/null
+++ b/addon-modules/OpenSim.Modules.Warp3DCachedImageModule/src/Warp3DImageModule.cs
@@ -0,0 +1,1073 @@
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.Collections.Generic;
30using System.Drawing;
31using System.Drawing.Imaging;
32using System.IO;
33using System.Reflection;
34using System.Runtime;
35
36using CSJ2K;
37using Nini.Config;
38using log4net;
39using Warp3D;
40using Mono.Addins;
41
42using OpenSim.Framework;
43using OpenSim.Region.Framework.Interfaces;
44using OpenSim.Region.Framework.Scenes;
45using OpenSim.Region.PhysicsModules.SharedBase;
46using OpenSim.Services.Interfaces;
47
48using OpenMetaverse;
49using OpenMetaverse.Assets;
50using OpenMetaverse.Imaging;
51using OpenMetaverse.Rendering;
52using OpenMetaverse.StructuredData;
53
54using WarpRenderer = Warp3D.Warp3D;
55using System.Drawing.Drawing2D;
56
57[assembly: Addin("Warp3DCachedImageModule", "1.1")]
58[assembly: AddinDependency("OpenSim.Region.Framework", OpenSim.VersionInfo.VersionNumber)]
59namespace OpenSim.Region.CoreModules.World.Warp3DMap
60{
61 [Extension(Path = "/OpenSim/RegionModules", NodeName = "RegionModule", Id = "Warp3DCachedImageModule")]
62 public class Warp3DImageModule : IMapImageGenerator, INonSharedRegionModule
63 {
64 private static readonly Color4 WATER_COLOR = new Color4(29, 72, 96, 216);
65 // private static readonly Color4 WATER_COLOR = new Color4(29, 72, 96, 128);
66
67 private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
68
69#pragma warning disable 414
70 private static string LogHeader = "[WARP 3D CACHED IMAGE MODULE]";
71#pragma warning restore 414
72 private const float m_cameraHeight = 4096f;
73
74 internal Scene m_scene;
75 private IRendering m_primMesher;
76 internal IJ2KDecoder m_imgDecoder;
77
78 // caches per rendering
79 private Dictionary<string, warp_Texture> m_warpTextures = new Dictionary<string, warp_Texture>();
80 private Dictionary<UUID, int> m_colors = new Dictionary<UUID, int>();
81
82 private IConfigSource m_config;
83 private bool m_drawPrimVolume = true; // true if should render the prims on the tile
84 private bool m_textureTerrain = true; // true if to create terrain splatting texture
85 private bool m_textureAverageTerrain = false; // replace terrain textures by their average color
86 private bool m_texturePrims = true; // true if should texture the rendered prims
87 private float m_texturePrimSize = 48f; // size of prim before we consider texturing it
88 private bool m_renderMeshes = false; // true if to render meshes rather than just bounding boxes
89 private float m_renderMinHeight = -100f;
90 private float m_renderMaxHeight = 4096f;
91
92 private String m_cacheDirectory = "";
93 private bool m_enable_date = false;
94 private bool m_enable_regionName = false;
95 private bool m_enable_regionPosition = false;
96 private bool m_enable_refreshEveryMonth = false;
97 private bool m_enable_HostedBy = false;
98 private String m_enable_HostedByText = "";
99
100 private bool m_Enabled = false;
101
102 // private Bitmap lastImage = null;
103 private DateTime lastImageTime = DateTime.MinValue;
104
105 #region Region Module interface
106
107 public void Initialise(IConfigSource source)
108 {
109 m_config = source;
110
111 string[] configSections = new string[] { "Map", "Startup" };
112
113 if (Util.GetConfigVarFromSections<string>(
114 m_config, "MapImageModule", configSections, "MapImageModule") != "Warp3DCachedImageModule")
115 return;
116
117 m_Enabled = true;
118
119 m_drawPrimVolume =
120 Util.GetConfigVarFromSections<bool>(m_config, "DrawPrimOnMapTile", configSections, m_drawPrimVolume);
121 m_textureTerrain =
122 Util.GetConfigVarFromSections<bool>(m_config, "TextureOnMapTile", configSections, m_textureTerrain);
123 m_textureAverageTerrain =
124 Util.GetConfigVarFromSections<bool>(m_config, "AverageTextureColorOnMapTile", configSections, m_textureAverageTerrain);
125 if (m_textureAverageTerrain)
126 m_textureTerrain = true;
127 m_texturePrims =
128 Util.GetConfigVarFromSections<bool>(m_config, "TexturePrims", configSections, m_texturePrims);
129 m_texturePrimSize =
130 Util.GetConfigVarFromSections<float>(m_config, "TexturePrimSize", configSections, m_texturePrimSize);
131 m_renderMeshes =
132 Util.GetConfigVarFromSections<bool>(m_config, "RenderMeshes", configSections, m_renderMeshes);
133 m_cacheDirectory =
134 Util.GetConfigVarFromSections<string>(m_config, "CacheDirectory", configSections, System.IO.Path.Combine(new DirectoryInfo(".").FullName, "MapImageCache"));
135
136
137 m_enable_date = Util.GetConfigVarFromSections<bool>(m_config, "enableDate", configSections, false);
138 m_enable_regionName = Util.GetConfigVarFromSections<bool>(m_config, "enableName", configSections, false);
139 m_enable_regionPosition = Util.GetConfigVarFromSections<bool>(m_config, "enablePosition", configSections, false);
140 m_enable_refreshEveryMonth = Util.GetConfigVarFromSections<bool>(m_config, "RefreshEveryMonth", configSections, true);
141 m_enable_HostedBy = Util.GetConfigVarFromSections<bool>(m_config, "enableHostedBy", configSections, false);
142 m_enable_HostedByText = Util.GetConfigVarFromSections<String>(m_config, "HosterText", configSections, String.Empty);
143
144 m_renderMaxHeight = Util.GetConfigVarFromSections<float>(m_config, "RenderMaxHeight", configSections, m_renderMaxHeight);
145 m_renderMinHeight = Util.GetConfigVarFromSections<float>(m_config, "RenderMinHeight", configSections, m_renderMinHeight);
146
147 if (!Directory.Exists(m_cacheDirectory))
148 Directory.CreateDirectory(m_cacheDirectory);
149
150 if (m_renderMaxHeight < 100f)
151 m_renderMaxHeight = 100f;
152 else if (m_renderMaxHeight > m_cameraHeight - 10f)
153 m_renderMaxHeight = m_cameraHeight - 10f;
154
155 if (m_renderMinHeight < -100f)
156 m_renderMinHeight = -100f;
157 else if (m_renderMinHeight > m_renderMaxHeight - 10f)
158 m_renderMinHeight = m_renderMaxHeight - 10f;
159 }
160
161 public void AddRegion(Scene scene)
162 {
163 if (!m_Enabled)
164 return;
165
166 m_scene = scene;
167
168 List<string> renderers = RenderingLoader.ListRenderers(Util.ExecutingDirectory());
169 if (renderers.Count > 0)
170 m_log.Info("[MAPTILE]: Loaded prim mesher " + renderers[0]);
171 else
172 m_log.Info("[MAPTILE]: No prim mesher loaded, prim rendering will be disabled");
173
174 m_scene.RegisterModuleInterface<IMapImageGenerator>(this);
175 }
176
177 public void RegionLoaded(Scene scene)
178 {
179 if (!m_Enabled)
180 return;
181
182 m_imgDecoder = m_scene.RequestModuleInterface<IJ2KDecoder>();
183 }
184
185 public void RemoveRegion(Scene scene)
186 {
187 }
188
189 public void Close()
190 {
191 }
192
193 public string Name
194 {
195 get { return "Warp3DImageModule"; }
196 }
197
198 public Type ReplaceableInterface
199 {
200 get { return null; }
201 }
202
203 #endregion
204
205 #region IMapImageGenerator Members
206
207 private Vector3 cameraPos;
208 private Vector3 cameraDir;
209 private int viewWitdh = 256;
210 private int viewHeight = 256;
211 private float fov;
212 private bool orto;
213
214 public static string fillInt(int _i, int _l)
215 {
216 String _return = _i.ToString();
217
218 while (_return.Length < _l)
219 {
220 _return = 0 + _return;
221 }
222
223 return _return;
224 }
225
226 public static int getCurrentUnixTime()
227 {
228 return (Int32)(DateTime.UtcNow.Subtract(new DateTime(1970, 1, 1))).TotalSeconds;
229 }
230
231 public static String unixTimeToDateString(int unixTime)
232 {
233 DateTime unixStart = new DateTime(1970, 1, 1, 0, 0, 0, 0, System.DateTimeKind.Utc);
234 long unixTimeStampInTicks = (long)(unixTime * TimeSpan.TicksPerSecond);
235 DateTime _date = new DateTime(unixStart.Ticks + unixTimeStampInTicks, System.DateTimeKind.Utc);
236
237 return fillInt(_date.Day, 2) + "." + fillInt(_date.Month, 2) + "." + fillInt(_date.Year, 4) + " " + fillInt(_date.Hour, 2) + ":" + fillInt(_date.Minute, 2);
238 }
239
240 private void writeDateOnMap(ref Bitmap _map)
241 {
242 RectangleF rectf = new RectangleF(2, 1, 200, 25);
243
244 Graphics g = Graphics.FromImage(_map);
245 g.SmoothingMode = SmoothingMode.AntiAlias;
246 g.InterpolationMode = InterpolationMode.HighQualityBicubic;
247 g.PixelOffsetMode = PixelOffsetMode.HighQuality;
248 g.DrawString(unixTimeToDateString(getCurrentUnixTime()), new Font("Arial", 8), Brushes.White, rectf);
249 g.Flush();
250 }
251
252 private void writeNameOnMap(ref Bitmap _map)
253 {
254 RectangleF rectf = new RectangleF(2, m_scene.RegionInfo.RegionSizeX - 15, 200, 25);
255
256 Graphics g = Graphics.FromImage(_map);
257 g.SmoothingMode = SmoothingMode.AntiAlias;
258 g.InterpolationMode = InterpolationMode.HighQualityBicubic;
259 g.PixelOffsetMode = PixelOffsetMode.HighQuality;
260 g.DrawString(m_scene.Name, new Font("Arial", 8), Brushes.White, rectf);
261 g.Flush();
262 }
263
264 private void writePositionOnMap(ref Bitmap _map)
265 {
266 RectangleF rectf = new RectangleF(m_scene.RegionInfo.RegionSizeY - 85, m_scene.RegionInfo.RegionSizeX - 15, 80, 25);
267
268 Graphics g = Graphics.FromImage(_map);
269 g.SmoothingMode = SmoothingMode.AntiAlias;
270 g.InterpolationMode = InterpolationMode.HighQualityBicubic;
271 g.PixelOffsetMode = PixelOffsetMode.HighQuality;
272 g.DrawString(m_scene.RegionInfo.RegionLocX + ", " + m_scene.RegionInfo.RegionLocY, new Font("Arial", 8), Brushes.White, rectf);
273 g.Flush();
274 }
275
276 private void writeHostedByOnMap(ref Bitmap _map)
277 {
278 RectangleF rectf = new RectangleF(2, m_scene.RegionInfo.RegionSizeX - 15, 200, 25);
279
280 Graphics g = Graphics.FromImage(_map);
281 g.SmoothingMode = SmoothingMode.AntiAlias;
282 g.InterpolationMode = InterpolationMode.HighQualityBicubic;
283 g.PixelOffsetMode = PixelOffsetMode.HighQuality;
284 g.DrawString(m_enable_HostedByText, new Font("Arial", 8), Brushes.Gray, rectf);
285 g.Flush();
286 }
287
288
289 public Bitmap CreateMapTile()
290 {
291 if ((File.GetCreationTime(System.IO.Path.Combine(m_cacheDirectory, m_scene.RegionInfo.RegionID + ".bmp")).Month != DateTime.Now.Month) && m_enable_refreshEveryMonth == true)
292 File.Delete(System.IO.Path.Combine(m_cacheDirectory, m_scene.RegionInfo.RegionID + ".bmp"));
293
294 if (File.Exists(System.IO.Path.Combine(m_cacheDirectory, m_scene.RegionInfo.RegionID + ".bmp")))
295 {
296 return new Bitmap(System.IO.Path.Combine(m_cacheDirectory, m_scene.RegionInfo.RegionID + ".bmp"));
297 }
298 else
299 {
300 List<string> renderers = RenderingLoader.ListRenderers(Util.ExecutingDirectory());
301 if (renderers.Count > 0)
302 {
303 m_primMesher = RenderingLoader.LoadRenderer(renderers[0]);
304 }
305
306 cameraPos = new Vector3(
307 (m_scene.RegionInfo.RegionSizeX) * 0.5f,
308 (m_scene.RegionInfo.RegionSizeY) * 0.5f,
309 m_cameraHeight);
310
311 cameraDir = -Vector3.UnitZ;
312 viewWitdh = (int)m_scene.RegionInfo.RegionSizeX;
313 viewHeight = (int)m_scene.RegionInfo.RegionSizeY;
314 orto = true;
315
316 // fov = warp_Math.rad2deg(2f * (float)Math.Atan2(viewWitdh, 4096f));
317 // orto = false;
318
319 Bitmap tile = GenImage();
320
321 if (m_enable_date)
322 writeDateOnMap(ref tile);
323
324 if (m_enable_regionName)
325 writeNameOnMap(ref tile);
326
327 if (m_enable_regionPosition)
328 writePositionOnMap(ref tile);
329
330 if (m_enable_HostedBy)
331 writeHostedByOnMap(ref tile);
332
333 tile.Save(System.IO.Path.Combine(m_cacheDirectory, m_scene.RegionInfo.RegionID + ".bmp"));
334
335 // image may be reloaded elsewhere, so no compression format
336 string filename = "MAP-" + m_scene.RegionInfo.RegionID.ToString() + ".png";
337 tile.Save(filename, ImageFormat.Png);
338 m_primMesher = null;
339 return tile;
340 }
341 }
342
343 public Bitmap CreateViewImage(Vector3 camPos, Vector3 camDir, float pfov, int width, int height, bool useTextures)
344 {
345 List<string> renderers = RenderingLoader.ListRenderers(Util.ExecutingDirectory());
346 if (renderers.Count > 0)
347 {
348 m_primMesher = RenderingLoader.LoadRenderer(renderers[0]);
349 }
350
351 cameraPos = camPos;
352 cameraDir = camDir;
353 viewWitdh = width;
354 viewHeight = height;
355 fov = pfov;
356 orto = false;
357
358 Bitmap tile = GenImage();
359 m_primMesher = null;
360 return tile;
361 }
362
363 private Bitmap GenImage()
364 {
365 m_colors.Clear();
366 m_warpTextures.Clear();
367
368 WarpRenderer renderer = new WarpRenderer();
369
370 if (!renderer.CreateScene(viewWitdh, viewHeight))
371 return new Bitmap(viewWitdh, viewHeight);
372
373 #region Camera
374
375 warp_Vector pos = ConvertVector(cameraPos);
376 warp_Vector lookat = warp_Vector.add(pos, ConvertVector(cameraDir));
377
378 if (orto)
379 renderer.Scene.defaultCamera.setOrthographic(true, viewWitdh, viewHeight);
380 else
381 renderer.Scene.defaultCamera.setFov(fov);
382
383 renderer.Scene.defaultCamera.setPos(pos);
384 renderer.Scene.defaultCamera.lookAt(lookat);
385 #endregion Camera
386
387 renderer.Scene.setAmbient(warp_Color.getColor(192, 191, 173));
388 renderer.Scene.addLight("Light1", new warp_Light(new warp_Vector(0f, 1f, 8f), warp_Color.White, 0, 320, 40));
389
390 CreateWater(renderer);
391 CreateTerrain(renderer);
392 if (m_drawPrimVolume)
393 CreateAllPrims(renderer);
394
395 renderer.Render();
396 Bitmap bitmap = renderer.Scene.getImage();
397
398 renderer.Scene.destroy();
399 renderer.Reset();
400 renderer = null;
401
402 m_colors.Clear();
403 m_warpTextures.Clear();
404
405 GCSettings.LargeObjectHeapCompactionMode = GCLargeObjectHeapCompactionMode.CompactOnce;
406 GC.Collect();
407 GC.WaitForPendingFinalizers();
408 GC.Collect();
409 GCSettings.LargeObjectHeapCompactionMode = GCLargeObjectHeapCompactionMode.Default;
410 return bitmap;
411 }
412
413 public byte[] WriteJpeg2000Image()
414 {
415 try
416 {
417 using (Bitmap mapbmp = CreateMapTile())
418 return OpenJPEG.EncodeFromImage(mapbmp, false);
419 }
420 catch (Exception e)
421 {
422 // JPEG2000 encoder failed
423 m_log.Error("[WARP 3D IMAGE MODULE]: Failed generating terrain map: ", e);
424 }
425
426 return null;
427 }
428
429 #endregion
430
431 #region Rendering Methods
432
433 // Add a water plane to the renderer.
434 private void CreateWater(WarpRenderer renderer)
435 {
436 float waterHeight = (float)m_scene.RegionInfo.RegionSettings.WaterHeight;
437
438 renderer.AddPlane("Water", m_scene.RegionInfo.RegionSizeX * 0.5f);
439 renderer.Scene.sceneobject("Water").setPos(m_scene.RegionInfo.RegionSizeX * 0.5f,
440 waterHeight,
441 m_scene.RegionInfo.RegionSizeY * 0.5f);
442
443 warp_Material waterMaterial = new warp_Material(ConvertColor(WATER_COLOR));
444 renderer.Scene.addMaterial("WaterMat", waterMaterial);
445 renderer.SetObjectMaterial("Water", "WaterMat");
446 }
447
448 // Add a terrain to the renderer.
449 // Note that we create a 'low resolution' 257x257 vertex terrain rather than trying for
450 // full resolution. This saves a lot of memory especially for very large regions.
451 private void CreateTerrain(WarpRenderer renderer)
452 {
453 ITerrainChannel terrain = m_scene.Heightmap;
454
455 float regionsx = m_scene.RegionInfo.RegionSizeX;
456 float regionsy = m_scene.RegionInfo.RegionSizeY;
457
458 // 'diff' is the difference in scale between the real region size and the size of terrain we're buiding
459
460 int bitWidth;
461 int bitHeight;
462
463 const double log2inv = 1.4426950408889634073599246810019;
464 bitWidth = (int)Math.Ceiling((Math.Log(terrain.Width) * log2inv));
465 bitHeight = (int)Math.Ceiling((Math.Log(terrain.Height) * log2inv));
466
467 if (bitWidth > 8) // more than 256 is very heavy :(
468 bitWidth = 8;
469 if (bitHeight > 8)
470 bitHeight = 8;
471
472 int twidth = (int)Math.Pow(2, bitWidth);
473 int theight = (int)Math.Pow(2, bitHeight);
474
475 float diff = regionsx / twidth;
476
477 int npointsx = (int)(regionsx / diff);
478 int npointsy = (int)(regionsy / diff);
479
480 float invsx = 1.0f / (npointsx * diff);
481 float invsy = 1.0f / (npointsy * diff);
482
483 npointsx++;
484 npointsy++;
485
486 // Create all the vertices for the terrain
487 warp_Object obj = new warp_Object();
488 warp_Vector pos;
489 float x, y;
490 float tv;
491 for (y = 0; y < regionsy; y += diff)
492 {
493 tv = y * invsy;
494 for (x = 0; x < regionsx; x += diff)
495 {
496 pos = ConvertVector(x, y, (float)terrain[(int)x, (int)y]);
497 obj.addVertex(new warp_Vertex(pos, x * invsx, tv));
498 }
499 pos = ConvertVector(x, y, (float)terrain[(int)(x - diff), (int)y]);
500 obj.addVertex(new warp_Vertex(pos, 1.0f, tv));
501 }
502
503 int lastY = (int)(y - diff);
504 for (x = 0; x < regionsx; x += diff)
505 {
506 pos = ConvertVector(x, y, (float)terrain[(int)x, lastY]);
507 obj.addVertex(new warp_Vertex(pos, x * invsx, 1.0f));
508 }
509 pos = ConvertVector(x, y, (float)terrain[(int)(x - diff), lastY]);
510 obj.addVertex(new warp_Vertex(pos, 1.0f, 1.0f));
511
512 // create triangles.
513 int limx = npointsx - 1;
514 int limy = npointsy - 1;
515 for (int j = 0; j < limy; j++)
516 {
517 for (int i = 0; i < limx; i++)
518 {
519 int v = j * npointsx + i;
520
521 // Make two triangles for each of the squares in the grid of vertices
522 obj.addTriangle(
523 v,
524 v + 1,
525 v + npointsx);
526
527 obj.addTriangle(
528 v + npointsx + 1,
529 v + npointsx,
530 v + 1);
531 }
532 }
533
534 renderer.Scene.addObject("Terrain", obj);
535
536 UUID[] textureIDs = new UUID[4];
537 float[] startHeights = new float[4];
538 float[] heightRanges = new float[4];
539
540 OpenSim.Framework.RegionSettings regionInfo = m_scene.RegionInfo.RegionSettings;
541
542 textureIDs[0] = regionInfo.TerrainTexture1;
543 textureIDs[1] = regionInfo.TerrainTexture2;
544 textureIDs[2] = regionInfo.TerrainTexture3;
545 textureIDs[3] = regionInfo.TerrainTexture4;
546
547 startHeights[0] = (float)regionInfo.Elevation1SW;
548 startHeights[1] = (float)regionInfo.Elevation1NW;
549 startHeights[2] = (float)regionInfo.Elevation1SE;
550 startHeights[3] = (float)regionInfo.Elevation1NE;
551
552 heightRanges[0] = (float)regionInfo.Elevation2SW;
553 heightRanges[1] = (float)regionInfo.Elevation2NW;
554 heightRanges[2] = (float)regionInfo.Elevation2SE;
555 heightRanges[3] = (float)regionInfo.Elevation2NE;
556
557 warp_Texture texture;
558 using (Bitmap image = TerrainSplat.Splat(terrain, textureIDs, startHeights, heightRanges,
559 m_scene.RegionInfo.WorldLocX, m_scene.RegionInfo.WorldLocY,
560 m_scene.AssetService, m_imgDecoder, m_textureTerrain, m_textureAverageTerrain,
561 twidth, twidth))
562 texture = new warp_Texture(image);
563
564 warp_Material material = new warp_Material(texture);
565 renderer.Scene.addMaterial("TerrainMat", material);
566 renderer.SetObjectMaterial("Terrain", "TerrainMat");
567 }
568
569 private void CreateAllPrims(WarpRenderer renderer)
570 {
571 if (m_primMesher == null)
572 return;
573
574 m_scene.ForEachSOG(
575 delegate (SceneObjectGroup group)
576 {
577 foreach (SceneObjectPart child in group.Parts)
578 CreatePrim(renderer, child);
579 }
580 );
581 }
582
583 private void UVPlanarMap(Vertex v, Vector3 scale, out float tu, out float tv)
584 {
585 Vector3 scaledPos = v.Position * scale;
586 float d = v.Normal.X;
587 if (d >= 0.5f)
588 {
589 tu = 2f * scaledPos.Y;
590 tv = scaledPos.X * v.Normal.Z - scaledPos.Z * v.Normal.X;
591 }
592 else if (d <= -0.5f)
593 {
594 tu = -2f * scaledPos.Y;
595 tv = -scaledPos.X * v.Normal.Z + scaledPos.Z * v.Normal.X;
596 }
597 else if (v.Normal.Y > 0f)
598 {
599 tu = -2f * scaledPos.X;
600 tv = scaledPos.Y * v.Normal.Z - scaledPos.Z * v.Normal.Y;
601 }
602 else
603 {
604 tu = 2f * scaledPos.X;
605 tv = -scaledPos.Y * v.Normal.Z + scaledPos.Z * v.Normal.Y;
606 }
607
608 tv *= 2f;
609 }
610
611 private void CreatePrim(WarpRenderer renderer, SceneObjectPart prim)
612 {
613 if ((PCode)prim.Shape.PCode != PCode.Prim)
614 return;
615
616 Vector3 ppos = prim.GetWorldPosition();
617 if (ppos.Z < m_renderMinHeight || ppos.Z > m_renderMaxHeight)
618 return;
619
620 warp_Vector primPos = ConvertVector(ppos);
621 warp_Quaternion primRot = ConvertQuaternion(prim.GetWorldRotation());
622 warp_Matrix m = warp_Matrix.quaternionMatrix(primRot);
623
624 float screenFactor = renderer.Scene.EstimateBoxProjectedArea(primPos, ConvertVector(prim.Scale), m);
625 if (screenFactor < 0)
626 return;
627
628 int p2 = (int)(-(float)Math.Log(screenFactor) * 1.442695f * 0.5 - 1);
629
630 if (p2 < 0)
631 p2 = 0;
632 else if (p2 > 3)
633 p2 = 3;
634
635 DetailLevel lod = (DetailLevel)(3 - p2);
636
637 FacetedMesh renderMesh = null;
638 Primitive omvPrim = prim.Shape.ToOmvPrimitive(prim.OffsetPosition, prim.RotationOffset);
639
640 if (m_renderMeshes)
641 {
642 if (omvPrim.Sculpt != null && omvPrim.Sculpt.SculptTexture != UUID.Zero)
643 {
644 // Try fetchinng the asset
645 byte[] sculptAsset = m_scene.AssetService.GetData(omvPrim.Sculpt.SculptTexture.ToString());
646 if (sculptAsset != null)
647 {
648 // Is it a mesh?
649 if (omvPrim.Sculpt.Type == SculptType.Mesh)
650 {
651 AssetMesh meshAsset = new AssetMesh(omvPrim.Sculpt.SculptTexture, sculptAsset);
652 FacetedMesh.TryDecodeFromAsset(omvPrim, meshAsset, lod, out renderMesh);
653 meshAsset = null;
654 }
655 else // It's sculptie
656 {
657 if (m_imgDecoder != null)
658 {
659 Image sculpt = m_imgDecoder.DecodeToImage(sculptAsset);
660 if (sculpt != null)
661 {
662 renderMesh = m_primMesher.GenerateFacetedSculptMesh(omvPrim, (Bitmap)sculpt, lod);
663 sculpt.Dispose();
664 }
665 }
666 }
667 }
668 else
669 {
670 m_log.WarnFormat("[Warp3D] failed to get mesh or sculpt asset {0} of prim {1} at {2}",
671 omvPrim.Sculpt.SculptTexture.ToString(), prim.Name, prim.GetWorldPosition().ToString());
672 }
673 }
674 }
675
676 // If not a mesh or sculptie, try the regular mesher
677 if (renderMesh == null)
678 {
679 renderMesh = m_primMesher.GenerateFacetedMesh(omvPrim, lod);
680 }
681
682 if (renderMesh == null)
683 return;
684
685 string primID = prim.UUID.ToString();
686
687 // Create the prim faces
688 // TODO: Implement the useTextures flag behavior
689 for (int i = 0; i < renderMesh.Faces.Count; i++)
690 {
691 Face face = renderMesh.Faces[i];
692 string meshName = primID + i.ToString();
693
694 // Avoid adding duplicate meshes to the scene
695 if (renderer.Scene.objectData.ContainsKey(meshName))
696 continue;
697
698 warp_Object faceObj = new warp_Object();
699
700 Primitive.TextureEntryFace teFace = prim.Shape.Textures.GetFace((uint)i);
701 Color4 faceColor = teFace.RGBA;
702 if (faceColor.A == 0)
703 continue;
704
705 string materialName = String.Empty;
706 if (m_texturePrims)
707 {
708 // if(lod > DetailLevel.Low)
709 {
710 // materialName = GetOrCreateMaterial(renderer, faceColor, teFace.TextureID, lod == DetailLevel.Low);
711 materialName = GetOrCreateMaterial(renderer, faceColor, teFace.TextureID, false, prim);
712 if (String.IsNullOrEmpty(materialName))
713 continue;
714 int c = renderer.Scene.material(materialName).getColor();
715 if ((c & warp_Color.MASKALPHA) == 0)
716 continue;
717 }
718 }
719 else
720 materialName = GetOrCreateMaterial(renderer, faceColor);
721
722 if (renderer.Scene.material(materialName).getTexture() == null)
723 {
724 // uv map details dont not matter for color;
725 for (int j = 0; j < face.Vertices.Count; j++)
726 {
727 Vertex v = face.Vertices[j];
728 warp_Vector pos = ConvertVector(v.Position);
729 warp_Vertex vert = new warp_Vertex(pos, v.TexCoord.X, v.TexCoord.Y);
730 faceObj.addVertex(vert);
731 }
732 }
733 else
734 {
735 float tu;
736 float tv;
737 float offsetu = teFace.OffsetU + 0.5f;
738 float offsetv = teFace.OffsetV + 0.5f;
739 float scaleu = teFace.RepeatU;
740 float scalev = teFace.RepeatV;
741 float rotation = teFace.Rotation;
742 float rc = 0;
743 float rs = 0;
744 if (rotation != 0)
745 {
746 rc = (float)Math.Cos(rotation);
747 rs = (float)Math.Sin(rotation);
748 }
749
750 for (int j = 0; j < face.Vertices.Count; j++)
751 {
752 warp_Vertex vert;
753 Vertex v = face.Vertices[j];
754 warp_Vector pos = ConvertVector(v.Position);
755 if (teFace.TexMapType == MappingType.Planar)
756 UVPlanarMap(v, prim.Scale, out tu, out tv);
757 else
758 {
759 tu = v.TexCoord.X - 0.5f;
760 tv = 0.5f - v.TexCoord.Y;
761 }
762 if (rotation != 0)
763 {
764 float tur = tu * rc - tv * rs;
765 float tvr = tu * rs + tv * rc;
766 tur *= scaleu;
767 tur += offsetu;
768
769 tvr *= scalev;
770 tvr += offsetv;
771 vert = new warp_Vertex(pos, tur, tvr);
772 }
773 else
774 {
775 tu *= scaleu;
776 tu += offsetu;
777 tv *= scalev;
778 tv += offsetv;
779 vert = new warp_Vertex(pos, tu, tv);
780 }
781
782 faceObj.addVertex(vert);
783 }
784 }
785
786 for (int j = 0; j < face.Indices.Count; j += 3)
787 {
788 faceObj.addTriangle(
789 face.Indices[j + 0],
790 face.Indices[j + 1],
791 face.Indices[j + 2]);
792 }
793
794 faceObj.scaleSelf(prim.Scale.X, prim.Scale.Z, prim.Scale.Y);
795 faceObj.transform(m);
796 faceObj.setPos(primPos);
797
798 renderer.Scene.addObject(meshName, faceObj);
799 renderer.SetObjectMaterial(meshName, materialName);
800 }
801 }
802
803 private int GetFaceColor(Primitive.TextureEntryFace face)
804 {
805 int color;
806 Color4 ctmp = Color4.White;
807
808 if (face.TextureID == UUID.Zero)
809 return warp_Color.White;
810
811 if (!m_colors.TryGetValue(face.TextureID, out color))
812 {
813 bool fetched = false;
814
815 // Attempt to fetch the texture metadata
816 string cacheName = "MAPCLR" + face.TextureID.ToString();
817 AssetBase metadata = m_scene.AssetService.GetCached(cacheName);
818 if (metadata != null)
819 {
820 OSDMap map = null;
821 try { map = OSDParser.Deserialize(metadata.Data) as OSDMap; } catch { }
822
823 if (map != null)
824 {
825 ctmp = map["X-RGBA"].AsColor4();
826 fetched = true;
827 }
828 }
829
830 if (!fetched)
831 {
832 // Fetch the texture, decode and get the average color,
833 // then save it to a temporary metadata asset
834 AssetBase textureAsset = m_scene.AssetService.Get(face.TextureID.ToString());
835 if (textureAsset != null)
836 {
837 int width, height;
838 ctmp = GetAverageColor(textureAsset.FullID, textureAsset.Data, out width, out height);
839
840 OSDMap data = new OSDMap { { "X-RGBA", OSD.FromColor4(ctmp) } };
841 metadata = new AssetBase
842 {
843 Data = System.Text.Encoding.UTF8.GetBytes(OSDParser.SerializeJsonString(data)),
844 Description = "Metadata for texture color" + face.TextureID.ToString(),
845 Flags = AssetFlags.Collectable,
846 FullID = UUID.Zero,
847 ID = cacheName,
848 Local = true,
849 Temporary = true,
850 Name = String.Empty,
851 Type = (sbyte)AssetType.Unknown
852 };
853 m_scene.AssetService.Store(metadata);
854 }
855 else
856 {
857 ctmp = new Color4(0.5f, 0.5f, 0.5f, 1.0f);
858 }
859 }
860 color = ConvertColor(ctmp);
861 m_colors[face.TextureID] = color;
862 }
863
864 return color;
865 }
866
867 private string GetOrCreateMaterial(WarpRenderer renderer, Color4 color)
868 {
869 string name = color.ToString();
870
871 warp_Material material = renderer.Scene.material(name);
872 if (material != null)
873 return name;
874
875 renderer.AddMaterial(name, ConvertColor(color));
876 return name;
877 }
878
879 public string GetOrCreateMaterial(WarpRenderer renderer, Color4 faceColor, UUID textureID, bool useAverageTextureColor, SceneObjectPart sop)
880 {
881 int color = ConvertColor(faceColor);
882 string idstr = textureID.ToString() + color.ToString();
883 string materialName = "MAPMAT" + idstr;
884
885 if (renderer.Scene.material(materialName) != null)
886 return materialName;
887
888 warp_Material mat = new warp_Material();
889 warp_Texture texture = GetTexture(textureID, sop);
890 if (texture != null)
891 {
892 if (useAverageTextureColor)
893 color = warp_Color.multiply(color, texture.averageColor);
894 else
895 mat.setTexture(texture);
896 }
897 else
898 color = warp_Color.multiply(color, warp_Color.Grey);
899
900 mat.setColor(color);
901 renderer.Scene.addMaterial(materialName, mat);
902
903 return materialName;
904 }
905
906 private warp_Texture GetTexture(UUID id, SceneObjectPart sop)
907 {
908 warp_Texture ret = null;
909 if (id == UUID.Zero)
910 return ret;
911
912 if (m_warpTextures.TryGetValue(id.ToString(), out ret))
913 return ret;
914
915 byte[] asset = m_scene.AssetService.GetData(id.ToString());
916
917 if (asset != null)
918 {
919 try
920 {
921 using (Bitmap img = (Bitmap)m_imgDecoder.DecodeToImage(asset))
922 ret = new warp_Texture(img, 8); // reduce textures size to 256x256
923 }
924 catch (Exception e)
925 {
926 m_log.WarnFormat("[Warp3D]: Failed to decode texture {0} for prim {1} at {2}, exception {3}", id.ToString(), sop.Name, sop.GetWorldPosition().ToString(), e.Message);
927 }
928 }
929 else
930 m_log.WarnFormat("[Warp3D]: missing texture {0} data for prim {1} at {2}",
931 id.ToString(), sop.Name, sop.GetWorldPosition().ToString());
932
933 m_warpTextures[id.ToString()] = ret;
934 return ret;
935 }
936
937 #endregion Rendering Methods
938
939 #region Static Helpers
940 // Note: axis change.
941 private static warp_Vector ConvertVector(float x, float y, float z)
942 {
943 return new warp_Vector(x, z, y);
944 }
945
946 private static warp_Vector ConvertVector(Vector3 vector)
947 {
948 return new warp_Vector(vector.X, vector.Z, vector.Y);
949 }
950
951 private static warp_Quaternion ConvertQuaternion(Quaternion quat)
952 {
953 return new warp_Quaternion(quat.X, quat.Z, quat.Y, -quat.W);
954 }
955
956 private static int ConvertColor(Color4 color)
957 {
958 int c = warp_Color.getColor((byte)(color.R * 255f), (byte)(color.G * 255f), (byte)(color.B * 255f), (byte)(color.A * 255f));
959 return c;
960 }
961
962 private static Vector3 SurfaceNormal(Vector3 c1, Vector3 c2, Vector3 c3)
963 {
964 Vector3 edge1 = new Vector3(c2.X - c1.X, c2.Y - c1.Y, c2.Z - c1.Z);
965 Vector3 edge2 = new Vector3(c3.X - c1.X, c3.Y - c1.Y, c3.Z - c1.Z);
966
967 Vector3 normal = Vector3.Cross(edge1, edge2);
968 normal.Normalize();
969
970 return normal;
971 }
972
973 public Color4 GetAverageColor(UUID textureID, byte[] j2kData, out int width, out int height)
974 {
975 ulong r = 0;
976 ulong g = 0;
977 ulong b = 0;
978 ulong a = 0;
979 int pixelBytes;
980
981 try
982 {
983 using (MemoryStream stream = new MemoryStream(j2kData))
984 using (Bitmap bitmap = (Bitmap)J2kImage.FromStream(stream))
985 {
986 width = bitmap.Width;
987 height = bitmap.Height;
988
989 BitmapData bitmapData = bitmap.LockBits(new Rectangle(0, 0, width, height), ImageLockMode.ReadOnly, bitmap.PixelFormat);
990 pixelBytes = (bitmap.PixelFormat == PixelFormat.Format24bppRgb) ? 3 : 4;
991
992 // Sum up the individual channels
993 unsafe
994 {
995 if (pixelBytes == 4)
996 {
997 for (int y = 0; y < height; y++)
998 {
999 byte* row = (byte*)bitmapData.Scan0 + (y * bitmapData.Stride);
1000
1001 for (int x = 0; x < width; x++)
1002 {
1003 b += row[x * pixelBytes + 0];
1004 g += row[x * pixelBytes + 1];
1005 r += row[x * pixelBytes + 2];
1006 a += row[x * pixelBytes + 3];
1007 }
1008 }
1009 }
1010 else
1011 {
1012 for (int y = 0; y < height; y++)
1013 {
1014 byte* row = (byte*)bitmapData.Scan0 + (y * bitmapData.Stride);
1015
1016 for (int x = 0; x < width; x++)
1017 {
1018 b += row[x * pixelBytes + 0];
1019 g += row[x * pixelBytes + 1];
1020 r += row[x * pixelBytes + 2];
1021 }
1022 }
1023 }
1024 }
1025 }
1026 // Get the averages for each channel
1027 const decimal OO_255 = 1m / 255m;
1028 decimal totalPixels = (decimal)(width * height);
1029
1030 decimal rm = ((decimal)r / totalPixels) * OO_255;
1031 decimal gm = ((decimal)g / totalPixels) * OO_255;
1032 decimal bm = ((decimal)b / totalPixels) * OO_255;
1033 decimal am = ((decimal)a / totalPixels) * OO_255;
1034
1035 if (pixelBytes == 3)
1036 am = 1m;
1037
1038 return new Color4((float)rm, (float)gm, (float)bm, (float)am);
1039
1040 }
1041 catch (Exception ex)
1042 {
1043 m_log.WarnFormat(
1044 "[WARP 3D IMAGE MODULE]: Error decoding JPEG2000 texture {0} ({1} bytes): {2}",
1045 textureID, j2kData.Length, ex.Message);
1046
1047 width = 0;
1048 height = 0;
1049 return new Color4(0.5f, 0.5f, 0.5f, 1.0f);
1050 }
1051 }
1052
1053 #endregion Static Helpers
1054 }
1055
1056 public static class ImageUtils
1057 {
1058 /// <summary>
1059 /// Performs bilinear interpolation between four values
1060 /// </summary>
1061 /// <param name="v00">First, or top left value</param>
1062 /// <param name="v01">Second, or top right value</param>
1063 /// <param name="v10">Third, or bottom left value</param>
1064 /// <param name="v11">Fourth, or bottom right value</param>
1065 /// <param name="xPercent">Interpolation value on the X axis, between 0.0 and 1.0</param>
1066 /// <param name="yPercent">Interpolation value on fht Y axis, between 0.0 and 1.0</param>
1067 /// <returns>The bilinearly interpolated result</returns>
1068 public static float Bilinear(float v00, float v01, float v10, float v11, float xPercent, float yPercent)
1069 {
1070 return Utils.Lerp(Utils.Lerp(v00, v01, xPercent), Utils.Lerp(v10, v11, xPercent), yPercent);
1071 }
1072 }
1073}