diff options
Diffstat (limited to 'OpenSim/Region/Framework/Scenes/TerrainChannel.cs')
-rw-r--r-- | OpenSim/Region/Framework/Scenes/TerrainChannel.cs | 354 |
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 | ||
28 | using OpenSim.Framework; | ||
29 | using OpenSim.Region.Framework.Interfaces; | ||
30 | using System; | 28 | using System; |
29 | using System.IO; | ||
31 | using System.Text; | 30 | using System.Text; |
31 | using System.Reflection; | ||
32 | using System.Xml; | 32 | using System.Xml; |
33 | using System.IO; | ||
34 | using System.Xml.Serialization; | 33 | using System.Xml.Serialization; |
35 | 34 | ||
35 | using OpenSim.Data; | ||
36 | using OpenSim.Framework; | ||
37 | using OpenSim.Region.Framework.Interfaces; | ||
38 | |||
39 | using OpenMetaverse; | ||
40 | |||
41 | using log4net; | ||
42 | |||
36 | namespace OpenSim.Region.Framework.Scenes | 43 | namespace 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 | } |