aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/OpenSim/Region/Framework/Scenes/TerrainChannel.cs
diff options
context:
space:
mode:
Diffstat (limited to 'OpenSim/Region/Framework/Scenes/TerrainChannel.cs')
-rw-r--r--OpenSim/Region/Framework/Scenes/TerrainChannel.cs354
1 files changed, 256 insertions, 98 deletions
diff --git a/OpenSim/Region/Framework/Scenes/TerrainChannel.cs b/OpenSim/Region/Framework/Scenes/TerrainChannel.cs
index c0ca48e..60dc6c9 100644
--- a/OpenSim/Region/Framework/Scenes/TerrainChannel.cs
+++ b/OpenSim/Region/Framework/Scenes/TerrainChannel.cs
@@ -25,14 +25,21 @@
25 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */ 26 */
27 27
28using OpenSim.Framework;
29using OpenSim.Region.Framework.Interfaces;
30using System; 28using System;
29using System.IO;
31using System.Text; 30using System.Text;
31using System.Reflection;
32using System.Xml; 32using System.Xml;
33using System.IO;
34using System.Xml.Serialization; 33using System.Xml.Serialization;
35 34
35using OpenSim.Data;
36using OpenSim.Framework;
37using OpenSim.Region.Framework.Interfaces;
38
39using OpenMetaverse;
40
41using log4net;
42
36namespace OpenSim.Region.Framework.Scenes 43namespace OpenSim.Region.Framework.Scenes
37{ 44{
38 /// <summary> 45 /// <summary>
@@ -40,132 +47,146 @@ namespace OpenSim.Region.Framework.Scenes
40 /// </summary> 47 /// </summary>
41 public class TerrainChannel : ITerrainChannel 48 public class TerrainChannel : ITerrainChannel
42 { 49 {
43 private readonly bool[,] taint; 50 private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
44 private double[,] map; 51 private static string LogHeader = "[TERRAIN CHANNEL]";
45 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
46 public TerrainChannel() 61 public TerrainChannel()
47 { 62 {
48 map = new double[Constants.RegionSize, Constants.RegionSize]; 63 m_terrainData = new HeightmapTerrainData((int)Constants.RegionSize, (int)Constants.RegionSize, (int)Constants.RegionHeight);
49 taint = new bool[Constants.RegionSize / 16, Constants.RegionSize / 16]; 64 FlatLand();
50 65 // PinHeadIsland();
51 PinHeadIsland();
52 } 66 }
53 67
54 public TerrainChannel(String type) 68 // Create terrain of given size
69 public TerrainChannel(int pX, int pY)
55 { 70 {
56 map = new double[Constants.RegionSize, Constants.RegionSize]; 71 m_terrainData = new HeightmapTerrainData(pX, pY, (int)Constants.RegionHeight);
57 taint = new bool[Constants.RegionSize / 16, Constants.RegionSize / 16]; 72 }
58 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);
59 if (type.Equals("flat")) 79 if (type.Equals("flat"))
60 FlatLand(); 80 FlatLand();
61 else 81 else
62 PinHeadIsland(); 82 PinHeadIsland();
63 } 83 }
64 84
65 public TerrainChannel(double[,] import) 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)
66 { 88 {
67 map = import; 89 int hmSizeX = pM.GetLength(0);
68 taint = new bool[import.GetLength(0),import.GetLength(1)]; 90 int hmSizeY = pM.GetLength(1);
69 }
70 91
71 public TerrainChannel(bool createMap) 92 m_terrainData = new HeightmapTerrainData(pSizeX, pSizeY, pAltitude);
72 { 93
73 if (createMap) 94 for (int xx = 0; xx < pSizeX; xx++)
74 { 95 for (int yy = 0; yy < pSizeY; yy++)
75 map = new double[Constants.RegionSize,Constants.RegionSize]; 96 if (xx > hmSizeX || yy > hmSizeY)
76 taint = new bool[Constants.RegionSize / 16,Constants.RegionSize / 16]; 97 m_terrainData[xx, yy] = TerrainData.DefaultTerrainHeight;
77 } 98 else
99 m_terrainData[xx, yy] = (float)pM[xx, yy];
78 } 100 }
79 101
80 public TerrainChannel(int w, int h) 102 public TerrainChannel(TerrainData pTerrData)
81 { 103 {
82 map = new double[w,h]; 104 m_terrainData = pTerrData;
83 taint = new bool[w / 16,h / 16];
84 } 105 }
85 106
86 #region ITerrainChannel Members 107 #region ITerrainChannel Members
87 108
88 public int Width 109 // ITerrainChannel.MakeCopy()
110 public ITerrainChannel MakeCopy()
89 { 111 {
90 get { return map.GetLength(0); } 112 return this.Copy();
91 } 113 }
92 114
93 public int Height 115 // ITerrainChannel.GetTerrainData()
116 public TerrainData GetTerrainData()
94 { 117 {
95 get { return map.GetLength(1); } 118 return m_terrainData;
96 } 119 }
97 120
98 public ITerrainChannel MakeCopy() 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()
99 { 126 {
100 TerrainChannel copy = new TerrainChannel(false); 127 int points = Width * Height;
101 copy.map = (double[,]) map.Clone(); 128 float[] heights = new float[points];
102 129
103 return copy; 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;
104 } 138 }
105 139
106 public float[] GetFloatsSerialised() 140 // ITerrainChannel.GetDoubles()
141 public double[,] GetDoubles()
107 { 142 {
108 // Move the member variables into local variables, calling 143 double[,] heights = new double[Width, Height];
109 // member variables 256*256 times gets expensive
110 int w = Width;
111 int h = Height;
112 float[] heights = new float[w * h];
113 144
114 int i, j; // map coordinates
115 int idx = 0; // index into serialized array 145 int idx = 0; // index into serialized array
116 for (i = 0; i < h; i++) 146 for (int ii = 0; ii < Width; ii++)
117 { 147 {
118 for (j = 0; j < w; j++) 148 for (int jj = 0; jj < Height; jj++)
119 { 149 {
120 heights[idx++] = (float)map[j, i]; 150 heights[ii, jj] = (double)m_terrainData[ii, jj];
151 idx++;
121 } 152 }
122 } 153 }
123 154
124 return heights; 155 return heights;
125 } 156 }
126 157
127 public double[,] GetDoubles() 158 // ITerrainChannel.this[x,y]
128 {
129 return map;
130 }
131
132 public double this[int x, int y] 159 public double this[int x, int y]
133 { 160 {
134 get { return map[x, y]; } 161 get {
162 if (x < 0 || x >= Width || y < 0 || y >= Height)
163 return 0;
164 return (double)m_terrainData[x, y];
165 }
135 set 166 set
136 { 167 {
137 // Will "fix" terrain hole problems. Although not fantastically.
138 if (Double.IsNaN(value) || Double.IsInfinity(value)) 168 if (Double.IsNaN(value) || Double.IsInfinity(value))
139 return; 169 return;
140 170
141 if (map[x, y] != value) 171 m_terrainData[x, y] = (float)value;
142 {
143 taint[x / 16, y / 16] = true;
144 map[x, y] = value;
145 }
146 } 172 }
147 } 173 }
148 174
149 public bool Tainted(int x, int y) 175 // ITerrainChannel.GetHieghtAtXYZ(x, y, z)
176 public float GetHeightAtXYZ(float x, float y, float z)
150 { 177 {
151 if (taint[x / 16, y / 16]) 178 if (x < 0 || x >= Width || y < 0 || y >= Height)
152 { 179 return 0;
153 taint[x / 16, y / 16] = false; 180 return m_terrainData[(int)x, (int)y];
154 return true;
155 }
156 return false;
157 } 181 }
158 182
159 #endregion 183 // ITerrainChannel.Tainted()
160 184 public bool Tainted(int x, int y)
161 public TerrainChannel Copy()
162 { 185 {
163 TerrainChannel copy = new TerrainChannel(false); 186 return m_terrainData.IsTaintedAt(x, y);
164 copy.map = (double[,]) map.Clone();
165
166 return copy;
167 } 187 }
168 188
189 // ITerrainChannel.SaveToXmlString()
169 public string SaveToXmlString() 190 public string SaveToXmlString()
170 { 191 {
171 XmlWriterSettings settings = new XmlWriterSettings(); 192 XmlWriterSettings settings = new XmlWriterSettings();
@@ -181,13 +202,7 @@ namespace OpenSim.Region.Framework.Scenes
181 } 202 }
182 } 203 }
183 204
184 private void WriteXml(XmlWriter writer) 205 // ITerrainChannel.LoadFromXmlString()
185 {
186 writer.WriteStartElement(String.Empty, "TerrainMap", String.Empty);
187 ToXml(writer);
188 writer.WriteEndElement();
189 }
190
191 public void LoadFromXmlString(string data) 206 public void LoadFromXmlString(string data)
192 { 207 {
193 StringReader sr = new StringReader(data); 208 StringReader sr = new StringReader(data);
@@ -199,12 +214,124 @@ namespace OpenSim.Region.Framework.Scenes
199 sr.Close(); 214 sr.Close();
200 } 215 }
201 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
202 private void ReadXml(XmlReader reader) 319 private void ReadXml(XmlReader reader)
203 { 320 {
204 reader.ReadStartElement("TerrainMap"); 321 // Check the first element. If legacy element, use the legacy reader.
205 FromXml(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 }
206 } 332 }
207 333
334 // Write legacy terrain map. Presumed to be 256x256 of data encoded as floats in a byte array.
208 private void ToXml(XmlWriter xmlWriter) 335 private void ToXml(XmlWriter xmlWriter)
209 { 336 {
210 float[] mapData = GetFloatsSerialised(); 337 float[] mapData = GetFloatsSerialised();
@@ -218,12 +345,15 @@ namespace OpenSim.Region.Framework.Scenes
218 serializer.Serialize(xmlWriter, buffer); 345 serializer.Serialize(xmlWriter, buffer);
219 } 346 }
220 347
348 // Read legacy terrain map. Presumed to be 256x256 of data encoded as floats in a byte array.
221 private void FromXml(XmlReader xmlReader) 349 private void FromXml(XmlReader xmlReader)
222 { 350 {
223 XmlSerializer serializer = new XmlSerializer(typeof(byte[])); 351 XmlSerializer serializer = new XmlSerializer(typeof(byte[]));
224 byte[] dataArray = (byte[])serializer.Deserialize(xmlReader); 352 byte[] dataArray = (byte[])serializer.Deserialize(xmlReader);
225 int index = 0; 353 int index = 0;
226 354
355 m_terrainData = new HeightmapTerrainData(Height, Width, (int)Constants.RegionHeight);
356
227 for (int y = 0; y < Height; y++) 357 for (int y = 0; y < Height; y++)
228 { 358 {
229 for (int x = 0; x < Width; x++) 359 for (int x = 0; x < Width; x++)
@@ -236,35 +366,63 @@ namespace OpenSim.Region.Framework.Scenes
236 } 366 }
237 } 367 }
238 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
239 private void PinHeadIsland() 406 private void PinHeadIsland()
240 { 407 {
241 int x; 408 for (int x = 0; x < Width; x++)
242 for (x = 0; x < Constants.RegionSize; x++)
243 { 409 {
244 int y; 410 for (int y = 0; y < Height; y++)
245 for (y = 0; y < Constants.RegionSize; y++)
246 { 411 {
247 map[x, y] = TerrainUtil.PerlinNoise2D(x, y, 2, 0.125) * 10; 412 m_terrainData[x, y] = (float)TerrainUtil.PerlinNoise2D(x, y, 2, 0.125) * 10;
248 double spherFacA = TerrainUtil.SphericalFactor(x, y, Constants.RegionSize / 2.0, Constants.RegionSize / 2.0, 50) * 0.01; 413 float spherFacA = (float)(TerrainUtil.SphericalFactor(x, y, m_terrainData.SizeX / 2.0, m_terrainData.SizeY / 2.0, 50) * 0.01d);
249 double spherFacB = TerrainUtil.SphericalFactor(x, y, Constants.RegionSize / 2.0, Constants.RegionSize / 2.0, 100) * 0.001; 414 float spherFacB = (float)(TerrainUtil.SphericalFactor(x, y, m_terrainData.SizeX / 2.0, m_terrainData.SizeY / 2.0, 100) * 0.001d);
250 if (map[x, y] < spherFacA) 415 if (m_terrainData[x, y]< spherFacA)
251 map[x, y] = spherFacA; 416 m_terrainData[x, y]= spherFacA;
252 if (map[x, y] < spherFacB) 417 if (m_terrainData[x, y]< spherFacB)
253 map[x, y] = spherFacB; 418 m_terrainData[x, y] = spherFacB;
254 } 419 }
255 } 420 }
256 } 421 }
257 422
258 private void FlatLand() 423 private void FlatLand()
259 { 424 {
260 int x; 425 m_terrainData.ClearLand();
261 for (x = 0; x < Constants.RegionSize; x++)
262 {
263 int y;
264 for (y = 0; y < Constants.RegionSize; y++)
265 map[x, y] = 21;
266 }
267 } 426 }
268
269 } 427 }
270} 428}