From ff5885ab234bc9a7efda49eea0e2200711c4933c Mon Sep 17 00:00:00 2001 From: Robert Adams Date: Fri, 1 Nov 2013 11:35:31 -0700 Subject: [PATCH] varregion: push TerrainData implementation up and down the database storage stack. Implement both LoadTerrain and StoreTerrain for all DBs. Move all database blob serialization/deserialization into TerrainData. --- OpenSim/Data/MSSQL/MSSQLSimulationData.cs | 62 +++--- OpenSim/Data/MySQL/MySQLSimulationData.cs | 32 +-- OpenSim/Data/Null/NullSimulationData.cs | 21 +- OpenSim/Data/PGSQL/PGSQLSimulationData.cs | 48 ++--- OpenSim/Data/SQLite/SQLiteSimulationData.cs | 29 +-- OpenSim/Framework/TerrainData.cs | 193 +++++++++++++++--- .../Interfaces/ISimulationDataService.cs | 9 + .../Interfaces/ISimulationDataStore.cs | 9 + OpenSim/Region/Framework/Scenes/Scene.cs | 4 +- .../Region/Framework/Scenes/TerrainChannel.cs | 27 ++- .../Simulation/SimulationDataService.cs | 10 + .../Tests/Common/Mock/MockRegionDataPlugin.cs | 29 ++- 12 files changed, 343 insertions(+), 130 deletions(-) diff --git a/OpenSim/Data/MSSQL/MSSQLSimulationData.cs b/OpenSim/Data/MSSQL/MSSQLSimulationData.cs index dbfd16c4a5..9f5991b70a 100644 --- a/OpenSim/Data/MSSQL/MSSQLSimulationData.cs +++ b/OpenSim/Data/MSSQL/MSSQLSimulationData.cs @@ -531,43 +531,43 @@ ELSE /// public double[,] LoadTerrain(UUID regionID) { - double[,] terrain = new double[(int)Constants.RegionSize, (int)Constants.RegionSize]; - terrain.Initialize(); + TerrainData terrData = LoadTerrain(regionID, (int)Constants.RegionSize, (int)Constants.RegionSize, (int)Constants.RegionHeight); + return terrData.GetDoubles(); + } + + public TerrainData LoadTerrain(UUID regionID, int pSizeX, int pSizeY, int pSizeZ) + { + TerrainData terrData = null; string sql = "select top 1 RegionUUID, Revision, Heightfield from terrain where RegionUUID = @RegionUUID order by Revision desc"; using (SqlConnection conn = new SqlConnection(m_connectionString)) - using (SqlCommand cmd = new SqlCommand(sql, conn)) { - // MySqlParameter param = new MySqlParameter(); - cmd.Parameters.Add(_Database.CreateParameter("@RegionUUID", regionID)); - conn.Open(); - using (SqlDataReader reader = cmd.ExecuteReader()) + using (SqlCommand cmd = new SqlCommand(sql, conn)) { - int rev; - if (reader.Read()) + // MySqlParameter param = new MySqlParameter(); + cmd.Parameters.Add(_Database.CreateParameter("@RegionUUID", regionID)); + conn.Open(); + using (SqlDataReader reader = cmd.ExecuteReader()) { - MemoryStream str = new MemoryStream((byte[])reader["Heightfield"]); - BinaryReader br = new BinaryReader(str); - for (int x = 0; x < (int)Constants.RegionSize; x++) + int rev; + if (reader.Read()) { - for (int y = 0; y < (int)Constants.RegionSize; y++) - { - terrain[x, y] = br.ReadDouble(); - } + rev = (int)reader["Revision"]; + byte[] blob = (byte[])reader["Heightfield"]; + terrData = TerrainData.CreateFromDatabaseBlobFactory(pSizeX, pSizeY, pSizeZ, rev, blob); } - rev = (int)reader["Revision"]; + else + { + _Log.Info("[REGION DB]: No terrain found for region"); + return null; + } + _Log.Info("[REGION DB]: Loaded terrain revision r" + rev); } - else - { - _Log.Info("[REGION DB]: No terrain found for region"); - return null; - } - _Log.Info("[REGION DB]: Loaded terrain revision r" + rev); } } - return terrain; + return terrData; } // Legacy entry point for when terrain was always a 256x256 hieghtmap @@ -600,13 +600,15 @@ ELSE terrData.GetDatabaseBlob(out terrainDBRevision, out terrainDBblob); using (SqlConnection conn = new SqlConnection(m_connectionString)) - using (SqlCommand cmd = new SqlCommand(sql, conn)) { - cmd.Parameters.Add(_Database.CreateParameter("@RegionUUID", regionID)); - cmd.Parameters.Add(_Database.CreateParameter("@Revision", terrainDBRevision)); - cmd.Parameters.Add(_Database.CreateParameter("@Heightfield", terrainDBblob)); - conn.Open(); - cmd.ExecuteNonQuery(); + using (SqlCommand cmd = new SqlCommand(sql, conn)) + { + cmd.Parameters.Add(_Database.CreateParameter("@RegionUUID", regionID)); + cmd.Parameters.Add(_Database.CreateParameter("@Revision", terrainDBRevision)); + cmd.Parameters.Add(_Database.CreateParameter("@Heightfield", terrainDBblob)); + conn.Open(); + cmd.ExecuteNonQuery(); + } } _Log.InfoFormat("{0} Stored terrain revision r={1}", LogHeader, terrainDBRevision); diff --git a/OpenSim/Data/MySQL/MySQLSimulationData.cs b/OpenSim/Data/MySQL/MySQLSimulationData.cs index 4bd861762e..42f2ebb71f 100644 --- a/OpenSim/Data/MySQL/MySQLSimulationData.cs +++ b/OpenSim/Data/MySQL/MySQLSimulationData.cs @@ -613,9 +613,16 @@ namespace OpenSim.Data.MySQL } } + // Legacy region loading public double[,] LoadTerrain(UUID regionID) { - double[,] terrain = null; + TerrainData terrData = LoadTerrain(regionID, (int)Constants.RegionSize, (int)Constants.RegionSize, (int)Constants.RegionHeight); + return terrData.GetDoubles(); + } + + public TerrainData LoadTerrain(UUID regionID, int pSizeX, int pSizeY, int pSizeZ) + { + TerrainData terrData = null; lock (m_dbLock) { @@ -635,32 +642,15 @@ namespace OpenSim.Data.MySQL while (reader.Read()) { int rev = Convert.ToInt32(reader["Revision"]); - - terrain = new double[(int)Constants.RegionSize, (int)Constants.RegionSize]; - terrain.Initialize(); - - using (MemoryStream mstr = new MemoryStream((byte[])reader["Heightfield"])) - { - using (BinaryReader br = new BinaryReader(mstr)) - { - for (int x = 0; x < (int)Constants.RegionSize; x++) - { - for (int y = 0; y < (int)Constants.RegionSize; y++) - { - terrain[x, y] = br.ReadDouble(); - } - } - } - - m_log.InfoFormat("[REGION DB]: Loaded terrain revision r{0}", rev); - } + byte[] blob = (byte[])reader["Heightfield"]; + terrData = TerrainData.CreateFromDatabaseBlobFactory(pSizeX, pSizeY, pSizeZ, rev, blob); } } } } } - return terrain; + return terrData; } public void RemoveLandObject(UUID globalID) diff --git a/OpenSim/Data/Null/NullSimulationData.cs b/OpenSim/Data/Null/NullSimulationData.cs index d11ad72f4e..acde1a164c 100644 --- a/OpenSim/Data/Null/NullSimulationData.cs +++ b/OpenSim/Data/Null/NullSimulationData.cs @@ -132,15 +132,32 @@ namespace OpenSim.Data.Null return new List(); } - Dictionary m_terrains = new Dictionary(); - public void StoreTerrain(double[,] ter, UUID regionID) + Dictionary m_terrains = new Dictionary(); + public void StoreTerrain(TerrainData ter, UUID regionID) { if (m_terrains.ContainsKey(regionID)) m_terrains.Remove(regionID); m_terrains.Add(regionID, ter); } + // Legacy. Just don't do this. + public void StoreTerrain(double[,] ter, UUID regionID) + { + TerrainData terrData = new HeightmapTerrainData(ter); + StoreTerrain(terrData, regionID); + } + + // Legacy. Just don't do this. public double[,] LoadTerrain(UUID regionID) + { + if (m_terrains.ContainsKey(regionID)) + { + return m_terrains[regionID].GetDoubles(); + } + return null; + } + + public TerrainData LoadTerrain(UUID regionID, int pSizeX, int pSizeY, int pSizeZ) { if (m_terrains.ContainsKey(regionID)) { diff --git a/OpenSim/Data/PGSQL/PGSQLSimulationData.cs b/OpenSim/Data/PGSQL/PGSQLSimulationData.cs index 433ffe9a85..34eb038ce5 100644 --- a/OpenSim/Data/PGSQL/PGSQLSimulationData.cs +++ b/OpenSim/Data/PGSQL/PGSQLSimulationData.cs @@ -524,44 +524,44 @@ namespace OpenSim.Data.PGSQL /// public double[,] LoadTerrain(UUID regionID) { - double[,] terrain = new double[(int)Constants.RegionSize, (int)Constants.RegionSize]; - terrain.Initialize(); + TerrainData terrData = LoadTerrain(regionID, (int)Constants.RegionSize, (int)Constants.RegionSize, (int)Constants.RegionHeight); + return terrData.GetDoubles(); + } + + public TerrainData LoadTerrain(UUID regionID, int pSizeX, int pSizeY, int pSizeZ) + { + TerrainData terrData = null; string sql = @"select ""RegionUUID"", ""Revision"", ""Heightfield"" from terrain where ""RegionUUID"" = :RegionUUID order by ""Revision"" desc limit 1; "; using (NpgsqlConnection conn = new NpgsqlConnection(m_connectionString)) - using (NpgsqlCommand cmd = new NpgsqlCommand(sql, conn)) { - // PGSqlParameter param = new PGSqlParameter(); - cmd.Parameters.Add(_Database.CreateParameter("RegionUUID", regionID)); - conn.Open(); - using (NpgsqlDataReader reader = cmd.ExecuteReader()) + using (NpgsqlCommand cmd = new NpgsqlCommand(sql, conn)) { - int rev; - if (reader.Read()) + // PGSqlParameter param = new PGSqlParameter(); + cmd.Parameters.Add(_Database.CreateParameter("RegionUUID", regionID)); + conn.Open(); + using (NpgsqlDataReader reader = cmd.ExecuteReader()) { - MemoryStream str = new MemoryStream((byte[])reader["Heightfield"]); - BinaryReader br = new BinaryReader(str); - for (int x = 0; x < (int)Constants.RegionSize; x++) + int rev; + if (reader.Read()) { - for (int y = 0; y < (int)Constants.RegionSize; y++) - { - terrain[x, y] = br.ReadDouble(); - } + rev = Convert.ToInt32(reader["Revision"]); + byte[] blob = (byte[])reader["Heightfield"]; + terrData = TerrainData.CreateFromDatabaseBlobFactory(pSizeX, pSizeY, pSizeZ, rev, blob); } - rev = (int)reader["Revision"]; + else + { + _Log.Info("[REGION DB]: No terrain found for region"); + return null; + } + _Log.Info("[REGION DB]: Loaded terrain revision r" + rev); } - else - { - _Log.Info("[REGION DB]: No terrain found for region"); - return null; - } - _Log.Info("[REGION DB]: Loaded terrain revision r" + rev); } } - return terrain; + return terrData; } // Legacy entry point for when terrain was always a 256x256 heightmap diff --git a/OpenSim/Data/SQLite/SQLiteSimulationData.cs b/OpenSim/Data/SQLite/SQLiteSimulationData.cs index cce59c1e83..dac44503ea 100644 --- a/OpenSim/Data/SQLite/SQLiteSimulationData.cs +++ b/OpenSim/Data/SQLite/SQLiteSimulationData.cs @@ -870,11 +870,16 @@ namespace OpenSim.Data.SQLite /// Heightfield data public double[,] LoadTerrain(UUID regionID) { + TerrainData terrData = LoadTerrain(regionID, (int)Constants.RegionSize, (int)Constants.RegionSize, (int)Constants.RegionHeight); + return terrData.GetDoubles(); + } + + public TerrainData LoadTerrain(UUID regionID, int pSizeX, int pSizeY, int pSizeZ) + { + TerrainData terrData = null; + lock (ds) { - double[,] terret = new double[(int)Constants.RegionSize, (int)Constants.RegionSize]; - terret.Initialize(); - String sql = "select RegionUUID, Revision, Heightfield from terrain" + " where RegionUUID=:RegionUUID order by Revision desc"; @@ -887,21 +892,9 @@ namespace OpenSim.Data.SQLite int rev = 0; if (row.Read()) { - // TODO: put this into a function - using (MemoryStream str = new MemoryStream((byte[])row["Heightfield"])) - { - using (BinaryReader br = new BinaryReader(str)) - { - for (int x = 0; x < (int)Constants.RegionSize; x++) - { - for (int y = 0; y < (int)Constants.RegionSize; y++) - { - terret[x, y] = br.ReadDouble(); - } - } - } - } rev = Convert.ToInt32(row["Revision"]); + byte[] blob = (byte[])row["Heightfield"]; + terrData = TerrainData.CreateFromDatabaseBlobFactory(pSizeX, pSizeY, pSizeZ, rev, blob); } else { @@ -912,8 +905,8 @@ namespace OpenSim.Data.SQLite m_log.Debug("[SQLITE REGION DB]: Loaded terrain revision r" + rev.ToString()); } } - return terret; } + return terrData; } public void RemoveLandObject(UUID globalID) diff --git a/OpenSim/Framework/TerrainData.cs b/OpenSim/Framework/TerrainData.cs index d7f1655155..58b203f3cf 100644 --- a/OpenSim/Framework/TerrainData.cs +++ b/OpenSim/Framework/TerrainData.cs @@ -43,6 +43,9 @@ namespace OpenSim.Framework public int SizeY { get; protected set; } public int SizeZ { get; protected set; } + // A height used when the user doesn't specify anything + public const float DefaultTerrainHeight = 21f; + public abstract float this[int x, int y] { get; set; } // Someday terrain will have caves public abstract float this[int x, int y, int z] { get; set; } @@ -51,15 +54,28 @@ namespace OpenSim.Framework public abstract bool IsTaintedAt(int xx, int yy); public abstract void ClearTaint(); + public abstract void ClearLand(); + public abstract void ClearLand(float height); + // Return a representation of this terrain for storing as a blob in the database. // Returns 'true' to say blob was stored in the 'out' locations. public abstract bool GetDatabaseBlob(out int DBFormatRevisionCode, out Array blob); + // Given a revision code and a blob from the database, create and return the right type of TerrainData. + // The sizes passed are the expected size of the region. The database info will be used to + // initialize the heightmap of that sized region with as much data is in the blob. + // Return created TerrainData or 'null' if unsuccessful. + public static TerrainData CreateFromDatabaseBlobFactory(int pSizeX, int pSizeY, int pSizeZ, int pFormatCode, byte[] pBlob) + { + // For the moment, there is only one implementation class + return new HeightmapTerrainData(pSizeX, pSizeY, pSizeZ, pFormatCode, pBlob); + } + // return a special compressed representation of the heightmap in shorts public abstract short[] GetCompressedMap(); public abstract float CompressionFactor { get; } - public abstract void SetCompressedMap(short[] cmap, float pCompressionFactor); + public abstract double[,] GetDoubles(); public abstract TerrainData Clone(); } @@ -76,9 +92,14 @@ namespace OpenSim.Framework { // Terrain is 'double[256,256]' Legacy256 = 11, - // Terrain is 'int32, int32, float[,]' where the shorts are X and Y dimensions + // Terrain is 'int32, int32, float[,]' where the ints are X and Y dimensions // The dimensions are presumed to be multiples of 16 and, more likely, multiples of 256. Variable2D = 22, + // Terrain is 'int32, int32, int32, int16[]' where the ints are X and Y dimensions + // and third int is the 'compression factor'. The heights are compressed as + // "short compressedHeight = (short)(height * compressionFactor);" + // The dimensions are presumed to be multiples of 16 and, more likely, multiples of 256. + Compressed2D = 27, // A revision that is not listed above or any revision greater than this value is 'Legacy256'. RevisionHigh = 1234 } @@ -124,6 +145,20 @@ namespace OpenSim.Framework m_taint[ii, jj] = false; } + // TerrainData.ClearLand + public override void ClearLand() + { + ClearLand(DefaultTerrainHeight); + } + // TerrainData.ClearLand(float) + public override void ClearLand(float pHeight) + { + short flatHeight = ToCompressedHeight(pHeight); + for (int xx = 0; xx < SizeX; xx++) + for (int yy = 0; yy < SizeY; yy++) + m_heightmap[xx, yy] = flatHeight; + } + public override bool IsTaintedAt(int xx, int yy) { return m_taint[xx / Constants.TerrainPatchSize, yy / Constants.TerrainPatchSize]; @@ -134,7 +169,7 @@ namespace OpenSim.Framework public override bool GetDatabaseBlob(out int DBRevisionCode, out Array blob) { DBRevisionCode = (int)DBTerrainRevision.Legacy256; - blob = LegacyTerrainSerialization(); + blob = ToLegacyTerrainSerialization(); return false; } @@ -155,17 +190,6 @@ namespace OpenSim.Framework return newMap; } - // TerrainData.SetCompressedMap - public override void SetCompressedMap(short[] cmap, float pCompressionFactor) - { - m_compressionFactor = pCompressionFactor; - - int ind = 0; - for (int xx = 0; xx < SizeX; xx++) - for (int yy = 0; yy < SizeY; yy++) - m_heightmap[xx, yy] = cmap[ind++]; - } - // TerrainData.Clone public override TerrainData Clone() { @@ -174,6 +198,18 @@ namespace OpenSim.Framework return ret; } + // TerrainData.GetDoubles + public override double[,] GetDoubles() + { + double[,] ret = new double[SizeX, SizeY]; + for (int xx = 0; xx < SizeX; xx++) + for (int yy = 0; yy < SizeY; yy++) + ret[xx, yy] = FromCompressedHeight(m_heightmap[xx, yy]); + + return ret; + } + + // ============================================================= private short[,] m_heightmap; @@ -230,31 +266,140 @@ namespace OpenSim.Framework public HeightmapTerrainData(short[] cmap, float pCompressionFactor, int pX, int pY, int pZ) : this(pX, pY, pZ) { - SetCompressedMap(cmap, pCompressionFactor); + m_compressionFactor = pCompressionFactor; + int ind = 0; + for (int xx = 0; xx < SizeX; xx++) + for (int yy = 0; yy < SizeY; yy++) + m_heightmap[xx, yy] = cmap[ind++]; } + // Create a heighmap from a database blob + public HeightmapTerrainData(int pSizeX, int pSizeY, int pSizeZ, int pFormatCode, byte[] pBlob) : this(pSizeX, pSizeY, pSizeZ) + { + switch ((DBTerrainRevision)pFormatCode) + { + case DBTerrainRevision.Compressed2D: + FromCompressedTerrainSerialization(pBlob); + break; + default: + FromLegacyTerrainSerialization(pBlob); + break; + } + } // Just create an array of doubles. Presumes the caller implicitly knows the size. - public Array LegacyTerrainSerialization() + public Array ToLegacyTerrainSerialization() { Array ret = null; - using (MemoryStream str = new MemoryStream(SizeX * SizeY * sizeof(double))) + + using (MemoryStream str = new MemoryStream((int)Constants.RegionSize * (int)Constants.RegionSize * sizeof(double))) { using (BinaryWriter bw = new BinaryWriter(str)) { - // TODO: COMPATIBILITY - Add byte-order conversions - for (int ii = 0; ii < SizeX; ii++) - for (int jj = 0; jj < SizeY; jj++) + for (int xx = 0; xx < Constants.RegionSize; xx++) { - double height = this[ii, jj]; - if (height == 0.0) - height = double.Epsilon; - bw.Write(height); + for (int yy = 0; yy < Constants.RegionSize; yy++) + { + double height = this[xx, yy]; + if (height == 0.0) + height = double.Epsilon; + bw.Write(height); + } } } ret = str.ToArray(); } return ret; } + + // Just create an array of doubles. Presumes the caller implicitly knows the size. + public void FromLegacyTerrainSerialization(byte[] pBlob) + { + // In case database info doesn't match real terrain size, initialize the whole terrain. + ClearLand(); + + using (MemoryStream mstr = new MemoryStream(pBlob)) + { + using (BinaryReader br = new BinaryReader(mstr)) + { + for (int xx = 0; xx < (int)Constants.RegionSize; xx++) + { + for (int yy = 0; yy < (int)Constants.RegionSize; yy++) + { + float val = (float)br.ReadDouble(); + if (xx < SizeX && yy < SizeY) + m_heightmap[xx, yy] = ToCompressedHeight(val); + } + } + } + ClearTaint(); + + m_log.InfoFormat("{0} Loaded legacy heightmap. SizeX={1}, SizeY={2}", LogHeader, SizeX, SizeY); + } + } + + // See the reader below. + public Array ToCompressedTerrainSerialization() + { + Array ret = null; + using (MemoryStream str = new MemoryStream((3 * sizeof(Int32)) + (SizeX * SizeY * sizeof(Int16)))) + { + using (BinaryWriter bw = new BinaryWriter(str)) + { + bw.Write((Int32)DBTerrainRevision.Compressed2D); + bw.Write((Int32)SizeX); + bw.Write((Int32)SizeY); + bw.Write((Int32)CompressionFactor); + for (int yy = 0; yy < SizeY; yy++) + for (int xx = 0; xx < SizeX; xx++) + { + bw.Write((Int16)m_heightmap[xx, yy]); + } + } + ret = str.ToArray(); + } + return ret; + } + + // Initialize heightmap from blob consisting of: + // int32, int32, int32, int32, int16[] + // where the first int32 is format code, next two int32s are the X and y of heightmap data and + // the forth int is the compression factor for the following int16s + // This is just sets heightmap info. The actual size of the region was set on this instance's + // creation and any heights not initialized by theis blob are set to the default height. + public void FromCompressedTerrainSerialization(byte[] pBlob) + { + Int32 hmFormatCode, hmSizeX, hmSizeY, hmCompressionFactor; + + using (MemoryStream mstr = new MemoryStream(pBlob)) + { + using (BinaryReader br = new BinaryReader(mstr)) + { + hmFormatCode = br.ReadInt32(); + hmSizeX = br.ReadInt32(); + hmSizeY = br.ReadInt32(); + hmCompressionFactor = br.ReadInt32(); + + m_compressionFactor = hmCompressionFactor; + + // In case database info doesn't match real terrain size, initialize the whole terrain. + ClearLand(); + + for (int yy = 0; yy < hmSizeY; yy++) + { + for (int xx = 0; xx < hmSizeX; xx++) + { + Int16 val = br.ReadInt16(); + if (xx < SizeX && yy < SizeY) + m_heightmap[xx, yy] = ToCompressedHeight(val); + } + } + } + ClearTaint(); + + m_log.InfoFormat("{0} Read compressed 2d heightmap. Heightmap size=<{1},{2}>. Region size={<{3},{4}>. CompFact={5}", LogHeader, + hmSizeX, hmSizeY, SizeX, SizeY, hmCompressionFactor); + } + } } } diff --git a/OpenSim/Region/Framework/Interfaces/ISimulationDataService.cs b/OpenSim/Region/Framework/Interfaces/ISimulationDataService.cs index 085b5caf69..8948f0449f 100644 --- a/OpenSim/Region/Framework/Interfaces/ISimulationDataService.cs +++ b/OpenSim/Region/Framework/Interfaces/ISimulationDataService.cs @@ -68,13 +68,22 @@ namespace OpenSim.Region.Framework.Interfaces /// /// HeightField data /// region UUID + void StoreTerrain(TerrainData terrain, UUID regionID); + + // Legacy version kept for downward compabibility void StoreTerrain(double[,] terrain, UUID regionID); /// /// Load the latest terrain revision from region storage /// /// the region UUID + /// the X dimension of the region being filled + /// the Y dimension of the region being filled + /// the Z dimension of the region being filled /// Heightfield data + TerrainData LoadTerrain(UUID regionID, int pSizeX, int pSizeY, int pSizeZ); + + // Legacy version kept for downward compabibility double[,] LoadTerrain(UUID regionID); void StoreLandObject(ILandObject Parcel); diff --git a/OpenSim/Region/Framework/Interfaces/ISimulationDataStore.cs b/OpenSim/Region/Framework/Interfaces/ISimulationDataStore.cs index 5ba5b31793..917b5d18d4 100644 --- a/OpenSim/Region/Framework/Interfaces/ISimulationDataStore.cs +++ b/OpenSim/Region/Framework/Interfaces/ISimulationDataStore.cs @@ -79,13 +79,22 @@ namespace OpenSim.Region.Framework.Interfaces /// /// HeightField data /// region UUID + void StoreTerrain(TerrainData terrain, UUID regionID); + + // Legacy version kept for downward compabibility void StoreTerrain(double[,] terrain, UUID regionID); /// /// Load the latest terrain revision from region storage /// /// the region UUID + /// the X dimension of the terrain being filled + /// the Y dimension of the terrain being filled + /// the Z dimension of the terrain being filled /// Heightfield data + TerrainData LoadTerrain(UUID regionID, int pSizeX, int pSizeY, int pSizeZ); + + // Legacy version kept for downward compabibility double[,] LoadTerrain(UUID regionID); void StoreLandObject(ILandObject Parcel); diff --git a/OpenSim/Region/Framework/Scenes/Scene.cs b/OpenSim/Region/Framework/Scenes/Scene.cs index e2880e389f..a19f31f489 100644 --- a/OpenSim/Region/Framework/Scenes/Scene.cs +++ b/OpenSim/Region/Framework/Scenes/Scene.cs @@ -1894,7 +1894,7 @@ namespace OpenSim.Region.Framework.Scenes { try { - double[,] map = SimulationDataService.LoadTerrain(RegionInfo.RegionID); + TerrainData map = SimulationDataService.LoadTerrain(RegionInfo.RegionID, (int)RegionInfo.RegionSizeX, (int)RegionInfo.RegionSizeY, (int)RegionInfo.RegionSizeZ); if (map == null) { // This should be in the Terrain module, but it isn't because @@ -1911,7 +1911,7 @@ namespace OpenSim.Region.Framework.Scenes } else { - Heightmap = new TerrainChannel(map, RegionInfo.RegionSizeZ); + Heightmap = new TerrainChannel(map); } } catch (IOException e) diff --git a/OpenSim/Region/Framework/Scenes/TerrainChannel.cs b/OpenSim/Region/Framework/Scenes/TerrainChannel.cs index d641c878bc..03499e8f51 100644 --- a/OpenSim/Region/Framework/Scenes/TerrainChannel.cs +++ b/OpenSim/Region/Framework/Scenes/TerrainChannel.cs @@ -80,9 +80,26 @@ namespace OpenSim.Region.Framework.Scenes PinHeadIsland(); } - public TerrainChannel(double[,] pM, uint pAltitude) + // Create channel passed a heightmap and expected dimensions of the region. + // The heightmap might not fit the passed size so accomodations must be made. + public TerrainChannel(double[,] pM, int pSizeX, int pSizeY, int pAltitude) { - m_terrainData = new HeightmapTerrainData(pM); + int hmSizeX = pM.GetLength(0); + int hmSizeY = pM.GetLength(1); + + m_terrainData = new HeightmapTerrainData(pSizeX, pSizeY, pAltitude); + + for (int xx = 0; xx < pSizeX; xx++) + for (int yy = 0; yy < pSizeY; yy++) + if (xx > hmSizeX || yy > hmSizeY) + m_terrainData[xx, yy] = TerrainData.DefaultTerrainHeight; + else + m_terrainData[xx, yy] = (float)pM[xx, yy]; + } + + public TerrainChannel(TerrainData pTerrData) + { + m_terrainData = pTerrData; } #region ITerrainChannel Members @@ -247,7 +264,7 @@ namespace OpenSim.Region.Framework.Scenes byte[] dataArray = (byte[])serializer.Deserialize(xmlReader); int index = 0; - m_terrainData = new HeightmapTerrainData(Width, Height, Altitude); + m_terrainData = new HeightmapTerrainData((int)Constants.RegionSize, (int)Constants.RegionSize, (int)Constants.RegionHeight); for (int y = 0; y < Height; y++) { @@ -317,9 +334,7 @@ namespace OpenSim.Region.Framework.Scenes private void FlatLand() { - for (int xx = 0; xx < Width; xx++) - for (int yy = 0; yy < Height; yy++) - m_terrainData[xx, yy] = 21; + m_terrainData.ClearLand(); } } } diff --git a/OpenSim/Services/Connectors/Simulation/SimulationDataService.cs b/OpenSim/Services/Connectors/Simulation/SimulationDataService.cs index 504fcaf1e9..2cbf9676ab 100644 --- a/OpenSim/Services/Connectors/Simulation/SimulationDataService.cs +++ b/OpenSim/Services/Connectors/Simulation/SimulationDataService.cs @@ -100,6 +100,11 @@ namespace OpenSim.Services.Connectors return m_database.LoadObjects(regionUUID); } + public void StoreTerrain(TerrainData terrain, UUID regionID) + { + m_database.StoreTerrain(terrain, regionID); + } + public void StoreTerrain(double[,] terrain, UUID regionID) { m_database.StoreTerrain(terrain, regionID); @@ -110,6 +115,11 @@ namespace OpenSim.Services.Connectors return m_database.LoadTerrain(regionID); } + public TerrainData LoadTerrain(UUID regionID, int pSizeX, int pSizeY, int pSizeZ) + { + return m_database.LoadTerrain(regionID, pSizeX, pSizeY, pSizeZ); + } + public void StoreLandObject(ILandObject Parcel) { m_database.StoreLandObject(Parcel); diff --git a/OpenSim/Tests/Common/Mock/MockRegionDataPlugin.cs b/OpenSim/Tests/Common/Mock/MockRegionDataPlugin.cs index ed29c39378..5df8e0456f 100644 --- a/OpenSim/Tests/Common/Mock/MockRegionDataPlugin.cs +++ b/OpenSim/Tests/Common/Mock/MockRegionDataPlugin.cs @@ -69,11 +69,21 @@ namespace OpenSim.Data.Null m_store.StoreTerrain(terrain, regionID); } + public void StoreTerrain(TerrainData terrain, UUID regionID) + { + m_store.StoreTerrain(terrain, regionID); + } + public double[,] LoadTerrain(UUID regionID) { return m_store.LoadTerrain(regionID); } + public TerrainData LoadTerrain(UUID regionID, int pSizeX, int pSizeY, int pSizeZ) + { + return m_store.LoadTerrain(regionID, pSizeX, pSizeY, pSizeZ); + } + public void StoreLandObject(ILandObject Parcel) { m_store.StoreLandObject(Parcel); @@ -154,7 +164,7 @@ namespace OpenSim.Data.Null protected Dictionary m_sceneObjectParts = new Dictionary(); protected Dictionary> m_primItems = new Dictionary>(); - protected Dictionary m_terrains = new Dictionary(); + protected Dictionary m_terrains = new Dictionary(); protected Dictionary m_landData = new Dictionary(); public void Initialise(string dbfile) @@ -299,15 +309,28 @@ namespace OpenSim.Data.Null return new List(objects.Values); } - public void StoreTerrain(double[,] ter, UUID regionID) + public void StoreTerrain(TerrainData ter, UUID regionID) { m_terrains[regionID] = ter; } + public void StoreTerrain(double[,] ter, UUID regionID) + { + m_terrains[regionID] = new HeightmapTerrainData(ter); + } + + public TerrainData LoadTerrain(UUID regionID, int pSizeX, int pSizeY, int pSizeZ) + { + if (m_terrains.ContainsKey(regionID)) + return m_terrains[regionID]; + else + return null; + } + public double[,] LoadTerrain(UUID regionID) { if (m_terrains.ContainsKey(regionID)) - return m_terrains[regionID]; + return m_terrains[regionID].GetDoubles(); else return null; }