Merge branch 'master' into lickx
This commit is contained in:
@@ -40,6 +40,11 @@ namespace OpenSim.Region.CoreModules.World.Terrain.FileLoaders
|
||||
/// </summary>
|
||||
internal class BMP : GenericSystemDrawing
|
||||
{
|
||||
public override int SupportedHeight
|
||||
{
|
||||
get { return 256; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Exports a file to a image on the disk using a System.Drawing exporter.
|
||||
/// </summary>
|
||||
@@ -76,5 +81,10 @@ namespace OpenSim.Region.CoreModules.World.Terrain.FileLoaders
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
public override bool SupportsExtendedTileSave()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -49,6 +49,11 @@ namespace OpenSim.Region.CoreModules.World.Terrain.FileLoaders
|
||||
get { return ".gsd"; }
|
||||
}
|
||||
|
||||
public virtual int SupportedHeight
|
||||
{
|
||||
get { return 256; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,32 +25,22 @@
|
||||
* 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 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; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Exports a stream using a System.Drawing exporter.
|
||||
/// </summary>
|
||||
/// <param name="stream">The target stream</param>
|
||||
/// <param name="map">The terrain channel being saved</param>
|
||||
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 +49,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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Exports a stream to 32 bit tiff
|
||||
/// </summary>
|
||||
/// <param name="stream">The target stream</param>
|
||||
/// <param name="map">The terrain channel being saved</param>
|
||||
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<ushort, (ushort Type, uint Count, uint ValueOffset)> 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<int> bits = new List<int>();
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
/// <param name="regionSizeX">The width of a map tile.</param>
|
||||
/// <param name="regionSizeY">The height of a map tile.</param>
|
||||
void SaveFile(ITerrainChannel map, string filename, int offsetX, int offsetY, int fileWidth, int fileHeight, int regionSizeX, int regionSizeY);
|
||||
|
||||
/// <summary>
|
||||
/// Save all or part of region to larger or smaller file
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// If the image file already exists then the tiles saved will replace those already in the file - other tiles
|
||||
/// will be untouched.
|
||||
/// </remarks>
|
||||
/// <param name="filename">The terrain file to save</param>
|
||||
/// <param name="fileWidth">Width of the file to save to, overwritten by existing file</param>
|
||||
/// <param name="fileHeight">Height of the file to save to, overwritten by existing file</param>
|
||||
/// <param name="startX">Start point in meters on x axis on existing terrain map</param>
|
||||
/// <param name="startY">Start point in meters on y axis on existing terrain map</param>
|
||||
/// <param name="stopX">End point in meters on x axis on existing terrain map</param>
|
||||
/// <param name="stopY">End point in meters on y axis on existing terrain map</param>
|
||||
/// <param name="offsetX">Offset start point in meters on x axis to write to file</param>
|
||||
/// <param name="offsetY">Offset start point in meters on y axis to write to file</param>
|
||||
void SaveFile(ITerrainChannel map, string filename, int fileWidth, int fileHeight, int startX, int startY, int stopX, int stopY, int offsetX, int offsetY);
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
@@ -25,7 +25,6 @@
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Reflection;
|
||||
@@ -74,17 +73,13 @@ namespace OpenSim.Region.CoreModules.World.Terrain
|
||||
|
||||
private object _modifyLock = new();
|
||||
|
||||
#pragma warning disable 414
|
||||
private static readonly string LogHeader = "[TERRAIN MODULE]";
|
||||
#pragma warning restore 414
|
||||
private const string LogHeader = "[TERRAIN]: ";
|
||||
|
||||
private readonly Commander m_commander = new Commander("terrain");
|
||||
private readonly Dictionary<string, ITerrainLoader> m_loaders = new Dictionary<string, ITerrainLoader>();
|
||||
private readonly Dictionary<StandardTerrainEffects, ITerrainFloodEffect> m_floodeffects =
|
||||
new Dictionary<StandardTerrainEffects, ITerrainFloodEffect>();
|
||||
private readonly Dictionary<StandardTerrainEffects, ITerrainPaintableEffect> m_painteffects =
|
||||
new Dictionary<StandardTerrainEffects, ITerrainPaintableEffect>();
|
||||
private Dictionary<string, ITerrainModifier> m_modifyOperations = new Dictionary<string, ITerrainModifier>();
|
||||
private readonly Commander m_commander = new("terrain");
|
||||
private readonly Dictionary<string, ITerrainLoader> m_loaders = [];
|
||||
private readonly Dictionary<StandardTerrainEffects, ITerrainFloodEffect> m_floodeffects = [];
|
||||
private readonly Dictionary<StandardTerrainEffects, ITerrainPaintableEffect> m_painteffects = [];
|
||||
private Dictionary<string, ITerrainModifier> m_modifyOperations = [];
|
||||
private Dictionary<string, ITerrainEffect> m_plugineffects;
|
||||
private ITerrainChannel m_channel;
|
||||
private ITerrainChannel m_baked;
|
||||
@@ -208,15 +203,15 @@ namespace OpenSim.Region.CoreModules.World.Terrain
|
||||
}
|
||||
|
||||
// The flags of which terrain patches to send for each of the ScenePresence's
|
||||
private Dictionary<UUID, PatchUpdates> m_perClientPatchUpdates = new Dictionary<UUID, PatchUpdates>();
|
||||
private Dictionary<UUID, PatchUpdates> m_perClientPatchUpdates = [];
|
||||
|
||||
/// <summary>
|
||||
/// Human readable list of terrain file extensions that are supported.
|
||||
/// </summary>
|
||||
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 +279,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<string, ITerrainLoader> loader in m_loaders)
|
||||
foreach (KeyValuePair<string, ITerrainLoader> 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 +363,7 @@ namespace OpenSim.Region.CoreModules.World.Terrain
|
||||
{
|
||||
if (filename.EndsWith(loader.Key))
|
||||
{
|
||||
lock(m_scene)
|
||||
lock (m_scene)
|
||||
{
|
||||
try
|
||||
{
|
||||
@@ -369,38 +374,34 @@ 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.Debug($"{LogHeader}Loaded terrain, wd/ht: {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)");
|
||||
throw new TerrainException(String.Format("unable to load heightmap: parser {0} does not support loading", loader.Value));
|
||||
m_log.ErrorFormat($"{LogHeader}Unable to load heightmap, the {loader.Value} parser does not support file loading. (May be save only)");
|
||||
throw new TerrainException($"unable to load heightmap: parser {loader.Value} does not support loading");
|
||||
}
|
||||
catch(FileNotFoundException)
|
||||
catch (FileNotFoundException)
|
||||
{
|
||||
m_log.Error(
|
||||
"[TERRAIN]: 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));
|
||||
m_log.ErrorFormat($"{LogHeader}Unable to load heightmap, file not found. (A directory permissions error may also cause this)");
|
||||
throw new TerrainException($"unable to load heightmap: file {filename} not found (or permissions do not allow access");
|
||||
}
|
||||
catch(ArgumentException e)
|
||||
catch (ArgumentException e)
|
||||
{
|
||||
m_log.ErrorFormat("[TERRAIN]: Unable to load heightmap: {0}", e.Message);
|
||||
throw new TerrainException(
|
||||
String.Format("Unable to load heightmap: {0}", e.Message));
|
||||
m_log.Error($"{LogHeader}Unable to load heightmap: {e.Message}");
|
||||
throw new TerrainException($"Unable to load heightmap: {e.Message}");
|
||||
}
|
||||
}
|
||||
m_log.Info("[TERRAIN]: File (" + filename + ") loaded successfully");
|
||||
m_log.Info($"{LogHeader}File ({filename}) loaded successfully");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
m_log.Error("[TERRAIN]: 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));
|
||||
m_log.Error($"{LogHeader}Unable to load heightmap, no file loader available for that format.");
|
||||
throw new TerrainException($"unable to load heightmap from file {filename}: no loader available for that format");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -413,22 +414,21 @@ namespace OpenSim.Region.CoreModules.World.Terrain
|
||||
{
|
||||
foreach(KeyValuePair<string, ITerrainLoader> 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.Info($"{LogHeader}Saved terrain from {m_scene.RegionInfo.RegionName} to {filename}");
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch(IOException ioe)
|
||||
{
|
||||
m_log.Error(String.Format("[TERRAIN]: Unable to save to {0}, {1}", filename, ioe.Message));
|
||||
m_log.Error($"{LogHeader}Unable to save to {filename}, {ioe.Message}");
|
||||
}
|
||||
|
||||
m_log.ErrorFormat(
|
||||
"[TERRAIN]: Could not save terrain from {0} to {1}. Valid file extensions are {2}",
|
||||
m_scene.RegionInfo.RegionName, filename, m_supportedFileExtensions);
|
||||
m_log.Error(
|
||||
$"{LogHeader}Could not save terrain from {m_scene.RegionInfo.RegionName} to {filename}. Valid file extensions are {m_supportedFileExtensions}");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -468,18 +468,17 @@ 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)");
|
||||
throw new TerrainException(String.Format("unable to load heightmap: parser {0} does not support loading", loader.Value));
|
||||
m_log.Error($"{LogHeader}Unable to load heightmap, the {loader.Value} parser does not support file loading. (May be save only)");
|
||||
throw new TerrainException($"unable to load heightmap: parser {loader.Value} does not support loading");
|
||||
}
|
||||
}
|
||||
|
||||
m_log.Info("[TERRAIN]: File (" + filename + ") loaded successfully");
|
||||
m_log.Info($"{LogHeader}File ({filename}) loaded successfully");
|
||||
return;
|
||||
}
|
||||
}
|
||||
m_log.Error("[TERRAIN]: 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));
|
||||
m_log.Error($"{LogHeader}Unable to load heightmap, no file loader available for that format.");
|
||||
throw new TerrainException($"unable to load heightmap from file {filename}: no loader available for that format");
|
||||
}
|
||||
|
||||
public void LoadFromStream(string filename, Vector3 displacement,
|
||||
@@ -499,17 +498,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)");
|
||||
throw new TerrainException(String.Format("unable to load heightmap: parser {0} does not support loading", loader.Value));
|
||||
m_log.ErrorFormat($"{LogHeader}Unable to load heightmap, the { loader.Value} parser does not support file loading. (May be save only)");
|
||||
throw new TerrainException("unable to load heightmap: parser {loader.Value} does not support loading");
|
||||
}
|
||||
}
|
||||
|
||||
m_log.Info("[TERRAIN]: File (" + filename + ") loaded successfully");
|
||||
m_log.Info($"{LogHeader}File ({filename}) loaded successfully");
|
||||
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));
|
||||
}
|
||||
|
||||
@@ -526,7 +524,7 @@ namespace OpenSim.Region.CoreModules.World.Terrain
|
||||
Stream file = response.GetResponseStream();
|
||||
|
||||
if (response.ContentLength == 0)
|
||||
throw new Exception(String.Format("{0} returned an empty file", uri.ToString()));
|
||||
throw new Exception($"{uri} returned an empty file");
|
||||
|
||||
// return new BufferedStream(file, (int) response.ContentLength);
|
||||
return new BufferedStream(file, 1000000);
|
||||
@@ -585,7 +583,7 @@ namespace OpenSim.Region.CoreModules.World.Terrain
|
||||
{
|
||||
foreach(KeyValuePair<string, ITerrainLoader> 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,8 +592,8 @@ 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.");
|
||||
throw new TerrainException(String.Format("Unable to save heightmap: saving of this file format not implemented"));
|
||||
m_log.Error($"{LogHeader}Unable to save to {filename}, saving of this file format has not been implemented.");
|
||||
throw new TerrainException("Unable to save heightmap: saving of this file format not implemented");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -627,7 +625,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 +644,7 @@ namespace OpenSim.Region.CoreModules.World.Terrain
|
||||
|
||||
private void LoadPlugins()
|
||||
{
|
||||
m_plugineffects = new Dictionary<string, ITerrainEffect>();
|
||||
m_plugineffects = [];
|
||||
LoadPlugins(Assembly.GetCallingAssembly());
|
||||
string plugineffectsPath = "Terrain";
|
||||
|
||||
@@ -657,7 +655,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.Info($"{LogHeader}Loading effects in {file}");
|
||||
try
|
||||
{
|
||||
Assembly library = Assembly.LoadFrom(file);
|
||||
@@ -844,7 +842,7 @@ namespace OpenSim.Region.CoreModules.World.Terrain
|
||||
// this region is included in the tile request
|
||||
foreach(KeyValuePair<string, ITerrainLoader> 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 +866,43 @@ namespace OpenSim.Region.CoreModules.World.Terrain
|
||||
m_scene.RegionInfo.RegionName, filename, m_supportFileExtensionsForTileSave);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Save all or part of region to larger or smaller file
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// If the image file already exists then the tiles saved will replace those already in the file - other tiles
|
||||
/// will be untouched.
|
||||
/// </remarks>
|
||||
/// <param name="filename">The terrain file to save</param>
|
||||
/// <param name="fileWidth">Width of the file to save to, overwritten by existing file</param>
|
||||
/// <param name="fileHeight">Height of the file to save to, overwritten by existing file</param>
|
||||
/// <param name="startX">Start point in meters on x axis on existing terrain map</param>
|
||||
/// <param name="startY">Start point in meters on y axis on existing terrain map</param>
|
||||
/// <param name="stopX">End point in meters on x axis on existing terrain map</param>
|
||||
/// <param name="stopY">End point in meters on y axis on existing terrain map</param>
|
||||
/// <param name="offsetX">Offset start point in meters on x axis to write to file</param>
|
||||
/// <param name="offsetY">Offset start point in meters on y axis to write to file</param>
|
||||
public void SaveToFile(string filename, int fileWidth, int fileHeight, int startX, int startY, int stopX, int stopY, int offsetX, int offsetY)
|
||||
{
|
||||
foreach (KeyValuePair<string, ITerrainLoader> 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);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This is used to check to see of any of the terrain is tainted and, if so, schedule
|
||||
@@ -885,7 +920,7 @@ namespace OpenSim.Region.CoreModules.World.Terrain
|
||||
}
|
||||
}
|
||||
|
||||
object TerrainCheckUpdatesLock = new object();
|
||||
object TerrainCheckUpdatesLock = new();
|
||||
|
||||
private void EventManager_TerrainCheckUpdatesAsync(object o)
|
||||
{
|
||||
@@ -952,7 +987,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 +1160,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 +1236,7 @@ namespace OpenSim.Region.CoreModules.World.Terrain
|
||||
next = 0;
|
||||
|
||||
int npatchs = 0;
|
||||
List<int> patchs = new List<int>(128);
|
||||
List<int> patchs = new(128);
|
||||
while ((next = pups.taints.GetAndClearNextTrue(next)) >= 0)
|
||||
{
|
||||
patchs.Add(next);
|
||||
@@ -1237,7 +1272,7 @@ namespace OpenSim.Region.CoreModules.World.Terrain
|
||||
|
||||
private List<PatchesToSend> GetModifiedPatchesInViewDistance(PatchUpdates pups)
|
||||
{
|
||||
List<PatchesToSend> ret = new List<PatchesToSend>();
|
||||
List<PatchesToSend> ret = [];
|
||||
if (!pups.HasUpdates())
|
||||
return ret;
|
||||
|
||||
@@ -1518,7 +1553,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 +1590,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 +1621,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 +1667,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 +1781,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 +1838,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 +1851,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 +1875,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 +1889,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 <x>,<y> 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 +2007,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);
|
||||
}
|
||||
|
||||
@@ -439,7 +439,13 @@ namespace OpenSim.Region.CoreModules.World.Warp3DMap
|
||||
delegate (SceneObjectGroup group)
|
||||
{
|
||||
foreach (SceneObjectPart child in group.Parts)
|
||||
CreatePrim(renderer, child);
|
||||
{
|
||||
try { CreatePrim(renderer, child); }
|
||||
catch (Exception e)
|
||||
{
|
||||
m_log.Warn($"[Warp3D] failed to render prim {child.Name} at {child.GetWorldPosition()}: {e.Message}");
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@@ -68,5 +68,6 @@ namespace OpenSim.Region.Framework.Interfaces
|
||||
/// <param name="boundingOrigin"><x, y></param>
|
||||
/// <param name="boundingSize"><x, y></param>
|
||||
void MergeWithBounding(ITerrainChannel newTerrain, Vector3 displacement, float rotationDegrees, Vector2 boundingOrigin, Vector2 boundingSize);
|
||||
int MaxHeight();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2290,14 +2290,17 @@ namespace OpenSim.Region.Framework.Scenes
|
||||
|
||||
public void SetText(string text, Vector3 color, double alpha)
|
||||
{
|
||||
Color = Color.FromArgb(0xff - (int) (alpha * 0xff),
|
||||
Color newcolor = Color.FromArgb(0xff - (int) (alpha * 0xff),
|
||||
(int) (color.X * 0xff),
|
||||
(int) (color.Y * 0xff),
|
||||
(int) (color.Z * 0xff));
|
||||
Text = text;
|
||||
|
||||
HasGroupChanged = true;
|
||||
m_rootPart.ScheduleFullUpdate();
|
||||
if(Text != text || newcolor!= Color)
|
||||
{
|
||||
Text = text;
|
||||
Color = newcolor;
|
||||
HasGroupChanged = true;
|
||||
m_rootPart.ScheduleFullUpdate();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -47,8 +47,9 @@ namespace OpenSim.Region.Framework.Scenes
|
||||
/// </summary>
|
||||
public class TerrainChannel : ITerrainChannel
|
||||
{
|
||||
const string LogHeader = "[TERRAIN CHANNEL]: ";
|
||||
|
||||
private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
|
||||
private static string LogHeader = "[TERRAIN CHANNEL]";
|
||||
|
||||
protected TerrainData m_terrainData;
|
||||
|
||||
@@ -175,12 +176,29 @@ namespace OpenSim.Region.Framework.Scenes
|
||||
return m_terrainData.IsTaintedAt(x, y);
|
||||
}
|
||||
|
||||
public int MaxHeight()
|
||||
{
|
||||
float max = float.MinValue;
|
||||
for (int ii = 0; ii < Width; ii++)
|
||||
{
|
||||
for (int jj = 0; jj < Height; jj++)
|
||||
{
|
||||
float cur = m_terrainData[ii, jj];
|
||||
if(cur > max)
|
||||
max = cur;
|
||||
}
|
||||
}
|
||||
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 +212,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 +225,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 +313,7 @@ namespace OpenSim.Region.Framework.Scenes
|
||||
/// <param name="boundingSize"><x, y></param>
|
||||
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 +328,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 +382,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 +431,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 +442,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 +493,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 +540,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);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user