diff options
Diffstat (limited to 'OpenSim/Region/Framework/Scenes/TerrainChannel.cs')
-rw-r--r-- | OpenSim/Region/Framework/Scenes/TerrainChannel.cs | 278 |
1 files changed, 180 insertions, 98 deletions
diff --git a/OpenSim/Region/Framework/Scenes/TerrainChannel.cs b/OpenSim/Region/Framework/Scenes/TerrainChannel.cs index c0ca48e..b4b1823 100644 --- a/OpenSim/Region/Framework/Scenes/TerrainChannel.cs +++ b/OpenSim/Region/Framework/Scenes/TerrainChannel.cs | |||
@@ -25,14 +25,19 @@ | |||
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 log4net; | ||
40 | |||
36 | namespace OpenSim.Region.Framework.Scenes | 41 | namespace OpenSim.Region.Framework.Scenes |
37 | { | 42 | { |
38 | /// <summary> | 43 | /// <summary> |
@@ -40,132 +45,146 @@ namespace OpenSim.Region.Framework.Scenes | |||
40 | /// </summary> | 45 | /// </summary> |
41 | public class TerrainChannel : ITerrainChannel | 46 | public class TerrainChannel : ITerrainChannel |
42 | { | 47 | { |
43 | private readonly bool[,] taint; | 48 | private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); |
44 | private double[,] map; | 49 | private static string LogHeader = "[TERRAIN CHANNEL]"; |
50 | |||
51 | protected TerrainData m_terrainData; | ||
45 | 52 | ||
53 | public int Width { get { return m_terrainData.SizeX; } } // X dimension | ||
54 | // Unfortunately, for historical reasons, in this module 'Width' is X and 'Height' is Y | ||
55 | public int Height { get { return m_terrainData.SizeY; } } // Y dimension | ||
56 | public int Altitude { get { return m_terrainData.SizeZ; } } // Y dimension | ||
57 | |||
58 | // Default, not-often-used builder | ||
46 | public TerrainChannel() | 59 | public TerrainChannel() |
47 | { | 60 | { |
48 | map = new double[Constants.RegionSize, Constants.RegionSize]; | 61 | m_terrainData = new HeightmapTerrainData((int)Constants.RegionSize, (int)Constants.RegionSize, (int)Constants.RegionHeight); |
49 | taint = new bool[Constants.RegionSize / 16, Constants.RegionSize / 16]; | 62 | FlatLand(); |
50 | 63 | // PinHeadIsland(); | |
51 | PinHeadIsland(); | ||
52 | } | 64 | } |
53 | 65 | ||
54 | public TerrainChannel(String type) | 66 | // Create terrain of given size |
67 | public TerrainChannel(int pX, int pY) | ||
55 | { | 68 | { |
56 | map = new double[Constants.RegionSize, Constants.RegionSize]; | 69 | m_terrainData = new HeightmapTerrainData(pX, pY, (int)Constants.RegionHeight); |
57 | taint = new bool[Constants.RegionSize / 16, Constants.RegionSize / 16]; | 70 | } |
58 | 71 | ||
72 | // Create terrain of specified size and initialize with specified terrain. | ||
73 | // TODO: join this with the terrain initializers. | ||
74 | public TerrainChannel(String type, int pX, int pY, int pZ) | ||
75 | { | ||
76 | m_terrainData = new HeightmapTerrainData(pX, pY, pZ); | ||
59 | if (type.Equals("flat")) | 77 | if (type.Equals("flat")) |
60 | FlatLand(); | 78 | FlatLand(); |
61 | else | 79 | else |
62 | PinHeadIsland(); | 80 | PinHeadIsland(); |
63 | } | 81 | } |
64 | 82 | ||
65 | public TerrainChannel(double[,] import) | 83 | // Create channel passed a heightmap and expected dimensions of the region. |
84 | // The heightmap might not fit the passed size so accomodations must be made. | ||
85 | public TerrainChannel(double[,] pM, int pSizeX, int pSizeY, int pAltitude) | ||
66 | { | 86 | { |
67 | map = import; | 87 | int hmSizeX = pM.GetLength(0); |
68 | taint = new bool[import.GetLength(0),import.GetLength(1)]; | 88 | int hmSizeY = pM.GetLength(1); |
69 | } | ||
70 | 89 | ||
71 | public TerrainChannel(bool createMap) | 90 | m_terrainData = new HeightmapTerrainData(pSizeX, pSizeY, pAltitude); |
72 | { | 91 | |
73 | if (createMap) | 92 | for (int xx = 0; xx < pSizeX; xx++) |
74 | { | 93 | for (int yy = 0; yy < pSizeY; yy++) |
75 | map = new double[Constants.RegionSize,Constants.RegionSize]; | 94 | if (xx > hmSizeX || yy > hmSizeY) |
76 | taint = new bool[Constants.RegionSize / 16,Constants.RegionSize / 16]; | 95 | m_terrainData[xx, yy] = TerrainData.DefaultTerrainHeight; |
77 | } | 96 | else |
97 | m_terrainData[xx, yy] = (float)pM[xx, yy]; | ||
78 | } | 98 | } |
79 | 99 | ||
80 | public TerrainChannel(int w, int h) | 100 | public TerrainChannel(TerrainData pTerrData) |
81 | { | 101 | { |
82 | map = new double[w,h]; | 102 | m_terrainData = pTerrData; |
83 | taint = new bool[w / 16,h / 16]; | ||
84 | } | 103 | } |
85 | 104 | ||
86 | #region ITerrainChannel Members | 105 | #region ITerrainChannel Members |
87 | 106 | ||
88 | public int Width | 107 | // ITerrainChannel.MakeCopy() |
108 | public ITerrainChannel MakeCopy() | ||
89 | { | 109 | { |
90 | get { return map.GetLength(0); } | 110 | return this.Copy(); |
91 | } | 111 | } |
92 | 112 | ||
93 | public int Height | 113 | // ITerrainChannel.GetTerrainData() |
114 | public TerrainData GetTerrainData() | ||
94 | { | 115 | { |
95 | get { return map.GetLength(1); } | 116 | return m_terrainData; |
96 | } | 117 | } |
97 | 118 | ||
98 | public ITerrainChannel MakeCopy() | 119 | // ITerrainChannel.GetFloatsSerialized() |
120 | // This one dimensional version is ordered so height = map[y*sizeX+x]; | ||
121 | // DEPRECATED: don't use this function as it does not retain the dimensions of the terrain | ||
122 | // and the caller will probably do the wrong thing if the terrain is not the legacy 256x256. | ||
123 | public float[] GetFloatsSerialised() | ||
99 | { | 124 | { |
100 | TerrainChannel copy = new TerrainChannel(false); | 125 | int points = Width * Height; |
101 | copy.map = (double[,]) map.Clone(); | 126 | float[] heights = new float[points]; |
102 | 127 | ||
103 | return copy; | 128 | int idx = 0; |
129 | for (int jj = 0; jj < Height; jj++) | ||
130 | for (int ii = 0; ii < Width; ii++) | ||
131 | { | ||
132 | heights[idx++] = m_terrainData[ii, jj]; | ||
133 | } | ||
134 | |||
135 | return heights; | ||
104 | } | 136 | } |
105 | 137 | ||
106 | public float[] GetFloatsSerialised() | 138 | // ITerrainChannel.GetDoubles() |
139 | public double[,] GetDoubles() | ||
107 | { | 140 | { |
108 | // Move the member variables into local variables, calling | 141 | 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 | 142 | ||
114 | int i, j; // map coordinates | ||
115 | int idx = 0; // index into serialized array | 143 | int idx = 0; // index into serialized array |
116 | for (i = 0; i < h; i++) | 144 | for (int ii = 0; ii < Width; ii++) |
117 | { | 145 | { |
118 | for (j = 0; j < w; j++) | 146 | for (int jj = 0; jj < Height; jj++) |
119 | { | 147 | { |
120 | heights[idx++] = (float)map[j, i]; | 148 | heights[ii, jj] = (double)m_terrainData[ii, jj]; |
149 | idx++; | ||
121 | } | 150 | } |
122 | } | 151 | } |
123 | 152 | ||
124 | return heights; | 153 | return heights; |
125 | } | 154 | } |
126 | 155 | ||
127 | public double[,] GetDoubles() | 156 | // ITerrainChannel.this[x,y] |
128 | { | ||
129 | return map; | ||
130 | } | ||
131 | |||
132 | public double this[int x, int y] | 157 | public double this[int x, int y] |
133 | { | 158 | { |
134 | get { return map[x, y]; } | 159 | get { |
160 | if (x < 0 || x >= Width || y < 0 || y >= Height) | ||
161 | return 0; | ||
162 | return (double)m_terrainData[x, y]; | ||
163 | } | ||
135 | set | 164 | set |
136 | { | 165 | { |
137 | // Will "fix" terrain hole problems. Although not fantastically. | ||
138 | if (Double.IsNaN(value) || Double.IsInfinity(value)) | 166 | if (Double.IsNaN(value) || Double.IsInfinity(value)) |
139 | return; | 167 | return; |
140 | 168 | ||
141 | if (map[x, y] != value) | 169 | m_terrainData[x, y] = (float)value; |
142 | { | ||
143 | taint[x / 16, y / 16] = true; | ||
144 | map[x, y] = value; | ||
145 | } | ||
146 | } | 170 | } |
147 | } | 171 | } |
148 | 172 | ||
149 | public bool Tainted(int x, int y) | 173 | // ITerrainChannel.GetHieghtAtXYZ(x, y, z) |
174 | public float GetHeightAtXYZ(float x, float y, float z) | ||
150 | { | 175 | { |
151 | if (taint[x / 16, y / 16]) | 176 | if (x < 0 || x >= Width || y < 0 || y >= Height) |
152 | { | 177 | return 0; |
153 | taint[x / 16, y / 16] = false; | 178 | return m_terrainData[(int)x, (int)y]; |
154 | return true; | ||
155 | } | ||
156 | return false; | ||
157 | } | 179 | } |
158 | 180 | ||
159 | #endregion | 181 | // ITerrainChannel.Tainted() |
160 | 182 | public bool Tainted(int x, int y) | |
161 | public TerrainChannel Copy() | ||
162 | { | 183 | { |
163 | TerrainChannel copy = new TerrainChannel(false); | 184 | return m_terrainData.IsTaintedAt(x, y); |
164 | copy.map = (double[,]) map.Clone(); | ||
165 | |||
166 | return copy; | ||
167 | } | 185 | } |
168 | 186 | ||
187 | // ITerrainChannel.SaveToXmlString() | ||
169 | public string SaveToXmlString() | 188 | public string SaveToXmlString() |
170 | { | 189 | { |
171 | XmlWriterSettings settings = new XmlWriterSettings(); | 190 | XmlWriterSettings settings = new XmlWriterSettings(); |
@@ -181,13 +200,7 @@ namespace OpenSim.Region.Framework.Scenes | |||
181 | } | 200 | } |
182 | } | 201 | } |
183 | 202 | ||
184 | private void WriteXml(XmlWriter writer) | 203 | // 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) | 204 | public void LoadFromXmlString(string data) |
192 | { | 205 | { |
193 | StringReader sr = new StringReader(data); | 206 | StringReader sr = new StringReader(data); |
@@ -199,12 +212,50 @@ namespace OpenSim.Region.Framework.Scenes | |||
199 | sr.Close(); | 212 | sr.Close(); |
200 | } | 213 | } |
201 | 214 | ||
215 | #endregion | ||
216 | |||
217 | public TerrainChannel Copy() | ||
218 | { | ||
219 | TerrainChannel copy = new TerrainChannel(); | ||
220 | copy.m_terrainData = m_terrainData.Clone(); | ||
221 | return copy; | ||
222 | } | ||
223 | |||
224 | private void WriteXml(XmlWriter writer) | ||
225 | { | ||
226 | if (Width == Constants.RegionSize && Height == Constants.RegionSize) | ||
227 | { | ||
228 | // Downward compatibility for legacy region terrain maps. | ||
229 | // If region is exactly legacy size, return the old format XML. | ||
230 | writer.WriteStartElement(String.Empty, "TerrainMap", String.Empty); | ||
231 | ToXml(writer); | ||
232 | writer.WriteEndElement(); | ||
233 | } | ||
234 | else | ||
235 | { | ||
236 | // New format XML that includes width and length. | ||
237 | writer.WriteStartElement(String.Empty, "TerrainMap2", String.Empty); | ||
238 | ToXml2(writer); | ||
239 | writer.WriteEndElement(); | ||
240 | } | ||
241 | } | ||
242 | |||
202 | private void ReadXml(XmlReader reader) | 243 | private void ReadXml(XmlReader reader) |
203 | { | 244 | { |
204 | reader.ReadStartElement("TerrainMap"); | 245 | // Check the first element. If legacy element, use the legacy reader. |
205 | FromXml(reader); | 246 | if (reader.IsStartElement("TerrainMap")) |
247 | { | ||
248 | reader.ReadStartElement("TerrainMap"); | ||
249 | FromXml(reader); | ||
250 | } | ||
251 | else | ||
252 | { | ||
253 | reader.ReadStartElement("TerrainMap2"); | ||
254 | FromXml2(reader); | ||
255 | } | ||
206 | } | 256 | } |
207 | 257 | ||
258 | // Write legacy terrain map. Presumed to be 256x256 of data encoded as floats in a byte array. | ||
208 | private void ToXml(XmlWriter xmlWriter) | 259 | private void ToXml(XmlWriter xmlWriter) |
209 | { | 260 | { |
210 | float[] mapData = GetFloatsSerialised(); | 261 | float[] mapData = GetFloatsSerialised(); |
@@ -218,12 +269,15 @@ namespace OpenSim.Region.Framework.Scenes | |||
218 | serializer.Serialize(xmlWriter, buffer); | 269 | serializer.Serialize(xmlWriter, buffer); |
219 | } | 270 | } |
220 | 271 | ||
272 | // Read legacy terrain map. Presumed to be 256x256 of data encoded as floats in a byte array. | ||
221 | private void FromXml(XmlReader xmlReader) | 273 | private void FromXml(XmlReader xmlReader) |
222 | { | 274 | { |
223 | XmlSerializer serializer = new XmlSerializer(typeof(byte[])); | 275 | XmlSerializer serializer = new XmlSerializer(typeof(byte[])); |
224 | byte[] dataArray = (byte[])serializer.Deserialize(xmlReader); | 276 | byte[] dataArray = (byte[])serializer.Deserialize(xmlReader); |
225 | int index = 0; | 277 | int index = 0; |
226 | 278 | ||
279 | m_terrainData = new HeightmapTerrainData(Height, Width, (int)Constants.RegionHeight); | ||
280 | |||
227 | for (int y = 0; y < Height; y++) | 281 | for (int y = 0; y < Height; y++) |
228 | { | 282 | { |
229 | for (int x = 0; x < Width; x++) | 283 | for (int x = 0; x < Width; x++) |
@@ -236,35 +290,63 @@ namespace OpenSim.Region.Framework.Scenes | |||
236 | } | 290 | } |
237 | } | 291 | } |
238 | 292 | ||
293 | private class TerrainChannelXMLPackage | ||
294 | { | ||
295 | public int Version; | ||
296 | public int SizeX; | ||
297 | public int SizeY; | ||
298 | public int SizeZ; | ||
299 | public float CompressionFactor; | ||
300 | public short[] Map; | ||
301 | public TerrainChannelXMLPackage(int pX, int pY, int pZ, float pCompressionFactor, short[] pMap) | ||
302 | { | ||
303 | Version = 1; | ||
304 | SizeX = pX; | ||
305 | SizeY = pY; | ||
306 | SizeZ = pZ; | ||
307 | CompressionFactor = pCompressionFactor; | ||
308 | Map = pMap; | ||
309 | } | ||
310 | } | ||
311 | |||
312 | // New terrain serialization format that includes the width and length. | ||
313 | private void ToXml2(XmlWriter xmlWriter) | ||
314 | { | ||
315 | TerrainChannelXMLPackage package = new TerrainChannelXMLPackage(Width, Height, Altitude, m_terrainData.CompressionFactor, | ||
316 | m_terrainData.GetCompressedMap()); | ||
317 | XmlSerializer serializer = new XmlSerializer(typeof(TerrainChannelXMLPackage)); | ||
318 | serializer.Serialize(xmlWriter, package); | ||
319 | } | ||
320 | |||
321 | // New terrain serialization format that includes the width and length. | ||
322 | private void FromXml2(XmlReader xmlReader) | ||
323 | { | ||
324 | XmlSerializer serializer = new XmlSerializer(typeof(TerrainChannelXMLPackage)); | ||
325 | TerrainChannelXMLPackage package = (TerrainChannelXMLPackage)serializer.Deserialize(xmlReader); | ||
326 | m_terrainData = new HeightmapTerrainData(package.Map, package.CompressionFactor, package.SizeX, package.SizeY, package.SizeZ); | ||
327 | } | ||
328 | |||
329 | // Fill the heightmap with the center bump terrain | ||
239 | private void PinHeadIsland() | 330 | private void PinHeadIsland() |
240 | { | 331 | { |
241 | int x; | 332 | for (int x = 0; x < Width; x++) |
242 | for (x = 0; x < Constants.RegionSize; x++) | ||
243 | { | 333 | { |
244 | int y; | 334 | for (int y = 0; y < Height; y++) |
245 | for (y = 0; y < Constants.RegionSize; y++) | ||
246 | { | 335 | { |
247 | map[x, y] = TerrainUtil.PerlinNoise2D(x, y, 2, 0.125) * 10; | 336 | 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; | 337 | 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; | 338 | 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) | 339 | if (m_terrainData[x, y]< spherFacA) |
251 | map[x, y] = spherFacA; | 340 | m_terrainData[x, y]= spherFacA; |
252 | if (map[x, y] < spherFacB) | 341 | if (m_terrainData[x, y]< spherFacB) |
253 | map[x, y] = spherFacB; | 342 | m_terrainData[x, y] = spherFacB; |
254 | } | 343 | } |
255 | } | 344 | } |
256 | } | 345 | } |
257 | 346 | ||
258 | private void FlatLand() | 347 | private void FlatLand() |
259 | { | 348 | { |
260 | int x; | 349 | 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 | } | 350 | } |
268 | |||
269 | } | 351 | } |
270 | } | 352 | } |