aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/OpenSim/Region/CoreModules
diff options
context:
space:
mode:
authorKevin Cozens2011-01-26 20:53:21 -0500
committerJustin Clark-Casey (justincc)2011-01-28 21:19:53 +0000
commit9798b044fe92df6bd1b3f18c04937bd99a4d98cb (patch)
tree0fad6c8fccf80e395f150f776330bd29f7c1ac7f /OpenSim/Region/CoreModules
parentMake the new style stuff compatible with the older revision (diff)
downloadopensim-SC-9798b044fe92df6bd1b3f18c04937bd99a4d98cb.zip
opensim-SC-9798b044fe92df6bd1b3f18c04937bd99a4d98cb.tar.gz
opensim-SC-9798b044fe92df6bd1b3f18c04937bd99a4d98cb.tar.bz2
opensim-SC-9798b044fe92df6bd1b3f18c04937bd99a4d98cb.tar.xz
Added loading and saving of terrain files using Terragen format (Mantis #1564)
Terrain files can now be loaded and saved using the Terragen (.ter) format. Selection of the terrain file loader to use is now based on the extension of the filename being loaded and the data is loaded using a memory stream instead of writing it to a file and then loading it from the file.
Diffstat (limited to 'OpenSim/Region/CoreModules')
-rw-r--r--OpenSim/Region/CoreModules/World/Estate/EstateManagementModule.cs35
-rw-r--r--OpenSim/Region/CoreModules/World/Terrain/FileLoaders/Terragen.cs239
2 files changed, 224 insertions, 50 deletions
diff --git a/OpenSim/Region/CoreModules/World/Estate/EstateManagementModule.cs b/OpenSim/Region/CoreModules/World/Estate/EstateManagementModule.cs
index b0563c5..01f04d9 100644
--- a/OpenSim/Region/CoreModules/World/Estate/EstateManagementModule.cs
+++ b/OpenSim/Region/CoreModules/World/Estate/EstateManagementModule.cs
@@ -555,37 +555,12 @@ namespace OpenSim.Region.CoreModules.World.Estate
555 555
556 try 556 try
557 { 557 {
558 MemoryStream terrainStream = new MemoryStream(terrainData);
559 terr.LoadFromStream(filename, terrainStream);
560 terrainStream.Close();
558 561
559 string localfilename = "terrain.raw"; 562 FileInfo x = new FileInfo(filename);
560 563 remoteClient.SendAlertMessage("Your terrain was loaded as a " + x.Extension + " file. It may take a few moments to appear.");
561 if (terrainData.Length == 851968)
562 {
563 localfilename = Path.Combine(Util.dataDir(),"terrain.raw"); // It's a .LLRAW
564 }
565
566 if (terrainData.Length == 196662) // 24-bit 256x256 Bitmap
567 localfilename = Path.Combine(Util.dataDir(), "terrain.bmp");
568
569 if (terrainData.Length == 256 * 256 * 4) // It's a .R32
570 localfilename = Path.Combine(Util.dataDir(), "terrain.r32");
571
572 if (terrainData.Length == 256 * 256 * 8) // It's a .R64
573 localfilename = Path.Combine(Util.dataDir(), "terrain.r64");
574
575 if (File.Exists(localfilename))
576 {
577 File.Delete(localfilename);
578 }
579
580 FileStream input = new FileStream(localfilename, FileMode.CreateNew);
581 input.Write(terrainData, 0, terrainData.Length);
582 input.Close();
583
584 FileInfo x = new FileInfo(localfilename);
585
586 terr.LoadFromFile(localfilename);
587 remoteClient.SendAlertMessage("Your terrain was loaded as a ." + x.Extension + " file. It may take a few moments to appear.");
588
589 } 564 }
590 catch (IOException e) 565 catch (IOException e)
591 { 566 {
diff --git a/OpenSim/Region/CoreModules/World/Terrain/FileLoaders/Terragen.cs b/OpenSim/Region/CoreModules/World/Terrain/FileLoaders/Terragen.cs
index 3ee20ae..2919897 100644
--- a/OpenSim/Region/CoreModules/World/Terrain/FileLoaders/Terragen.cs
+++ b/OpenSim/Region/CoreModules/World/Terrain/FileLoaders/Terragen.cs
@@ -30,6 +30,7 @@ using System.IO;
30using System.Text; 30using System.Text;
31using OpenSim.Region.Framework.Interfaces; 31using OpenSim.Region.Framework.Interfaces;
32using OpenSim.Region.Framework.Scenes; 32using OpenSim.Region.Framework.Scenes;
33using OpenSim.Framework;
33 34
34namespace OpenSim.Region.CoreModules.World.Terrain.FileLoaders 35namespace OpenSim.Region.CoreModules.World.Terrain.FileLoaders
35{ 36{
@@ -53,17 +54,120 @@ namespace OpenSim.Region.CoreModules.World.Terrain.FileLoaders
53 return retval; 54 return retval;
54 } 55 }
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
141 break;
142 default:
143 bs.ReadInt32();
144 break;
145 }
146 }
147
148 bs.Close();
149 s.Close();
150
151 return retval;
152 }
153
56 public ITerrainChannel LoadStream(Stream s) 154 public ITerrainChannel LoadStream(Stream s)
57 { 155 {
58 TerrainChannel retval = new TerrainChannel(); 156
157 int w = (int)Constants.RegionSize;
158 int h = (int)Constants.RegionSize;
159
160 TerrainChannel retval = new TerrainChannel(w, h);
59 161
60 BinaryReader bs = new BinaryReader(s); 162 BinaryReader bs = new BinaryReader(s);
61 163
62 bool eof = false; 164 bool eof = false;
63 if (Encoding.ASCII.GetString(bs.ReadBytes(16)) == "TERRAGENTERRAIN ") 165 if (Encoding.ASCII.GetString(bs.ReadBytes(16)) == "TERRAGENTERRAIN ")
64 { 166 {
65 int w = 256; 167
66 int h = 256; 168 int fileWidth = w;
169 int fileHeight = h;
170
67 171
68 // Terragen file 172 // Terragen file
69 while (eof == false) 173 while (eof == false)
@@ -73,30 +177,27 @@ namespace OpenSim.Region.CoreModules.World.Terrain.FileLoaders
73 { 177 {
74 case "SIZE": 178 case "SIZE":
75 int sztmp = bs.ReadInt16() + 1; 179 int sztmp = bs.ReadInt16() + 1;
76 w = sztmp; 180 fileWidth = sztmp;
77 h = sztmp; 181 fileHeight = sztmp;
78 bs.ReadInt16(); 182 bs.ReadInt16();
79 break; 183 break;
80 case "XPTS": 184 case "XPTS":
81 w = bs.ReadInt16(); 185 fileWidth = bs.ReadInt16();
82 bs.ReadInt16(); 186 bs.ReadInt16();
83 break; 187 break;
84 case "YPTS": 188 case "YPTS":
85 h = bs.ReadInt16(); 189 fileHeight = bs.ReadInt16();
86 bs.ReadInt16(); 190 bs.ReadInt16();
87 break; 191 break;
88 case "ALTW": 192 case "ALTW":
89 eof = true; 193 eof = true;
90 Int16 heightScale = bs.ReadInt16(); 194 Int16 heightScale = bs.ReadInt16();
91 Int16 baseHeight = bs.ReadInt16(); 195 Int16 baseHeight = bs.ReadInt16();
92 retval = new TerrainChannel(w, h); 196 for (int y = 0; y < h; y++)
93 int x;
94 for (x = 0; x < w; x++)
95 { 197 {
96 int y; 198 for (int x = 0; x < w; x++)
97 for (y = 0; y < h; y++)
98 { 199 {
99 retval[x, y] = baseHeight + bs.ReadInt16() * (double) heightScale / 65536.0; 200 retval[x, y] = baseHeight + bs.ReadInt16() * (double)heightScale / 65536.0;
100 } 201 }
101 } 202 }
102 break; 203 break;
@@ -114,12 +215,92 @@ namespace OpenSim.Region.CoreModules.World.Terrain.FileLoaders
114 215
115 public void SaveFile(string filename, ITerrainChannel map) 216 public void SaveFile(string filename, ITerrainChannel map)
116 { 217 {
117 throw new NotImplementedException(); 218 FileInfo file = new FileInfo(filename);
219 FileStream s = file.Open(FileMode.Create, FileAccess.Write);
220 SaveStream(s, map);
221
222 s.Close();
118 } 223 }
119 224
120 public void SaveStream(Stream stream, ITerrainChannel map) 225 public void SaveStream(Stream stream, ITerrainChannel map)
121 { 226 {
122 throw new NotImplementedException(); 227 BinaryWriter bs = new BinaryWriter(stream);
228
229 //find the max and min heights on the map
230 double heightMax = map[0,0];
231 double heightMin = map[0,0];
232
233 for (int y = 0; y < map.Height; y++)
234 {
235 for (int x = 0; x < map.Width; x++)
236 {
237 double current = map[x,y];
238 if (heightMax < current)
239 heightMax = current;
240 if (heightMin > current)
241 heightMin = current;
242 }
243 }
244
245 double baseHeight = Math.Floor( (heightMax + heightMin) / 2d );
246
247 double horizontalScale = Math.Ceiling((heightMax - heightMin));
248
249 // if we are completely flat add 1cm range to avoid NaN divisions
250 if (horizontalScale < 0.01d)
251 horizontalScale = 0.01d;
252
253 System.Text.ASCIIEncoding enc = new System.Text.ASCIIEncoding();
254
255 bs.Write(enc.GetBytes("TERRAGENTERRAIN "));
256
257 bs.Write(enc.GetBytes("SIZE"));
258 bs.Write(Convert.ToInt16(Constants.RegionSize));
259 bs.Write(Convert.ToInt16(0)); // necessary padding
260
261 //The XPTS and YPTS chunks are not needed for square regions
262 //but L3DT won't load the terrain file properly without them.
263 bs.Write(enc.GetBytes("XPTS"));
264 bs.Write(Convert.ToInt16(Constants.RegionSize));
265 bs.Write(Convert.ToInt16(0)); // necessary padding
266
267 bs.Write(enc.GetBytes("YPTS"));
268 bs.Write(Convert.ToInt16(Constants.RegionSize));
269 bs.Write(Convert.ToInt16(0)); // necessary padding
270
271 bs.Write(enc.GetBytes("SCAL"));
272 bs.Write(ToLittleEndian(1f)); //we're going to say that 1 terrain unit is 1 metre
273 bs.Write(ToLittleEndian(1f));
274 bs.Write(ToLittleEndian(1f));
275
276 // as we are square and not projected on a sphere then the other
277 // header blocks are not required
278
279 // now write the elevation data
280 bs.Write(enc.GetBytes("ALTW"));
281 bs.Write(Convert.ToInt16(horizontalScale)); // range between max and min
282 bs.Write(Convert.ToInt16(baseHeight)); // base height or mid point
283
284 for (int y = 0; y < map.Height; y++)
285 {
286 for (int x = 0; x < map.Width; x++)
287 {
288 float elevation = (float)((map[x,y] - baseHeight) * 65536 ) / (float)horizontalScale; // see LoadStream for inverse
289
290 // clamp rounding issues
291 if (elevation > Int16.MaxValue)
292 elevation = Int16.MaxValue;
293 else if (elevation < Int16.MinValue)
294 elevation = Int16.MinValue;
295
296 bs.Write(Convert.ToInt16(elevation));
297 }
298 }
299
300 //This is only necessary for older versions of Terragen.
301 bs.Write(enc.GetBytes("EOF "));
302
303 bs.Close();
123 } 304 }
124 305
125 public string FileExtension 306 public string FileExtension
@@ -127,16 +308,34 @@ namespace OpenSim.Region.CoreModules.World.Terrain.FileLoaders
127 get { return ".ter"; } 308 get { return ".ter"; }
128 } 309 }
129 310
130 public ITerrainChannel LoadFile(string filename, int x, int y, int fileWidth, int fileHeight, int w, int h)
131 {
132 throw new NotImplementedException();
133 }
134
135 #endregion 311 #endregion
136 312
137 public override string ToString() 313 public override string ToString()
138 { 314 {
139 return "Terragen"; 315 return "Terragen";
140 } 316 }
317
318 /// <summary>
319 /// terragen SCAL floats need to be written intel ordered regardless of
320 /// big or little endian system
321 /// </summary>
322 /// <param name="number"></param>
323 /// <returns></returns>
324 private byte[] ToLittleEndian( float number)
325 {
326 byte[] retVal = BitConverter.GetBytes(number);
327 if (BitConverter.IsLittleEndian == false)
328 {
329 byte[] tmp = new byte[4];
330 for (int i = 0; i < 4; i++)
331 {
332 tmp[i] = retVal[3 - i];
333 }
334 retVal = tmp;
335
336 }
337 return retVal ;
338 }
339
141 } 340 }
142} 341}