diff --git a/OpenSim/Framework/Constants.cs b/OpenSim/Framework/Constants.cs
index a2eb5ee18e..79791327db 100644
--- a/OpenSim/Framework/Constants.cs
+++ b/OpenSim/Framework/Constants.cs
@@ -30,9 +30,18 @@ namespace OpenSim.Framework
{
public class Constants
{
+ // 'RegionSize' captures the legacy region size.
+ // DO NOT USE THIS FOR ANY NEW CODE. Use Scene.RegionSize[XYZ] as a region might not
+ // be the legacy region size.
public const uint RegionSize = 256;
public const uint RegionHeight = 4096;
+
+ // Terrain heightmap is kept as shorts that are the float value times this compression factor
+ public const float TerrainCompression = 100.0f;
+ // Since terrain is stored in 16x16 heights, regions must be a multiple of this number and that is the minimum
+ public const int MinRegionSize = 16;
public const byte TerrainPatchSize = 16;
+
public const string DefaultTexture = "89556747-24cb-43ed-920b-47caed15465f";
public enum EstateAccessCodex : uint
diff --git a/OpenSim/Framework/RegionInfo.cs b/OpenSim/Framework/RegionInfo.cs
index 2d3c9ea9d6..882fe332bd 100644
--- a/OpenSim/Framework/RegionInfo.cs
+++ b/OpenSim/Framework/RegionInfo.cs
@@ -472,7 +472,7 @@ namespace OpenSim.Framework
/// The x co-ordinate of this region in map tiles (e.g. 1000).
/// Coordinate is scaled as world coordinates divided by the legacy region size
/// and is thus is the number of legacy regions.
- /// This entrypoint exists for downward compatability for external modules.
+ /// DO NOT USE FOR NEW CODE! This entrypoint exists for downward compatability with external modules.
///
public uint RegionLocX
{
@@ -480,6 +480,18 @@ namespace OpenSim.Framework
set { LegacyRegionLocX = value; }
}
+ ///
+ /// The y co-ordinate of this region in map tiles (e.g. 1000).
+ /// Coordinate is scaled as world coordinates divided by the legacy region size
+ /// and is thus is the number of legacy regions.
+ /// DO NOT USE FOR NEW CODE! This entrypoint exists for downward compatability with external modules.
+ ///
+ public uint RegionLocY
+ {
+ get { return LegacyRegionLocY; }
+ set { LegacyRegionLocY = value; }
+ }
+
public void SetDefaultRegionSize()
{
RegionWorldLocX = 0;
@@ -490,19 +502,6 @@ namespace OpenSim.Framework
RegionSizeZ = Constants.RegionHeight;
}
-
- ///
- /// The y co-ordinate of this region in map tiles (e.g. 1000).
- /// Coordinate is scaled as world coordinates divided by the legacy region size
- /// and is thus is the number of legacy regions.
- /// This entrypoint exists for downward compatability for external modules.
- ///
- public uint RegionLocY
- {
- get { return LegacyRegionLocY; }
- set { LegacyRegionLocY = value; }
- }
-
// A unique region handle is created from the region's world coordinates.
// This cannot be changed because some code expects to receive the region handle and then
// compute the region coordinates from it.
diff --git a/OpenSim/Region/ClientStack/Linden/UDP/LLClientView.cs b/OpenSim/Region/ClientStack/Linden/UDP/LLClientView.cs
index 7b7daedf30..3396c322db 100644
--- a/OpenSim/Region/ClientStack/Linden/UDP/LLClientView.cs
+++ b/OpenSim/Region/ClientStack/Linden/UDP/LLClientView.cs
@@ -1240,9 +1240,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
try
{
int[] patches = new int[] { py * 16 + px };
- float[] heightmap = (map.Length == 65536) ?
- map :
- LLHeightFieldMoronize(map);
+ float[] heightmap = (map.Length == 65536) ? map : LLHeightFieldMoronize(map);
LayerDataPacket layerpack = TerrainCompressor.CreateLandPacket(heightmap, patches);
diff --git a/OpenSim/Region/CoreModules/World/Terrain/FileLoaders/GenericSystemDrawing.cs b/OpenSim/Region/CoreModules/World/Terrain/FileLoaders/GenericSystemDrawing.cs
index d78ade5fa7..d5c77ec3e1 100644
--- a/OpenSim/Region/CoreModules/World/Terrain/FileLoaders/GenericSystemDrawing.cs
+++ b/OpenSim/Region/CoreModules/World/Terrain/FileLoaders/GenericSystemDrawing.cs
@@ -67,7 +67,7 @@ namespace OpenSim.Region.CoreModules.World.Terrain.FileLoaders
{
using (Bitmap bitmap = new Bitmap(filename))
{
- ITerrainChannel retval = new TerrainChannel(true);
+ ITerrainChannel retval = new TerrainChannel(w, h);
for (int x = 0; x < retval.Width; x++)
{
diff --git a/OpenSim/Region/CoreModules/World/Terrain/TerrainModule.cs b/OpenSim/Region/CoreModules/World/Terrain/TerrainModule.cs
index c1ffd229db..2fff4c1ac0 100644
--- a/OpenSim/Region/CoreModules/World/Terrain/TerrainModule.cs
+++ b/OpenSim/Region/CoreModules/World/Terrain/TerrainModule.cs
@@ -130,15 +130,14 @@ namespace OpenSim.Region.CoreModules.World.Terrain
{
if (m_scene.Heightmap == null)
{
- m_channel = new TerrainChannel(m_InitialTerrain);
+ m_channel = new TerrainChannel(m_InitialTerrain,
+ m_scene.RegionInfo.RegionSizeX, m_scene.RegionInfo.RegionSizeY, m_scene.RegionInfo.RegionSizeZ);
m_scene.Heightmap = m_channel;
- m_revert = new TerrainChannel();
UpdateRevertMap();
}
else
{
m_channel = m_scene.Heightmap;
- m_revert = new TerrainChannel();
UpdateRevertMap();
}
@@ -532,6 +531,7 @@ namespace OpenSim.Region.CoreModules.World.Terrain
///
public void UpdateRevertMap()
{
+ /*
int x;
for (x = 0; x < m_channel.Width; x++)
{
@@ -541,6 +541,8 @@ namespace OpenSim.Region.CoreModules.World.Terrain
m_revert[x, y] = m_channel[x, y];
}
}
+ */
+ m_revert = m_channel.MakeCopy();
}
///
diff --git a/OpenSim/Region/Framework/Interfaces/ISimulationDataStore.cs b/OpenSim/Region/Framework/Interfaces/ISimulationDataStore.cs
index c936a8478f..847d245366 100644
--- a/OpenSim/Region/Framework/Interfaces/ISimulationDataStore.cs
+++ b/OpenSim/Region/Framework/Interfaces/ISimulationDataStore.cs
@@ -134,26 +134,26 @@ namespace OpenSim.Region.Framework.Interfaces
Dictionary GetExtra(UUID regionID);
void Shutdown();
- }
-
- // The terrain is stored as a blob in the database with a 'revision' field.
- // Some implementations of terrain storage would fill the revision field with
- // the time the terrain was stored. When real revisions were added and this
- // feature removed, that left some old entries with the time in the revision
- // field.
- // Thus, if revision is greater than 'RevisionHigh' then terrain db entry is
- // left over and it is presumed to be 'Legacy256'.
- // Numbers are arbitrary and are chosen to to reduce possible mis-interpretation.
- // If a revision does not match any of these, it is assumed to be Legacy256.
- public enum DBTerrainRevision
- {
- // Terrain is 'double[256,256]'
- Legacy256 = 11,
- // Terrain is 'int32, int32, float[,]' where the shorts are X and Y dimensions
- // The dimensions are presumed to be multiples of 16 and, more likely, multiples of 256.
- Variable2D = 22,
- // A revision that is not listed above or any revision greater than this value is 'Legacy256'.
- RevisionHigh = 1234
+ }
+
+ // The terrain is stored as a blob in the database with a 'revision' field.
+ // Some implementations of terrain storage would fill the revision field with
+ // the time the terrain was stored. When real revisions were added and this
+ // feature removed, that left some old entries with the time in the revision
+ // field.
+ // Thus, if revision is greater than 'RevisionHigh' then terrain db entry is
+ // left over and it is presumed to be 'Legacy256'.
+ // Numbers are arbitrary and are chosen to to reduce possible mis-interpretation.
+ // If a revision does not match any of these, it is assumed to be Legacy256.
+ public enum DBTerrainRevision
+ {
+ // Terrain is 'double[256,256]'
+ Legacy256 = 11,
+ // Terrain is 'int32, int32, float[,]' where the shorts are X and Y dimensions
+ // The dimensions are presumed to be multiples of 16 and, more likely, multiples of 256.
+ Variable2D = 22,
+ // A revision that is not listed above or any revision greater than this value is 'Legacy256'.
+ RevisionHigh = 1234
}
}
diff --git a/OpenSim/Region/Framework/Interfaces/ITerrainChannel.cs b/OpenSim/Region/Framework/Interfaces/ITerrainChannel.cs
index e467701351..3c060a47e2 100644
--- a/OpenSim/Region/Framework/Interfaces/ITerrainChannel.cs
+++ b/OpenSim/Region/Framework/Interfaces/ITerrainChannel.cs
@@ -29,15 +29,21 @@ namespace OpenSim.Region.Framework.Interfaces
{
public interface ITerrainChannel
{
- int Height { get; }
+ int Width { get;} // X dimension
+ int Height { get;} // Y dimension
+ int Altitude { get;} // Z dimension
+
double this[int x, int y] { get; set; }
- int Width { get; }
///
/// Squash the entire heightmap into a single dimensioned array
///
///
float[] GetFloatsSerialised();
+ // Get version of map as a single dimensioned array and each value compressed
+ // into an int (compressedHeight = (int)(floatHeight * Constants.TerrainCompression);)
+ // This is done to make the map smaller as it can get pretty larger for variable sized regions.
+ short[] GetCompressedMap();
double[,] GetDoubles();
bool Tainted(int x, int y);
diff --git a/OpenSim/Region/Framework/Scenes/Scene.cs b/OpenSim/Region/Framework/Scenes/Scene.cs
index e63963a69a..b714c7fa0e 100644
--- a/OpenSim/Region/Framework/Scenes/Scene.cs
+++ b/OpenSim/Region/Framework/Scenes/Scene.cs
@@ -1905,13 +1905,13 @@ namespace OpenSim.Region.Framework.Scenes
m_InitialTerrain = terrainConfig.GetString("InitialTerrain", m_InitialTerrain);
m_log.InfoFormat("[TERRAIN]: No default terrain. Generating a new terrain {0}.", m_InitialTerrain);
- Heightmap = new TerrainChannel(m_InitialTerrain);
+ Heightmap = new TerrainChannel(m_InitialTerrain, RegionInfo.RegionSizeX, RegionInfo.RegionSizeY, RegionInfo.RegionSizeZ);
SimulationDataService.StoreTerrain(Heightmap.GetDoubles(), RegionInfo.RegionID);
}
else
{
- Heightmap = new TerrainChannel(map);
+ Heightmap = new TerrainChannel(map, RegionInfo.RegionSizeZ);
}
}
catch (IOException e)
diff --git a/OpenSim/Region/Framework/Scenes/TerrainChannel.cs b/OpenSim/Region/Framework/Scenes/TerrainChannel.cs
index c0ca48ef39..fef93bf7c3 100644
--- a/OpenSim/Region/Framework/Scenes/TerrainChannel.cs
+++ b/OpenSim/Region/Framework/Scenes/TerrainChannel.cs
@@ -40,132 +40,125 @@ namespace OpenSim.Region.Framework.Scenes
///
public class TerrainChannel : ITerrainChannel
{
- private readonly bool[,] taint;
- private double[,] map;
+ protected bool[,] m_taint;
+ protected short[] m_map;
+ public int Width { get; private set; } // X dimension
+ // Unfortunately, for historical reasons, in this module 'Width' is X and 'Height' is Y
+ public int Height { get; private set; } // Y dimension
+ public int Altitude { get; private set; } // Y dimension
+
+ // Default, not-often-used builder
public TerrainChannel()
{
- map = new double[Constants.RegionSize, Constants.RegionSize];
- taint = new bool[Constants.RegionSize / 16, Constants.RegionSize / 16];
-
- PinHeadIsland();
+ InitializeStructures(Constants.RegionSize, Constants.RegionSize, Constants.RegionHeight, false);
+ FlatLand();
+ // PinHeadIsland();
}
- public TerrainChannel(String type)
+ // Create terrain of given size
+ public TerrainChannel(int pX, int pY)
{
- map = new double[Constants.RegionSize, Constants.RegionSize];
- taint = new bool[Constants.RegionSize / 16, Constants.RegionSize / 16];
+ InitializeStructures((uint)pX, (uint)pY, Constants.RegionHeight, true);
+ }
+ // Create terrain of specified size and initialize with specified terrain.
+ // TODO: join this with the terrain initializers.
+ public TerrainChannel(String type, uint pX, uint pY, uint pZ)
+ {
+ InitializeStructures(pX, pY, pZ, false);
if (type.Equals("flat"))
FlatLand();
else
PinHeadIsland();
}
- public TerrainChannel(double[,] import)
+ public TerrainChannel(double[,] pM, uint pH)
{
- map = import;
- taint = new bool[import.GetLength(0),import.GetLength(1)];
- }
-
- public TerrainChannel(bool createMap)
- {
- if (createMap)
- {
- map = new double[Constants.RegionSize,Constants.RegionSize];
- taint = new bool[Constants.RegionSize / 16,Constants.RegionSize / 16];
- }
- }
-
- public TerrainChannel(int w, int h)
- {
- map = new double[w,h];
- taint = new bool[w / 16,h / 16];
+ InitializeStructures((uint)pM.GetLength(0), (uint)pM.GetLength(1), pH, false);
+ int idx = 0;
+ for (int ii = 0; ii < Height; ii++)
+ for (int jj = 0; jj < Width; jj++)
+ m_map[idx++] = ToCompressedHeight(pM[ii, jj]);
}
#region ITerrainChannel Members
- public int Width
- {
- get { return map.GetLength(0); }
- }
-
- public int Height
- {
- get { return map.GetLength(1); }
- }
-
+ // ITerrainChannel.MakeCopy()
public ITerrainChannel MakeCopy()
{
- TerrainChannel copy = new TerrainChannel(false);
- copy.map = (double[,]) map.Clone();
-
- return copy;
+ return this.Copy();
}
+ // ITerrainChannel.GetCompressedMap()
+ public short[] GetCompressedMap()
+ {
+ return m_map;
+ }
+
+ // ITerrainChannel.GetFloatsSerialized()
public float[] GetFloatsSerialised()
{
- // Move the member variables into local variables, calling
- // member variables 256*256 times gets expensive
- int w = Width;
- int h = Height;
- float[] heights = new float[w * h];
+ int points = Width * Height;
+ float[] heights = new float[points];
+
+ for (int ii = 0; ii < points; ii++)
+ heights[ii] = FromCompressedHeight(m_map[ii]);
+
+ return heights;
+ }
+
+ // ITerrainChannel.GetDoubles()
+ public double[,] GetDoubles()
+ {
+ int w = Width;
+ int l = Height;
+ double[,] heights = new double[w, l];
- int i, j; // map coordinates
int idx = 0; // index into serialized array
- for (i = 0; i < h; i++)
+ for (int ii = 0; ii < l; ii++)
{
- for (j = 0; j < w; j++)
+ for (int jj = 0; jj < w; jj++)
{
- heights[idx++] = (float)map[j, i];
+ heights[ii, jj] = (double)FromCompressedHeight(m_map[idx]);
+ idx++;
}
}
return heights;
}
- public double[,] GetDoubles()
- {
- return map;
- }
-
+ // ITerrainChannel.this[x,y]
public double this[int x, int y]
{
- get { return map[x, y]; }
+ get { return m_map[x * Width + y]; }
set
{
// Will "fix" terrain hole problems. Although not fantastically.
if (Double.IsNaN(value) || Double.IsInfinity(value))
return;
- if (map[x, y] != value)
+ int idx = x * Width + y;
+ if (m_map[idx] != value)
{
- taint[x / 16, y / 16] = true;
- map[x, y] = value;
+ m_taint[x / Constants.TerrainPatchSize, y / Constants.TerrainPatchSize] = true;
+ m_map[idx] = ToCompressedHeight(value);
}
}
}
+ // ITerrainChannel.Tainted()
public bool Tainted(int x, int y)
{
- if (taint[x / 16, y / 16])
+ if (m_taint[x / Constants.TerrainPatchSize, y / Constants.TerrainPatchSize])
{
- taint[x / 16, y / 16] = false;
+ m_taint[x / Constants.TerrainPatchSize, y / Constants.TerrainPatchSize] = false;
return true;
}
return false;
}
- #endregion
-
- public TerrainChannel Copy()
- {
- TerrainChannel copy = new TerrainChannel(false);
- copy.map = (double[,]) map.Clone();
-
- return copy;
- }
-
+ // ITerrainChannel.SaveToXmlString()
public string SaveToXmlString()
{
XmlWriterSettings settings = new XmlWriterSettings();
@@ -181,13 +174,7 @@ namespace OpenSim.Region.Framework.Scenes
}
}
- private void WriteXml(XmlWriter writer)
- {
- writer.WriteStartElement(String.Empty, "TerrainMap", String.Empty);
- ToXml(writer);
- writer.WriteEndElement();
- }
-
+ // ITerrainChannel.LoadFromXmlString()
public void LoadFromXmlString(string data)
{
StringReader sr = new StringReader(data);
@@ -199,12 +186,89 @@ namespace OpenSim.Region.Framework.Scenes
sr.Close();
}
- private void ReadXml(XmlReader reader)
+ #endregion
+
+ private void InitializeStructures(uint pX, uint pY, uint pZ, bool shouldInitializeHeightmap)
{
- reader.ReadStartElement("TerrainMap");
- FromXml(reader);
+ Width = (int)pX;
+ Height = (int)pY;
+ Altitude = (int)pZ;
+ m_map = new short[Width * Height];
+ m_taint = new bool[Width / Constants.TerrainPatchSize, Height / Constants.TerrainPatchSize];
+ ClearTaint();
+ if (shouldInitializeHeightmap)
+ {
+ FlatLand();
+ }
}
+ public void ClearTaint()
+ {
+ for (int ii = 0; ii < Width / Constants.TerrainPatchSize; ii++)
+ for (int jj = 0; jj < Height / Constants.TerrainPatchSize; jj++)
+ m_taint[ii, jj] = false;
+ }
+
+ // To save space (especially for large regions), keep the height as a short integer
+ // that is coded as the float height times the compression factor (usually '100'
+ // to make for two decimal points).
+ public short ToCompressedHeight(double pHeight)
+ {
+ return (short)(pHeight * Constants.TerrainCompression);
+ }
+
+ public float FromCompressedHeight(short pHeight)
+ {
+ return ((float)pHeight) / Constants.TerrainCompression;
+ }
+
+ public TerrainChannel Copy()
+ {
+ TerrainChannel copy = new TerrainChannel();
+ copy.m_map = (short[])m_map.Clone();
+ copy.m_taint = (bool[,])m_taint.Clone();
+ copy.Width = Width;
+ copy.Height = Height;
+ copy.Altitude = Altitude;
+
+ return copy;
+ }
+
+ private void WriteXml(XmlWriter writer)
+ {
+ if (Width == Constants.RegionSize && Height == Constants.RegionSize)
+ {
+ // Downward compatibility for legacy region terrain maps.
+ // If region is exactly legacy size, return the old format XML.
+ writer.WriteStartElement(String.Empty, "TerrainMap", String.Empty);
+ ToXml(writer);
+ writer.WriteEndElement();
+ }
+ else
+ {
+ // New format XML that includes width and length.
+ writer.WriteStartElement(String.Empty, "TerrainMap2", String.Empty);
+ ToXml2(writer);
+ writer.WriteEndElement();
+ }
+ }
+
+ private void ReadXml(XmlReader reader)
+ {
+ // Check the first element. If legacy element, use the legacy reader.
+ if (reader.IsStartElement("TerrainMap"))
+ {
+ reader.ReadStartElement("TerrainMap");
+ FromXml(reader);
+ }
+ else
+ {
+ reader.ReadStartElement("TerrainMap2");
+ FromXml2(reader);
+ }
+ }
+
+ // Write legacy terrain map. Presumed to be 256x256 of data encoded as floats in a byte array.
private void ToXml(XmlWriter xmlWriter)
{
float[] mapData = GetFloatsSerialised();
@@ -218,6 +282,7 @@ namespace OpenSim.Region.Framework.Scenes
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[]));
@@ -236,35 +301,68 @@ namespace OpenSim.Region.Framework.Scenes
}
}
+ private class TerrainChannelXMLPackage
+ {
+ public int Version;
+ public int SizeX;
+ public int SizeY;
+ public int SizeZ;
+ public short[] Map;
+ public TerrainChannelXMLPackage(int pX, int pY, int pZ, short[] pMap)
+ {
+ Version = 1;
+ SizeX = pX;
+ SizeY = pY;
+ SizeZ = pZ;
+ Map = pMap;
+ }
+ }
+
+ // New terrain serialization format that includes the width and length.
+ private void ToXml2(XmlWriter xmlWriter)
+ {
+ TerrainChannelXMLPackage package = new TerrainChannelXMLPackage(Width, Height, Altitude, m_map);
+ XmlSerializer serializer = new XmlSerializer(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));
+ TerrainChannelXMLPackage package = (TerrainChannelXMLPackage)serializer.Deserialize(xmlReader);
+ Width = package.SizeX;
+ Height = package.SizeY;
+ Altitude = package.SizeZ;
+ m_map = package.Map;
+ }
+
+ // Fill the heightmap with the center bump terrain
private void PinHeadIsland()
{
int x;
- for (x = 0; x < Constants.RegionSize; x++)
+ for (x = 0; x < Width; x++)
{
int y;
- for (y = 0; y < Constants.RegionSize; y++)
+ for (y = 0; y < Height; y++)
{
- map[x, y] = TerrainUtil.PerlinNoise2D(x, y, 2, 0.125) * 10;
- double spherFacA = TerrainUtil.SphericalFactor(x, y, Constants.RegionSize / 2.0, Constants.RegionSize / 2.0, 50) * 0.01;
- double spherFacB = TerrainUtil.SphericalFactor(x, y, Constants.RegionSize / 2.0, Constants.RegionSize / 2.0, 100) * 0.001;
- if (map[x, y] < spherFacA)
- map[x, y] = spherFacA;
- if (map[x, y] < spherFacB)
- map[x, y] = spherFacB;
+ int idx = x * (int)Width + y;
+ m_map[idx] = ToCompressedHeight(TerrainUtil.PerlinNoise2D(x, y, 2, 0.125) * 10);
+ short spherFacA = ToCompressedHeight(TerrainUtil.SphericalFactor(x, y, Constants.RegionSize / 2.0, Constants.RegionSize / 2.0, 50) * 0.01);
+ short spherFacB = ToCompressedHeight(TerrainUtil.SphericalFactor(x, y, Constants.RegionSize / 2.0, Constants.RegionSize / 2.0, 100) * 0.001);
+ if (m_map[idx] < spherFacA)
+ m_map[idx] = spherFacA;
+ if (m_map[idx] < spherFacB)
+ m_map[idx] = spherFacB;
}
}
}
private void FlatLand()
{
- int x;
- for (x = 0; x < Constants.RegionSize; x++)
- {
- int y;
- for (y = 0; y < Constants.RegionSize; y++)
- map[x, y] = 21;
- }
+ short flatHeight = ToCompressedHeight(21);
+ for (int ii = 0; ii < m_map.Length; ii++)
+ m_map[ii] = flatHeight;
}
-
}
}