opensim – Blame information for rev 1

Subversion Repositories:
Rev:
Rev Author Line No. Line
1 eva 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  
28 using System;
29 using System.IO;
30 using System.Text;
31 using System.Reflection;
32 using System.Xml;
33 using System.Xml.Serialization;
34  
35 using OpenSim.Data;
36 using OpenSim.Framework;
37 using OpenSim.Region.Framework.Interfaces;
38  
39 using OpenMetaverse;
40  
41 using log4net;
42  
43 namespace OpenSim.Region.Framework.Scenes
44 {
45 /// <summary>
46 /// A new version of the old Channel class, simplified
47 /// </summary>
48 public class TerrainChannel : ITerrainChannel
49 {
50 private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
51 private static string LogHeader = "[TERRAIN CHANNEL]";
52  
53 protected TerrainData m_terrainData;
54  
55 public int Width { get { return m_terrainData.SizeX; } } // X dimension
56 // Unfortunately, for historical reasons, in this module 'Width' is X and 'Height' is Y
57 public int Height { get { return m_terrainData.SizeY; } } // Y dimension
58 public int Altitude { get { return m_terrainData.SizeZ; } } // Y dimension
59  
60 // Default, not-often-used builder
61 public TerrainChannel()
62 {
63 m_terrainData = new HeightmapTerrainData((int)Constants.RegionSize, (int)Constants.RegionSize, (int)Constants.RegionHeight);
64 FlatLand();
65 // PinHeadIsland();
66 }
67  
68 // Create terrain of given size
69 public TerrainChannel(int pX, int pY)
70 {
71 m_terrainData = new HeightmapTerrainData(pX, pY, (int)Constants.RegionHeight);
72 }
73  
74 // Create terrain of specified size and initialize with specified terrain.
75 // TODO: join this with the terrain initializers.
76 public TerrainChannel(String type, int pX, int pY, int pZ)
77 {
78 m_terrainData = new HeightmapTerrainData(pX, pY, pZ);
79 if (type.Equals("flat"))
80 FlatLand();
81 else
82 PinHeadIsland();
83 }
84  
85 // Create channel passed a heightmap and expected dimensions of the region.
86 // The heightmap might not fit the passed size so accomodations must be made.
87 public TerrainChannel(double[,] pM, int pSizeX, int pSizeY, int pAltitude)
88 {
89 int hmSizeX = pM.GetLength(0);
90 int hmSizeY = pM.GetLength(1);
91  
92 m_terrainData = new HeightmapTerrainData(pSizeX, pSizeY, pAltitude);
93  
94 for (int xx = 0; xx < pSizeX; xx++)
95 for (int yy = 0; yy < pSizeY; yy++)
96 if (xx > hmSizeX || yy > hmSizeY)
97 m_terrainData[xx, yy] = TerrainData.DefaultTerrainHeight;
98 else
99 m_terrainData[xx, yy] = (float)pM[xx, yy];
100 }
101  
102 public TerrainChannel(TerrainData pTerrData)
103 {
104 m_terrainData = pTerrData;
105 }
106  
107 #region ITerrainChannel Members
108  
109 // ITerrainChannel.MakeCopy()
110 public ITerrainChannel MakeCopy()
111 {
112 return this.Copy();
113 }
114  
115 // ITerrainChannel.GetTerrainData()
116 public TerrainData GetTerrainData()
117 {
118 return m_terrainData;
119 }
120  
121 // ITerrainChannel.GetFloatsSerialized()
122 // This one dimensional version is ordered so height = map[y*sizeX+x];
123 // DEPRECATED: don't use this function as it does not retain the dimensions of the terrain
124 // and the caller will probably do the wrong thing if the terrain is not the legacy 256x256.
125 public float[] GetFloatsSerialised()
126 {
127 int points = Width * Height;
128 float[] heights = new float[points];
129  
130 int idx = 0;
131 for (int jj = 0; jj < Height; jj++)
132 for (int ii = 0; ii < Width; ii++)
133 {
134 heights[idx++] = m_terrainData[ii, jj];
135 }
136  
137 return heights;
138 }
139  
140 // ITerrainChannel.GetDoubles()
141 public double[,] GetDoubles()
142 {
143 double[,] heights = new double[Width, Height];
144  
145 int idx = 0; // index into serialized array
146 for (int ii = 0; ii < Width; ii++)
147 {
148 for (int jj = 0; jj < Height; jj++)
149 {
150 heights[ii, jj] = (double)m_terrainData[ii, jj];
151 idx++;
152 }
153 }
154  
155 return heights;
156 }
157  
158 // ITerrainChannel.this[x,y]
159 public double this[int x, int y]
160 {
161 get {
162 if (x < 0 || x >= Width || y < 0 || y >= Height)
163 return 0;
164 return (double)m_terrainData[x, y];
165 }
166 set
167 {
168 if (Double.IsNaN(value) || Double.IsInfinity(value))
169 return;
170  
171 m_terrainData[x, y] = (float)value;
172 }
173 }
174  
175 // ITerrainChannel.GetHieghtAtXYZ(x, y, z)
176 public float GetHeightAtXYZ(float x, float y, float z)
177 {
178 if (x < 0 || x >= Width || y < 0 || y >= Height)
179 return 0;
180 return m_terrainData[(int)x, (int)y];
181 }
182  
183 // ITerrainChannel.Tainted()
184 public bool Tainted(int x, int y)
185 {
186 return m_terrainData.IsTaintedAt(x, y);
187 }
188  
189 // ITerrainChannel.SaveToXmlString()
190 public string SaveToXmlString()
191 {
192 XmlWriterSettings settings = new XmlWriterSettings();
193 settings.Encoding = Util.UTF8;
194 using (StringWriter sw = new StringWriter())
195 {
196 using (XmlWriter writer = XmlWriter.Create(sw, settings))
197 {
198 WriteXml(writer);
199 }
200 string output = sw.ToString();
201 return output;
202 }
203 }
204  
205 // ITerrainChannel.LoadFromXmlString()
206 public void LoadFromXmlString(string data)
207 {
208 StringReader sr = new StringReader(data);
209 XmlTextReader reader = new XmlTextReader(sr);
210 reader.Read();
211  
212 ReadXml(reader);
213 reader.Close();
214 sr.Close();
215 }
216  
217 // ITerrainChannel.Merge
218 public void Merge(ITerrainChannel newTerrain, Vector3 displacement, float radianRotation, Vector2 rotationDisplacement)
219 {
220 m_log.DebugFormat("{0} Merge. inSize=<{1},{2}>, disp={3}, rot={4}, rotDisp={5}, outSize=<{6},{7}>", LogHeader,
221 newTerrain.Width, newTerrain.Height,
222 displacement, radianRotation, rotationDisplacement,
223 m_terrainData.SizeX, m_terrainData.SizeY);
224 for (int xx = 0; xx < newTerrain.Width; xx++)
225 {
226 for (int yy = 0; yy < newTerrain.Height; yy++)
227 {
228 int dispX = (int)displacement.X;
229 int dispY = (int)displacement.Y;
230 float newHeight = (float)newTerrain[xx, yy] + displacement.Z;
231 if (radianRotation == 0)
232 {
233 // If no rotation, place the new height in the specified location
234 dispX += xx;
235 dispY += yy;
236 if (dispX >= 0 && dispX < m_terrainData.SizeX && dispY >= 0 && dispY < m_terrainData.SizeY)
237 {
238 m_terrainData[dispX, dispY] = newHeight;
239 }
240 }
241 else
242 {
243 // If rotating, we have to smooth the result because the conversion
244 // to ints will mean heightmap entries will not get changed
245 // First compute the rotation location for the new height.
246 dispX += (int)(rotationDisplacement.X
247 + ((float)xx - rotationDisplacement.X) * Math.Cos(radianRotation)
248 - ((float)yy - rotationDisplacement.Y) * Math.Sin(radianRotation) );
249  
250 dispY += (int)(rotationDisplacement.Y
251 + ((float)xx - rotationDisplacement.X) * Math.Sin(radianRotation)
252 + ((float)yy - rotationDisplacement.Y) * Math.Cos(radianRotation) );
253  
254 if (dispX >= 0 && dispX < m_terrainData.SizeX && dispY >= 0 && dispY < m_terrainData.SizeY)
255 {
256 float oldHeight = m_terrainData[dispX, dispY];
257 // Smooth the heights around this location if the old height is far from this one
258 for (int sxx = dispX - 2; sxx < dispX + 2; sxx++)
259 {
260 for (int syy = dispY - 2; syy < dispY + 2; syy++)
261 {
262 if (sxx >= 0 && sxx < m_terrainData.SizeX && syy >= 0 && syy < m_terrainData.SizeY)
263 {
264 if (sxx == dispX && syy == dispY)
265 {
266 // Set height for the exact rotated point
267 m_terrainData[dispX, dispY] = newHeight;
268 }
269 else
270 {
271 if (Math.Abs(m_terrainData[sxx, syy] - newHeight) > 1f)
272 {
273 // If the adjacent height is far off, force it to this height
274 m_terrainData[sxx, syy] = newHeight;
275 }
276 }
277 }
278 }
279 }
280 }
281  
282 if (dispX >= 0 && dispX < m_terrainData.SizeX && dispY >= 0 && dispY < m_terrainData.SizeY)
283 {
284 m_terrainData[dispX, dispY] = (float)newTerrain[xx, yy];
285 }
286 }
287 }
288 }
289 }
290  
291 #endregion
292  
293 public TerrainChannel Copy()
294 {
295 TerrainChannel copy = new TerrainChannel();
296 copy.m_terrainData = m_terrainData.Clone();
297 return copy;
298 }
299  
300 private void WriteXml(XmlWriter writer)
301 {
302 if (Width == Constants.RegionSize && Height == Constants.RegionSize)
303 {
304 // Downward compatibility for legacy region terrain maps.
305 // If region is exactly legacy size, return the old format XML.
306 writer.WriteStartElement(String.Empty, "TerrainMap", String.Empty);
307 ToXml(writer);
308 writer.WriteEndElement();
309 }
310 else
311 {
312 // New format XML that includes width and length.
313 writer.WriteStartElement(String.Empty, "TerrainMap2", String.Empty);
314 ToXml2(writer);
315 writer.WriteEndElement();
316 }
317 }
318  
319 private void ReadXml(XmlReader reader)
320 {
321 // Check the first element. If legacy element, use the legacy reader.
322 if (reader.IsStartElement("TerrainMap"))
323 {
324 reader.ReadStartElement("TerrainMap");
325 FromXml(reader);
326 }
327 else
328 {
329 reader.ReadStartElement("TerrainMap2");
330 FromXml2(reader);
331 }
332 }
333  
334 // Write legacy terrain map. Presumed to be 256x256 of data encoded as floats in a byte array.
335 private void ToXml(XmlWriter xmlWriter)
336 {
337 float[] mapData = GetFloatsSerialised();
338 byte[] buffer = new byte[mapData.Length * 4];
339 for (int i = 0; i < mapData.Length; i++)
340 {
341 byte[] value = BitConverter.GetBytes(mapData[i]);
342 Array.Copy(value, 0, buffer, (i * 4), 4);
343 }
344 XmlSerializer serializer = new XmlSerializer(typeof(byte[]));
345 serializer.Serialize(xmlWriter, buffer);
346 }
347  
348 // Read legacy terrain map. Presumed to be 256x256 of data encoded as floats in a byte array.
349 private void FromXml(XmlReader xmlReader)
350 {
351 XmlSerializer serializer = new XmlSerializer(typeof(byte[]));
352 byte[] dataArray = (byte[])serializer.Deserialize(xmlReader);
353 int index = 0;
354  
355 m_terrainData = new HeightmapTerrainData(Height, Width, (int)Constants.RegionHeight);
356  
357 for (int y = 0; y < Height; y++)
358 {
359 for (int x = 0; x < Width; x++)
360 {
361 float value;
362 value = BitConverter.ToSingle(dataArray, index);
363 index += 4;
364 this[x, y] = (double)value;
365 }
366 }
367 }
368  
369 private class TerrainChannelXMLPackage
370 {
371 public int Version;
372 public int SizeX;
373 public int SizeY;
374 public int SizeZ;
375 public float CompressionFactor;
376 public short[] Map;
377 public TerrainChannelXMLPackage(int pX, int pY, int pZ, float pCompressionFactor, short[] pMap)
378 {
379 Version = 1;
380 SizeX = pX;
381 SizeY = pY;
382 SizeZ = pZ;
383 CompressionFactor = pCompressionFactor;
384 Map = pMap;
385 }
386 }
387  
388 // New terrain serialization format that includes the width and length.
389 private void ToXml2(XmlWriter xmlWriter)
390 {
391 TerrainChannelXMLPackage package = new TerrainChannelXMLPackage(Width, Height, Altitude, m_terrainData.CompressionFactor,
392 m_terrainData.GetCompressedMap());
393 XmlSerializer serializer = new XmlSerializer(typeof(TerrainChannelXMLPackage));
394 serializer.Serialize(xmlWriter, package);
395 }
396  
397 // New terrain serialization format that includes the width and length.
398 private void FromXml2(XmlReader xmlReader)
399 {
400 XmlSerializer serializer = new XmlSerializer(typeof(TerrainChannelXMLPackage));
401 TerrainChannelXMLPackage package = (TerrainChannelXMLPackage)serializer.Deserialize(xmlReader);
402 m_terrainData = new HeightmapTerrainData(package.Map, package.CompressionFactor, package.SizeX, package.SizeY, package.SizeZ);
403 }
404  
405 // Fill the heightmap with the center bump terrain
406 private void PinHeadIsland()
407 {
408 for (int x = 0; x < Width; x++)
409 {
410 for (int y = 0; y < Height; y++)
411 {
412 m_terrainData[x, y] = (float)TerrainUtil.PerlinNoise2D(x, y, 2, 0.125) * 10;
413 float spherFacA = (float)(TerrainUtil.SphericalFactor(x, y, m_terrainData.SizeX / 2.0, m_terrainData.SizeY / 2.0, 50) * 0.01d);
414 float spherFacB = (float)(TerrainUtil.SphericalFactor(x, y, m_terrainData.SizeX / 2.0, m_terrainData.SizeY / 2.0, 100) * 0.001d);
415 if (m_terrainData[x, y]< spherFacA)
416 m_terrainData[x, y]= spherFacA;
417 if (m_terrainData[x, y]< spherFacB)
418 m_terrainData[x, y] = spherFacB;
419 }
420 }
421 }
422  
423 private void FlatLand()
424 {
425 m_terrainData.ClearLand();
426 }
427 }
428 }