diff --git a/OpenSim/Region/CoreModules/World/Terrain/FileLoaders/BMP.cs b/OpenSim/Region/CoreModules/World/Terrain/FileLoaders/BMP.cs
index ec2d085878..ba80091842 100644
--- a/OpenSim/Region/CoreModules/World/Terrain/FileLoaders/BMP.cs
+++ b/OpenSim/Region/CoreModules/World/Terrain/FileLoaders/BMP.cs
@@ -40,6 +40,11 @@ namespace OpenSim.Region.CoreModules.World.Terrain.FileLoaders
///
internal class BMP : GenericSystemDrawing
{
+ public override int SupportedHeight
+ {
+ get { return 256; }
+ }
+
///
/// Exports a file to a image on the disk using a System.Drawing exporter.
///
@@ -76,5 +81,10 @@ namespace OpenSim.Region.CoreModules.World.Terrain.FileLoaders
{
return false;
}
+
+ public override bool SupportsExtendedTileSave()
+ {
+ return false;
+ }
}
}
diff --git a/OpenSim/Region/CoreModules/World/Terrain/FileLoaders/GIF.cs b/OpenSim/Region/CoreModules/World/Terrain/FileLoaders/GIF.cs
index 384370890d..0956534eaa 100644
--- a/OpenSim/Region/CoreModules/World/Terrain/FileLoaders/GIF.cs
+++ b/OpenSim/Region/CoreModules/World/Terrain/FileLoaders/GIF.cs
@@ -34,6 +34,10 @@ namespace OpenSim.Region.CoreModules.World.Terrain.FileLoaders
{
internal class GIF : GenericSystemDrawing
{
+ public override int SupportedHeight
+ {
+ get { return 256; }
+ }
public override void SaveFile(string filename, ITerrainChannel map)
{
using(Bitmap colours = CreateGrayscaleBitmapFromMap(map))
@@ -61,5 +65,10 @@ namespace OpenSim.Region.CoreModules.World.Terrain.FileLoaders
{
return false;
}
+
+ public override bool SupportsExtendedTileSave()
+ {
+ return false;
+ }
}
}
diff --git a/OpenSim/Region/CoreModules/World/Terrain/FileLoaders/GenericSystemDrawing.cs b/OpenSim/Region/CoreModules/World/Terrain/FileLoaders/GenericSystemDrawing.cs
index bcd9dcd21c..251d8a6bad 100644
--- a/OpenSim/Region/CoreModules/World/Terrain/FileLoaders/GenericSystemDrawing.cs
+++ b/OpenSim/Region/CoreModules/World/Terrain/FileLoaders/GenericSystemDrawing.cs
@@ -49,6 +49,11 @@ namespace OpenSim.Region.CoreModules.World.Terrain.FileLoaders
get { return ".gsd"; }
}
+ public virtual int SupportedHeight
+ {
+ get { return 256; }
+ }
+
///
/// Loads a file from a specified filename on the disk,
/// parses the image using the System.Drawing parsers
@@ -203,6 +208,11 @@ namespace OpenSim.Region.CoreModules.World.Terrain.FileLoaders
return false;
}
+ public virtual bool SupportsExtendedTileSave()
+ {
+ return false;
+ }
+
///
/// Protected method, generates a grayscale bitmap
/// image from a specified terrain channel.
@@ -244,6 +254,11 @@ namespace OpenSim.Region.CoreModules.World.Terrain.FileLoaders
}
return bmp;
}
+
+ public virtual void SaveFile(ITerrainChannel map, string filename, int fileWidth, int fileHeight, int startX, int startY, int stopX, int stopY, int offsetX, int offsetY)
+ {
+ throw new NotImplementedException();
+ }
}
}
diff --git a/OpenSim/Region/CoreModules/World/Terrain/FileLoaders/JPEG.cs b/OpenSim/Region/CoreModules/World/Terrain/FileLoaders/JPEG.cs
index d604dc72f3..0abb4693d2 100644
--- a/OpenSim/Region/CoreModules/World/Terrain/FileLoaders/JPEG.cs
+++ b/OpenSim/Region/CoreModules/World/Terrain/FileLoaders/JPEG.cs
@@ -42,6 +42,11 @@ namespace OpenSim.Region.CoreModules.World.Terrain.FileLoaders
get { return ".jpg"; }
}
+ public int SupportedHeight
+ {
+ get { return 256; }
+ }
+
public ITerrainChannel LoadFile(string filename)
{
throw new NotImplementedException();
@@ -94,6 +99,11 @@ namespace OpenSim.Region.CoreModules.World.Terrain.FileLoaders
{
return false;
}
+ public bool SupportsExtendedTileSave()
+ {
+ return false;
+ }
+
private static Bitmap CreateBitmapFromMap(ITerrainChannel map)
{
@@ -125,5 +135,10 @@ namespace OpenSim.Region.CoreModules.World.Terrain.FileLoaders
}
return bmp;
}
+
+ public void SaveFile(ITerrainChannel map, string filename, int fileWidth, int fileHeight, int startX, int startY, int stopX, int stopY, int offsetX, int offsetY)
+ {
+ throw new NotImplementedException();
+ }
}
}
diff --git a/OpenSim/Region/CoreModules/World/Terrain/FileLoaders/LLRAW.cs b/OpenSim/Region/CoreModules/World/Terrain/FileLoaders/LLRAW.cs
index d8f72d942d..3ec50cbe68 100644
--- a/OpenSim/Region/CoreModules/World/Terrain/FileLoaders/LLRAW.cs
+++ b/OpenSim/Region/CoreModules/World/Terrain/FileLoaders/LLRAW.cs
@@ -255,6 +255,11 @@ namespace OpenSim.Region.CoreModules.World.Terrain.FileLoaders
{
get { return ".raw"; }
}
+ public int SupportedHeight
+ {
+ get { return 4096; }
+ }
+
public virtual void SaveFile(ITerrainChannel m_channel, string filename,
int offsetX, int offsetY,
@@ -276,5 +281,15 @@ namespace OpenSim.Region.CoreModules.World.Terrain.FileLoaders
{
return false;
}
+
+ public bool SupportsExtendedTileSave()
+ {
+ return false;
+ }
+
+ public void SaveFile(ITerrainChannel map, string filename, int fileWidth, int fileHeight, int startX, int startY, int stopX, int stopY, int offsetX, int offsetY)
+ {
+ throw new NotImplementedException();
+ }
}
}
\ No newline at end of file
diff --git a/OpenSim/Region/CoreModules/World/Terrain/FileLoaders/PNG.cs b/OpenSim/Region/CoreModules/World/Terrain/FileLoaders/PNG.cs
index 8ea8e9d61d..f135d6b3d4 100644
--- a/OpenSim/Region/CoreModules/World/Terrain/FileLoaders/PNG.cs
+++ b/OpenSim/Region/CoreModules/World/Terrain/FileLoaders/PNG.cs
@@ -34,6 +34,11 @@ namespace OpenSim.Region.CoreModules.World.Terrain.FileLoaders
{
internal class PNG : GenericSystemDrawing
{
+ public override int SupportedHeight
+ {
+ get { return 256; }
+ }
+
public override void SaveFile(string filename, ITerrainChannel map)
{
using(Bitmap colours = CreateGrayscaleBitmapFromMap(map))
@@ -61,5 +66,10 @@ namespace OpenSim.Region.CoreModules.World.Terrain.FileLoaders
{
return true;
}
+
+ public override bool SupportsExtendedTileSave()
+ {
+ return false;
+ }
}
}
diff --git a/OpenSim/Region/CoreModules/World/Terrain/FileLoaders/RAW32.cs b/OpenSim/Region/CoreModules/World/Terrain/FileLoaders/RAW32.cs
index d467abb80a..1f99f13721 100644
--- a/OpenSim/Region/CoreModules/World/Terrain/FileLoaders/RAW32.cs
+++ b/OpenSim/Region/CoreModules/World/Terrain/FileLoaders/RAW32.cs
@@ -43,6 +43,11 @@ namespace OpenSim.Region.CoreModules.World.Terrain.FileLoaders
get { return ".r32"; }
}
+ public int SupportedHeight
+ {
+ get { return 4096; }
+ }
+
public ITerrainChannel LoadFile(string filename)
{
FileInfo file = new FileInfo(filename);
@@ -190,5 +195,15 @@ namespace OpenSim.Region.CoreModules.World.Terrain.FileLoaders
{
return false;
}
+
+ public bool SupportsExtendedTileSave()
+ {
+ return false;
+ }
+
+ public void SaveFile(ITerrainChannel map, string filename, int fileWidth, int fileHeight, int startX, int startY, int stopX, int stopY, int offsetX, int offsetY)
+ {
+ throw new NotImplementedException();
+ }
}
}
diff --git a/OpenSim/Region/CoreModules/World/Terrain/FileLoaders/TIFF.cs b/OpenSim/Region/CoreModules/World/Terrain/FileLoaders/TIFF.cs
index d103a6fd1e..1327ef5d98 100644
--- a/OpenSim/Region/CoreModules/World/Terrain/FileLoaders/TIFF.cs
+++ b/OpenSim/Region/CoreModules/World/Terrain/FileLoaders/TIFF.cs
@@ -25,32 +25,23 @@
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
+using System;
+using System.Collections.Generic;
using System.Drawing;
-using System.Drawing.Imaging;
using System.IO;
+using System.Linq;
+using OpenMetaverse.ImportExport.Collada14;
using OpenSim.Region.Framework.Interfaces;
+using OpenSim.Region.Framework.Scenes;
namespace OpenSim.Region.CoreModules.World.Terrain.FileLoaders
{
internal class TIFF : GenericSystemDrawing
{
- public override void SaveFile(string filename, ITerrainChannel map)
+ public override int SupportedHeight
{
- using(Bitmap colours = CreateGrayscaleBitmapFromMap(map))
- colours.Save(filename,ImageFormat.Tiff);
+ get { return 4096; }
}
-
- ///
- /// Exports a stream using a System.Drawing exporter.
- ///
- /// The target stream
- /// The terrain channel being saved
- public override void SaveStream(Stream stream, ITerrainChannel map)
- {
- using(Bitmap colours = CreateGrayscaleBitmapFromMap(map))
- colours.Save(stream,ImageFormat.Tiff);
- }
-
public override string ToString()
{
return "TIFF";
@@ -59,7 +50,926 @@ namespace OpenSim.Region.CoreModules.World.Terrain.FileLoaders
//Returns true if this extension is supported for terrain save-tile
public override bool SupportsTileSave()
{
- return false;
+ return true;
}
+
+ public override bool SupportsExtendedTileSave()
+ {
+ return true;
+ }
+
+ public override ITerrainChannel LoadFile(string filename)
+ {
+ return LoadTIFF(filename, out int _, null, null);
+ }
+
+ public override ITerrainChannel LoadFile(string filename, int x, int y, int fileWidth, int fileHeight, int w, int h)
+ {
+ return LoadTIFF(filename, out int _, null, [x, y, fileWidth, fileHeight, w, h]);
+ }
+
+ public override ITerrainChannel LoadStream(Stream stream)
+ {
+ return LoadTIFF(String.Empty, out int _, stream, null);
+ }
+
+ public override void SaveFile(string filename, ITerrainChannel map)
+ {
+ // From now saving in 32 bit format only
+ FileStream fs = new(filename, FileMode.Create);
+ CreateFile32Bit(fs, map);
+ }
+
+ // Saves existing terrain to larger or smaller file with meter based coordinates and offsets
+ public override void SaveFile(ITerrainChannel map, string filename, int fileWidth, int fileHeight, int startX, int startY, int stopX, int stopY, int offsetX, int offsetY)
+ {
+ if (map is null)
+ {
+ Console.WriteLine("Existing terrain not found");
+ return;
+ }
+
+ if (startX >= map.Width || stopX >= map.Width || startY >= map.Height || stopY >= map.Height)
+ {
+ Console.WriteLine("Map coordinates outside of region");
+ return;
+ }
+
+ if (offsetX >= fileWidth || offsetY >= fileHeight || startX + offsetX >= fileWidth || stopX + offsetX >= fileWidth || startY + offsetY >= fileHeight || stopY + offsetY >= fileHeight)
+ {
+ Console.WriteLine("Save target coordinates outside of file");
+ return;
+ }
+
+ string tempName = null;
+ int bitsPerPixel = 16;
+ ITerrainChannel existingBitmap = null;
+
+ if (File.Exists(filename))
+ {
+ try
+ {
+ tempName = Path.GetTempFileName();
+ File.Copy(filename, tempName, true);
+ Console.WriteLine("Using existing file " + tempName);
+ ITerrainChannel loaded = LoadTIFF(tempName, out bitsPerPixel, null, null);
+
+ if (loaded != null)
+ {
+ existingBitmap = loaded;
+ }
+ }
+ catch (Exception e)
+ {
+ Console.WriteLine("Unable to copy temp file " + e.ToString());
+ return;
+ }
+ }
+
+ if (existingBitmap is null || !File.Exists(filename))
+ {
+ Console.WriteLine("Generating new image");
+ existingBitmap = new TerrainChannel(fileWidth, fileHeight);
+ }
+
+ if ((bitsPerPixel.Equals(8) || bitsPerPixel.Equals(16)) && existingBitmap is not null)
+ {
+ ITerrainChannel cutmap = new TerrainChannel(stopX - startX, stopY - startY);
+ // First cut map to size
+ for (int x = 0; x < cutmap.Width; x++)
+ {
+ for (int y = 0; y < cutmap.Height; y++)
+ {
+ cutmap[x, y] = map[startX + x, startY + y];
+ }
+ }
+
+ // Now insert it at the requested spot
+ for (int x = 0; x < existingBitmap.Width; x++)
+ {
+ for (int y = 0; y < existingBitmap.Height; y++)
+ {
+ if (x >= offsetX || y >= offsetY)
+ {
+ existingBitmap[x, y] = cutmap[(x - offsetX), (y - offsetY)];
+ }
+ }
+ }
+
+ Console.WriteLine(existingBitmap.Width + " " + existingBitmap.Height);
+
+ if (bitsPerPixel.Equals(32))
+ {
+ FileStream fs = new(filename, FileMode.Create);
+ CreateFile32Bit(fs, existingBitmap);
+ }
+ else if (bitsPerPixel.Equals(16))
+ {
+ FileStream fs = new(filename, FileMode.Create);
+ CreateFile16Bit(fs, existingBitmap);
+ }
+ else if (bitsPerPixel.Equals(8))
+ {
+ CreateFile8Bit(filename, existingBitmap);
+ }
+
+ if (tempName is not null)
+ {
+ if (File.Exists(tempName))
+ {
+ try
+ {
+ Console.WriteLine("Removing " + tempName);
+ File.Delete(tempName);
+ }
+ catch (Exception e)
+ {
+ Console.WriteLine("Could not remove temp file " + e.ToString());
+ return;
+ }
+ }
+ }
+ }
+ else
+ {
+ Console.WriteLine("Unrecognized color bit depth!");
+ return;
+ }
+
+ }
+
+ public override void SaveFile(ITerrainChannel map, string filename, int offsetX, int offsetY, int fileWidth, int fileHeight, int regionSizeX, int regionSizeY)
+ {
+ // We need to do this because:
+ // "Saving the image to the same file it was constructed from is not allowed and throws an exception."
+ string tempName = null;
+
+ ITerrainChannel existingBitmap = null;
+
+ // Assume 16 bit from now on
+ int bitsPerPixel = 16;
+
+ // Load existing file or create a new heightmap
+ if (File.Exists(filename))
+ {
+ try
+ {
+ tempName = Path.GetTempFileName();
+ File.Copy(filename, tempName, true);
+ Console.WriteLine("Using existing file " + tempName);
+ ITerrainChannel loaded = LoadTIFF(tempName, out bitsPerPixel, null, null);
+
+ if (loaded != null)
+ {
+ if (loaded.Width.Equals(fileWidth * regionSizeX) && loaded.Height.Equals(fileHeight * regionSizeY))
+ {
+ existingBitmap = loaded;
+ }
+ }
+ }
+ catch (Exception e)
+ {
+ Console.WriteLine("Unable to copy temp file " + e.ToString());
+ return;
+ }
+ }
+
+ if (existingBitmap is null || !File.Exists(filename))
+ {
+ Console.WriteLine("Generating new image");
+ existingBitmap = new TerrainChannel(fileWidth * regionSizeX, fileHeight * regionSizeY);
+ }
+
+ if ((bitsPerPixel.Equals(8) || bitsPerPixel.Equals(16)) && existingBitmap is not null)
+ {
+ // Go over the whole thing and if the selected offsets match place the data there
+ for (int x = 0; x < existingBitmap.Width; x++)
+ {
+ for (int y = 0; y < existingBitmap.Height; y++)
+ {
+ if (x >= (offsetX * regionSizeX) && x <= ((offsetX * regionSizeX) + map.Width)
+ && y >= (offsetY * regionSizeY) && y <= ((offsetY * regionSizeY) + map.Height))
+ {
+ existingBitmap[x, y] = map[x - (offsetX * regionSizeX), y - (offsetY * regionSizeY)];
+ }
+ }
+ }
+
+ Console.WriteLine(existingBitmap.Width + " " + existingBitmap.Height);
+
+ if (bitsPerPixel.Equals(32))
+ {
+ FileStream fs = new(filename, FileMode.Create);
+ CreateFile32Bit(fs, existingBitmap);
+ }
+ else if (bitsPerPixel.Equals(16))
+ {
+ FileStream fs = new(filename, FileMode.Create);
+ CreateFile16Bit(fs, existingBitmap);
+ }
+ else if (bitsPerPixel.Equals(8))
+ {
+ CreateFile8Bit(filename, existingBitmap);
+ }
+
+ if (tempName is not null)
+ {
+ if (File.Exists(tempName))
+ {
+ try
+ {
+ Console.WriteLine("Removing " + tempName);
+ File.Delete(tempName);
+ }
+ catch (Exception e)
+ {
+ Console.WriteLine("Could not remove temp file " + e.ToString());
+ return;
+ }
+ }
+ }
+ }
+ else
+ {
+ Console.WriteLine("Unrecognized color bit depth!");
+ return;
+ }
+ }
+
+
+ ///
+ /// Exports a stream to 32 bit tiff
+ ///
+ /// The target stream
+ /// The terrain channel being saved
+ public override void SaveStream(Stream stream, ITerrainChannel map)
+ {
+ // Switched to 32 bit from now on
+ CreateFile32Bit(stream, map);
+ }
+
+ protected static ITerrainChannel LoadTIFF(string filename, out int bitsPerPixel, Stream stream = null, int[] options = null)
+ {
+ bitsPerPixel = 0;
+ if (stream == null)
+ {
+ FileStream fs = new(filename, FileMode.Open, FileAccess.Read);
+ bitsPerPixel = GetColorDepth(fs);
+ //int bitsPerPixel = Image.GetPixelFormatSize(file.PixelFormat);
+ if (!bitsPerPixel.Equals(0))
+ {
+ if (bitsPerPixel.Equals(8))
+ {
+ if (options is not null)
+ return Load8BitBitmapTile(filename, options[0], options[1], options[2], options[3], options[4], options[5]);
+ return LoadFrom8BitStream(fs);
+ }
+ else if (bitsPerPixel.Equals(16))
+ {
+ if (options is not null)
+ return Load16BitBitmapTile(filename, options[0], options[1], options[2], options[3], options[4], options[5]);
+ return LoadFrom16BitStream(fs);
+ }
+ else if (bitsPerPixel.Equals(32))
+ {
+ if (options is not null)
+ return Load32BitBitmapTile(filename, options[0], options[1], options[2], options[3], options[4], options[5]);
+ return LoadFrom32BitStream(fs);
+ }
+ }
+ }
+ else
+ {
+ bitsPerPixel = GetColorDepth(stream);
+ //int bitsPerPixel = Image.GetPixelFormatSize(file.PixelFormat);
+ if (!bitsPerPixel.Equals(0))
+ {
+ if (bitsPerPixel.Equals(8))
+ return LoadFrom8BitStream(stream);
+ else if (bitsPerPixel.Equals(16))
+ return LoadFrom16BitStream(stream);
+ else if (bitsPerPixel.Equals(32))
+ return LoadFrom32BitStream(stream);
+ }
+ }
+ Console.WriteLine("Unrecognized color bit depth!");
+ return null;
+ }
+
+ protected static void CreateFile8Bit(string filename, ITerrainChannel map)
+ {
+ Bitmap newbitmap = CreateGrayscaleBitmapFromMap(map);
+ newbitmap.Save(filename);
+ }
+
+ protected static void CreateFile16Bit(Stream fs, ITerrainChannel map)
+ {
+ int width = map.Width;
+ int height = map.Height;
+
+ ushort bitsPerSample = 16;
+
+ // TIFF Header
+ fs.Write(BitConverter.GetBytes((ushort)0x4949), 0, 2); // Byte order: Little endian (II)
+ fs.Write(BitConverter.GetBytes((ushort)42), 0, 2); // TIFF magic number
+ fs.Write(BitConverter.GetBytes((uint)8), 0, 4); // Offset to first IFD (starts right after header)
+
+ // IFD (Image File Directory)
+ fs.Write(BitConverter.GetBytes((ushort)8), 0, 2); // Number of directory entries
+
+ long entriesStartOffset = fs.Position;
+ fs.Seek(8 * 12 + 4, SeekOrigin.Current); // Skip space for entries and next IFD offset
+
+ // Image Width tag
+ fs.Seek(entriesStartOffset, SeekOrigin.Begin);
+ WriteIfdEntry(fs, 256, 4, 1, (uint)width);
+
+ // Image Length tag
+ fs.Seek(entriesStartOffset + 12, SeekOrigin.Begin);
+ WriteIfdEntry(fs, 257, 4, 1, (uint)height);
+
+ // Bits per Sample tag
+ fs.Seek(entriesStartOffset + 24, SeekOrigin.Begin);
+ WriteIfdEntry(fs, 258, 3, 1, bitsPerSample);
+
+ // Compression tag
+ fs.Seek(entriesStartOffset + 36, SeekOrigin.Begin);
+ WriteIfdEntry(fs, 259, 3, 1, 1); // No compression (1)
+
+ // Photometric Interpretation tag
+ fs.Seek(entriesStartOffset + 48, SeekOrigin.Begin);
+ WriteIfdEntry(fs, 262, 3, 1, 1); // Black is zero
+
+ // Strip Offsets tag
+ fs.Seek(entriesStartOffset + 60, SeekOrigin.Begin);
+ long stripOffsetsPos = fs.Position;
+ WriteIfdEntry(fs, 273, 4, 1, 0); // Placeholder for strip offsets
+
+ // Rows per Strip tag
+ fs.Seek(entriesStartOffset + 72, SeekOrigin.Begin);
+ WriteIfdEntry(fs, 278, 4, 1, (uint)height);
+
+ // Strip Byte Counts tag
+ fs.Seek(entriesStartOffset + 84, SeekOrigin.Begin);
+ long stripByteCountsPos = fs.Position;
+ WriteIfdEntry(fs, 279, 4, 1, 0); // Placeholder for strip byte counts
+
+ // Next IFD offset
+ fs.Seek(entriesStartOffset + 96, SeekOrigin.Begin);
+ fs.Write(BitConverter.GetBytes((uint)0), 0, 4); // No next IFD
+
+ // Write Image Data
+ long imageDataOffset = fs.Position;
+ fs.Seek(stripOffsetsPos, SeekOrigin.Begin);
+ fs.Write(BitConverter.GetBytes((uint)imageDataOffset), 0, 4);
+ fs.Seek(stripByteCountsPos, SeekOrigin.Begin);
+ fs.Write(BitConverter.GetBytes((uint)(width * height * (bitsPerSample / 8))), 0, 4);
+
+ fs.Seek(imageDataOffset, SeekOrigin.Begin);
+
+ // Write grayscale image data (16-bit per pixel)
+ for (int y = 0; y < height; y++)
+ {
+ for (int x = 0; x < width; x++)
+ {
+ float terrainheight = map[x, y] + 256;
+ ushort pixelValue = (ushort)((int)Normalize(terrainheight));
+ fs.Write(BitConverter.GetBytes(pixelValue), 0, 2);
+ }
+ }
+ fs.Dispose();
+ }
+
+ /*protected static void CreateFile16Bit(Stream fs, ITerrainChannel map)
+ {
+ int width = map.Width;
+ int height = map.Height;
+
+ ushort bitsPerSample = 16;
+
+ BinaryWriter writer = new(fs);
+
+ // TIFF Header
+ writer.Write((ushort)0x4949); // Byte order: Little endian (II)
+ writer.Write((ushort)42); // TIFF magic number
+ writer.Write((uint)8); // Offset to first IFD (starts right after header)
+
+ // IFD (Image File Directory)
+ writer.Write((ushort)8); // Number of directory entries
+
+ long entriesStartOffset = fs.Position;
+ fs.Seek(8 * 12 + 4, SeekOrigin.Current); // Skip space for entries and next IFD offset
+
+ // Image Width tag
+ writer.Seek((int)entriesStartOffset, SeekOrigin.Begin);
+ writer.Write((ushort)256); // Tag ID
+ writer.Write((ushort)4); // Type: LONG
+ writer.Write((uint)1); // Count
+ writer.Write((uint)width); // Value
+
+ // Image Length tag
+ writer.Seek((int)entriesStartOffset + 12, SeekOrigin.Begin);
+ writer.Write((ushort)257); // Tag ID
+ writer.Write((ushort)4); // Type: LONG
+ writer.Write((uint)1); // Count
+ writer.Write((uint)height); // Value
+
+ // Bits per Sample tag
+ writer.Seek((int)entriesStartOffset + 24, SeekOrigin.Begin);
+ writer.Write((ushort)258); // Tag ID
+ writer.Write((ushort)3); // Type: SHORT
+ writer.Write((uint)1); // Count
+ writer.Write((uint)bitsPerSample); // Value
+
+ // Compression tag
+ writer.Seek((int)entriesStartOffset + 36, SeekOrigin.Begin);
+ writer.Write((ushort)259); // Tag ID
+ writer.Write((ushort)3); // Type: SHORT
+ writer.Write((uint)1); // Count
+ writer.Write((ushort)1); // No compression (1)
+
+ // Photometric Interpretation tag
+ writer.Seek((int)entriesStartOffset + 48, SeekOrigin.Begin);
+ writer.Write((ushort)262); // Tag ID
+ writer.Write((ushort)3); // Type: SHORT
+ writer.Write((uint)1); // Count
+ writer.Write((ushort)1); // Black is zero
+
+ // Strip Offsets tag
+ writer.Seek((int)entriesStartOffset + 60, SeekOrigin.Begin);
+ writer.Write((ushort)273); // Tag ID
+ writer.Write((ushort)4); // Type: LONG
+ writer.Write((uint)1); // Count
+ long stripOffsetsPos = fs.Position;
+ writer.Write((uint)0); // Placeholder for strip offsets
+
+ // Rows per Strip tag
+ writer.Seek((int)entriesStartOffset + 72, SeekOrigin.Begin);
+ writer.Write((ushort)278); // Tag ID
+ writer.Write((ushort)4); // Type: LONG
+ writer.Write((uint)1); // Count
+ writer.Write((uint)height); // Value
+
+ // Strip Byte Counts tag
+ writer.Seek((int)entriesStartOffset + 84, SeekOrigin.Begin);
+ writer.Write((ushort)279); // Tag ID
+ writer.Write((ushort)4); // Type: LONG
+ writer.Write((uint)1); // Count
+ long stripByteCountsPos = fs.Position;
+ writer.Write((uint)0); // Placeholder for strip byte counts
+
+ // Next IFD offset
+ writer.Seek((int)entriesStartOffset + 96, SeekOrigin.Begin);
+ writer.Write((uint)0); // No next IFD
+
+ // Write Image Data
+ long imageDataOffset = fs.Position;
+ writer.Seek((int)stripOffsetsPos, SeekOrigin.Begin);
+ writer.Write((uint)imageDataOffset);
+ writer.Seek((int)stripByteCountsPos, SeekOrigin.Begin);
+ writer.Write((uint)(width * height * (bitsPerSample / 8)));
+
+ writer.Seek((int)imageDataOffset, SeekOrigin.Begin);
+
+ // Write grayscale image data (16-bit per pixel)
+ for (int y = 0; y < height; y++)
+ {
+ for (int x = 0; x < width; x++)
+ {
+ float terrainheight = map[x, y] + 256;
+ ushort pixelValue = (ushort)((int)Normalize(terrainheight));
+ writer.Write(pixelValue);
+ }
+ }
+ fs.Dispose();
+ }*/
+
+ protected static void CreateFile32Bit(Stream fs, ITerrainChannel map)
+ {
+ Console.WriteLine("Saving 32 bit...");
+
+ // Prepare TIFF header
+ byte[] tiffHeader = new byte[8] { 0x49, 0x49, 0x2A, 0x00, 0x08, 0x00, 0x00, 0x00 };
+ fs.Write(tiffHeader, 0, 8);
+
+ // Number of IFD entries
+ ushort numEntries = 13;
+ fs.Write(BitConverter.GetBytes(numEntries), 0, 2);
+
+ // Calculate offsets
+ uint resolutionOffset = (uint)(8 + 2 + numEntries * 12 + 4);
+ uint stripOffset = resolutionOffset + 16; // 16 bytes for XResolution and YResolution
+ uint stripByteCount = (uint)(map.Width * map.Height * 4);
+
+ // Write IFD entries
+ WriteIfdEntry(fs, 0x0100, 4, 1, (uint)map.Width); // ImageWidth
+ WriteIfdEntry(fs, 0x0101, 4, 1, (uint)map.Height); // ImageLength
+ WriteIfdEntry(fs, 0x0102, 3, 1, 32); // BitsPerSample (32 bits per sample)
+ WriteIfdEntry(fs, 0x0103, 3, 1, 1); // Compression (1 = No compression)
+ WriteIfdEntry(fs, 0x0106, 3, 1, 1); // Photometric Interpretation (1 = BlackIsZero)
+ WriteIfdEntry(fs, 0x0111, 4, 1, stripOffset); // StripOffsets
+ WriteIfdEntry(fs, 0x0115, 3, 1, 1); // SamplesPerPixel (1 = grayscale)
+ WriteIfdEntry(fs, 0x0116, 4, 1, (uint)map.Height); // RowsPerStrip (same as image height)
+ WriteIfdEntry(fs, 0x0117, 4, 1, stripByteCount); // StripByteCounts
+ WriteIfdEntry(fs, 0x011A, 5, 1, resolutionOffset); // XResolution (offset to resolution data)
+ WriteIfdEntry(fs, 0x011B, 5, 1, resolutionOffset + 8); // YResolution (offset to resolution data)
+ WriteIfdEntry(fs, 0x0128, 3, 1, 2); // ResolutionUnit (2 = inch)
+ WriteIfdEntry(fs, 0x0153, 3, 1, 3); // SampleFormat (3 = Floating Point)
+
+ // Offset to next IFD (0 = no next IFD)
+ fs.Write(BitConverter.GetBytes(0u), 0, 4);
+
+ // Write resolution data (XResolution and YResolution)
+ WriteRational(fs, (uint)map.Width, 1);
+ WriteRational(fs, (uint)map.Height, 1);
+
+ // Write pixel data
+ for (int y = 0; y < map.Height; y++)
+ {
+ for (int x = 0; x < map.Width; x++)
+ {
+ float terrainheight = map[x, y] + 256.0f;
+ float pixelValue = Normalize(terrainheight);
+ //Console.Write(pixelValue.ToString() + " ");
+ byte[] pixelBytes = BitConverter.GetBytes(pixelValue);
+ fs.Write(pixelBytes, 0, pixelBytes.Length);
+ }
+ }
+ fs.Dispose();
+ }
+
+ protected static ITerrainChannel LoadFrom8BitStream(Stream stream)
+ {
+ Bitmap bitmap = new(stream);
+ ITerrainChannel retval = new TerrainChannel(bitmap.Width, bitmap.Height);
+
+ for (int x = 0; x < bitmap.Width; x++)
+ {
+ for (int y = 0; y < bitmap.Height; y++)
+ {
+ retval[x, y] = bitmap.GetPixel(x, bitmap.Height - y - 1).GetBrightness() * 128;
+ }
+ }
+ return retval;
+ }
+
+ protected static ITerrainChannel LoadFrom16BitStream(Stream stream)
+ {
+ BinaryReader reader = new(stream);
+
+ // TIFF Header
+ ushort byteOrder = reader.ReadUInt16();
+ if (byteOrder != 0x4949) // Only handle little-endian (II)
+ throw new Exception("Unsupported byte order");
+
+ ushort magicNumber = reader.ReadUInt16();
+ if (magicNumber != 42)
+ throw new Exception("Invalid TIFF file");
+
+ uint ifdOffset = reader.ReadUInt32();
+ stream.Seek(ifdOffset, SeekOrigin.Begin);
+
+ // IFD (Image File Directory)
+ ushort numberOfEntries = reader.ReadUInt16();
+
+ Dictionary ifdEntries = [];
+
+ for (int i = 0; i < numberOfEntries; i++)
+ {
+ ushort tagID = reader.ReadUInt16();
+ ushort type = reader.ReadUInt16();
+ uint count = reader.ReadUInt32();
+ uint valueOffset = reader.ReadUInt32();
+ ifdEntries[tagID] = (type, count, valueOffset);
+ }
+
+ uint width = (ifdEntries.TryGetValue(256, out (ushort Type, uint Count, uint ValueOffset) out1)) ? out1.ValueOffset : 0;
+ uint height = (ifdEntries.TryGetValue(257, out (ushort Type, uint Count, uint ValueOffset) out2)) ? out2.ValueOffset : 0;
+ uint bitsPerSample = (ifdEntries.TryGetValue(258, out (ushort Type, uint Count, uint ValueOffset) out3)) ? out3.ValueOffset : 0;
+ uint stripOffsets = (ifdEntries.TryGetValue(273, out (ushort Type, uint Count, uint ValueOffset) out4)) ? out4.ValueOffset : 0;
+ uint stripByteCounts = (ifdEntries.TryGetValue(279, out (ushort Type, uint Count, uint ValueOffset) out5)) ? out5.ValueOffset : 0;
+
+ if (width == 0 || height == 0 || bitsPerSample != 16 || stripOffsets == 0 || stripByteCounts == 0)
+ throw new Exception("Invalid TIFF metadata");
+
+ // Calculate the size of the image data
+ long imageDataSize = width * height * (bitsPerSample / 8);
+
+ // Seek from the end of the file to the start of the image data
+ stream.Seek(-imageDataSize, SeekOrigin.End);
+
+ Console.WriteLine("Image " + width.ToString() + " " + height.ToString());
+
+ ITerrainChannel map = new TerrainChannel((int)width, (int)height);
+
+ // Convert heightmap values back to the original range
+ for (int x = 0; x < width; x++)
+ {
+ for (int y = 0; y < height; y++)
+ {
+ float heightvalue = InverseNormalize(reader.ReadUInt16()) - 255;
+ //Console.Write(heightvalue.ToString() + " ");
+ map[x, y] = heightvalue;
+ }
+ }
+ return map;
+ }
+
+ public static ITerrainChannel LoadFrom32BitStream(Stream fs)
+ {
+ ITerrainChannel map = null;
+ int width = 0;
+ int height = 0;
+
+ // Read TIFF header
+ byte[] tiffHeader = new byte[8];
+ fs.Read(tiffHeader, 0, 8);
+
+ if (tiffHeader[0] != 0x49 || tiffHeader[1] != 0x49 || tiffHeader[2] != 0x2A || tiffHeader[3] != 0x00)
+ throw new Exception("Not a valid little-endian TIFF file");
+
+ // Read the IFD (Image File Directory) offset
+ uint ifdOffset = BitConverter.ToUInt32(tiffHeader, 4);
+ fs.Seek(ifdOffset, SeekOrigin.Begin);
+
+ // Read the number of IFD entries
+ byte[] numEntriesBytes = new byte[2];
+ fs.Read(numEntriesBytes, 0, 2);
+ ushort numEntries = BitConverter.ToUInt16(numEntriesBytes, 0);
+
+ uint imageWidth = 0, imageHeight = 0, stripOffset = 0;
+ ushort bitsPerSample = 0, sampleFormat = 0;
+
+ // Read each IFD entry
+ for (int i = 0; i < numEntries; i++)
+ {
+ byte[] entry = new byte[12];
+ fs.Read(entry, 0, 12);
+
+ ushort tag = BitConverter.ToUInt16(entry, 0);
+ ushort type = BitConverter.ToUInt16(entry, 2);
+ uint count = BitConverter.ToUInt32(entry, 4);
+ uint valueOffset = BitConverter.ToUInt32(entry, 8);
+
+ switch (tag)
+ {
+ case 0x0100: // ImageWidth
+ imageWidth = valueOffset;
+ break;
+ case 0x0101: // ImageLength
+ imageHeight = valueOffset;
+ break;
+ case 0x0102: // BitsPerSample
+ bitsPerSample = (ushort)valueOffset;
+ break;
+ case 0x0111: // StripOffsets
+ stripOffset = valueOffset;
+ break;
+ case 0x0153: // SampleFormat
+ sampleFormat = (ushort)valueOffset;
+ break;
+ }
+ }
+
+ if (!imageWidth.Equals(0) && !imageHeight.Equals(0))
+ {
+ width = (int)imageWidth;
+ height = (int)imageHeight;
+
+ if (bitsPerSample != 32 || sampleFormat != 3)
+ throw new Exception("TIFF file is not a 32-bit floating-point grayscale image");
+
+ // Seek to the strip offset to read pixel data
+ fs.Seek(stripOffset, SeekOrigin.Begin);
+
+ map = new TerrainChannel(width, height);
+
+ // Read pixel data
+ for (int y = 0; y < imageHeight; y++)
+ {
+ for (int x = 0; x < imageWidth; x++)
+ {
+ byte[] pixelBytes = new byte[4];
+ fs.Read(pixelBytes, 0, 4);
+ float pixelValue = BitConverter.ToSingle(pixelBytes, 0);
+ map[x, y] = InverseNormalize(pixelValue) - 256;
+ }
+ }
+ }
+ fs.Dispose();
+ return map;
+ }
+
+ protected static ITerrainChannel Load8BitBitmapTile(string filename, int offsetX, int offsetY, int fileWidth, int fileHeight, int w, int h)
+ {
+ ITerrainChannel retval = new TerrainChannel(w, h);
+
+ string filePath = filename;
+
+ FileStream fs = new(filePath, FileMode.Open, FileAccess.Read);
+ ITerrainChannel map = LoadFrom8BitStream(fs);
+
+ for (int x = offsetX; x < map.Width && x < fileWidth; x++)
+ {
+ for (int y = offsetY; y < map.Height && y < fileHeight; y++)
+ {
+ retval[x, y] = map[x, y];
+ }
+ }
+
+ return retval;
+ }
+
+ protected static ITerrainChannel Load16BitBitmapTile(string filename, int offsetX, int offsetY, int fileWidth, int fileHeight, int w, int h)
+ {
+ ITerrainChannel retval = new TerrainChannel(w, h);
+
+ string filePath = filename;
+
+ FileStream fs = new(filePath, FileMode.Open, FileAccess.Read);
+ ITerrainChannel map = LoadFrom16BitStream(fs);
+
+ for (int x = offsetX; x < map.Width && x < fileWidth; x++)
+ {
+ for (int y = offsetY; y < map.Height && y < fileHeight; y++)
+ {
+ retval[x, y] = map[x, y];
+ }
+ }
+
+ return retval;
+ }
+
+ protected static ITerrainChannel Load32BitBitmapTile(string filename, int offsetX, int offsetY, int fileWidth, int fileHeight, int w, int h)
+ {
+ ITerrainChannel retval = new TerrainChannel(w, h);
+
+ string filePath = filename;
+
+ FileStream fs = new(filePath, FileMode.Open, FileAccess.Read);
+ ITerrainChannel map = LoadFrom32BitStream(fs);
+
+ for (int x = offsetX; x < map.Width && x < fileWidth; x++)
+ {
+ for (int y = offsetY; y < map.Height && y < fileHeight; y++)
+ {
+ retval[x, y] = map[x, y];
+ }
+ }
+
+ return retval;
+ }
+
+ #region Helpers
+
+ private static void WriteRational(Stream fs, uint numerator, uint denominator)
+ {
+ fs.Write(BitConverter.GetBytes(numerator), 0, 4);
+ fs.Write(BitConverter.GetBytes(denominator), 0, 4);
+ }
+
+ private static void WriteIfdEntry(Stream fs, ushort tag, ushort type, uint count, uint value)
+ {
+ fs.Write(BitConverter.GetBytes(tag), 0, 2);
+ fs.Write(BitConverter.GetBytes(type), 0, 2);
+ fs.Write(BitConverter.GetBytes(count), 0, 4);
+ fs.Write(BitConverter.GetBytes(value), 0, 4);
+ }
+
+ protected static float Normalize(float value)
+ {
+ float minHeight = 0;
+ float maxHeight = 4096;
+ return (value - minHeight) / (maxHeight - minHeight);
+ }
+
+ protected static float InverseNormalize(float value)
+ {
+ float minHeight = 0;
+ float maxHeight = 4096;
+ return minHeight + value * (maxHeight - minHeight);
+ }
+
+ protected static int GetColorDepth(Stream stream)
+ {
+ byte[] buffer = new byte[8];
+
+ // Read byte order mark (first 2 bytes)
+ stream.Read(buffer, 0, 2);
+ bool isLittleEndian = buffer[0] == 'I' && buffer[1] == 'I';
+
+ // Read TIFF magic number (next 2 bytes)
+ stream.Read(buffer, 0, 2);
+ ushort magicNumber = BitConverter.ToUInt16(isLittleEndian ? buffer : buffer.Reverse().ToArray(), 0);
+ if (magicNumber != 42)
+ {
+ Console.WriteLine("Not a valid TIFF file.");
+ stream.Position = 0;
+ return 0;
+ }
+
+ // Read offset to first IFD (next 4 bytes)
+ stream.Read(buffer, 0, 4);
+ uint ifdOffset = BitConverter.ToUInt32(isLittleEndian ? buffer : buffer.Reverse().ToArray(), 0);
+
+ // Move to the first IFD
+ stream.Seek(ifdOffset, SeekOrigin.Begin);
+
+ // Read number of directory entries (next 2 bytes)
+ stream.Read(buffer, 0, 2);
+ ushort numEntries = BitConverter.ToUInt16(isLittleEndian ? buffer : buffer.Reverse().ToArray(), 0);
+
+ buffer = new byte[12]; // Allocate buffer for IFD entries
+
+ for (int i = 0; i < numEntries; i++)
+ {
+ // Read each IFD entry (12 bytes each)
+ stream.Read(buffer, 0, 12);
+
+ // Extract the tag ID
+ ushort tagId = BitConverter.ToUInt16(isLittleEndian ? buffer : buffer.Take(2).Reverse().ToArray(), 0);
+
+ // Check for BitsPerSample tag (ID 258)
+ if (tagId == 258)
+ {
+ ushort type = BitConverter.ToUInt16(isLittleEndian ? buffer.Skip(2).Take(2).ToArray() : buffer.Skip(2).Take(2).Reverse().ToArray(), 0);
+ uint count = BitConverter.ToUInt32(isLittleEndian ? buffer.Skip(4).Take(4).ToArray() : buffer.Skip(4).Take(4).Reverse().ToArray(), 0);
+
+ // Read the value/offset
+ uint valueOffset = BitConverter.ToUInt32(isLittleEndian ? buffer.Skip(8).Take(4).ToArray() : buffer.Skip(8).Take(4).Reverse().ToArray(), 0);
+
+ if (count == 1)
+ {
+ ushort bitsPerSample = (ushort)valueOffset;
+ Console.WriteLine($"Color depth: {bitsPerSample} bits per sample");
+ if (bitsPerSample == 8)
+ {
+ Console.WriteLine("The image is 8-bit.");
+ stream.Position = 0;
+ return 8;
+ }
+ else if (bitsPerSample == 16)
+ {
+ Console.WriteLine("The image is 16-bit.");
+ stream.Position = 0;
+ return 16;
+ }
+ else if (bitsPerSample == 32)
+ {
+ Console.WriteLine("The image is 32-bit.");
+ stream.Position = 0;
+ return 32;
+ }
+ else
+ {
+ Console.WriteLine("The image is neither 8-bit nor 16-bit.");
+ stream.Position = 0;
+ return 0;
+ }
+ }
+ else
+ {
+ // Handle the case where BitsPerSample is an offset to an array
+ long currentPos = stream.Position;
+ stream.Seek(valueOffset, SeekOrigin.Begin);
+ List bits = new List();
+ for (int j = 0; j < count; j++)
+ {
+ stream.Read(buffer, 0, 2);
+ ushort bitsPerSample = BitConverter.ToUInt16(isLittleEndian ? buffer : buffer.Take(2).Reverse().ToArray(), 0);
+ Console.WriteLine($"Color depth: {bitsPerSample} bits per sample (channel {j + 1})");
+ bits.Add(bitsPerSample);
+ }
+ stream.Seek(currentPos, SeekOrigin.Begin);
+ if (bits.Any(b => b == 32))
+ {
+ stream.Position = 0;
+ return 32;
+ }
+ else if (bits.Any(b => b == 16))
+ {
+ stream.Position = 0;
+ return 16;
+ }
+ else if (bits.Any(b => b == 8))
+ {
+ stream.Position = 0;
+ return 8;
+ }
+ else
+ {
+ stream.Position = 0;
+ return 0;
+ }
+ }
+ }
+ }
+
+ Console.WriteLine("BitsPerSample tag not found.");
+ stream.Position = 0;
+ return 0;
+ }
+
+ #endregion
}
}
diff --git a/OpenSim/Region/CoreModules/World/Terrain/FileLoaders/Terragen.cs b/OpenSim/Region/CoreModules/World/Terrain/FileLoaders/Terragen.cs
index d61a09aaa6..01ba700c3b 100644
--- a/OpenSim/Region/CoreModules/World/Terrain/FileLoaders/Terragen.cs
+++ b/OpenSim/Region/CoreModules/World/Terrain/FileLoaders/Terragen.cs
@@ -43,6 +43,11 @@ namespace OpenSim.Region.CoreModules.World.Terrain.FileLoaders
{
#region ITerrainLoader Members
+ public int SupportedHeight
+ {
+ get { return 4096; }
+ }
+
public ITerrainChannel LoadFile(string filename)
{
FileInfo file = new FileInfo(filename);
@@ -326,6 +331,11 @@ namespace OpenSim.Region.CoreModules.World.Terrain.FileLoaders
return false;
}
+ public bool SupportsExtendedTileSave()
+ {
+ return false;
+ }
+
///
/// terragen SCAL floats need to be written intel ordered regardless of
/// big or little endian system
@@ -348,5 +358,9 @@ namespace OpenSim.Region.CoreModules.World.Terrain.FileLoaders
return retVal ;
}
+ public void SaveFile(ITerrainChannel map, string filename, int fileWidth, int fileHeight, int startX, int startY, int stopX, int stopY, int offsetX, int offsetY)
+ {
+ throw new NotImplementedException();
+ }
}
}
diff --git a/OpenSim/Region/CoreModules/World/Terrain/ITerrainLoader.cs b/OpenSim/Region/CoreModules/World/Terrain/ITerrainLoader.cs
index 855d1314d5..825b5a5ca1 100644
--- a/OpenSim/Region/CoreModules/World/Terrain/ITerrainLoader.cs
+++ b/OpenSim/Region/CoreModules/World/Terrain/ITerrainLoader.cs
@@ -35,8 +35,10 @@ namespace OpenSim.Region.CoreModules.World.Terrain
// Returns true if that extension can be used for terrain save-tile
// (Look into each file in Region.CoreModules.World.Terrain.FileLoaders)
bool SupportsTileSave();
+ bool SupportsExtendedTileSave();
string FileExtension { get; }
+ int SupportedHeight { get; }
ITerrainChannel LoadFile(string filename);
ITerrainChannel LoadFile(string filename, int fileStartX, int fileStartY, int fileWidth, int fileHeight, int sectionWidth, int sectionHeight);
ITerrainChannel LoadStream(Stream stream);
@@ -58,5 +60,25 @@ namespace OpenSim.Region.CoreModules.World.Terrain
/// The width of a map tile.
/// The height of a map tile.
void SaveFile(ITerrainChannel map, string filename, int offsetX, int offsetY, int fileWidth, int fileHeight, int regionSizeX, int regionSizeY);
+
+ ///
+ /// Save all or part of region to larger or smaller file
+ ///
+ ///
+ /// If the image file already exists then the tiles saved will replace those already in the file - other tiles
+ /// will be untouched.
+ ///
+ /// The terrain file to save
+ /// Width of the file to save to, overwritten by existing file
+ /// Height of the file to save to, overwritten by existing file
+ /// Start point in meters on x axis on existing terrain map
+ /// Start point in meters on y axis on existing terrain map
+ /// End point in meters on x axis on existing terrain map
+ /// End point in meters on y axis on existing terrain map
+ /// Offset start point in meters on x axis to write to file
+ /// Offset start point in meters on y axis to write to file
+ void SaveFile(ITerrainChannel map, string filename, int fileWidth, int fileHeight, int startX, int startY, int stopX, int stopY, int offsetX, int offsetY);
+
+
}
}
\ No newline at end of file
diff --git a/OpenSim/Region/CoreModules/World/Terrain/TerrainModule.cs b/OpenSim/Region/CoreModules/World/Terrain/TerrainModule.cs
index 6f32bc501d..eb3afc1a94 100755
--- a/OpenSim/Region/CoreModules/World/Terrain/TerrainModule.cs
+++ b/OpenSim/Region/CoreModules/World/Terrain/TerrainModule.cs
@@ -75,16 +75,14 @@ namespace OpenSim.Region.CoreModules.World.Terrain
private object _modifyLock = new();
#pragma warning disable 414
- private static readonly string LogHeader = "[TERRAIN MODULE]";
+ private static readonly string LogHeader = "[TERRAIN]: ";
#pragma warning restore 414
- private readonly Commander m_commander = new Commander("terrain");
- private readonly Dictionary m_loaders = new Dictionary();
- private readonly Dictionary m_floodeffects =
- new Dictionary();
- private readonly Dictionary m_painteffects =
- new Dictionary();
- private Dictionary m_modifyOperations = new Dictionary();
+ private readonly Commander m_commander = new("terrain");
+ private readonly Dictionary m_loaders = [];
+ private readonly Dictionary m_floodeffects = [];
+ private readonly Dictionary m_painteffects = [];
+ private Dictionary m_modifyOperations = [];
private Dictionary m_plugineffects;
private ITerrainChannel m_channel;
private ITerrainChannel m_baked;
@@ -208,15 +206,15 @@ namespace OpenSim.Region.CoreModules.World.Terrain
}
// The flags of which terrain patches to send for each of the ScenePresence's
- private Dictionary m_perClientPatchUpdates = new Dictionary();
+ private Dictionary m_perClientPatchUpdates = [];
///
/// Human readable list of terrain file extensions that are supported.
///
- private string m_supportedFileExtensions = "";
+ private string m_supportedFileExtensions = String.Empty;
//For terrain save-tile file extensions
- private string m_supportFileExtensionsForTileSave = "";
+ private string m_supportFileExtensionsForTileSave = String.Empty;
#region ICommandableModule Members
@@ -284,13 +282,23 @@ namespace OpenSim.Region.CoreModules.World.Terrain
LoadPlugins();
// Generate user-readable extensions list
- string supportedFilesSeparator = "";
- string supportedFilesSeparatorForTileSave = "";
+ string supportedFilesSeparator = String.Empty;
+ string supportedFilesSeparatorForTileSave = String.Empty;
m_supportFileExtensionsForTileSave = "";
- foreach(KeyValuePair loader in m_loaders)
+ foreach (KeyValuePair loader in m_loaders)
{
m_supportedFileExtensions += supportedFilesSeparator + loader.Key + " (" + loader.Value + ")";
+
+ if (loader.Value.SupportedHeight < 257)
+ {
+ m_supportedFileExtensions += " Terrain max altitude: 256";
+ }
+ else if (loader.Value.SupportedHeight > 256)
+ {
+ m_supportedFileExtensions += " Terrain max altitude: 4096";
+ }
+
supportedFilesSeparator = ", ";
//For terrain save-tile file extensions
@@ -358,7 +366,7 @@ namespace OpenSim.Region.CoreModules.World.Terrain
{
if (filename.EndsWith(loader.Key))
{
- lock(m_scene)
+ lock (m_scene)
{
try
{
@@ -369,37 +377,35 @@ namespace OpenSim.Region.CoreModules.World.Terrain
throw new ArgumentException(String.Format("wrong size, use a file with size {0} x {1}",
m_scene.RegionInfo.RegionSizeX, m_scene.RegionInfo.RegionSizeY));
}
- m_log.DebugFormat("[TERRAIN]: Loaded terrain, wd/ht: {0}/{1}", channel.Width, channel.Height);
+ m_log.DebugFormat(LogHeader + "Loaded terrain, wd/ht: {0}/{1}", channel.Width, channel.Height);
m_scene.Heightmap = channel;
m_channel = channel;
UpdateBakedMap();
}
- catch(NotImplementedException)
+ catch (NotImplementedException)
{
- m_log.Error("[TERRAIN]: Unable to load heightmap, the " + loader.Value +
- " parser does not support file loading. (May be save only)");
+ m_log.ErrorFormat(LogHeader + "Unable to load heightmap, the {0} parser does not support file loading. (May be save only)", loader.Value);
throw new TerrainException(String.Format("unable to load heightmap: parser {0} does not support loading", loader.Value));
}
- catch(FileNotFoundException)
+ catch (FileNotFoundException)
{
- m_log.Error(
- "[TERRAIN]: Unable to load heightmap, file not found. (A directory permissions error may also cause this)");
+ m_log.ErrorFormat(LogHeader + "Unable to load heightmap, file not found. (A directory permissions error may also cause this)");
throw new TerrainException(
String.Format("unable to load heightmap: file {0} not found (or permissions do not allow access", filename));
}
- catch(ArgumentException e)
+ catch (ArgumentException e)
{
- m_log.ErrorFormat("[TERRAIN]: Unable to load heightmap: {0}", e.Message);
+ m_log.ErrorFormat(LogHeader + "Unable to load heightmap: {0}", e.Message);
throw new TerrainException(
String.Format("Unable to load heightmap: {0}", e.Message));
}
}
- m_log.Info("[TERRAIN]: File (" + filename + ") loaded successfully");
+ m_log.InfoFormat(LogHeader + "File ({0}) loaded successfully", filename);
return;
}
}
- m_log.Error("[TERRAIN]: Unable to load heightmap, no file loader available for that format.");
+ m_log.Error(LogHeader + "Unable to load heightmap, no file loader available for that format.");
throw new TerrainException(String.Format("unable to load heightmap from file {0}: no loader available for that format", filename));
}
@@ -413,21 +419,20 @@ namespace OpenSim.Region.CoreModules.World.Terrain
{
foreach(KeyValuePair loader in m_loaders)
{
- if (filename.EndsWith(loader.Key))
+ if (filename.EndsWith(loader.Key) && loader.Value.SupportedHeight > m_channel.MaxHeight())
{
loader.Value.SaveFile(filename, m_channel);
- m_log.InfoFormat("[TERRAIN]: Saved terrain from {0} to {1}", m_scene.RegionInfo.RegionName, filename);
+ m_log.InfoFormat(LogHeader + "Saved terrain from {0} to {1}", m_scene.RegionInfo.RegionName, filename);
return;
}
}
}
catch(IOException ioe)
{
- m_log.Error(String.Format("[TERRAIN]: Unable to save to {0}, {1}", filename, ioe.Message));
+ m_log.ErrorFormat(LogHeader + "Unable to save to {0}, {1}", filename, ioe.Message);
}
- m_log.ErrorFormat(
- "[TERRAIN]: Could not save terrain from {0} to {1}. Valid file extensions are {2}",
+ m_log.ErrorFormat(LogHeader + "Could not save terrain from {0} to {1}. Valid file extensions are {2}",
m_scene.RegionInfo.RegionName, filename, m_supportedFileExtensions);
}
@@ -468,13 +473,12 @@ namespace OpenSim.Region.CoreModules.World.Terrain
}
catch(NotImplementedException)
{
- m_log.Error("[TERRAIN]: Unable to load heightmap, the " + loader.Value +
- " parser does not support file loading. (May be save only)");
+ m_log.ErrorFormat(LogHeader + "Unable to load heightmap, the {0} parser does not support file loading. (May be save only)", loader.Value);
throw new TerrainException(String.Format("unable to load heightmap: parser {0} does not support loading", loader.Value));
}
}
- m_log.Info("[TERRAIN]: File (" + filename + ") loaded successfully");
+ m_log.InfoFormat(LogHeader + "File ({0}) loaded successfully", filename);
return;
}
}
@@ -499,17 +503,16 @@ namespace OpenSim.Region.CoreModules.World.Terrain
}
catch (NotImplementedException)
{
- m_log.Error("[TERRAIN]: Unable to load heightmap, the " + loader.Value +
- " parser does not support file loading. (May be save only)");
+ m_log.ErrorFormat(LogHeader + "Unable to load heightmap, the {0} parser does not support file loading. (May be save only)", loader.Value);
throw new TerrainException(String.Format("unable to load heightmap: parser {0} does not support loading", loader.Value));
}
}
- m_log.Info("[TERRAIN]: File (" + filename + ") loaded successfully");
+ m_log.InfoFormat(LogHeader + "File ({0}) loaded successfully", filename);
return;
}
}
- m_log.Error("[TERRAIN]: Unable to load heightmap, no file loader available for that format.");
+ m_log.Error(LogHeader + "Unable to load heightmap, no file loader available for that format.");
throw new TerrainException(String.Format("unable to load heightmap from file {0}: no loader available for that format", filename));
}
@@ -585,7 +588,7 @@ namespace OpenSim.Region.CoreModules.World.Terrain
{
foreach(KeyValuePair loader in m_loaders)
{
- if (filename.EndsWith(loader.Key))
+ if (filename.EndsWith(loader.Key) && loader.Value.SupportedHeight > m_channel.MaxHeight())
{
loader.Value.SaveStream(stream, m_channel);
return;
@@ -594,7 +597,7 @@ namespace OpenSim.Region.CoreModules.World.Terrain
}
catch(NotImplementedException)
{
- m_log.Error("Unable to save to " + filename + ", saving of this file format has not been implemented.");
+ m_log.ErrorFormat(LogHeader + "Unable to save to {0}, saving of this file format has not been implemented.", filename);
throw new TerrainException(String.Format("Unable to save heightmap: saving of this file format not implemented"));
}
}
@@ -627,7 +630,7 @@ namespace OpenSim.Region.CoreModules.World.Terrain
if (!m_perClientPatchUpdates.TryGetValue(pClient.AgentId, out pups))
{
// There is a ScenePresence without a send patch map. Create one.
- pups = new PatchUpdates(m_scene.Heightmap.GetTerrainData(), presence);
+ pups = new(m_scene.Heightmap.GetTerrainData(), presence);
m_perClientPatchUpdates.Add(presence.UUID, pups);
}
else
@@ -646,7 +649,7 @@ namespace OpenSim.Region.CoreModules.World.Terrain
private void LoadPlugins()
{
- m_plugineffects = new Dictionary();
+ m_plugineffects = [];
LoadPlugins(Assembly.GetCallingAssembly());
string plugineffectsPath = "Terrain";
@@ -657,7 +660,7 @@ namespace OpenSim.Region.CoreModules.World.Terrain
string[] files = Directory.GetFiles(plugineffectsPath);
foreach(string file in files)
{
- m_log.Info("Loading effects in " + file);
+ m_log.InfoFormat(LogHeader + "Loading effects in {0}", file);
try
{
Assembly library = Assembly.LoadFrom(file);
@@ -844,7 +847,7 @@ namespace OpenSim.Region.CoreModules.World.Terrain
// this region is included in the tile request
foreach(KeyValuePair loader in m_loaders)
{
- if (filename.EndsWith(loader.Key) && loader.Value.SupportsTileSave())
+ if (filename.EndsWith(loader.Key) && loader.Value.SupportsTileSave() && loader.Value.SupportedHeight > m_channel.MaxHeight())
{
lock(m_scene)
{
@@ -868,6 +871,43 @@ namespace OpenSim.Region.CoreModules.World.Terrain
m_scene.RegionInfo.RegionName, filename, m_supportFileExtensionsForTileSave);
}
+ ///
+ /// Save all or part of region to larger or smaller file
+ ///
+ ///
+ /// If the image file already exists then the tiles saved will replace those already in the file - other tiles
+ /// will be untouched.
+ ///
+ /// The terrain file to save
+ /// Width of the file to save to, overwritten by existing file
+ /// Height of the file to save to, overwritten by existing file
+ /// Start point in meters on x axis on existing terrain map
+ /// Start point in meters on y axis on existing terrain map
+ /// End point in meters on x axis on existing terrain map
+ /// End point in meters on y axis on existing terrain map
+ /// Offset start point in meters on x axis to write to file
+ /// Offset start point in meters on y axis to write to file
+ public void SaveToFile(string filename, int fileWidth, int fileHeight, int startX, int startY, int stopX, int stopY, int offsetX, int offsetY)
+ {
+ foreach (KeyValuePair loader in m_loaders)
+ {
+ if (filename.EndsWith(loader.Key) && loader.Value.SupportsTileSave() && loader.Value.SupportsExtendedTileSave() && loader.Value.SupportedHeight > m_channel.MaxHeight())
+ {
+ lock (m_scene)
+ {
+ loader.Value.SaveFile(m_channel, filename, fileWidth, fileHeight, startX, startY, stopX, stopY, offsetX, offsetY);
+
+ MainConsole.Instance.Output(
+ "Saved terrain from ({0},{1}) to ({2},{3}) to {4} starting from ({5},{6})",
+ startX, startY, stopX, stopY, filename, offsetX, offsetY);
+ }
+ }
+ }
+
+ MainConsole.Instance.Output(
+ "ERROR: Could not save terrain from {0} to {1}. Valid file extensions are {2}",
+ m_scene.RegionInfo.RegionName, filename, m_supportFileExtensionsForTileSave);
+ }
///
/// This is used to check to see of any of the terrain is tainted and, if so, schedule
@@ -885,7 +925,7 @@ namespace OpenSim.Region.CoreModules.World.Terrain
}
}
- object TerrainCheckUpdatesLock = new object();
+ object TerrainCheckUpdatesLock = new();
private void EventManager_TerrainCheckUpdatesAsync(object o)
{
@@ -952,7 +992,7 @@ namespace OpenSim.Region.CoreModules.World.Terrain
{
if (args.Length == 1)
{
- m_commander.ProcessConsoleCommand("help", new string[0]);
+ m_commander.ProcessConsoleCommand("help", []);
return;
}
@@ -1125,7 +1165,7 @@ namespace OpenSim.Region.CoreModules.World.Terrain
int sx = terrData.SizeX / Constants.TerrainPatchSize;
int py = patchIndex / sx;
int px = patchIndex - py * sx;
- int[] map = new int[]{px, py};
+ int[] map = [px, py];
m_scene.ForEachClient(
delegate (IClientAPI controller)
{
@@ -1201,7 +1241,7 @@ namespace OpenSim.Region.CoreModules.World.Terrain
next = 0;
int npatchs = 0;
- List patchs = new List(128);
+ List patchs = new(128);
while ((next = pups.taints.GetAndClearNextTrue(next)) >= 0)
{
patchs.Add(next);
@@ -1237,7 +1277,7 @@ namespace OpenSim.Region.CoreModules.World.Terrain
private List GetModifiedPatchesInViewDistance(PatchUpdates pups)
{
- List ret = new List();
+ List ret = [];
if (!pups.HasUpdates())
return ret;
@@ -1518,7 +1558,7 @@ namespace OpenSim.Region.CoreModules.World.Terrain
{
//m_log.Debug("Terrain packet unacked, resending patch: " + patchX + " , " + patchY);
// SendLayerData does not use the heightmap parameter. This kludge is so as to not change IClientAPI.
- client.SendLayerData(new int[]{patchX, patchY});
+ client.SendLayerData([patchX, patchY]);
}
private void StoreUndoState()
@@ -1555,6 +1595,19 @@ namespace OpenSim.Region.CoreModules.World.Terrain
(int)args[4]);
}
+ private void InterfaceSaveTileFileExtended(Object[] args)
+ {
+ SaveToFile((string)args[0],
+ (int)args[1],
+ (int)args[2],
+ (int)args[3],
+ (int)args[4],
+ (int)args[5],
+ (int)args[6],
+ (int)args[7],
+ (int)args[8]);
+ }
+
private void InterfaceBakeTerrain(Object[] args)
{
UpdateBakedMap();
@@ -1573,7 +1626,7 @@ namespace OpenSim.Region.CoreModules.World.Terrain
{
String direction = (String)args[0];
- if (direction.ToLower().StartsWith("y"))
+ if (direction.StartsWith("y", StringComparison.CurrentCultureIgnoreCase))
{
for (int x = 0; x < m_channel.Width; x++)
{
@@ -1619,7 +1672,7 @@ namespace OpenSim.Region.CoreModules.World.Terrain
if (desiredRange == 0d)
{
// delta is zero so flatten at requested height
- InterfaceFillTerrain(new Object[] { args[1] });
+ InterfaceFillTerrain([args[1]]);
}
else
{
@@ -1733,9 +1786,7 @@ namespace OpenSim.Region.CoreModules.World.Terrain
private void InterfaceShow(Object[] args)
{
- Vector2 point;
-
- if (!ConsoleUtil.TryParseConsole2DVector((string)args[0], null, out point))
+ if (!ConsoleUtil.TryParseConsole2DVector((string)args[0], null, out Vector2 point))
{
Console.WriteLine("ERROR: {0} is not a valid vector", args[0]);
return;
@@ -1792,9 +1843,9 @@ namespace OpenSim.Region.CoreModules.World.Terrain
return;
}
- if (m_plugineffects.ContainsKey(firstArg))
+ if (m_plugineffects.TryGetValue(firstArg, out ITerrainEffect value))
{
- m_plugineffects[firstArg].RunEffect(m_channel);
+ value.RunEffect(m_channel);
}
else
{
@@ -1805,19 +1856,19 @@ namespace OpenSim.Region.CoreModules.World.Terrain
private void InstallInterfaces()
{
Command loadFromFileCommand =
- new Command("load", CommandIntentions.COMMAND_HAZARDOUS, InterfaceLoadFile, "Loads a terrain from a specified file.");
+ new("load", CommandIntentions.COMMAND_HAZARDOUS, InterfaceLoadFile, "Loads a terrain from a specified file.");
loadFromFileCommand.AddArgument("filename",
"The file you wish to load from, the file extension determines the loader to be used. Supported extensions include: " +
m_supportedFileExtensions, "String");
Command saveToFileCommand =
- new Command("save", CommandIntentions.COMMAND_NON_HAZARDOUS, InterfaceSaveFile, "Saves the current heightmap to a specified file.");
+ new("save", CommandIntentions.COMMAND_NON_HAZARDOUS, InterfaceSaveFile, "Saves the current heightmap to a specified file.");
saveToFileCommand.AddArgument("filename",
"The destination filename for your heightmap, the file extension determines the format to save in. Supported extensions include: " +
m_supportedFileExtensions, "String");
Command loadFromTileCommand =
- new Command("load-tile", CommandIntentions.COMMAND_HAZARDOUS, InterfaceLoadTileFile, "Loads a terrain from a section of a larger file.");
+ new("load-tile", CommandIntentions.COMMAND_HAZARDOUS, InterfaceLoadTileFile, "Loads a terrain from a section of a larger file.");
loadFromTileCommand.AddArgument("filename",
"The file you wish to load from, the file extension determines the loader to be used. Supported extensions include: " +
m_supportedFileExtensions, "String");
@@ -1829,7 +1880,7 @@ namespace OpenSim.Region.CoreModules.World.Terrain
"Integer");
Command saveToTileCommand =
- new Command("save-tile", CommandIntentions.COMMAND_HAZARDOUS, InterfaceSaveTileFile, "Saves the current heightmap to the larger file.");
+ new("save-tile", CommandIntentions.COMMAND_HAZARDOUS, InterfaceSaveTileFile, "Saves the current heightmap to the larger file.");
saveToTileCommand.AddArgument("filename",
"The file you wish to save to, the file extension determines the loader to be used. Supported extensions include: " +
m_supportFileExtensionsForTileSave, "String");
@@ -1843,64 +1894,80 @@ namespace OpenSim.Region.CoreModules.World.Terrain
+ " # terrain save-tile ST06.png 2 3 9910 10234\n",
"Integer");
+ Command saveToTileExtendedCommand =
+ new("save-tile-ext", CommandIntentions.COMMAND_HAZARDOUS, InterfaceSaveTileFileExtended, "Saves the current heightmap to a larger or smaller file.");
+ saveToTileExtendedCommand.AddArgument("filename",
+ "The file you wish to save to, the file extension determines the loader to be used. Supported extensions include: " +
+ m_supportFileExtensionsForTileSave, "String");
+ saveToTileExtendedCommand.AddArgument("file width", "The width of the file in tiles", "Integer");
+ saveToTileExtendedCommand.AddArgument("file height", "The height of the file in tiles", "Integer");
+ saveToTileExtendedCommand.AddArgument("start X", "Start point in meters on x axis on existing terrain map", "Integer");
+ saveToTileExtendedCommand.AddArgument("start Y", "Start point in meters on y axis on existing terrain map", "Integer");
+ saveToTileExtendedCommand.AddArgument("stop X", "End point in meters on x axis on existing terrain map", "Integer");
+ saveToTileExtendedCommand.AddArgument("stop Y", "End point in meters on y axis on existing terrain map", "Integer");
+ saveToTileExtendedCommand.AddArgument("offset X", "Offset start point in meters on x axis to write to file", "Integer");
+ saveToTileExtendedCommand.AddArgument("offset Y", "Offset start point in meters on y axis to write to file", "Integer");
+
+
// Terrain adjustments
Command fillRegionCommand =
- new Command("fill", CommandIntentions.COMMAND_HAZARDOUS, InterfaceFillTerrain, "Fills the current heightmap with a specified value.");
+ new("fill", CommandIntentions.COMMAND_HAZARDOUS, InterfaceFillTerrain, "Fills the current heightmap with a specified value.");
fillRegionCommand.AddArgument("value", "The numeric value of the height you wish to set your region to.",
"Float");
Command elevateCommand =
- new Command("elevate", CommandIntentions.COMMAND_HAZARDOUS, InterfaceElevateTerrain, "Raises the current heightmap by the specified amount.");
+ new("elevate", CommandIntentions.COMMAND_HAZARDOUS, InterfaceElevateTerrain, "Raises the current heightmap by the specified amount.");
elevateCommand.AddArgument("amount", "The amount of height to add to the terrain in meters.", "Float");
Command lowerCommand =
- new Command("lower", CommandIntentions.COMMAND_HAZARDOUS, InterfaceLowerTerrain, "Lowers the current heightmap by the specified amount.");
+ new("lower", CommandIntentions.COMMAND_HAZARDOUS, InterfaceLowerTerrain, "Lowers the current heightmap by the specified amount.");
lowerCommand.AddArgument("amount", "The amount of height to remove from the terrain in meters.", "Float");
Command multiplyCommand =
- new Command("multiply", CommandIntentions.COMMAND_HAZARDOUS, InterfaceMultiplyTerrain, "Multiplies the heightmap by the value specified.");
+ new("multiply", CommandIntentions.COMMAND_HAZARDOUS, InterfaceMultiplyTerrain, "Multiplies the heightmap by the value specified.");
multiplyCommand.AddArgument("value", "The value to multiply the heightmap by.", "Float");
Command bakeRegionCommand =
- new Command("bake", CommandIntentions.COMMAND_HAZARDOUS, InterfaceBakeTerrain, "Saves the current terrain into the regions baked map.");
+ new("bake", CommandIntentions.COMMAND_HAZARDOUS, InterfaceBakeTerrain, "Saves the current terrain into the regions baked map.");
Command revertRegionCommand =
- new Command("revert", CommandIntentions.COMMAND_HAZARDOUS, InterfaceRevertTerrain, "Loads the baked map terrain into the regions heightmap.");
+ new("revert", CommandIntentions.COMMAND_HAZARDOUS, InterfaceRevertTerrain, "Loads the baked map terrain into the regions heightmap.");
Command flipCommand =
- new Command("flip", CommandIntentions.COMMAND_HAZARDOUS, InterfaceFlipTerrain, "Flips the current terrain about the X or Y axis");
+ new("flip", CommandIntentions.COMMAND_HAZARDOUS, InterfaceFlipTerrain, "Flips the current terrain about the X or Y axis");
flipCommand.AddArgument("direction", "[x|y] the direction to flip the terrain in", "String");
Command rescaleCommand =
- new Command("rescale", CommandIntentions.COMMAND_HAZARDOUS, InterfaceRescaleTerrain, "Rescales the current terrain to fit between the given min and max heights");
+ new("rescale", CommandIntentions.COMMAND_HAZARDOUS, InterfaceRescaleTerrain, "Rescales the current terrain to fit between the given min and max heights");
rescaleCommand.AddArgument("min", "min terrain height after rescaling", "Float");
rescaleCommand.AddArgument("max", "max terrain height after rescaling", "Float");
- Command minCommand = new Command("min", CommandIntentions.COMMAND_HAZARDOUS, InterfaceMinTerrain, "Sets the minimum terrain height to the specified value.");
+ Command minCommand = new("min", CommandIntentions.COMMAND_HAZARDOUS, InterfaceMinTerrain, "Sets the minimum terrain height to the specified value.");
minCommand.AddArgument("min", "terrain height to use as minimum", "Float");
- Command maxCommand = new Command("max", CommandIntentions.COMMAND_HAZARDOUS, InterfaceMaxTerrain, "Sets the maximum terrain height to the specified value.");
+ Command maxCommand = new("max", CommandIntentions.COMMAND_HAZARDOUS, InterfaceMaxTerrain, "Sets the maximum terrain height to the specified value.");
maxCommand.AddArgument("min", "terrain height to use as maximum", "Float");
// Debug
Command showDebugStatsCommand =
- new Command("stats", CommandIntentions.COMMAND_STATISTICAL, InterfaceShowDebugStats,
+ new("stats", CommandIntentions.COMMAND_STATISTICAL, InterfaceShowDebugStats,
"Shows some information about the regions heightmap for debugging purposes.");
Command showCommand =
- new Command("show", CommandIntentions.COMMAND_NON_HAZARDOUS, InterfaceShow,
+ new("show", CommandIntentions.COMMAND_NON_HAZARDOUS, InterfaceShow,
"Shows terrain height at a given co-ordinate.");
showCommand.AddArgument("point", "point in , format with no spaces (e.g. 45,45)", "String");
// Plugins
Command pluginRunCommand =
- new Command("effect", CommandIntentions.COMMAND_HAZARDOUS, InterfaceRunPluginEffect, "Runs a specified plugin effect");
+ new("effect", CommandIntentions.COMMAND_HAZARDOUS, InterfaceRunPluginEffect, "Runs a specified plugin effect");
pluginRunCommand.AddArgument("name", "The plugin effect you wish to run, or 'list' to see all plugins", "String");
m_commander.RegisterCommand("load", loadFromFileCommand);
m_commander.RegisterCommand("load-tile", loadFromTileCommand);
m_commander.RegisterCommand("save", saveToFileCommand);
m_commander.RegisterCommand("save-tile", saveToTileCommand);
+ m_commander.RegisterCommand("save-file-ext", saveToTileExtendedCommand);
m_commander.RegisterCommand("fill", fillRegionCommand);
m_commander.RegisterCommand("elevate", elevateCommand);
m_commander.RegisterCommand("lower", lowerCommand);
@@ -1945,9 +2012,7 @@ namespace OpenSim.Region.CoreModules.World.Terrain
{
string operationType = cmd[2];
-
- ITerrainModifier operation;
- if (!m_modifyOperations.TryGetValue(operationType, out operation))
+ if (!m_modifyOperations.TryGetValue(operationType, out ITerrainModifier operation))
{
result = String.Format("Terrain Modify \"{0}\" not found.", operationType);
}
diff --git a/OpenSim/Region/Framework/Interfaces/ITerrainChannel.cs b/OpenSim/Region/Framework/Interfaces/ITerrainChannel.cs
index d5073589f2..3232b619e9 100644
--- a/OpenSim/Region/Framework/Interfaces/ITerrainChannel.cs
+++ b/OpenSim/Region/Framework/Interfaces/ITerrainChannel.cs
@@ -68,5 +68,6 @@ namespace OpenSim.Region.Framework.Interfaces
/// <x, y>
/// <x, y>
void MergeWithBounding(ITerrainChannel newTerrain, Vector3 displacement, float rotationDegrees, Vector2 boundingOrigin, Vector2 boundingSize);
+ int MaxHeight();
}
}
diff --git a/OpenSim/Region/Framework/Scenes/TerrainChannel.cs b/OpenSim/Region/Framework/Scenes/TerrainChannel.cs
index fc404c2d9c..4ac97d9c8a 100644
--- a/OpenSim/Region/Framework/Scenes/TerrainChannel.cs
+++ b/OpenSim/Region/Framework/Scenes/TerrainChannel.cs
@@ -48,7 +48,7 @@ namespace OpenSim.Region.Framework.Scenes
public class TerrainChannel : ITerrainChannel
{
private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
- private static string LogHeader = "[TERRAIN CHANNEL]";
+ private static string LogHeader = "[TERRAIN CHANNEL]: ";
protected TerrainData m_terrainData;
@@ -175,12 +175,27 @@ namespace OpenSim.Region.Framework.Scenes
return m_terrainData.IsTaintedAt(x, y);
}
+ public int MaxHeight()
+ {
+ float max = 0;
+ for (int ii = 0; ii < Width; ii++)
+ {
+ for (int jj = 0; jj < Height; jj++)
+ {
+ max = (m_terrainData[ii, jj] > max) ? m_terrainData[ii, jj] : max;
+ }
+ }
+ return (int)max + 1;
+ }
+
// ITerrainChannel.SaveToXmlString()
public string SaveToXmlString()
{
- XmlWriterSettings settings = new XmlWriterSettings();
- settings.Encoding = Util.UTF8;
- using (StringWriter sw = new StringWriter())
+ XmlWriterSettings settings = new()
+ {
+ Encoding = Util.UTF8
+ };
+ using (StringWriter sw = new())
{
using (XmlWriter writer = XmlWriter.Create(sw, settings))
{
@@ -194,9 +209,9 @@ namespace OpenSim.Region.Framework.Scenes
// ITerrainChannel.LoadFromXmlString()
public void LoadFromXmlString(string data)
{
- using(StringReader sr = new StringReader(data))
+ using(StringReader sr = new(data))
{
- using(XmlTextReader reader = new XmlTextReader(sr))
+ using(XmlTextReader reader = new(sr))
{
reader.DtdProcessing = DtdProcessing.Ignore;
ReadXml(reader);
@@ -207,7 +222,7 @@ namespace OpenSim.Region.Framework.Scenes
// ITerrainChannel.Merge
public void Merge(ITerrainChannel newTerrain, Vector3 displacement, float radianRotation, Vector2 rotationDisplacement)
{
- m_log.DebugFormat("{0} Merge. inSize=<{1},{2}>, disp={3}, rot={4}, rotDisp={5}, outSize=<{6},{7}>", LogHeader,
+ m_log.DebugFormat(LogHeader + "{0} Merge. inSize=<{1},{2}>, disp={3}, rot={4}, rotDisp={5}, outSize=<{6},{7}>", LogHeader,
newTerrain.Width, newTerrain.Height,
displacement, radianRotation, rotationDisplacement,
m_terrainData.SizeX, m_terrainData.SizeY);
@@ -295,7 +310,7 @@ namespace OpenSim.Region.Framework.Scenes
/// <x, y>
public void MergeWithBounding(ITerrainChannel newTerrain, Vector3 displacement, float rotationDegrees, Vector2 boundingOrigin, Vector2 boundingSize)
{
- m_log.DebugFormat("{0} MergeWithBounding: inSize=<{1},{2}>, rot={3}, boundingOrigin={4}, boundingSize={5}, disp={6}, outSize=<{7},{8}>",
+ m_log.DebugFormat(LogHeader + "{0} MergeWithBounding: inSize=<{1},{2}>, rot={3}, boundingOrigin={4}, boundingSize={5}, disp={6}, outSize=<{7},{8}>",
LogHeader, newTerrain.Width, newTerrain.Height, rotationDegrees, boundingOrigin.ToString(),
boundingSize.ToString(), displacement, m_terrainData.SizeX, m_terrainData.SizeY);
@@ -310,7 +325,7 @@ namespace OpenSim.Region.Framework.Scenes
int tmpY = baseY + baseY / 2;
int centreX = tmpX / 2;
int centreY = tmpY / 2;
- TerrainData terrain_tmp = new TerrainData(tmpX, tmpY, (int)Constants.RegionHeight);
+ TerrainData terrain_tmp = new(tmpX, tmpY, (int)Constants.RegionHeight);
for (int xx = 0; xx < tmpX; xx++)
for (int yy = 0; yy < tmpY; yy++)
terrain_tmp[xx, yy] = -65535f; //use this height like an 'alpha' mask channel
@@ -364,7 +379,7 @@ namespace OpenSim.Region.Framework.Scenes
}
catch (Exception) //just in case we've still not taken care of every way the arrays might go out of bounds! ;)
{
- m_log.DebugFormat("{0} MergeWithBounding - Rotate: Out of Bounds sx={1} sy={2} dx={3} dy={4}", sx, sy, x, y);
+ m_log.DebugFormat(LogHeader + "{0} MergeWithBounding - Rotate: Out of Bounds sx={1} sy={2} dx={3} dy={4}", sx, sy, x, y);
}
}
}
@@ -413,7 +428,7 @@ namespace OpenSim.Region.Framework.Scenes
}
catch (Exception) //just in case we've still not taken care of every way the arrays might go out of bounds! ;)
{
- m_log.DebugFormat("{0} MergeWithBounding - Bound & Displace: Out of Bounds sx={1} sy={2} dx={3} dy={4}", x, y, dx, dy);
+ m_log.DebugFormat(LogHeader + "{0} MergeWithBounding - Bound & Displace: Out of Bounds sx={1} sy={2} dx={3} dy={4}", x, y, dx, dy);
}
}
}
@@ -424,8 +439,10 @@ namespace OpenSim.Region.Framework.Scenes
public TerrainChannel Copy()
{
- TerrainChannel copy = new TerrainChannel();
- copy.m_terrainData = m_terrainData.Clone();
+ TerrainChannel copy = new()
+ {
+ m_terrainData = m_terrainData.Clone()
+ };
return copy;
}
@@ -473,14 +490,14 @@ namespace OpenSim.Region.Framework.Scenes
byte[] value = BitConverter.GetBytes(mapData[i]);
Array.Copy(value, 0, buffer, (i * 4), 4);
}
- XmlSerializer serializer = new XmlSerializer(typeof(byte[]));
+ XmlSerializer serializer = new(typeof(byte[]));
serializer.Serialize(xmlWriter, buffer);
}
// Read legacy terrain map. Presumed to be 256x256 of data encoded as floats in a byte array.
private void FromXml(XmlReader xmlReader)
{
- XmlSerializer serializer = new XmlSerializer(typeof(byte[]));
+ XmlSerializer serializer = new(typeof(byte[]));
byte[] dataArray = (byte[])serializer.Deserialize(xmlReader);
int index = 0;
@@ -520,16 +537,16 @@ namespace OpenSim.Region.Framework.Scenes
// New terrain serialization format that includes the width and length.
private void ToXml2(XmlWriter xmlWriter)
{
- TerrainChannelXMLPackage package = new TerrainChannelXMLPackage(Width, Height, Altitude, m_terrainData.CompressionFactor,
+ TerrainChannelXMLPackage package = new(Width, Height, Altitude, m_terrainData.CompressionFactor,
m_terrainData.GetCompressedMap());
- XmlSerializer serializer = new XmlSerializer(typeof(TerrainChannelXMLPackage));
+ XmlSerializer serializer = new(typeof(TerrainChannelXMLPackage));
serializer.Serialize(xmlWriter, package);
}
// New terrain serialization format that includes the width and length.
private void FromXml2(XmlReader xmlReader)
{
- XmlSerializer serializer = new XmlSerializer(typeof(TerrainChannelXMLPackage));
+ XmlSerializer serializer = new(typeof(TerrainChannelXMLPackage));
TerrainChannelXMLPackage package = (TerrainChannelXMLPackage)serializer.Deserialize(xmlReader);
m_terrainData = new TerrainData(package.Map, package.CompressionFactor, package.SizeX, package.SizeY, package.SizeZ);
}