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 ; + } + } }