OpenSim
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Events Macros
Terragen.cs
Go to the documentation of this file.
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 {
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 
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 }
A new version of the old Channel class, simplified