clockwerk-opensim – Blame information for rev 1

Subversion Repositories:
Rev:
Rev Author Line No. Line
1 vero 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 OpenSim.Region.Framework.Interfaces;
32 using OpenSim.Region.Framework.Scenes;
33 using OpenSim.Framework;
34  
35 namespace OpenSim.Region.CoreModules.World.Terrain.FileLoaders
36 {
37 /// <summary>
38 /// Terragen File Format Loader
39 /// Built from specification at
40 /// http://www.planetside.co.uk/terragen/dev/tgterrain.html
41 /// </summary>
42 internal class Terragen : ITerrainLoader
43 {
44 #region ITerrainLoader Members
45  
46 public ITerrainChannel LoadFile(string filename)
47 {
48 FileInfo file = new FileInfo(filename);
49 FileStream s = file.Open(FileMode.Open, FileAccess.Read);
50 ITerrainChannel retval = LoadStream(s);
51  
52 s.Close();
53  
54 return retval;
55 }
56  
57 public ITerrainChannel LoadFile(string filename, int offsetX, int offsetY, int fileWidth, int fileHeight, int sectionWidth, int sectionHeight)
58 {
59 TerrainChannel retval = new TerrainChannel(sectionWidth, sectionHeight);
60  
61 FileInfo file = new FileInfo(filename);
62 FileStream s = file.Open(FileMode.Open, FileAccess.Read);
63 BinaryReader bs = new BinaryReader(s);
64  
65 bool eof = false;
66  
67 int fileXPoints = 0;
68 int fileYPoints = 0;
69  
70 // Terragen file
71 while (eof == false)
72 {
73 string tmp = Encoding.ASCII.GetString(bs.ReadBytes(4));
74 switch (tmp)
75 {
76 case "SIZE":
77 fileXPoints = bs.ReadInt16() + 1;
78 fileYPoints = fileXPoints;
79 bs.ReadInt16();
80 break;
81 case "XPTS":
82 fileXPoints = bs.ReadInt16();
83 bs.ReadInt16();
84 break;
85 case "YPTS":
86 fileYPoints = bs.ReadInt16();
87 bs.ReadInt16();
88 break;
89 case "ALTW":
90 eof = true;
91 Int16 heightScale = bs.ReadInt16();
92 Int16 baseHeight = bs.ReadInt16();
93  
94 int currFileYOffset = 0;
95  
96 // if our region isn't on the first X section of the areas to be landscaped, then
97 // advance to our section of the file
98 while (currFileYOffset < offsetY)
99 {
100 // read a whole strip of regions
101 int heightsToRead = sectionHeight * fileXPoints;
102 bs.ReadBytes(heightsToRead * 2); // because the shorts are 2 bytes in the file
103 currFileYOffset++;
104 }
105  
106 for (int y = 0; y < sectionHeight; y++)
107 {
108 int currFileXOffset = 0;
109  
110 // if our region isn't the first X section of the areas to be landscaped, then
111 // advance the stream to the X start pos of our section in the file
112 // i.e. eat X upto where we start
113 while (currFileXOffset < offsetX)
114 {
115 bs.ReadBytes(sectionWidth * 2); // 2 bytes = short
116 currFileXOffset++;
117 }
118  
119 // got to our X offset, so write our regions X line
120 for (int x = 0; x < sectionWidth; x++)
121 {
122 // Read a strip and continue
123 retval[x, y] = baseHeight + bs.ReadInt16() * (double)heightScale / 65536.0;
124 }
125 // record that we wrote it
126 currFileXOffset++;
127  
128 // if our region isn't the last X section of the areas to be landscaped, then
129 // advance the stream to the end of this Y column
130 while (currFileXOffset < fileWidth)
131 {
132 // eat the next regions x line
133 bs.ReadBytes(sectionWidth * 2); // 2 bytes = short
134 currFileXOffset++;
135 }
136 //eat the last additional point
137 bs.ReadInt16();
138 }
139  
140 break;
141 default:
142 bs.ReadInt32();
143 break;
144 }
145 }
146  
147 bs.Close();
148 s.Close();
149  
150 return retval;
151 }
152  
153 public ITerrainChannel LoadStream(Stream s)
154 {
155 // Set to default size
156 int w = (int)Constants.RegionSize;
157 int h = (int)Constants.RegionSize;
158  
159 // create a dummy channel (in case data is bad)
160 TerrainChannel retval = new TerrainChannel(w, h);
161  
162 BinaryReader bs = new BinaryReader(s);
163  
164 bool eof = false;
165 if (Encoding.ASCII.GetString(bs.ReadBytes(16)) == "TERRAGENTERRAIN ")
166 {
167  
168 // Terragen file
169 while (eof == false)
170 {
171 string tmp = Encoding.ASCII.GetString(bs.ReadBytes(4));
172 switch (tmp)
173 {
174 case "SIZE":
175 w = bs.ReadInt16() + 1;
176 h = w;
177 bs.ReadInt16();
178 break;
179 case "XPTS":
180 w = bs.ReadInt16();
181 bs.ReadInt16();
182 break;
183 case "YPTS":
184 h = bs.ReadInt16();
185 bs.ReadInt16();
186 break;
187 case "ALTW":
188 eof = true;
189 // create new channel of proper size (now that we know it)
190 retval = new TerrainChannel(w, h);
191 double heightScale = (double)bs.ReadInt16() / 65536.0;
192 double baseHeight = (double)bs.ReadInt16();
193 for (int y = 0; y < h; y++)
194 {
195 for (int x = 0; x < w; x++)
196 {
197 retval[x, y] = baseHeight + (double)bs.ReadInt16() * heightScale;
198 }
199 }
200 break;
201 default:
202 bs.ReadInt32();
203 break;
204 }
205 }
206 }
207 bs.Close();
208 return retval;
209 }
210  
211 public void SaveFile(string filename, ITerrainChannel map)
212 {
213 FileInfo file = new FileInfo(filename);
214 FileStream s = file.Open(FileMode.Create, FileAccess.Write);
215 SaveStream(s, map);
216  
217 s.Close();
218 }
219  
220 public void SaveStream(Stream stream, ITerrainChannel map)
221 {
222 BinaryWriter bs = new BinaryWriter(stream);
223  
224 //find the max and min heights on the map
225 double heightMax = map[0,0];
226 double heightMin = map[0,0];
227  
228 for (int y = 0; y < map.Height; y++)
229 {
230 for (int x = 0; x < map.Width; x++)
231 {
232 double current = map[x,y];
233 if (heightMax < current)
234 heightMax = current;
235 if (heightMin > current)
236 heightMin = current;
237 }
238 }
239  
240 double baseHeight = Math.Floor( (heightMax + heightMin) / 2d );
241  
242 double horizontalScale = Math.Ceiling((heightMax - heightMin));
243  
244 // if we are completely flat add 1cm range to avoid NaN divisions
245 if (horizontalScale < 0.01d)
246 horizontalScale = 0.01d;
247  
248 Encoding enc = Encoding.ASCII;
249  
250 bs.Write(enc.GetBytes("TERRAGENTERRAIN "));
251  
252 bs.Write(enc.GetBytes("SIZE"));
253 bs.Write(Convert.ToInt16(map.Width));
254 bs.Write(Convert.ToInt16(0)); // necessary padding
255  
256 //The XPTS and YPTS chunks are not needed for square regions
257 //but L3DT won't load the terrain file properly without them.
258 bs.Write(enc.GetBytes("XPTS"));
259 bs.Write(Convert.ToInt16(map.Width));
260 bs.Write(Convert.ToInt16(0)); // necessary padding
261  
262 bs.Write(enc.GetBytes("YPTS"));
263 bs.Write(Convert.ToInt16(map.Height));
264 bs.Write(Convert.ToInt16(0)); // necessary padding
265  
266 bs.Write(enc.GetBytes("SCAL"));
267 bs.Write(ToLittleEndian(1f)); //we're going to say that 1 terrain unit is 1 metre
268 bs.Write(ToLittleEndian(1f));
269 bs.Write(ToLittleEndian(1f));
270  
271 // as we are square and not projected on a sphere then the other
272 // header blocks are not required
273  
274 // now write the elevation data
275 bs.Write(enc.GetBytes("ALTW"));
276 bs.Write(Convert.ToInt16(horizontalScale)); // range between max and min
277 bs.Write(Convert.ToInt16(baseHeight)); // base height or mid point
278  
279 double factor = 65536.0 / horizontalScale; // avoid computing this on each iteration
280  
281 for (int y = 0; y < map.Height; y++)
282 {
283 for (int x = 0; x < map.Width; x++)
284 {
285 float elevation = (float)((map[x,y] - baseHeight) * factor); // see LoadStream for inverse
286  
287 // clamp rounding issues
288 if (elevation > Int16.MaxValue)
289 elevation = Int16.MaxValue;
290 else if (elevation < Int16.MinValue)
291 elevation = Int16.MinValue;
292  
293 bs.Write(Convert.ToInt16(elevation));
294 }
295 }
296  
297 //This is necessary for older versions of Terragen.
298 bs.Write(enc.GetBytes("EOF "));
299  
300 bs.Close();
301 }
302  
303 public string FileExtension
304 {
305 get { return ".ter"; }
306 }
307  
308 public virtual void SaveFile(ITerrainChannel m_channel, string filename,
309 int offsetX, int offsetY,
310 int fileWidth, int fileHeight,
311 int regionSizeX, int regionSizeY)
312 {
313 throw new System.Exception("Not Implemented");
314 }
315  
316 #endregion
317  
318 public override string ToString()
319 {
320 return "Terragen";
321 }
322  
323 //Returns true if this extension is supported for terrain save-tile
324 public bool SupportsTileSave()
325 {
326 return false;
327 }
328  
329 /// <summary>
330 /// terragen SCAL floats need to be written intel ordered regardless of
331 /// big or little endian system
332 /// </summary>
333 /// <param name="number"></param>
334 /// <returns></returns>
335 private byte[] ToLittleEndian( float number)
336 {
337 byte[] retVal = BitConverter.GetBytes(number);
338 if (BitConverter.IsLittleEndian == false)
339 {
340 byte[] tmp = new byte[4];
341 for (int i = 3; i >= 0; i--)
342 {
343 tmp[i] = retVal[3 - i];
344 }
345 retVal = tmp;
346  
347 }
348 return retVal ;
349 }
350  
351 }
352 }