diff options
Diffstat (limited to 'OpenSim/Region/Framework/Scenes/TerrainChannel.cs')
-rw-r--r-- | OpenSim/Region/Framework/Scenes/TerrainChannel.cs | 280 |
1 files changed, 189 insertions, 91 deletions
diff --git a/OpenSim/Region/Framework/Scenes/TerrainChannel.cs b/OpenSim/Region/Framework/Scenes/TerrainChannel.cs index c0ca48e..fef93bf 100644 --- a/OpenSim/Region/Framework/Scenes/TerrainChannel.cs +++ b/OpenSim/Region/Framework/Scenes/TerrainChannel.cs | |||
@@ -40,132 +40,125 @@ namespace OpenSim.Region.Framework.Scenes | |||
40 | /// </summary> | 40 | /// </summary> |
41 | public class TerrainChannel : ITerrainChannel | 41 | public class TerrainChannel : ITerrainChannel |
42 | { | 42 | { |
43 | private readonly bool[,] taint; | 43 | protected bool[,] m_taint; |
44 | private double[,] map; | 44 | protected short[] m_map; |
45 | 45 | ||
46 | public int Width { get; private set; } // X dimension | ||
47 | // Unfortunately, for historical reasons, in this module 'Width' is X and 'Height' is Y | ||
48 | public int Height { get; private set; } // Y dimension | ||
49 | public int Altitude { get; private set; } // Y dimension | ||
50 | |||
51 | // Default, not-often-used builder | ||
46 | public TerrainChannel() | 52 | public TerrainChannel() |
47 | { | 53 | { |
48 | map = new double[Constants.RegionSize, Constants.RegionSize]; | 54 | InitializeStructures(Constants.RegionSize, Constants.RegionSize, Constants.RegionHeight, false); |
49 | taint = new bool[Constants.RegionSize / 16, Constants.RegionSize / 16]; | 55 | FlatLand(); |
50 | 56 | // PinHeadIsland(); | |
51 | PinHeadIsland(); | ||
52 | } | 57 | } |
53 | 58 | ||
54 | public TerrainChannel(String type) | 59 | // Create terrain of given size |
60 | public TerrainChannel(int pX, int pY) | ||
55 | { | 61 | { |
56 | map = new double[Constants.RegionSize, Constants.RegionSize]; | 62 | InitializeStructures((uint)pX, (uint)pY, Constants.RegionHeight, true); |
57 | taint = new bool[Constants.RegionSize / 16, Constants.RegionSize / 16]; | 63 | } |
58 | 64 | ||
65 | // Create terrain of specified size and initialize with specified terrain. | ||
66 | // TODO: join this with the terrain initializers. | ||
67 | public TerrainChannel(String type, uint pX, uint pY, uint pZ) | ||
68 | { | ||
69 | InitializeStructures(pX, pY, pZ, false); | ||
59 | if (type.Equals("flat")) | 70 | if (type.Equals("flat")) |
60 | FlatLand(); | 71 | FlatLand(); |
61 | else | 72 | else |
62 | PinHeadIsland(); | 73 | PinHeadIsland(); |
63 | } | 74 | } |
64 | 75 | ||
65 | public TerrainChannel(double[,] import) | 76 | public TerrainChannel(double[,] pM, uint pH) |
66 | { | ||
67 | map = import; | ||
68 | taint = new bool[import.GetLength(0),import.GetLength(1)]; | ||
69 | } | ||
70 | |||
71 | public TerrainChannel(bool createMap) | ||
72 | { | ||
73 | if (createMap) | ||
74 | { | ||
75 | map = new double[Constants.RegionSize,Constants.RegionSize]; | ||
76 | taint = new bool[Constants.RegionSize / 16,Constants.RegionSize / 16]; | ||
77 | } | ||
78 | } | ||
79 | |||
80 | public TerrainChannel(int w, int h) | ||
81 | { | 77 | { |
82 | map = new double[w,h]; | 78 | InitializeStructures((uint)pM.GetLength(0), (uint)pM.GetLength(1), pH, false); |
83 | taint = new bool[w / 16,h / 16]; | 79 | int idx = 0; |
80 | for (int ii = 0; ii < Height; ii++) | ||
81 | for (int jj = 0; jj < Width; jj++) | ||
82 | m_map[idx++] = ToCompressedHeight(pM[ii, jj]); | ||
84 | } | 83 | } |
85 | 84 | ||
86 | #region ITerrainChannel Members | 85 | #region ITerrainChannel Members |
87 | 86 | ||
88 | public int Width | 87 | // ITerrainChannel.MakeCopy() |
88 | public ITerrainChannel MakeCopy() | ||
89 | { | 89 | { |
90 | get { return map.GetLength(0); } | 90 | return this.Copy(); |
91 | } | 91 | } |
92 | 92 | ||
93 | public int Height | 93 | // ITerrainChannel.GetCompressedMap() |
94 | public short[] GetCompressedMap() | ||
94 | { | 95 | { |
95 | get { return map.GetLength(1); } | 96 | return m_map; |
96 | } | 97 | } |
97 | 98 | ||
98 | public ITerrainChannel MakeCopy() | 99 | // ITerrainChannel.GetFloatsSerialized() |
100 | public float[] GetFloatsSerialised() | ||
99 | { | 101 | { |
100 | TerrainChannel copy = new TerrainChannel(false); | 102 | int points = Width * Height; |
101 | copy.map = (double[,]) map.Clone(); | 103 | float[] heights = new float[points]; |
102 | 104 | ||
103 | return copy; | 105 | for (int ii = 0; ii < points; ii++) |
106 | heights[ii] = FromCompressedHeight(m_map[ii]); | ||
107 | |||
108 | return heights; | ||
104 | } | 109 | } |
105 | 110 | ||
106 | public float[] GetFloatsSerialised() | 111 | // ITerrainChannel.GetDoubles() |
112 | public double[,] GetDoubles() | ||
107 | { | 113 | { |
108 | // Move the member variables into local variables, calling | ||
109 | // member variables 256*256 times gets expensive | ||
110 | int w = Width; | 114 | int w = Width; |
111 | int h = Height; | 115 | int l = Height; |
112 | float[] heights = new float[w * h]; | 116 | double[,] heights = new double[w, l]; |
113 | 117 | ||
114 | int i, j; // map coordinates | ||
115 | int idx = 0; // index into serialized array | 118 | int idx = 0; // index into serialized array |
116 | for (i = 0; i < h; i++) | 119 | for (int ii = 0; ii < l; ii++) |
117 | { | 120 | { |
118 | for (j = 0; j < w; j++) | 121 | for (int jj = 0; jj < w; jj++) |
119 | { | 122 | { |
120 | heights[idx++] = (float)map[j, i]; | 123 | heights[ii, jj] = (double)FromCompressedHeight(m_map[idx]); |
124 | idx++; | ||
121 | } | 125 | } |
122 | } | 126 | } |
123 | 127 | ||
124 | return heights; | 128 | return heights; |
125 | } | 129 | } |
126 | 130 | ||
127 | public double[,] GetDoubles() | 131 | // ITerrainChannel.this[x,y] |
128 | { | ||
129 | return map; | ||
130 | } | ||
131 | |||
132 | public double this[int x, int y] | 132 | public double this[int x, int y] |
133 | { | 133 | { |
134 | get { return map[x, y]; } | 134 | get { return m_map[x * Width + y]; } |
135 | set | 135 | set |
136 | { | 136 | { |
137 | // Will "fix" terrain hole problems. Although not fantastically. | 137 | // Will "fix" terrain hole problems. Although not fantastically. |
138 | if (Double.IsNaN(value) || Double.IsInfinity(value)) | 138 | if (Double.IsNaN(value) || Double.IsInfinity(value)) |
139 | return; | 139 | return; |
140 | 140 | ||
141 | if (map[x, y] != value) | 141 | int idx = x * Width + y; |
142 | if (m_map[idx] != value) | ||
142 | { | 143 | { |
143 | taint[x / 16, y / 16] = true; | 144 | m_taint[x / Constants.TerrainPatchSize, y / Constants.TerrainPatchSize] = true; |
144 | map[x, y] = value; | 145 | m_map[idx] = ToCompressedHeight(value); |
145 | } | 146 | } |
146 | } | 147 | } |
147 | } | 148 | } |
148 | 149 | ||
150 | // ITerrainChannel.Tainted() | ||
149 | public bool Tainted(int x, int y) | 151 | public bool Tainted(int x, int y) |
150 | { | 152 | { |
151 | if (taint[x / 16, y / 16]) | 153 | if (m_taint[x / Constants.TerrainPatchSize, y / Constants.TerrainPatchSize]) |
152 | { | 154 | { |
153 | taint[x / 16, y / 16] = false; | 155 | m_taint[x / Constants.TerrainPatchSize, y / Constants.TerrainPatchSize] = false; |
154 | return true; | 156 | return true; |
155 | } | 157 | } |
156 | return false; | 158 | return false; |
157 | } | 159 | } |
158 | 160 | ||
159 | #endregion | 161 | // ITerrainChannel.SaveToXmlString() |
160 | |||
161 | public TerrainChannel Copy() | ||
162 | { | ||
163 | TerrainChannel copy = new TerrainChannel(false); | ||
164 | copy.map = (double[,]) map.Clone(); | ||
165 | |||
166 | return copy; | ||
167 | } | ||
168 | |||
169 | public string SaveToXmlString() | 162 | public string SaveToXmlString() |
170 | { | 163 | { |
171 | XmlWriterSettings settings = new XmlWriterSettings(); | 164 | XmlWriterSettings settings = new XmlWriterSettings(); |
@@ -181,13 +174,7 @@ namespace OpenSim.Region.Framework.Scenes | |||
181 | } | 174 | } |
182 | } | 175 | } |
183 | 176 | ||
184 | private void WriteXml(XmlWriter writer) | 177 | // 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) | 178 | public void LoadFromXmlString(string data) |
192 | { | 179 | { |
193 | StringReader sr = new StringReader(data); | 180 | StringReader sr = new StringReader(data); |
@@ -199,12 +186,89 @@ namespace OpenSim.Region.Framework.Scenes | |||
199 | sr.Close(); | 186 | sr.Close(); |
200 | } | 187 | } |
201 | 188 | ||
189 | #endregion | ||
190 | |||
191 | private void InitializeStructures(uint pX, uint pY, uint pZ, bool shouldInitializeHeightmap) | ||
192 | { | ||
193 | Width = (int)pX; | ||
194 | Height = (int)pY; | ||
195 | Altitude = (int)pZ; | ||
196 | m_map = new short[Width * Height]; | ||
197 | m_taint = new bool[Width / Constants.TerrainPatchSize, Height / Constants.TerrainPatchSize]; | ||
198 | ClearTaint(); | ||
199 | if (shouldInitializeHeightmap) | ||
200 | { | ||
201 | FlatLand(); | ||
202 | } | ||
203 | } | ||
204 | |||
205 | public void ClearTaint() | ||
206 | { | ||
207 | for (int ii = 0; ii < Width / Constants.TerrainPatchSize; ii++) | ||
208 | for (int jj = 0; jj < Height / Constants.TerrainPatchSize; jj++) | ||
209 | m_taint[ii, jj] = false; | ||
210 | } | ||
211 | |||
212 | // To save space (especially for large regions), keep the height as a short integer | ||
213 | // that is coded as the float height times the compression factor (usually '100' | ||
214 | // to make for two decimal points). | ||
215 | public short ToCompressedHeight(double pHeight) | ||
216 | { | ||
217 | return (short)(pHeight * Constants.TerrainCompression); | ||
218 | } | ||
219 | |||
220 | public float FromCompressedHeight(short pHeight) | ||
221 | { | ||
222 | return ((float)pHeight) / Constants.TerrainCompression; | ||
223 | } | ||
224 | |||
225 | public TerrainChannel Copy() | ||
226 | { | ||
227 | TerrainChannel copy = new TerrainChannel(); | ||
228 | copy.m_map = (short[])m_map.Clone(); | ||
229 | copy.m_taint = (bool[,])m_taint.Clone(); | ||
230 | copy.Width = Width; | ||
231 | copy.Height = Height; | ||
232 | copy.Altitude = Altitude; | ||
233 | |||
234 | return copy; | ||
235 | } | ||
236 | |||
237 | private void WriteXml(XmlWriter writer) | ||
238 | { | ||
239 | if (Width == Constants.RegionSize && Height == Constants.RegionSize) | ||
240 | { | ||
241 | // Downward compatibility for legacy region terrain maps. | ||
242 | // If region is exactly legacy size, return the old format XML. | ||
243 | writer.WriteStartElement(String.Empty, "TerrainMap", String.Empty); | ||
244 | ToXml(writer); | ||
245 | writer.WriteEndElement(); | ||
246 | } | ||
247 | else | ||
248 | { | ||
249 | // New format XML that includes width and length. | ||
250 | writer.WriteStartElement(String.Empty, "TerrainMap2", String.Empty); | ||
251 | ToXml2(writer); | ||
252 | writer.WriteEndElement(); | ||
253 | } | ||
254 | } | ||
255 | |||
202 | private void ReadXml(XmlReader reader) | 256 | private void ReadXml(XmlReader reader) |
203 | { | 257 | { |
204 | reader.ReadStartElement("TerrainMap"); | 258 | // Check the first element. If legacy element, use the legacy reader. |
205 | FromXml(reader); | 259 | if (reader.IsStartElement("TerrainMap")) |
260 | { | ||
261 | reader.ReadStartElement("TerrainMap"); | ||
262 | FromXml(reader); | ||
263 | } | ||
264 | else | ||
265 | { | ||
266 | reader.ReadStartElement("TerrainMap2"); | ||
267 | FromXml2(reader); | ||
268 | } | ||
206 | } | 269 | } |
207 | 270 | ||
271 | // Write legacy terrain map. Presumed to be 256x256 of data encoded as floats in a byte array. | ||
208 | private void ToXml(XmlWriter xmlWriter) | 272 | private void ToXml(XmlWriter xmlWriter) |
209 | { | 273 | { |
210 | float[] mapData = GetFloatsSerialised(); | 274 | float[] mapData = GetFloatsSerialised(); |
@@ -218,6 +282,7 @@ namespace OpenSim.Region.Framework.Scenes | |||
218 | serializer.Serialize(xmlWriter, buffer); | 282 | serializer.Serialize(xmlWriter, buffer); |
219 | } | 283 | } |
220 | 284 | ||
285 | // Read legacy terrain map. Presumed to be 256x256 of data encoded as floats in a byte array. | ||
221 | private void FromXml(XmlReader xmlReader) | 286 | private void FromXml(XmlReader xmlReader) |
222 | { | 287 | { |
223 | XmlSerializer serializer = new XmlSerializer(typeof(byte[])); | 288 | XmlSerializer serializer = new XmlSerializer(typeof(byte[])); |
@@ -236,35 +301,68 @@ namespace OpenSim.Region.Framework.Scenes | |||
236 | } | 301 | } |
237 | } | 302 | } |
238 | 303 | ||
304 | private class TerrainChannelXMLPackage | ||
305 | { | ||
306 | public int Version; | ||
307 | public int SizeX; | ||
308 | public int SizeY; | ||
309 | public int SizeZ; | ||
310 | public short[] Map; | ||
311 | public TerrainChannelXMLPackage(int pX, int pY, int pZ, short[] pMap) | ||
312 | { | ||
313 | Version = 1; | ||
314 | SizeX = pX; | ||
315 | SizeY = pY; | ||
316 | SizeZ = pZ; | ||
317 | Map = pMap; | ||
318 | } | ||
319 | } | ||
320 | |||
321 | // New terrain serialization format that includes the width and length. | ||
322 | private void ToXml2(XmlWriter xmlWriter) | ||
323 | { | ||
324 | TerrainChannelXMLPackage package = new TerrainChannelXMLPackage(Width, Height, Altitude, m_map); | ||
325 | XmlSerializer serializer = new XmlSerializer(typeof(TerrainChannelXMLPackage)); | ||
326 | serializer.Serialize(xmlWriter, package); | ||
327 | } | ||
328 | |||
329 | // New terrain serialization format that includes the width and length. | ||
330 | private void FromXml2(XmlReader xmlReader) | ||
331 | { | ||
332 | XmlSerializer serializer = new XmlSerializer(typeof(TerrainChannelXMLPackage)); | ||
333 | TerrainChannelXMLPackage package = (TerrainChannelXMLPackage)serializer.Deserialize(xmlReader); | ||
334 | Width = package.SizeX; | ||
335 | Height = package.SizeY; | ||
336 | Altitude = package.SizeZ; | ||
337 | m_map = package.Map; | ||
338 | } | ||
339 | |||
340 | // Fill the heightmap with the center bump terrain | ||
239 | private void PinHeadIsland() | 341 | private void PinHeadIsland() |
240 | { | 342 | { |
241 | int x; | 343 | int x; |
242 | for (x = 0; x < Constants.RegionSize; x++) | 344 | for (x = 0; x < Width; x++) |
243 | { | 345 | { |
244 | int y; | 346 | int y; |
245 | for (y = 0; y < Constants.RegionSize; y++) | 347 | for (y = 0; y < Height; y++) |
246 | { | 348 | { |
247 | map[x, y] = TerrainUtil.PerlinNoise2D(x, y, 2, 0.125) * 10; | 349 | int idx = x * (int)Width + y; |
248 | double spherFacA = TerrainUtil.SphericalFactor(x, y, Constants.RegionSize / 2.0, Constants.RegionSize / 2.0, 50) * 0.01; | 350 | m_map[idx] = ToCompressedHeight(TerrainUtil.PerlinNoise2D(x, y, 2, 0.125) * 10); |
249 | double spherFacB = TerrainUtil.SphericalFactor(x, y, Constants.RegionSize / 2.0, Constants.RegionSize / 2.0, 100) * 0.001; | 351 | short spherFacA = ToCompressedHeight(TerrainUtil.SphericalFactor(x, y, Constants.RegionSize / 2.0, Constants.RegionSize / 2.0, 50) * 0.01); |
250 | if (map[x, y] < spherFacA) | 352 | short spherFacB = ToCompressedHeight(TerrainUtil.SphericalFactor(x, y, Constants.RegionSize / 2.0, Constants.RegionSize / 2.0, 100) * 0.001); |
251 | map[x, y] = spherFacA; | 353 | if (m_map[idx] < spherFacA) |
252 | if (map[x, y] < spherFacB) | 354 | m_map[idx] = spherFacA; |
253 | map[x, y] = spherFacB; | 355 | if (m_map[idx] < spherFacB) |
356 | m_map[idx] = spherFacB; | ||
254 | } | 357 | } |
255 | } | 358 | } |
256 | } | 359 | } |
257 | 360 | ||
258 | private void FlatLand() | 361 | private void FlatLand() |
259 | { | 362 | { |
260 | int x; | 363 | short flatHeight = ToCompressedHeight(21); |
261 | for (x = 0; x < Constants.RegionSize; x++) | 364 | for (int ii = 0; ii < m_map.Length; ii++) |
262 | { | 365 | m_map[ii] = flatHeight; |
263 | int y; | ||
264 | for (y = 0; y < Constants.RegionSize; y++) | ||
265 | map[x, y] = 21; | ||
266 | } | ||
267 | } | 366 | } |
268 | |||
269 | } | 367 | } |
270 | } | 368 | } |