diff --git a/OpenSim/Region/CoreModules/World/Estate/EstateManagementModule.cs b/OpenSim/Region/CoreModules/World/Estate/EstateManagementModule.cs
index b0563c5ff1..01f04d93d3 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
try
{
+ MemoryStream terrainStream = new MemoryStream(terrainData);
+ terr.LoadFromStream(filename, terrainStream);
+ terrainStream.Close();
- string localfilename = "terrain.raw";
-
- if (terrainData.Length == 851968)
- {
- localfilename = Path.Combine(Util.dataDir(),"terrain.raw"); // It's a .LLRAW
- }
-
- if (terrainData.Length == 196662) // 24-bit 256x256 Bitmap
- localfilename = Path.Combine(Util.dataDir(), "terrain.bmp");
-
- if (terrainData.Length == 256 * 256 * 4) // It's a .R32
- localfilename = Path.Combine(Util.dataDir(), "terrain.r32");
-
- if (terrainData.Length == 256 * 256 * 8) // It's a .R64
- localfilename = Path.Combine(Util.dataDir(), "terrain.r64");
-
- if (File.Exists(localfilename))
- {
- File.Delete(localfilename);
- }
-
- FileStream input = new FileStream(localfilename, FileMode.CreateNew);
- input.Write(terrainData, 0, terrainData.Length);
- input.Close();
-
- FileInfo x = new FileInfo(localfilename);
-
- terr.LoadFromFile(localfilename);
- remoteClient.SendAlertMessage("Your terrain was loaded as a ." + x.Extension + " file. It may take a few moments to appear.");
-
+ FileInfo x = new FileInfo(filename);
+ remoteClient.SendAlertMessage("Your terrain was loaded as a " + x.Extension + " file. It may take a few moments to appear.");
}
catch (IOException e)
{
diff --git a/OpenSim/Region/CoreModules/World/Terrain/FileLoaders/Terragen.cs b/OpenSim/Region/CoreModules/World/Terrain/FileLoaders/Terragen.cs
index 3ee20ae48f..2919897153 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;
using System.Text;
using OpenSim.Region.Framework.Interfaces;
using OpenSim.Region.Framework.Scenes;
+using OpenSim.Framework;
namespace OpenSim.Region.CoreModules.World.Terrain.FileLoaders
{
@@ -53,17 +54,120 @@ namespace OpenSim.Region.CoreModules.World.Terrain.FileLoaders
return retval;
}
+ public ITerrainChannel LoadFile(string filename, int offsetX, int offsetY, int fileWidth, int fileHeight, int sectionWidth, int sectionHeight)
+ {
+ TerrainChannel retval = new TerrainChannel(sectionWidth, sectionHeight);
+
+ FileInfo file = new FileInfo(filename);
+ FileStream s = file.Open(FileMode.Open, FileAccess.Read);
+ BinaryReader bs = new BinaryReader(s);
+
+ bool eof = false;
+
+ int fileXPoints = 0;
+ int fileYPoints = 0;
+
+ // Terragen file
+ while (eof == false)
+ {
+ string tmp = Encoding.ASCII.GetString(bs.ReadBytes(4));
+ switch (tmp)
+ {
+ case "SIZE":
+ fileXPoints = bs.ReadInt16() + 1;
+ fileYPoints = fileXPoints;
+ bs.ReadInt16();
+ break;
+ case "XPTS":
+ fileXPoints = bs.ReadInt16();
+ bs.ReadInt16();
+ break;
+ case "YPTS":
+ fileYPoints = bs.ReadInt16();
+ bs.ReadInt16();
+ break;
+ case "ALTW":
+ eof = true;
+ Int16 heightScale = bs.ReadInt16();
+ Int16 baseHeight = bs.ReadInt16();
+
+ int currFileYOffset = 0;
+
+ // if our region isn't on the first X section of the areas to be landscaped, then
+ // advance to our section of the file
+ while (currFileYOffset < offsetY)
+ {
+ // read a whole strip of regions
+ int heightsToRead = sectionHeight * fileXPoints;
+ bs.ReadBytes(heightsToRead * 2); // because the shorts are 2 bytes in the file
+ currFileYOffset++;
+ }
+
+ for (int y = 0; y < sectionHeight; y++)
+ {
+ int currFileXOffset = 0;
+
+ // if our region isn't the first X section of the areas to be landscaped, then
+ // advance the stream to the X start pos of our section in the file
+ // i.e. eat X upto where we start
+ while (currFileXOffset < offsetX)
+ {
+ bs.ReadBytes(sectionWidth * 2); // 2 bytes = short
+ currFileXOffset++;
+ }
+
+ // got to our X offset, so write our regions X line
+ for (int x = 0; x < sectionWidth; x++)
+ {
+ // Read a strip and continue
+ retval[x, y] = baseHeight + bs.ReadInt16() * (double)heightScale / 65536.0;
+ }
+ // record that we wrote it
+ currFileXOffset++;
+
+ // if our region isn't the last X section of the areas to be landscaped, then
+ // advance the stream to the end of this Y column
+ while (currFileXOffset < fileWidth)
+ {
+ // eat the next regions x line
+ bs.ReadBytes(sectionWidth * 2); // 2 bytes = short
+ currFileXOffset++;
+ }
+ //eat the last additional point
+ bs.ReadInt16();
+ }
+
+
+ break;
+ default:
+ bs.ReadInt32();
+ break;
+ }
+ }
+
+ bs.Close();
+ s.Close();
+
+ return retval;
+ }
+
public ITerrainChannel LoadStream(Stream s)
{
- TerrainChannel retval = new TerrainChannel();
+
+ int w = (int)Constants.RegionSize;
+ int h = (int)Constants.RegionSize;
+
+ TerrainChannel retval = new TerrainChannel(w, h);
BinaryReader bs = new BinaryReader(s);
bool eof = false;
if (Encoding.ASCII.GetString(bs.ReadBytes(16)) == "TERRAGENTERRAIN ")
{
- int w = 256;
- int h = 256;
+
+ int fileWidth = w;
+ int fileHeight = h;
+
// Terragen file
while (eof == false)
@@ -73,30 +177,27 @@ namespace OpenSim.Region.CoreModules.World.Terrain.FileLoaders
{
case "SIZE":
int sztmp = bs.ReadInt16() + 1;
- w = sztmp;
- h = sztmp;
+ fileWidth = sztmp;
+ fileHeight = sztmp;
bs.ReadInt16();
break;
case "XPTS":
- w = bs.ReadInt16();
+ fileWidth = bs.ReadInt16();
bs.ReadInt16();
break;
case "YPTS":
- h = bs.ReadInt16();
+ fileHeight = bs.ReadInt16();
bs.ReadInt16();
break;
case "ALTW":
eof = true;
Int16 heightScale = bs.ReadInt16();
Int16 baseHeight = bs.ReadInt16();
- retval = new TerrainChannel(w, h);
- int x;
- for (x = 0; x < w; x++)
+ for (int y = 0; y < h; y++)
{
- int y;
- for (y = 0; y < h; y++)
+ for (int x = 0; x < w; x++)
{
- retval[x, y] = baseHeight + bs.ReadInt16() * (double) heightScale / 65536.0;
+ retval[x, y] = baseHeight + bs.ReadInt16() * (double)heightScale / 65536.0;
}
}
break;
@@ -114,12 +215,92 @@ namespace OpenSim.Region.CoreModules.World.Terrain.FileLoaders
public void SaveFile(string filename, ITerrainChannel map)
{
- throw new NotImplementedException();
+ FileInfo file = new FileInfo(filename);
+ FileStream s = file.Open(FileMode.Create, FileAccess.Write);
+ SaveStream(s, map);
+
+ s.Close();
}
public void SaveStream(Stream stream, ITerrainChannel map)
{
- throw new NotImplementedException();
+ BinaryWriter bs = new BinaryWriter(stream);
+
+ //find the max and min heights on the map
+ double heightMax = map[0,0];
+ double heightMin = map[0,0];
+
+ for (int y = 0; y < map.Height; y++)
+ {
+ for (int x = 0; x < map.Width; x++)
+ {
+ double current = map[x,y];
+ if (heightMax < current)
+ heightMax = current;
+ if (heightMin > current)
+ heightMin = current;
+ }
+ }
+
+ double baseHeight = Math.Floor( (heightMax + heightMin) / 2d );
+
+ double horizontalScale = Math.Ceiling((heightMax - heightMin));
+
+ // if we are completely flat add 1cm range to avoid NaN divisions
+ if (horizontalScale < 0.01d)
+ horizontalScale = 0.01d;
+
+ System.Text.ASCIIEncoding enc = new System.Text.ASCIIEncoding();
+
+ bs.Write(enc.GetBytes("TERRAGENTERRAIN "));
+
+ bs.Write(enc.GetBytes("SIZE"));
+ bs.Write(Convert.ToInt16(Constants.RegionSize));
+ bs.Write(Convert.ToInt16(0)); // necessary padding
+
+ //The XPTS and YPTS chunks are not needed for square regions
+ //but L3DT won't load the terrain file properly without them.
+ bs.Write(enc.GetBytes("XPTS"));
+ bs.Write(Convert.ToInt16(Constants.RegionSize));
+ bs.Write(Convert.ToInt16(0)); // necessary padding
+
+ bs.Write(enc.GetBytes("YPTS"));
+ bs.Write(Convert.ToInt16(Constants.RegionSize));
+ bs.Write(Convert.ToInt16(0)); // necessary padding
+
+ bs.Write(enc.GetBytes("SCAL"));
+ bs.Write(ToLittleEndian(1f)); //we're going to say that 1 terrain unit is 1 metre
+ bs.Write(ToLittleEndian(1f));
+ bs.Write(ToLittleEndian(1f));
+
+ // as we are square and not projected on a sphere then the other
+ // header blocks are not required
+
+ // now write the elevation data
+ bs.Write(enc.GetBytes("ALTW"));
+ bs.Write(Convert.ToInt16(horizontalScale)); // range between max and min
+ bs.Write(Convert.ToInt16(baseHeight)); // base height or mid point
+
+ for (int y = 0; y < map.Height; y++)
+ {
+ for (int x = 0; x < map.Width; x++)
+ {
+ float elevation = (float)((map[x,y] - baseHeight) * 65536 ) / (float)horizontalScale; // see LoadStream for inverse
+
+ // clamp rounding issues
+ if (elevation > Int16.MaxValue)
+ elevation = Int16.MaxValue;
+ else if (elevation < Int16.MinValue)
+ elevation = Int16.MinValue;
+
+ bs.Write(Convert.ToInt16(elevation));
+ }
+ }
+
+ //This is only necessary for older versions of Terragen.
+ bs.Write(enc.GetBytes("EOF "));
+
+ bs.Close();
}
public string FileExtension
@@ -127,16 +308,34 @@ namespace OpenSim.Region.CoreModules.World.Terrain.FileLoaders
get { return ".ter"; }
}
- public ITerrainChannel LoadFile(string filename, int x, int y, int fileWidth, int fileHeight, int w, int h)
- {
- throw new NotImplementedException();
- }
-
#endregion
public override string ToString()
{
return "Terragen";
}
+
+ ///
+ /// terragen SCAL floats need to be written intel ordered regardless of
+ /// big or little endian system
+ ///
+ ///
+ ///
+ private byte[] ToLittleEndian( float number)
+ {
+ byte[] retVal = BitConverter.GetBytes(number);
+ if (BitConverter.IsLittleEndian == false)
+ {
+ byte[] tmp = new byte[4];
+ for (int i = 0; i < 4; i++)
+ {
+ tmp[i] = retVal[3 - i];
+ }
+ retVal = tmp;
+
+ }
+ return retVal ;
+ }
+
}
}