diff options
author | UbitUmarov | 2015-09-01 11:43:07 +0100 |
---|---|---|
committer | UbitUmarov | 2015-09-01 11:43:07 +0100 |
commit | fb78b182520fc9bb0f971afd0322029c70278ea6 (patch) | |
tree | b4e30d383938fdeef8c92d1d1c2f44bb61d329bd /OpenSim/Region/Framework/Scenes/TerrainChannel.cs | |
parent | lixo (diff) | |
parent | Mantis #7713: fixed bug introduced by 1st MOSES patch. (diff) | |
download | opensim-SC-fb78b182520fc9bb0f971afd0322029c70278ea6.zip opensim-SC-fb78b182520fc9bb0f971afd0322029c70278ea6.tar.gz opensim-SC-fb78b182520fc9bb0f971afd0322029c70278ea6.tar.bz2 opensim-SC-fb78b182520fc9bb0f971afd0322029c70278ea6.tar.xz |
Merge remote-tracking branch 'os/master'
Diffstat (limited to 'OpenSim/Region/Framework/Scenes/TerrainChannel.cs')
-rw-r--r-- | OpenSim/Region/Framework/Scenes/TerrainChannel.cs | 418 |
1 files changed, 418 insertions, 0 deletions
diff --git a/OpenSim/Region/Framework/Scenes/TerrainChannel.cs b/OpenSim/Region/Framework/Scenes/TerrainChannel.cs new file mode 100644 index 0000000..3d563a6 --- /dev/null +++ b/OpenSim/Region/Framework/Scenes/TerrainChannel.cs | |||
@@ -0,0 +1,418 @@ | |||
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 | return m_terrainData.GetFloatsSerialized(); | ||
128 | } | ||
129 | |||
130 | // ITerrainChannel.GetDoubles() | ||
131 | public double[,] GetDoubles() | ||
132 | { | ||
133 | double[,] heights = new double[Width, Height]; | ||
134 | |||
135 | int idx = 0; // index into serialized array | ||
136 | for (int ii = 0; ii < Width; ii++) | ||
137 | { | ||
138 | for (int jj = 0; jj < Height; jj++) | ||
139 | { | ||
140 | heights[ii, jj] = (double)m_terrainData[ii, jj]; | ||
141 | idx++; | ||
142 | } | ||
143 | } | ||
144 | |||
145 | return heights; | ||
146 | } | ||
147 | |||
148 | // ITerrainChannel.this[x,y] | ||
149 | public double this[int x, int y] | ||
150 | { | ||
151 | get { | ||
152 | if (x < 0 || x >= Width || y < 0 || y >= Height) | ||
153 | return 0; | ||
154 | return (double)m_terrainData[x, y]; | ||
155 | } | ||
156 | set | ||
157 | { | ||
158 | if (Double.IsNaN(value) || Double.IsInfinity(value)) | ||
159 | return; | ||
160 | |||
161 | m_terrainData[x, y] = (float)value; | ||
162 | } | ||
163 | } | ||
164 | |||
165 | // ITerrainChannel.GetHieghtAtXYZ(x, y, z) | ||
166 | public float GetHeightAtXYZ(float x, float y, float z) | ||
167 | { | ||
168 | if (x < 0 || x >= Width || y < 0 || y >= Height) | ||
169 | return 0; | ||
170 | return m_terrainData[(int)x, (int)y]; | ||
171 | } | ||
172 | |||
173 | // ITerrainChannel.Tainted() | ||
174 | public bool Tainted(int x, int y) | ||
175 | { | ||
176 | return m_terrainData.IsTaintedAt(x, y); | ||
177 | } | ||
178 | |||
179 | // ITerrainChannel.SaveToXmlString() | ||
180 | public string SaveToXmlString() | ||
181 | { | ||
182 | XmlWriterSettings settings = new XmlWriterSettings(); | ||
183 | settings.Encoding = Util.UTF8; | ||
184 | using (StringWriter sw = new StringWriter()) | ||
185 | { | ||
186 | using (XmlWriter writer = XmlWriter.Create(sw, settings)) | ||
187 | { | ||
188 | WriteXml(writer); | ||
189 | } | ||
190 | string output = sw.ToString(); | ||
191 | return output; | ||
192 | } | ||
193 | } | ||
194 | |||
195 | // ITerrainChannel.LoadFromXmlString() | ||
196 | public void LoadFromXmlString(string data) | ||
197 | { | ||
198 | StringReader sr = new StringReader(data); | ||
199 | XmlTextReader reader = new XmlTextReader(sr); | ||
200 | reader.Read(); | ||
201 | |||
202 | ReadXml(reader); | ||
203 | reader.Close(); | ||
204 | sr.Close(); | ||
205 | } | ||
206 | |||
207 | // ITerrainChannel.Merge | ||
208 | public void Merge(ITerrainChannel newTerrain, Vector3 displacement, float radianRotation, Vector2 rotationDisplacement) | ||
209 | { | ||
210 | m_log.DebugFormat("{0} Merge. inSize=<{1},{2}>, disp={3}, rot={4}, rotDisp={5}, outSize=<{6},{7}>", LogHeader, | ||
211 | newTerrain.Width, newTerrain.Height, | ||
212 | displacement, radianRotation, rotationDisplacement, | ||
213 | m_terrainData.SizeX, m_terrainData.SizeY); | ||
214 | for (int xx = 0; xx < newTerrain.Width; xx++) | ||
215 | { | ||
216 | for (int yy = 0; yy < newTerrain.Height; yy++) | ||
217 | { | ||
218 | int dispX = (int)displacement.X; | ||
219 | int dispY = (int)displacement.Y; | ||
220 | float newHeight = (float)newTerrain[xx, yy] + displacement.Z; | ||
221 | if (radianRotation == 0) | ||
222 | { | ||
223 | // If no rotation, place the new height in the specified location | ||
224 | dispX += xx; | ||
225 | dispY += yy; | ||
226 | if (dispX >= 0 && dispX < m_terrainData.SizeX && dispY >= 0 && dispY < m_terrainData.SizeY) | ||
227 | { | ||
228 | m_terrainData[dispX, dispY] = newHeight; | ||
229 | } | ||
230 | } | ||
231 | else | ||
232 | { | ||
233 | // If rotating, we have to smooth the result because the conversion | ||
234 | // to ints will mean heightmap entries will not get changed | ||
235 | // First compute the rotation location for the new height. | ||
236 | dispX += (int)(rotationDisplacement.X | ||
237 | + ((float)xx - rotationDisplacement.X) * Math.Cos(radianRotation) | ||
238 | - ((float)yy - rotationDisplacement.Y) * Math.Sin(radianRotation) ); | ||
239 | |||
240 | dispY += (int)(rotationDisplacement.Y | ||
241 | + ((float)xx - rotationDisplacement.X) * Math.Sin(radianRotation) | ||
242 | + ((float)yy - rotationDisplacement.Y) * Math.Cos(radianRotation) ); | ||
243 | |||
244 | if (dispX >= 0 && dispX < m_terrainData.SizeX && dispY >= 0 && dispY < m_terrainData.SizeY) | ||
245 | { | ||
246 | float oldHeight = m_terrainData[dispX, dispY]; | ||
247 | // Smooth the heights around this location if the old height is far from this one | ||
248 | for (int sxx = dispX - 2; sxx < dispX + 2; sxx++) | ||
249 | { | ||
250 | for (int syy = dispY - 2; syy < dispY + 2; syy++) | ||
251 | { | ||
252 | if (sxx >= 0 && sxx < m_terrainData.SizeX && syy >= 0 && syy < m_terrainData.SizeY) | ||
253 | { | ||
254 | if (sxx == dispX && syy == dispY) | ||
255 | { | ||
256 | // Set height for the exact rotated point | ||
257 | m_terrainData[dispX, dispY] = newHeight; | ||
258 | } | ||
259 | else | ||
260 | { | ||
261 | if (Math.Abs(m_terrainData[sxx, syy] - newHeight) > 1f) | ||
262 | { | ||
263 | // If the adjacent height is far off, force it to this height | ||
264 | m_terrainData[sxx, syy] = newHeight; | ||
265 | } | ||
266 | } | ||
267 | } | ||
268 | } | ||
269 | } | ||
270 | } | ||
271 | |||
272 | if (dispX >= 0 && dispX < m_terrainData.SizeX && dispY >= 0 && dispY < m_terrainData.SizeY) | ||
273 | { | ||
274 | m_terrainData[dispX, dispY] = (float)newTerrain[xx, yy]; | ||
275 | } | ||
276 | } | ||
277 | } | ||
278 | } | ||
279 | } | ||
280 | |||
281 | #endregion | ||
282 | |||
283 | public TerrainChannel Copy() | ||
284 | { | ||
285 | TerrainChannel copy = new TerrainChannel(); | ||
286 | copy.m_terrainData = m_terrainData.Clone(); | ||
287 | return copy; | ||
288 | } | ||
289 | |||
290 | private void WriteXml(XmlWriter writer) | ||
291 | { | ||
292 | if (Width == Constants.RegionSize && Height == Constants.RegionSize) | ||
293 | { | ||
294 | // Downward compatibility for legacy region terrain maps. | ||
295 | // If region is exactly legacy size, return the old format XML. | ||
296 | writer.WriteStartElement(String.Empty, "TerrainMap", String.Empty); | ||
297 | ToXml(writer); | ||
298 | writer.WriteEndElement(); | ||
299 | } | ||
300 | else | ||
301 | { | ||
302 | // New format XML that includes width and length. | ||
303 | writer.WriteStartElement(String.Empty, "TerrainMap2", String.Empty); | ||
304 | ToXml2(writer); | ||
305 | writer.WriteEndElement(); | ||
306 | } | ||
307 | } | ||
308 | |||
309 | private void ReadXml(XmlReader reader) | ||
310 | { | ||
311 | // Check the first element. If legacy element, use the legacy reader. | ||
312 | if (reader.IsStartElement("TerrainMap")) | ||
313 | { | ||
314 | reader.ReadStartElement("TerrainMap"); | ||
315 | FromXml(reader); | ||
316 | } | ||
317 | else | ||
318 | { | ||
319 | reader.ReadStartElement("TerrainMap2"); | ||
320 | FromXml2(reader); | ||
321 | } | ||
322 | } | ||
323 | |||
324 | // Write legacy terrain map. Presumed to be 256x256 of data encoded as floats in a byte array. | ||
325 | private void ToXml(XmlWriter xmlWriter) | ||
326 | { | ||
327 | float[] mapData = GetFloatsSerialised(); | ||
328 | byte[] buffer = new byte[mapData.Length * 4]; | ||
329 | for (int i = 0; i < mapData.Length; i++) | ||
330 | { | ||
331 | byte[] value = BitConverter.GetBytes(mapData[i]); | ||
332 | Array.Copy(value, 0, buffer, (i * 4), 4); | ||
333 | } | ||
334 | XmlSerializer serializer = new XmlSerializer(typeof(byte[])); | ||
335 | serializer.Serialize(xmlWriter, buffer); | ||
336 | } | ||
337 | |||
338 | // Read legacy terrain map. Presumed to be 256x256 of data encoded as floats in a byte array. | ||
339 | private void FromXml(XmlReader xmlReader) | ||
340 | { | ||
341 | XmlSerializer serializer = new XmlSerializer(typeof(byte[])); | ||
342 | byte[] dataArray = (byte[])serializer.Deserialize(xmlReader); | ||
343 | int index = 0; | ||
344 | |||
345 | m_terrainData = new HeightmapTerrainData(Height, Width, (int)Constants.RegionHeight); | ||
346 | |||
347 | for (int y = 0; y < Height; y++) | ||
348 | { | ||
349 | for (int x = 0; x < Width; x++) | ||
350 | { | ||
351 | float value; | ||
352 | value = BitConverter.ToSingle(dataArray, index); | ||
353 | index += 4; | ||
354 | this[x, y] = (double)value; | ||
355 | } | ||
356 | } | ||
357 | } | ||
358 | |||
359 | private class TerrainChannelXMLPackage | ||
360 | { | ||
361 | public int Version; | ||
362 | public int SizeX; | ||
363 | public int SizeY; | ||
364 | public int SizeZ; | ||
365 | public float CompressionFactor; | ||
366 | public int[] Map; | ||
367 | public TerrainChannelXMLPackage(int pX, int pY, int pZ, float pCompressionFactor, int[] pMap) | ||
368 | { | ||
369 | Version = 1; | ||
370 | SizeX = pX; | ||
371 | SizeY = pY; | ||
372 | SizeZ = pZ; | ||
373 | CompressionFactor = pCompressionFactor; | ||
374 | Map = pMap; | ||
375 | } | ||
376 | } | ||
377 | |||
378 | // New terrain serialization format that includes the width and length. | ||
379 | private void ToXml2(XmlWriter xmlWriter) | ||
380 | { | ||
381 | TerrainChannelXMLPackage package = new TerrainChannelXMLPackage(Width, Height, Altitude, m_terrainData.CompressionFactor, | ||
382 | m_terrainData.GetCompressedMap()); | ||
383 | XmlSerializer serializer = new XmlSerializer(typeof(TerrainChannelXMLPackage)); | ||
384 | serializer.Serialize(xmlWriter, package); | ||
385 | } | ||
386 | |||
387 | // New terrain serialization format that includes the width and length. | ||
388 | private void FromXml2(XmlReader xmlReader) | ||
389 | { | ||
390 | XmlSerializer serializer = new XmlSerializer(typeof(TerrainChannelXMLPackage)); | ||
391 | TerrainChannelXMLPackage package = (TerrainChannelXMLPackage)serializer.Deserialize(xmlReader); | ||
392 | m_terrainData = new HeightmapTerrainData(package.Map, package.CompressionFactor, package.SizeX, package.SizeY, package.SizeZ); | ||
393 | } | ||
394 | |||
395 | // Fill the heightmap with the center bump terrain | ||
396 | private void PinHeadIsland() | ||
397 | { | ||
398 | for (int x = 0; x < Width; x++) | ||
399 | { | ||
400 | for (int y = 0; y < Height; y++) | ||
401 | { | ||
402 | m_terrainData[x, y] = (float)TerrainUtil.PerlinNoise2D(x, y, 2, 0.125) * 10; | ||
403 | float spherFacA = (float)(TerrainUtil.SphericalFactor(x, y, m_terrainData.SizeX / 2.0, m_terrainData.SizeY / 2.0, 50) * 0.01d); | ||
404 | float spherFacB = (float)(TerrainUtil.SphericalFactor(x, y, m_terrainData.SizeX / 2.0, m_terrainData.SizeY / 2.0, 100) * 0.001d); | ||
405 | if (m_terrainData[x, y]< spherFacA) | ||
406 | m_terrainData[x, y]= spherFacA; | ||
407 | if (m_terrainData[x, y]< spherFacB) | ||
408 | m_terrainData[x, y] = spherFacB; | ||
409 | } | ||
410 | } | ||
411 | } | ||
412 | |||
413 | private void FlatLand() | ||
414 | { | ||
415 | m_terrainData.ClearLand(); | ||
416 | } | ||
417 | } | ||
418 | } | ||