aboutsummaryrefslogtreecommitdiffstatshomepage
diff options
context:
space:
mode:
authorRobert Adams2015-03-27 19:32:50 -0700
committerRobert Adams2015-03-27 19:32:50 -0700
commitbedafb8fae9898ef0c5fc6470236ee7244e616a9 (patch)
tree618728d92dad56dd587c243892d966b9ff9ea89b
parentvarregion: restore checkAgentAccessToRegion routine in EntityTransferModule. (diff)
downloadopensim-SC_OLD-bedafb8fae9898ef0c5fc6470236ee7244e616a9.zip
opensim-SC_OLD-bedafb8fae9898ef0c5fc6470236ee7244e616a9.tar.gz
opensim-SC_OLD-bedafb8fae9898ef0c5fc6470236ee7244e616a9.tar.bz2
opensim-SC_OLD-bedafb8fae9898ef0c5fc6470236ee7244e616a9.tar.xz
varregion: refactor use of 'double heightmap[,]' into references to new class TerrainData
and push the implementation from Scene into the database readers and writers.
-rw-r--r--OpenSim/Data/MSSQL/MSSQLSimulationData.cs83
-rw-r--r--OpenSim/Data/MySQL/MySQLSimulationData.cs66
-rw-r--r--OpenSim/Data/Null/NullSimulationData.cs22
-rw-r--r--OpenSim/Data/PGSQL/PGSQLSimulationData.cs72
-rw-r--r--OpenSim/Data/SQLite/SQLiteSimulationData.cs54
-rw-r--r--OpenSim/Framework/TerrainData.cs464
-rw-r--r--OpenSim/Region/CoreModules/World/Terrain/FileLoaders/GenericSystemDrawing.cs2
-rw-r--r--OpenSim/Region/CoreModules/World/Terrain/TerrainModule.cs488
-rw-r--r--OpenSim/Region/Framework/Interfaces/ISimulationDataService.cs9
-rw-r--r--OpenSim/Region/Framework/Interfaces/ISimulationDataStore.cs10
-rw-r--r--OpenSim/Region/Framework/Interfaces/ITerrainChannel.cs19
-rw-r--r--OpenSim/Region/Framework/Scenes/Scene.cs4
-rw-r--r--OpenSim/Region/Framework/Scenes/TerrainChannel.cs352
-rw-r--r--OpenSim/Services/Connectors/Simulation/SimulationDataService.cs10
-rw-r--r--OpenSim/Tests/Common/Mock/MockRegionDataPlugin.cs29
15 files changed, 1359 insertions, 325 deletions
diff --git a/OpenSim/Data/MSSQL/MSSQLSimulationData.cs b/OpenSim/Data/MSSQL/MSSQLSimulationData.cs
index 0d09be6..145b9c0 100644
--- a/OpenSim/Data/MSSQL/MSSQLSimulationData.cs
+++ b/OpenSim/Data/MSSQL/MSSQLSimulationData.cs
@@ -530,43 +530,52 @@ ELSE
530 /// <returns></returns> 530 /// <returns></returns>
531 public double[,] LoadTerrain(UUID regionID) 531 public double[,] LoadTerrain(UUID regionID)
532 { 532 {
533 double[,] terrain = new double[(int)Constants.RegionSize, (int)Constants.RegionSize]; 533 double[,] ret = null;
534 terrain.Initialize(); 534 TerrainData terrData = LoadTerrain(regionID, (int)Constants.RegionSize, (int)Constants.RegionSize, (int)Constants.RegionHeight);
535 if (terrData != null)
536 ret = terrData.GetDoubles();
537 return ret;
538 }
539
540 // Returns 'null' if region not found
541 public TerrainData LoadTerrain(UUID regionID, int pSizeX, int pSizeY, int pSizeZ)
542 {
543 TerrainData terrData = null;
535 544
536 string sql = "select top 1 RegionUUID, Revision, Heightfield from terrain where RegionUUID = @RegionUUID order by Revision desc"; 545 string sql = "select top 1 RegionUUID, Revision, Heightfield from terrain where RegionUUID = @RegionUUID order by Revision desc";
537 546
538 using (SqlConnection conn = new SqlConnection(m_connectionString)) 547 using (SqlConnection conn = new SqlConnection(m_connectionString))
539 using (SqlCommand cmd = new SqlCommand(sql, conn))
540 { 548 {
541 // MySqlParameter param = new MySqlParameter(); 549 using (SqlCommand cmd = new SqlCommand(sql, conn))
542 cmd.Parameters.Add(_Database.CreateParameter("@RegionUUID", regionID));
543 conn.Open();
544 using (SqlDataReader reader = cmd.ExecuteReader())
545 { 550 {
546 int rev; 551 // MySqlParameter param = new MySqlParameter();
547 if (reader.Read()) 552 cmd.Parameters.Add(_Database.CreateParameter("@RegionUUID", regionID));
553 conn.Open();
554 using (SqlDataReader reader = cmd.ExecuteReader())
548 { 555 {
549 MemoryStream str = new MemoryStream((byte[])reader["Heightfield"]); 556 if (reader.Read())
550 BinaryReader br = new BinaryReader(str);
551 for (int x = 0; x < (int)Constants.RegionSize; x++)
552 { 557 {
553 for (int y = 0; y < (int)Constants.RegionSize; y++) 558 int rev = (int)reader["Revision"];
554 { 559 byte[] blob = (byte[])reader["Heightfield"];
555 terrain[x, y] = br.ReadDouble(); 560 terrData = TerrainData.CreateFromDatabaseBlobFactory(pSizeX, pSizeY, pSizeZ, rev, blob);
556 }
557 } 561 }
558 rev = (int)reader["Revision"]; 562 else
559 } 563 {
560 else 564 _Log.Info("[REGION DB]: No terrain found for region");
561 { 565 return null;
562 _Log.Info("[REGION DB]: No terrain found for region"); 566 }
563 return null; 567 _Log.Info("[REGION DB]: Loaded terrain");
564 } 568 }
565 _Log.Info("[REGION DB]: Loaded terrain revision r" + rev);
566 } 569 }
567 } 570 }
568 571
569 return terrain; 572 return terrData;
573 }
574
575 // Legacy entry point for when terrain was always a 256x256 hieghtmap
576 public void StoreTerrain(double[,] ter, UUID regionID)
577 {
578 StoreTerrain(new HeightmapTerrainData(ter), regionID);
570 } 579 }
571 580
572 /// <summary> 581 /// <summary>
@@ -574,10 +583,8 @@ ELSE
574 /// </summary> 583 /// </summary>
575 /// <param name="terrain">terrain map data.</param> 584 /// <param name="terrain">terrain map data.</param>
576 /// <param name="regionID">regionID.</param> 585 /// <param name="regionID">regionID.</param>
577 public void StoreTerrain(double[,] terrain, UUID regionID) 586 public void StoreTerrain(TerrainData terrData, UUID regionID)
578 { 587 {
579 int revision = Util.UnixTimeSinceEpoch();
580
581 //Delete old terrain map 588 //Delete old terrain map
582 string sql = "delete from terrain where RegionUUID=@RegionUUID"; 589 string sql = "delete from terrain where RegionUUID=@RegionUUID";
583 using (SqlConnection conn = new SqlConnection(m_connectionString)) 590 using (SqlConnection conn = new SqlConnection(m_connectionString))
@@ -590,17 +597,23 @@ ELSE
590 597
591 sql = "insert into terrain(RegionUUID, Revision, Heightfield) values(@RegionUUID, @Revision, @Heightfield)"; 598 sql = "insert into terrain(RegionUUID, Revision, Heightfield) values(@RegionUUID, @Revision, @Heightfield)";
592 599
600 int terrainDBRevision;
601 Array terrainDBblob;
602 terrData.GetDatabaseBlob(out terrainDBRevision, out terrainDBblob);
603
593 using (SqlConnection conn = new SqlConnection(m_connectionString)) 604 using (SqlConnection conn = new SqlConnection(m_connectionString))
594 using (SqlCommand cmd = new SqlCommand(sql, conn))
595 { 605 {
596 cmd.Parameters.Add(_Database.CreateParameter("@RegionUUID", regionID)); 606 using (SqlCommand cmd = new SqlCommand(sql, conn))
597 cmd.Parameters.Add(_Database.CreateParameter("@Revision", revision)); 607 {
598 cmd.Parameters.Add(_Database.CreateParameter("@Heightfield", serializeTerrain(terrain))); 608 cmd.Parameters.Add(_Database.CreateParameter("@RegionUUID", regionID));
599 conn.Open(); 609 cmd.Parameters.Add(_Database.CreateParameter("@Revision", terrainDBRevision));
600 cmd.ExecuteNonQuery(); 610 cmd.Parameters.Add(_Database.CreateParameter("@Heightfield", terrainDBblob));
611 conn.Open();
612 cmd.ExecuteNonQuery();
613 }
601 } 614 }
602 615
603 _Log.Info("[REGION DB]: Stored terrain revision r " + revision); 616 _Log.Info("[REGION DB]: Stored terrain");
604 } 617 }
605 618
606 /// <summary> 619 /// <summary>
@@ -1344,6 +1357,7 @@ VALUES
1344 1357
1345 #region Private Methods 1358 #region Private Methods
1346 1359
1360 /*
1347 /// <summary> 1361 /// <summary>
1348 /// Serializes the terrain data for storage in DB. 1362 /// Serializes the terrain data for storage in DB.
1349 /// </summary> 1363 /// </summary>
@@ -1367,6 +1381,7 @@ VALUES
1367 1381
1368 return str.ToArray(); 1382 return str.ToArray();
1369 } 1383 }
1384 */
1370 1385
1371 /// <summary> 1386 /// <summary>
1372 /// Stores new regionsettings. 1387 /// Stores new regionsettings.
diff --git a/OpenSim/Data/MySQL/MySQLSimulationData.cs b/OpenSim/Data/MySQL/MySQLSimulationData.cs
index cc844ae..f910e44 100644
--- a/OpenSim/Data/MySQL/MySQLSimulationData.cs
+++ b/OpenSim/Data/MySQL/MySQLSimulationData.cs
@@ -48,8 +48,18 @@ namespace OpenSim.Data.MySQL
48 public class MySQLSimulationData : ISimulationDataStore 48 public class MySQLSimulationData : ISimulationDataStore
49 { 49 {
50 private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); 50 private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
51 private static string LogHeader = "[REGION DB MYSQL]";
51 52
52 private string m_connectionString; 53 private string m_connectionString;
54
55 /// <summary>
56 /// This lock was being used to serialize database operations when the connection was shared, but this has
57 /// been unnecessary for a long time after we switched to using MySQL's underlying connection pooling instead.
58 /// FIXME: However, the locks remain in many places since they are effectively providing a level of
59 /// transactionality. This should be replaced by more efficient database transactions which would not require
60 /// unrelated operations to block each other or unrelated operations on the same tables from blocking each
61 /// other.
62 /// </summary>
53 private object m_dbLock = new object(); 63 private object m_dbLock = new object();
54 64
55 protected virtual Assembly Assembly 65 protected virtual Assembly Assembly
@@ -91,7 +101,7 @@ namespace OpenSim.Data.MySQL
91 } 101 }
92 catch (Exception e) 102 catch (Exception e)
93 { 103 {
94 m_log.Error("[REGION DB]: MySQL error in ExecuteReader: " + e.Message); 104 m_log.ErrorFormat("{0} MySQL error in ExecuteReader: {1}", LogHeader, e);
95 throw; 105 throw;
96 } 106 }
97 107
@@ -574,12 +584,16 @@ namespace OpenSim.Data.MySQL
574 } 584 }
575 } 585 }
576 586
577 public virtual void StoreTerrain(double[,] ter, UUID regionID) 587 // Legacy entry point for when terrain was always a 256x256 hieghtmap
588 public void StoreTerrain(double[,] ter, UUID regionID)
589 {
590 StoreTerrain(new HeightmapTerrainData(ter), regionID);
591 }
592
593 public void StoreTerrain(TerrainData terrData, UUID regionID)
578 { 594 {
579 Util.FireAndForget(delegate(object x) 595 Util.FireAndForget(delegate(object x)
580 { 596 {
581 double[,] oldTerrain = LoadTerrain(regionID);
582
583 m_log.Info("[REGION DB]: Storing terrain"); 597 m_log.Info("[REGION DB]: Storing terrain");
584 598
585 lock (m_dbLock) 599 lock (m_dbLock)
@@ -601,8 +615,12 @@ namespace OpenSim.Data.MySQL
601 "Revision, Heightfield) values (?RegionUUID, " + 615 "Revision, Heightfield) values (?RegionUUID, " +
602 "1, ?Heightfield)"; 616 "1, ?Heightfield)";
603 617
604 cmd2.Parameters.AddWithValue("RegionUUID", regionID.ToString()); 618 int terrainDBRevision;
605 cmd2.Parameters.AddWithValue("Heightfield", SerializeTerrain(ter, oldTerrain)); 619 Array terrainDBblob;
620 terrData.GetDatabaseBlob(out terrainDBRevision, out terrainDBblob);
621
622 cmd2.Parameters.AddWithValue("Revision", terrainDBRevision);
623 cmd2.Parameters.AddWithValue("Heightfield", terrainDBblob);
606 624
607 ExecuteNonQuery(cmd); 625 ExecuteNonQuery(cmd);
608 ExecuteNonQuery(cmd2); 626 ExecuteNonQuery(cmd2);
@@ -618,9 +636,20 @@ namespace OpenSim.Data.MySQL
618 }); 636 });
619 } 637 }
620 638
639 // Legacy region loading
621 public virtual double[,] LoadTerrain(UUID regionID) 640 public virtual double[,] LoadTerrain(UUID regionID)
622 { 641 {
623 double[,] terrain = null; 642 double[,] ret = null;
643 TerrainData terrData = LoadTerrain(regionID, (int)Constants.RegionSize, (int)Constants.RegionSize, (int)Constants.RegionHeight);
644 if (terrData != null)
645 ret = terrData.GetDoubles();
646 return ret;
647 }
648
649 // Returns 'null' if region not found
650 public TerrainData LoadTerrain(UUID regionID, int pSizeX, int pSizeY, int pSizeZ)
651 {
652 TerrainData terrData = null;
624 653
625 lock (m_dbLock) 654 lock (m_dbLock)
626 { 655 {
@@ -640,32 +669,15 @@ namespace OpenSim.Data.MySQL
640 while (reader.Read()) 669 while (reader.Read())
641 { 670 {
642 int rev = Convert.ToInt32(reader["Revision"]); 671 int rev = Convert.ToInt32(reader["Revision"]);
643 672 byte[] blob = (byte[])reader["Heightfield"];
644 terrain = new double[(int)Constants.RegionSize, (int)Constants.RegionSize]; 673 terrData = TerrainData.CreateFromDatabaseBlobFactory(pSizeX, pSizeY, pSizeZ, rev, blob);
645 terrain.Initialize();
646
647 using (MemoryStream mstr = new MemoryStream((byte[])reader["Heightfield"]))
648 {
649 using (BinaryReader br = new BinaryReader(mstr))
650 {
651 for (int x = 0; x < (int)Constants.RegionSize; x++)
652 {
653 for (int y = 0; y < (int)Constants.RegionSize; y++)
654 {
655 terrain[x, y] = br.ReadDouble();
656 }
657 }
658 }
659
660 m_log.InfoFormat("[REGION DB]: Loaded terrain revision r{0}", rev);
661 }
662 } 674 }
663 } 675 }
664 } 676 }
665 } 677 }
666 } 678 }
667 679
668 return terrain; 680 return terrData;
669 } 681 }
670 682
671 public virtual void RemoveLandObject(UUID globalID) 683 public virtual void RemoveLandObject(UUID globalID)
diff --git a/OpenSim/Data/Null/NullSimulationData.cs b/OpenSim/Data/Null/NullSimulationData.cs
index 15824a9..339e7f4 100644
--- a/OpenSim/Data/Null/NullSimulationData.cs
+++ b/OpenSim/Data/Null/NullSimulationData.cs
@@ -132,18 +132,36 @@ namespace OpenSim.Data.Null
132 return new List<SceneObjectGroup>(); 132 return new List<SceneObjectGroup>();
133 } 133 }
134 134
135 Dictionary<UUID, double[,]> m_terrains = new Dictionary<UUID, double[,]>(); 135 Dictionary<UUID, TerrainData> m_terrains = new Dictionary<UUID, TerrainData>();
136 public void StoreTerrain(double[,] ter, UUID regionID) 136 public void StoreTerrain(TerrainData ter, UUID regionID)
137 { 137 {
138 if (m_terrains.ContainsKey(regionID)) 138 if (m_terrains.ContainsKey(regionID))
139 m_terrains.Remove(regionID); 139 m_terrains.Remove(regionID);
140 m_terrains.Add(regionID, ter); 140 m_terrains.Add(regionID, ter);
141 } 141 }
142 142
143 // Legacy. Just don't do this.
144 public void StoreTerrain(double[,] ter, UUID regionID)
145 {
146 TerrainData terrData = new HeightmapTerrainData(ter);
147 StoreTerrain(terrData, regionID);
148 }
149
150 // Legacy. Just don't do this.
151 // Returns 'null' if region not found
143 public double[,] LoadTerrain(UUID regionID) 152 public double[,] LoadTerrain(UUID regionID)
144 { 153 {
145 if (m_terrains.ContainsKey(regionID)) 154 if (m_terrains.ContainsKey(regionID))
146 { 155 {
156 return m_terrains[regionID].GetDoubles();
157 }
158 return null;
159 }
160
161 public TerrainData LoadTerrain(UUID regionID, int pSizeX, int pSizeY, int pSizeZ)
162 {
163 if (m_terrains.ContainsKey(regionID))
164 {
147 return m_terrains[regionID]; 165 return m_terrains[regionID];
148 } 166 }
149 return null; 167 return null;
diff --git a/OpenSim/Data/PGSQL/PGSQLSimulationData.cs b/OpenSim/Data/PGSQL/PGSQLSimulationData.cs
index 3243635..cd02e05 100644
--- a/OpenSim/Data/PGSQL/PGSQLSimulationData.cs
+++ b/OpenSim/Data/PGSQL/PGSQLSimulationData.cs
@@ -46,6 +46,7 @@ namespace OpenSim.Data.PGSQL
46 public class PGSQLSimulationData : ISimulationDataStore 46 public class PGSQLSimulationData : ISimulationDataStore
47 { 47 {
48 private const string _migrationStore = "RegionStore"; 48 private const string _migrationStore = "RegionStore";
49 private const string LogHeader = "[REGION DB PGSQL]";
49 50
50 // private static FileSystemDataStore Instance = new FileSystemDataStore(); 51 // private static FileSystemDataStore Instance = new FileSystemDataStore();
51 private static readonly ILog _Log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); 52 private static readonly ILog _Log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
@@ -523,8 +524,17 @@ namespace OpenSim.Data.PGSQL
523 /// <returns></returns> 524 /// <returns></returns>
524 public double[,] LoadTerrain(UUID regionID) 525 public double[,] LoadTerrain(UUID regionID)
525 { 526 {
526 double[,] terrain = new double[(int)Constants.RegionSize, (int)Constants.RegionSize]; 527 double[,] ret = null;
527 terrain.Initialize(); 528 TerrainData terrData = LoadTerrain(regionID, (int)Constants.RegionSize, (int)Constants.RegionSize, (int)Constants.RegionHeight);
529 if (terrData != null)
530 ret = terrData.GetDoubles();
531 return ret;
532 }
533
534 // Returns 'null' if region not found
535 public TerrainData LoadTerrain(UUID regionID, int pSizeX, int pSizeY, int pSizeZ)
536 {
537 TerrainData terrData = null;
528 538
529 string sql = @"select ""RegionUUID"", ""Revision"", ""Heightfield"" from terrain 539 string sql = @"select ""RegionUUID"", ""Revision"", ""Heightfield"" from terrain
530 where ""RegionUUID"" = :RegionUUID order by ""Revision"" desc limit 1; "; 540 where ""RegionUUID"" = :RegionUUID order by ""Revision"" desc limit 1; ";
@@ -540,16 +550,9 @@ namespace OpenSim.Data.PGSQL
540 int rev; 550 int rev;
541 if (reader.Read()) 551 if (reader.Read())
542 { 552 {
543 MemoryStream str = new MemoryStream((byte[])reader["Heightfield"]); 553 rev = Convert.ToInt32(reader["Revision"]);
544 BinaryReader br = new BinaryReader(str); 554 byte[] blob = (byte[])reader["Heightfield"];
545 for (int x = 0; x < (int)Constants.RegionSize; x++) 555 terrData = TerrainData.CreateFromDatabaseBlobFactory(pSizeX, pSizeY, pSizeZ, rev, blob);
546 {
547 for (int y = 0; y < (int)Constants.RegionSize; y++)
548 {
549 terrain[x, y] = br.ReadDouble();
550 }
551 }
552 rev = (int)reader["Revision"];
553 } 556 }
554 else 557 else
555 { 558 {
@@ -560,7 +563,13 @@ namespace OpenSim.Data.PGSQL
560 } 563 }
561 } 564 }
562 565
563 return terrain; 566 return terrData;
567 }
568
569 // Legacy entry point for when terrain was always a 256x256 heightmap
570 public void StoreTerrain(double[,] terrain, UUID regionID)
571 {
572 StoreTerrain(new HeightmapTerrainData(terrain), regionID);
564 } 573 }
565 574
566 /// <summary> 575 /// <summary>
@@ -568,35 +577,38 @@ namespace OpenSim.Data.PGSQL
568 /// </summary> 577 /// </summary>
569 /// <param name="terrain">terrain map data.</param> 578 /// <param name="terrain">terrain map data.</param>
570 /// <param name="regionID">regionID.</param> 579 /// <param name="regionID">regionID.</param>
571 public void StoreTerrain(double[,] terrain, UUID regionID) 580 public void StoreTerrain(TerrainData terrData, UUID regionID)
572 { 581 {
573 int revision = Util.UnixTimeSinceEpoch();
574
575 //Delete old terrain map 582 //Delete old terrain map
576 string sql = @"delete from terrain where ""RegionUUID""=:RegionUUID"; 583 string sql = @"delete from terrain where ""RegionUUID""=:RegionUUID";
577 using (NpgsqlConnection conn = new NpgsqlConnection(m_connectionString)) 584 using (NpgsqlConnection conn = new NpgsqlConnection(m_connectionString))
578 using (NpgsqlCommand cmd = new NpgsqlCommand(sql, conn))
579 { 585 {
580 cmd.Parameters.Add(_Database.CreateParameter("RegionUUID", regionID)); 586 using (NpgsqlCommand cmd = new NpgsqlCommand(sql, conn))
581 conn.Open(); 587 {
582 cmd.ExecuteNonQuery(); 588 cmd.Parameters.Add(_Database.CreateParameter("RegionUUID", regionID));
589 conn.Open();
590 cmd.ExecuteNonQuery();
591 }
583 } 592 }
584 593 int terrainDBRevision;
585 _Log.Info("[REGION DB]: Deleted terrain revision r " + revision); 594 Array terrainDBblob;
595 terrData.GetDatabaseBlob(out terrainDBRevision, out terrainDBblob);
586 596
587 sql = @"insert into terrain(""RegionUUID"", ""Revision"", ""Heightfield"") values(:RegionUUID, :Revision, :Heightfield)"; 597 sql = @"insert into terrain(""RegionUUID"", ""Revision"", ""Heightfield"") values(:RegionUUID, :Revision, :Heightfield)";
588 598
589 using (NpgsqlConnection conn = new NpgsqlConnection(m_connectionString)) 599 using (NpgsqlConnection conn = new NpgsqlConnection(m_connectionString))
590 using (NpgsqlCommand cmd = new NpgsqlCommand(sql, conn))
591 { 600 {
592 cmd.Parameters.Add(_Database.CreateParameter("RegionUUID", regionID)); 601 using (NpgsqlCommand cmd = new NpgsqlCommand(sql, conn))
593 cmd.Parameters.Add(_Database.CreateParameter("Revision", revision)); 602 {
594 cmd.Parameters.Add(_Database.CreateParameter("Heightfield", serializeTerrain(terrain))); 603 cmd.Parameters.Add(_Database.CreateParameter("RegionUUID", regionID));
595 conn.Open(); 604 cmd.Parameters.Add(_Database.CreateParameter("Revision", terrainDBRevision));
596 cmd.ExecuteNonQuery(); 605 cmd.Parameters.Add(_Database.CreateParameter("Heightfield", terrainDBblob));
606 conn.Open();
607 cmd.ExecuteNonQuery();
608 }
597 } 609 }
598 610
599 _Log.Info("[REGION DB]: Stored terrain revision r " + revision); 611 _Log.Info("[REGION DB]: Stored terrain revision r " + terrainDBRevision);
600 } 612 }
601 613
602 /// <summary> 614 /// <summary>
@@ -1349,6 +1361,7 @@ namespace OpenSim.Data.PGSQL
1349 1361
1350 #region Private Methods 1362 #region Private Methods
1351 1363
1364 /*
1352 /// <summary> 1365 /// <summary>
1353 /// Serializes the terrain data for storage in DB. 1366 /// Serializes the terrain data for storage in DB.
1354 /// </summary> 1367 /// </summary>
@@ -1372,6 +1385,7 @@ namespace OpenSim.Data.PGSQL
1372 1385
1373 return str.ToArray(); 1386 return str.ToArray();
1374 } 1387 }
1388 */
1375 1389
1376 /// <summary> 1390 /// <summary>
1377 /// Stores new regionsettings. 1391 /// Stores new regionsettings.
diff --git a/OpenSim/Data/SQLite/SQLiteSimulationData.cs b/OpenSim/Data/SQLite/SQLiteSimulationData.cs
index 4d6a80a..d28c227 100644
--- a/OpenSim/Data/SQLite/SQLiteSimulationData.cs
+++ b/OpenSim/Data/SQLite/SQLiteSimulationData.cs
@@ -51,6 +51,7 @@ namespace OpenSim.Data.SQLite
51 public class SQLiteSimulationData : ISimulationDataStore 51 public class SQLiteSimulationData : ISimulationDataStore
52 { 52 {
53 private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); 53 private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
54 private static readonly string LogHeader = "[REGION DB SQLLITE]";
54 55
55 private const string primSelect = "select * from prims"; 56 private const string primSelect = "select * from prims";
56 private const string shapeSelect = "select * from primshapes"; 57 private const string shapeSelect = "select * from primshapes";
@@ -819,12 +820,18 @@ namespace OpenSim.Data.SQLite
819 prim.Inventory.RestoreInventoryItems(inventory); 820 prim.Inventory.RestoreInventoryItems(inventory);
820 } 821 }
821 822
823 // Legacy entry point for when terrain was always a 256x256 hieghtmap
824 public void StoreTerrain(double[,] ter, UUID regionID)
825 {
826 StoreTerrain(new HeightmapTerrainData(ter), regionID);
827 }
828
822 /// <summary> 829 /// <summary>
823 /// Store a terrain revision in region storage 830 /// Store a terrain revision in region storage
824 /// </summary> 831 /// </summary>
825 /// <param name="ter">terrain heightfield</param> 832 /// <param name="ter">terrain heightfield</param>
826 /// <param name="regionID">region UUID</param> 833 /// <param name="regionID">region UUID</param>
827 public void StoreTerrain(double[,] ter, UUID regionID) 834 public void StoreTerrain(TerrainData terrData, UUID regionID)
828 { 835 {
829 lock (ds) 836 lock (ds)
830 { 837 {
@@ -853,11 +860,17 @@ namespace OpenSim.Data.SQLite
853 String sql = "insert into terrain(RegionUUID, Revision, Heightfield)" + 860 String sql = "insert into terrain(RegionUUID, Revision, Heightfield)" +
854 " values(:RegionUUID, :Revision, :Heightfield)"; 861 " values(:RegionUUID, :Revision, :Heightfield)";
855 862
863 int terrainDBRevision;
864 Array terrainDBblob;
865 terrData.GetDatabaseBlob(out terrainDBRevision, out terrainDBblob);
866
867 m_log.DebugFormat("{0} Storing terrain revision r {1}", LogHeader, terrainDBRevision);
868
856 using (SqliteCommand cmd = new SqliteCommand(sql, m_conn)) 869 using (SqliteCommand cmd = new SqliteCommand(sql, m_conn))
857 { 870 {
858 cmd.Parameters.Add(new SqliteParameter(":RegionUUID", regionID.ToString())); 871 cmd.Parameters.Add(new SqliteParameter(":RegionUUID", regionID.ToString()));
859 cmd.Parameters.Add(new SqliteParameter(":Revision", revision)); 872 cmd.Parameters.Add(new SqliteParameter(":Revision", terrainDBRevision));
860 cmd.Parameters.Add(new SqliteParameter(":Heightfield", serializeTerrain(ter))); 873 cmd.Parameters.Add(new SqliteParameter(":Heightfield", terrainDBblob));
861 cmd.ExecuteNonQuery(); 874 cmd.ExecuteNonQuery();
862 } 875 }
863 } 876 }
@@ -870,11 +883,20 @@ namespace OpenSim.Data.SQLite
870 /// <returns>Heightfield data</returns> 883 /// <returns>Heightfield data</returns>
871 public double[,] LoadTerrain(UUID regionID) 884 public double[,] LoadTerrain(UUID regionID)
872 { 885 {
886 double[,] ret = null;
887 TerrainData terrData = LoadTerrain(regionID, (int)Constants.RegionSize, (int)Constants.RegionSize, (int)Constants.RegionHeight);
888 if (terrData != null)
889 ret = terrData.GetDoubles();
890 return ret;
891 }
892
893 // Returns 'null' if region not found
894 public TerrainData LoadTerrain(UUID regionID, int pSizeX, int pSizeY, int pSizeZ)
895 {
896 TerrainData terrData = null;
897
873 lock (ds) 898 lock (ds)
874 { 899 {
875 double[,] terret = new double[(int)Constants.RegionSize, (int)Constants.RegionSize];
876 terret.Initialize();
877
878 String sql = "select RegionUUID, Revision, Heightfield from terrain" + 900 String sql = "select RegionUUID, Revision, Heightfield from terrain" +
879 " where RegionUUID=:RegionUUID order by Revision desc"; 901 " where RegionUUID=:RegionUUID order by Revision desc";
880 902
@@ -887,21 +909,9 @@ namespace OpenSim.Data.SQLite
887 int rev = 0; 909 int rev = 0;
888 if (row.Read()) 910 if (row.Read())
889 { 911 {
890 // TODO: put this into a function
891 using (MemoryStream str = new MemoryStream((byte[])row["Heightfield"]))
892 {
893 using (BinaryReader br = new BinaryReader(str))
894 {
895 for (int x = 0; x < (int)Constants.RegionSize; x++)
896 {
897 for (int y = 0; y < (int)Constants.RegionSize; y++)
898 {
899 terret[x, y] = br.ReadDouble();
900 }
901 }
902 }
903 }
904 rev = Convert.ToInt32(row["Revision"]); 912 rev = Convert.ToInt32(row["Revision"]);
913 byte[] blob = (byte[])row["Heightfield"];
914 terrData = TerrainData.CreateFromDatabaseBlobFactory(pSizeX, pSizeY, pSizeZ, rev, blob);
905 } 915 }
906 else 916 else
907 { 917 {
@@ -912,8 +922,8 @@ namespace OpenSim.Data.SQLite
912 m_log.Debug("[SQLITE REGION DB]: Loaded terrain revision r" + rev.ToString()); 922 m_log.Debug("[SQLITE REGION DB]: Loaded terrain revision r" + rev.ToString());
913 } 923 }
914 } 924 }
915 return terret;
916 } 925 }
926 return terrData;
917 } 927 }
918 928
919 public void RemoveLandObject(UUID globalID) 929 public void RemoveLandObject(UUID globalID)
@@ -2016,6 +2026,7 @@ namespace OpenSim.Data.SQLite
2016 return entry; 2026 return entry;
2017 } 2027 }
2018 2028
2029 /*
2019 /// <summary> 2030 /// <summary>
2020 /// 2031 ///
2021 /// </summary> 2032 /// </summary>
@@ -2033,6 +2044,7 @@ namespace OpenSim.Data.SQLite
2033 2044
2034 return str.ToArray(); 2045 return str.ToArray();
2035 } 2046 }
2047 */
2036 2048
2037 // private void fillTerrainRow(DataRow row, UUID regionUUID, int rev, double[,] val) 2049 // private void fillTerrainRow(DataRow row, UUID regionUUID, int rev, double[,] val)
2038 // { 2050 // {
diff --git a/OpenSim/Framework/TerrainData.cs b/OpenSim/Framework/TerrainData.cs
new file mode 100644
index 0000000..6b1be4e
--- /dev/null
+++ b/OpenSim/Framework/TerrainData.cs
@@ -0,0 +1,464 @@
1/*
2 * Copyright (c) Contributors, http://opensimulator.org/
3 * See CONTRIBUTORS.TXT for a full list of copyright holders.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 * * Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * * Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 * * Neither the name of the OpenSimulator Project nor the
13 * names of its contributors may be used to endorse or promote products
14 * derived from this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
17 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19 * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
20 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27
28using System;
29using System.Collections.Generic;
30using System.IO;
31using System.Reflection;
32
33using OpenMetaverse;
34
35using log4net;
36
37namespace OpenSim.Framework
38{
39 public abstract class TerrainData
40 {
41 // Terrain always is a square
42 public int SizeX { get; protected set; }
43 public int SizeY { get; protected set; }
44 public int SizeZ { get; protected set; }
45
46 // A height used when the user doesn't specify anything
47 public const float DefaultTerrainHeight = 21f;
48
49 public abstract float this[int x, int y] { get; set; }
50 // Someday terrain will have caves
51 public abstract float this[int x, int y, int z] { get; set; }
52
53 public abstract bool IsTaintedAt(int xx, int yy);
54 public abstract bool IsTaintedAt(int xx, int yy, bool clearOnTest);
55 public abstract void TaintAllTerrain();
56 public abstract void ClearTaint();
57
58 public abstract void ClearLand();
59 public abstract void ClearLand(float height);
60
61 // Return a representation of this terrain for storing as a blob in the database.
62 // Returns 'true' to say blob was stored in the 'out' locations.
63 public abstract bool GetDatabaseBlob(out int DBFormatRevisionCode, out Array blob);
64
65 // Given a revision code and a blob from the database, create and return the right type of TerrainData.
66 // The sizes passed are the expected size of the region. The database info will be used to
67 // initialize the heightmap of that sized region with as much data is in the blob.
68 // Return created TerrainData or 'null' if unsuccessful.
69 public static TerrainData CreateFromDatabaseBlobFactory(int pSizeX, int pSizeY, int pSizeZ, int pFormatCode, byte[] pBlob)
70 {
71 // For the moment, there is only one implementation class
72 return new HeightmapTerrainData(pSizeX, pSizeY, pSizeZ, pFormatCode, pBlob);
73 }
74
75 // return a special compressed representation of the heightmap in ints
76 public abstract int[] GetCompressedMap();
77 public abstract float CompressionFactor { get; }
78
79 public abstract float[] GetFloatsSerialized();
80 public abstract double[,] GetDoubles();
81 public abstract TerrainData Clone();
82 }
83
84 // The terrain is stored in the database as a blob with a 'revision' field.
85 // Some implementations of terrain storage would fill the revision field with
86 // the time the terrain was stored. When real revisions were added and this
87 // feature removed, that left some old entries with the time in the revision
88 // field.
89 // Thus, if revision is greater than 'RevisionHigh' then terrain db entry is
90 // left over and it is presumed to be 'Legacy256'.
91 // Numbers are arbitrary and are chosen to to reduce possible mis-interpretation.
92 // If a revision does not match any of these, it is assumed to be Legacy256.
93 public enum DBTerrainRevision
94 {
95 // Terrain is 'double[256,256]'
96 Legacy256 = 11,
97 // Terrain is 'int32, int32, float[,]' where the ints are X and Y dimensions
98 // The dimensions are presumed to be multiples of 16 and, more likely, multiples of 256.
99 Variable2D = 22,
100 // Terrain is 'int32, int32, int32, int16[]' where the ints are X and Y dimensions
101 // and third int is the 'compression factor'. The heights are compressed as
102 // "int compressedHeight = (int)(height * compressionFactor);"
103 // The dimensions are presumed to be multiples of 16 and, more likely, multiples of 256.
104 Compressed2D = 27,
105 // A revision that is not listed above or any revision greater than this value is 'Legacy256'.
106 RevisionHigh = 1234
107 }
108
109 // Version of terrain that is a heightmap.
110 // This should really be 'LLOptimizedHeightmapTerrainData' as it includes knowledge
111 // of 'patches' which are 16x16 terrain areas which can be sent separately to the viewer.
112 // The heighmap is kept as an array of integers. The integer values are converted to
113 // and from floats by TerrainCompressionFactor.
114 public class HeightmapTerrainData : TerrainData
115 {
116 private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
117 private static string LogHeader = "[HEIGHTMAP TERRAIN DATA]";
118
119 // TerrainData.this[x, y]
120 public override float this[int x, int y]
121 {
122 get { return FromCompressedHeight(m_heightmap[x, y]); }
123 set {
124 int newVal = ToCompressedHeight(value);
125 if (m_heightmap[x, y] != newVal)
126 {
127 m_heightmap[x, y] = newVal;
128 m_taint[x / Constants.TerrainPatchSize, y / Constants.TerrainPatchSize] = true;
129 }
130 }
131 }
132
133 // TerrainData.this[x, y, z]
134 public override float this[int x, int y, int z]
135 {
136 get { return this[x, y]; }
137 set { this[x, y] = value; }
138 }
139
140 // TerrainData.ClearTaint
141 public override void ClearTaint()
142 {
143 SetAllTaint(false);
144 }
145
146 // TerrainData.TaintAllTerrain
147 public override void TaintAllTerrain()
148 {
149 SetAllTaint(true);
150 }
151
152 private void SetAllTaint(bool setting)
153 {
154 for (int ii = 0; ii < m_taint.GetLength(0); ii++)
155 for (int jj = 0; jj < m_taint.GetLength(1); jj++)
156 m_taint[ii, jj] = setting;
157 }
158
159 // TerrainData.ClearLand
160 public override void ClearLand()
161 {
162 ClearLand(DefaultTerrainHeight);
163 }
164 // TerrainData.ClearLand(float)
165 public override void ClearLand(float pHeight)
166 {
167 int flatHeight = ToCompressedHeight(pHeight);
168 for (int xx = 0; xx < SizeX; xx++)
169 for (int yy = 0; yy < SizeY; yy++)
170 m_heightmap[xx, yy] = flatHeight;
171 }
172
173 // Return 'true' of the patch that contains these region coordinates has been modified.
174 // Note that checking the taint clears it.
175 // There is existing code that relies on this feature.
176 public override bool IsTaintedAt(int xx, int yy, bool clearOnTest)
177 {
178 int tx = xx / Constants.TerrainPatchSize;
179 int ty = yy / Constants.TerrainPatchSize;
180 bool ret = m_taint[tx, ty];
181 if (ret && clearOnTest)
182 m_taint[tx, ty] = false;
183 return ret;
184 }
185
186 // Old form that clears the taint flag when we check it.
187 public override bool IsTaintedAt(int xx, int yy)
188 {
189 return IsTaintedAt(xx, yy, true /* clearOnTest */);
190 }
191
192 // TerrainData.GetDatabaseBlob
193 // The user wants something to store in the database.
194 public override bool GetDatabaseBlob(out int DBRevisionCode, out Array blob)
195 {
196 bool ret = false;
197 if (SizeX == Constants.RegionSize && SizeY == Constants.RegionSize)
198 {
199 DBRevisionCode = (int)DBTerrainRevision.Legacy256;
200 blob = ToLegacyTerrainSerialization();
201 ret = true;
202 }
203 else
204 {
205 DBRevisionCode = (int)DBTerrainRevision.Compressed2D;
206 blob = ToCompressedTerrainSerialization();
207 ret = true;
208 }
209 return ret;
210 }
211
212 // TerrainData.CompressionFactor
213 private float m_compressionFactor = 100.0f;
214 public override float CompressionFactor { get { return m_compressionFactor; } }
215
216 // TerrainData.GetCompressedMap
217 public override int[] GetCompressedMap()
218 {
219 int[] newMap = new int[SizeX * SizeY];
220
221 int ind = 0;
222 for (int xx = 0; xx < SizeX; xx++)
223 for (int yy = 0; yy < SizeY; yy++)
224 newMap[ind++] = m_heightmap[xx, yy];
225
226 return newMap;
227
228 }
229 // TerrainData.Clone
230 public override TerrainData Clone()
231 {
232 HeightmapTerrainData ret = new HeightmapTerrainData(SizeX, SizeY, SizeZ);
233 ret.m_heightmap = (int[,])this.m_heightmap.Clone();
234 return ret;
235 }
236
237 // TerrainData.GetFloatsSerialized
238 // This one dimensional version is ordered so height = map[y*sizeX+x];
239 // DEPRECATED: don't use this function as it does not retain the dimensions of the terrain
240 // and the caller will probably do the wrong thing if the terrain is not the legacy 256x256.
241 public override float[] GetFloatsSerialized()
242 {
243 int points = SizeX * SizeY;
244 float[] heights = new float[points];
245
246 int idx = 0;
247 for (int jj = 0; jj < SizeY; jj++)
248 for (int ii = 0; ii < SizeX; ii++)
249 {
250 heights[idx++] = FromCompressedHeight(m_heightmap[ii, jj]);
251 }
252
253 return heights;
254 }
255
256 // TerrainData.GetDoubles
257 public override double[,] GetDoubles()
258 {
259 double[,] ret = new double[SizeX, SizeY];
260 for (int xx = 0; xx < SizeX; xx++)
261 for (int yy = 0; yy < SizeY; yy++)
262 ret[xx, yy] = FromCompressedHeight(m_heightmap[xx, yy]);
263
264 return ret;
265 }
266
267
268 // =============================================================
269
270 private int[,] m_heightmap;
271 // Remember subregions of the heightmap that has changed.
272 private bool[,] m_taint;
273
274 // To save space (especially for large regions), keep the height as a short integer
275 // that is coded as the float height times the compression factor (usually '100'
276 // to make for two decimal points).
277 public int ToCompressedHeight(double pHeight)
278 {
279 return (int)(pHeight * CompressionFactor);
280 }
281
282 public float FromCompressedHeight(int pHeight)
283 {
284 return ((float)pHeight) / CompressionFactor;
285 }
286
287 // To keep with the legacy theme, create an instance of this class based on the
288 // way terrain used to be passed around.
289 public HeightmapTerrainData(double[,] pTerrain)
290 {
291 SizeX = pTerrain.GetLength(0);
292 SizeY = pTerrain.GetLength(1);
293 SizeZ = (int)Constants.RegionHeight;
294 m_compressionFactor = 100.0f;
295
296 m_heightmap = new int[SizeX, SizeY];
297 for (int ii = 0; ii < SizeX; ii++)
298 {
299 for (int jj = 0; jj < SizeY; jj++)
300 {
301 m_heightmap[ii, jj] = ToCompressedHeight(pTerrain[ii, jj]);
302
303 }
304 }
305 // m_log.DebugFormat("{0} new by doubles. sizeX={1}, sizeY={2}, sizeZ={3}", LogHeader, SizeX, SizeY, SizeZ);
306
307 m_taint = new bool[SizeX / Constants.TerrainPatchSize, SizeY / Constants.TerrainPatchSize];
308 ClearTaint();
309 }
310
311 // Create underlying structures but don't initialize the heightmap assuming the caller will immediately do that
312 public HeightmapTerrainData(int pX, int pY, int pZ)
313 {
314 SizeX = pX;
315 SizeY = pY;
316 SizeZ = pZ;
317 m_compressionFactor = 100.0f;
318 m_heightmap = new int[SizeX, SizeY];
319 m_taint = new bool[SizeX / Constants.TerrainPatchSize, SizeY / Constants.TerrainPatchSize];
320 // m_log.DebugFormat("{0} new by dimensions. sizeX={1}, sizeY={2}, sizeZ={3}", LogHeader, SizeX, SizeY, SizeZ);
321 ClearTaint();
322 ClearLand(0f);
323 }
324
325 public HeightmapTerrainData(int[] cmap, float pCompressionFactor, int pX, int pY, int pZ) : this(pX, pY, pZ)
326 {
327 m_compressionFactor = pCompressionFactor;
328 int ind = 0;
329 for (int xx = 0; xx < SizeX; xx++)
330 for (int yy = 0; yy < SizeY; yy++)
331 m_heightmap[xx, yy] = cmap[ind++];
332 // m_log.DebugFormat("{0} new by compressed map. sizeX={1}, sizeY={2}, sizeZ={3}", LogHeader, SizeX, SizeY, SizeZ);
333 }
334
335 // Create a heighmap from a database blob
336 public HeightmapTerrainData(int pSizeX, int pSizeY, int pSizeZ, int pFormatCode, byte[] pBlob) : this(pSizeX, pSizeY, pSizeZ)
337 {
338 switch ((DBTerrainRevision)pFormatCode)
339 {
340 case DBTerrainRevision.Compressed2D:
341 FromCompressedTerrainSerialization(pBlob);
342 m_log.DebugFormat("{0} HeightmapTerrainData create from Compressed2D serialization. Size=<{1},{2}>", LogHeader, SizeX, SizeY);
343 break;
344 default:
345 FromLegacyTerrainSerialization(pBlob);
346 m_log.DebugFormat("{0} HeightmapTerrainData create from legacy serialization. Size=<{1},{2}>", LogHeader, SizeX, SizeY);
347 break;
348 }
349 }
350
351 // Just create an array of doubles. Presumes the caller implicitly knows the size.
352 public Array ToLegacyTerrainSerialization()
353 {
354 Array ret = null;
355
356 using (MemoryStream str = new MemoryStream((int)Constants.RegionSize * (int)Constants.RegionSize * sizeof(double)))
357 {
358 using (BinaryWriter bw = new BinaryWriter(str))
359 {
360 for (int xx = 0; xx < Constants.RegionSize; xx++)
361 {
362 for (int yy = 0; yy < Constants.RegionSize; yy++)
363 {
364 double height = this[xx, yy];
365 if (height == 0.0)
366 height = double.Epsilon;
367 bw.Write(height);
368 }
369 }
370 }
371 ret = str.ToArray();
372 }
373 return ret;
374 }
375
376 // Just create an array of doubles. Presumes the caller implicitly knows the size.
377 public void FromLegacyTerrainSerialization(byte[] pBlob)
378 {
379 // In case database info doesn't match real terrain size, initialize the whole terrain.
380 ClearLand();
381
382 using (MemoryStream mstr = new MemoryStream(pBlob))
383 {
384 using (BinaryReader br = new BinaryReader(mstr))
385 {
386 for (int xx = 0; xx < (int)Constants.RegionSize; xx++)
387 {
388 for (int yy = 0; yy < (int)Constants.RegionSize; yy++)
389 {
390 float val = (float)br.ReadDouble();
391 if (xx < SizeX && yy < SizeY)
392 m_heightmap[xx, yy] = ToCompressedHeight(val);
393 }
394 }
395 }
396 ClearTaint();
397 }
398 }
399
400 // See the reader below.
401 public Array ToCompressedTerrainSerialization()
402 {
403 Array ret = null;
404 using (MemoryStream str = new MemoryStream((3 * sizeof(Int32)) + (SizeX * SizeY * sizeof(Int16))))
405 {
406 using (BinaryWriter bw = new BinaryWriter(str))
407 {
408 bw.Write((Int32)DBTerrainRevision.Compressed2D);
409 bw.Write((Int32)SizeX);
410 bw.Write((Int32)SizeY);
411 bw.Write((Int32)CompressionFactor);
412 for (int yy = 0; yy < SizeY; yy++)
413 for (int xx = 0; xx < SizeX; xx++)
414 {
415 bw.Write((Int16)m_heightmap[xx, yy]);
416 }
417 }
418 ret = str.ToArray();
419 }
420 return ret;
421 }
422
423 // Initialize heightmap from blob consisting of:
424 // int32, int32, int32, int32, int16[]
425 // where the first int32 is format code, next two int32s are the X and y of heightmap data and
426 // the forth int is the compression factor for the following int16s
427 // This is just sets heightmap info. The actual size of the region was set on this instance's
428 // creation and any heights not initialized by theis blob are set to the default height.
429 public void FromCompressedTerrainSerialization(byte[] pBlob)
430 {
431 Int32 hmFormatCode, hmSizeX, hmSizeY, hmCompressionFactor;
432
433 using (MemoryStream mstr = new MemoryStream(pBlob))
434 {
435 using (BinaryReader br = new BinaryReader(mstr))
436 {
437 hmFormatCode = br.ReadInt32();
438 hmSizeX = br.ReadInt32();
439 hmSizeY = br.ReadInt32();
440 hmCompressionFactor = br.ReadInt32();
441
442 m_compressionFactor = hmCompressionFactor;
443
444 // In case database info doesn't match real terrain size, initialize the whole terrain.
445 ClearLand();
446
447 for (int yy = 0; yy < hmSizeY; yy++)
448 {
449 for (int xx = 0; xx < hmSizeX; xx++)
450 {
451 Int16 val = br.ReadInt16();
452 if (xx < SizeX && yy < SizeY)
453 m_heightmap[xx, yy] = val;
454 }
455 }
456 }
457 ClearTaint();
458
459 m_log.InfoFormat("{0} Read compressed 2d heightmap. Heightmap size=<{1},{2}>. Region size=<{3},{4}>. CompFact={5}",
460 LogHeader, hmSizeX, hmSizeY, SizeX, SizeY, hmCompressionFactor);
461 }
462 }
463 }
464}
diff --git a/OpenSim/Region/CoreModules/World/Terrain/FileLoaders/GenericSystemDrawing.cs b/OpenSim/Region/CoreModules/World/Terrain/FileLoaders/GenericSystemDrawing.cs
index d78ade5..d5c77ec 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
67 { 67 {
68 using (Bitmap bitmap = new Bitmap(filename)) 68 using (Bitmap bitmap = new Bitmap(filename))
69 { 69 {
70 ITerrainChannel retval = new TerrainChannel(true); 70 ITerrainChannel retval = new TerrainChannel(w, h);
71 71
72 for (int x = 0; x < retval.Width; x++) 72 for (int x = 0; x < retval.Width; x++)
73 { 73 {
diff --git a/OpenSim/Region/CoreModules/World/Terrain/TerrainModule.cs b/OpenSim/Region/CoreModules/World/Terrain/TerrainModule.cs
index 4d738a5..9a88804 100644
--- a/OpenSim/Region/CoreModules/World/Terrain/TerrainModule.cs
+++ b/OpenSim/Region/CoreModules/World/Terrain/TerrainModule.cs
@@ -71,6 +71,10 @@ namespace OpenSim.Region.CoreModules.World.Terrain
71 71
72 private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); 72 private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
73 73
74#pragma warning disable 414
75 private static readonly string LogHeader = "[TERRAIN MODULE]";
76#pragma warning restore 414
77
74 private readonly Commander m_commander = new Commander("terrain"); 78 private readonly Commander m_commander = new Commander("terrain");
75 79
76 private readonly Dictionary<StandardTerrainEffects, ITerrainFloodEffect> m_floodeffects = 80 private readonly Dictionary<StandardTerrainEffects, ITerrainFloodEffect> m_floodeffects =
@@ -81,8 +85,8 @@ namespace OpenSim.Region.CoreModules.World.Terrain
81 private readonly Dictionary<StandardTerrainEffects, ITerrainPaintableEffect> m_painteffects = 85 private readonly Dictionary<StandardTerrainEffects, ITerrainPaintableEffect> m_painteffects =
82 new Dictionary<StandardTerrainEffects, ITerrainPaintableEffect>(); 86 new Dictionary<StandardTerrainEffects, ITerrainPaintableEffect>();
83 87
84 private ITerrainChannel m_channel;
85 private Dictionary<string, ITerrainEffect> m_plugineffects; 88 private Dictionary<string, ITerrainEffect> m_plugineffects;
89 private ITerrainChannel m_channel;
86 private ITerrainChannel m_revert; 90 private ITerrainChannel m_revert;
87 private Scene m_scene; 91 private Scene m_scene;
88 private volatile bool m_tainted; 92 private volatile bool m_tainted;
@@ -90,6 +94,85 @@ namespace OpenSim.Region.CoreModules.World.Terrain
90 94
91 private String m_InitialTerrain = "pinhead-island"; 95 private String m_InitialTerrain = "pinhead-island";
92 96
97 // If true, send terrain patch updates to clients based on their view distance
98 private bool m_sendTerrainUpdatesByViewDistance = true;
99
100 // Class to keep the per client collection of terrain patches that must be sent.
101 // A patch is set to 'true' meaning it should be sent to the client. Once the
102 // patch packet is queued to the client, the bit for that patch is set to 'false'.
103 private class PatchUpdates
104 {
105 private bool[,] updated; // for each patch, whether it needs to be sent to this client
106 private int updateCount; // number of patches that need to be sent
107 public ScenePresence Presence; // a reference to the client to send to
108 public PatchUpdates(TerrainData terrData, ScenePresence pPresence)
109 {
110 updated = new bool[terrData.SizeX / Constants.TerrainPatchSize, terrData.SizeY / Constants.TerrainPatchSize];
111 updateCount = 0;
112 Presence = pPresence;
113 // Initially, send all patches to the client
114 SetAll(true);
115 }
116 // Returns 'true' if there are any patches marked for sending
117 public bool HasUpdates()
118 {
119 return (updateCount > 0);
120 }
121 public void SetByXY(int x, int y, bool state)
122 {
123 this.SetByPatch(x / Constants.TerrainPatchSize, y / Constants.TerrainPatchSize, state);
124 }
125 public bool GetByPatch(int patchX, int patchY)
126 {
127 return updated[patchX, patchY];
128 }
129 public void SetByPatch(int patchX, int patchY, bool state)
130 {
131 bool prevState = updated[patchX, patchY];
132 if (!prevState && state)
133 updateCount++;
134 if (prevState && !state)
135 updateCount--;
136 updated[patchX, patchY] = state;
137 }
138 public void SetAll(bool state)
139 {
140 updateCount = 0;
141 for (int xx = 0; xx < updated.GetLength(0); xx++)
142 for (int yy = 0; yy < updated.GetLength(1); yy++)
143 updated[xx, yy] = state;
144 if (state)
145 updateCount = updated.GetLength(0) * updated.GetLength(1);
146 }
147 // Logically OR's the terrain data's patch taint map into this client's update map.
148 public void SetAll(TerrainData terrData)
149 {
150 if (updated.GetLength(0) != (terrData.SizeX / Constants.TerrainPatchSize)
151 || updated.GetLength(1) != (terrData.SizeY / Constants.TerrainPatchSize))
152 {
153 throw new Exception(
154 String.Format("{0} PatchUpdates.SetAll: patch array not same size as terrain. arr=<{1},{2}>, terr=<{3},{4}>",
155 LogHeader, updated.GetLength(0), updated.GetLength(1),
156 terrData.SizeX / Constants.TerrainPatchSize, terrData.SizeY / Constants.TerrainPatchSize)
157 );
158 }
159 for (int xx = 0; xx < terrData.SizeX; xx += Constants.TerrainPatchSize)
160 {
161 for (int yy = 0; yy < terrData.SizeY; yy += Constants.TerrainPatchSize)
162 {
163 // Only set tainted. The patch bit may be set if the patch was to be sent later.
164 if (terrData.IsTaintedAt(xx, yy, false))
165 {
166 this.SetByXY(xx, yy, true);
167 }
168 }
169 }
170 }
171 }
172
173 // The flags of which terrain patches to send for each of the ScenePresence's
174 private Dictionary<UUID, PatchUpdates> m_perClientPatchUpdates = new Dictionary<UUID, PatchUpdates>();
175
93 /// <summary> 176 /// <summary>
94 /// Human readable list of terrain file extensions that are supported. 177 /// Human readable list of terrain file extensions that are supported.
95 /// </summary> 178 /// </summary>
@@ -118,7 +201,10 @@ namespace OpenSim.Region.CoreModules.World.Terrain
118 { 201 {
119 IConfig terrainConfig = config.Configs["Terrain"]; 202 IConfig terrainConfig = config.Configs["Terrain"];
120 if (terrainConfig != null) 203 if (terrainConfig != null)
204 {
121 m_InitialTerrain = terrainConfig.GetString("InitialTerrain", m_InitialTerrain); 205 m_InitialTerrain = terrainConfig.GetString("InitialTerrain", m_InitialTerrain);
206 m_sendTerrainUpdatesByViewDistance = terrainConfig.GetBoolean("SendTerrainUpdatesByViewDistance", m_sendTerrainUpdatesByViewDistance);
207 }
122 } 208 }
123 209
124 public void AddRegion(Scene scene) 210 public void AddRegion(Scene scene)
@@ -130,22 +216,24 @@ namespace OpenSim.Region.CoreModules.World.Terrain
130 { 216 {
131 if (m_scene.Heightmap == null) 217 if (m_scene.Heightmap == null)
132 { 218 {
133 m_channel = new TerrainChannel(m_InitialTerrain); 219 m_channel = new TerrainChannel(m_InitialTerrain, (int)m_scene.RegionInfo.RegionSizeX,
220 (int)m_scene.RegionInfo.RegionSizeY,
221 (int)m_scene.RegionInfo.RegionSizeZ);
134 m_scene.Heightmap = m_channel; 222 m_scene.Heightmap = m_channel;
135 m_revert = new TerrainChannel();
136 UpdateRevertMap(); 223 UpdateRevertMap();
137 } 224 }
138 else 225 else
139 { 226 {
140 m_channel = m_scene.Heightmap; 227 m_channel = m_scene.Heightmap;
141 m_revert = new TerrainChannel();
142 UpdateRevertMap(); 228 UpdateRevertMap();
143 } 229 }
144 230
145 m_scene.RegisterModuleInterface<ITerrainModule>(this); 231 m_scene.RegisterModuleInterface<ITerrainModule>(this);
146 m_scene.EventManager.OnNewClient += EventManager_OnNewClient; 232 m_scene.EventManager.OnNewClient += EventManager_OnNewClient;
233 m_scene.EventManager.OnClientClosed += EventManager_OnClientClosed;
147 m_scene.EventManager.OnPluginConsole += EventManager_OnPluginConsole; 234 m_scene.EventManager.OnPluginConsole += EventManager_OnPluginConsole;
148 m_scene.EventManager.OnTerrainTick += EventManager_OnTerrainTick; 235 m_scene.EventManager.OnTerrainTick += EventManager_OnTerrainTick;
236 m_scene.EventManager.OnFrame += EventManager_OnFrame;
149 } 237 }
150 238
151 InstallDefaultEffects(); 239 InstallDefaultEffects();
@@ -184,8 +272,10 @@ namespace OpenSim.Region.CoreModules.World.Terrain
184 // remove the commands 272 // remove the commands
185 m_scene.UnregisterModuleCommander(m_commander.Name); 273 m_scene.UnregisterModuleCommander(m_commander.Name);
186 // remove the event-handlers 274 // remove the event-handlers
275 m_scene.EventManager.OnFrame -= EventManager_OnFrame;
187 m_scene.EventManager.OnTerrainTick -= EventManager_OnTerrainTick; 276 m_scene.EventManager.OnTerrainTick -= EventManager_OnTerrainTick;
188 m_scene.EventManager.OnPluginConsole -= EventManager_OnPluginConsole; 277 m_scene.EventManager.OnPluginConsole -= EventManager_OnPluginConsole;
278 m_scene.EventManager.OnClientClosed -= EventManager_OnClientClosed;
189 m_scene.EventManager.OnNewClient -= EventManager_OnNewClient; 279 m_scene.EventManager.OnNewClient -= EventManager_OnNewClient;
190 // remove the interface 280 // remove the interface
191 m_scene.UnregisterModuleInterface<ITerrainModule>(this); 281 m_scene.UnregisterModuleInterface<ITerrainModule>(this);
@@ -230,11 +320,11 @@ namespace OpenSim.Region.CoreModules.World.Terrain
230 try 320 try
231 { 321 {
232 ITerrainChannel channel = loader.Value.LoadFile(filename); 322 ITerrainChannel channel = loader.Value.LoadFile(filename);
233 if (channel.Width != Constants.RegionSize || channel.Height != Constants.RegionSize) 323 if (channel.Width != m_scene.RegionInfo.RegionSizeX || channel.Height != m_scene.RegionInfo.RegionSizeY)
234 { 324 {
235 // TerrainChannel expects a RegionSize x RegionSize map, currently 325 // TerrainChannel expects a RegionSize x RegionSize map, currently
236 throw new ArgumentException(String.Format("wrong size, use a file with size {0} x {1}", 326 throw new ArgumentException(String.Format("wrong size, use a file with size {0} x {1}",
237 Constants.RegionSize, Constants.RegionSize)); 327 m_scene.RegionInfo.RegionSizeX, m_scene.RegionInfo.RegionSizeY));
238 } 328 }
239 m_log.DebugFormat("[TERRAIN]: Loaded terrain, wd/ht: {0}/{1}", channel.Width, channel.Height); 329 m_log.DebugFormat("[TERRAIN]: Loaded terrain, wd/ht: {0}/{1}", channel.Width, channel.Height);
240 m_scene.Heightmap = channel; 330 m_scene.Heightmap = channel;
@@ -261,7 +351,6 @@ namespace OpenSim.Region.CoreModules.World.Terrain
261 String.Format("Unable to load heightmap: {0}", e.Message)); 351 String.Format("Unable to load heightmap: {0}", e.Message));
262 } 352 }
263 } 353 }
264 CheckForTerrainUpdates();
265 m_log.Info("[TERRAIN]: File (" + filename + ") loaded successfully"); 354 m_log.Info("[TERRAIN]: File (" + filename + ") loaded successfully");
266 return; 355 return;
267 } 356 }
@@ -309,12 +398,18 @@ namespace OpenSim.Region.CoreModules.World.Terrain
309 LoadFromStream(filename, URIFetch(pathToTerrainHeightmap)); 398 LoadFromStream(filename, URIFetch(pathToTerrainHeightmap));
310 } 399 }
311 400
401 public void LoadFromStream(string filename, Stream stream)
402 {
403 LoadFromStream(filename, Vector3.Zero, 0f, Vector2.Zero, stream);
404 }
405
312 /// <summary> 406 /// <summary>
313 /// Loads a terrain file from a stream and installs it in the scene. 407 /// Loads a terrain file from a stream and installs it in the scene.
314 /// </summary> 408 /// </summary>
315 /// <param name="filename">Filename to terrain file. Type is determined by extension.</param> 409 /// <param name="filename">Filename to terrain file. Type is determined by extension.</param>
316 /// <param name="stream"></param> 410 /// <param name="stream"></param>
317 public void LoadFromStream(string filename, Stream stream) 411 public void LoadFromStream(string filename, Vector3 displacement,
412 float radianRotation, Vector2 rotationDisplacement, Stream stream)
318 { 413 {
319 foreach (KeyValuePair<string, ITerrainLoader> loader in m_loaders) 414 foreach (KeyValuePair<string, ITerrainLoader> loader in m_loaders)
320 { 415 {
@@ -325,8 +420,7 @@ namespace OpenSim.Region.CoreModules.World.Terrain
325 try 420 try
326 { 421 {
327 ITerrainChannel channel = loader.Value.LoadStream(stream); 422 ITerrainChannel channel = loader.Value.LoadStream(stream);
328 m_scene.Heightmap = channel; 423 m_channel.Merge(channel, displacement, radianRotation, rotationDisplacement);
329 m_channel = channel;
330 UpdateRevertMap(); 424 UpdateRevertMap();
331 } 425 }
332 catch (NotImplementedException) 426 catch (NotImplementedException)
@@ -337,7 +431,6 @@ namespace OpenSim.Region.CoreModules.World.Terrain
337 } 431 }
338 } 432 }
339 433
340 CheckForTerrainUpdates();
341 m_log.Info("[TERRAIN]: File (" + filename + ") loaded successfully"); 434 m_log.Info("[TERRAIN]: File (" + filename + ") loaded successfully");
342 return; 435 return;
343 } 436 }
@@ -406,9 +499,46 @@ namespace OpenSim.Region.CoreModules.World.Terrain
406 } 499 }
407 } 500 }
408 501
502 // Someone diddled terrain outside the normal code paths. Set the taintedness for all clients.
503 // ITerrainModule.TaintTerrain()
409 public void TaintTerrain () 504 public void TaintTerrain ()
410 { 505 {
411 CheckForTerrainUpdates(); 506 lock (m_perClientPatchUpdates)
507 {
508 // Set the flags for all clients so the tainted patches will be sent out
509 foreach (PatchUpdates pups in m_perClientPatchUpdates.Values)
510 {
511 pups.SetAll(m_scene.Heightmap.GetTerrainData());
512 }
513 }
514 }
515
516 // ITerrainModule.PushTerrain()
517 public void PushTerrain(IClientAPI pClient)
518 {
519 if (m_sendTerrainUpdatesByViewDistance)
520 {
521 ScenePresence presence = m_scene.GetScenePresence(pClient.AgentId);
522 if (presence != null)
523 {
524 lock (m_perClientPatchUpdates)
525 {
526 PatchUpdates pups;
527 if (!m_perClientPatchUpdates.TryGetValue(pClient.AgentId, out pups))
528 {
529 // There is a ScenePresence without a send patch map. Create one.
530 pups = new PatchUpdates(m_scene.Heightmap.GetTerrainData(), presence);
531 m_perClientPatchUpdates.Add(presence.UUID, pups);
532 }
533 pups.SetAll(true);
534 }
535 }
536 }
537 else
538 {
539 // The traditional way is to call into the protocol stack to send them all.
540 pClient.SendLayerData(new float[10]);
541 }
412 } 542 }
413 543
414 #region Plugin Loading Methods 544 #region Plugin Loading Methods
@@ -532,6 +662,7 @@ namespace OpenSim.Region.CoreModules.World.Terrain
532 /// </summary> 662 /// </summary>
533 public void UpdateRevertMap() 663 public void UpdateRevertMap()
534 { 664 {
665 /*
535 int x; 666 int x;
536 for (x = 0; x < m_channel.Width; x++) 667 for (x = 0; x < m_channel.Width; x++)
537 { 668 {
@@ -541,6 +672,8 @@ namespace OpenSim.Region.CoreModules.World.Terrain
541 m_revert[x, y] = m_channel[x, y]; 672 m_revert[x, y] = m_channel[x, y];
542 } 673 }
543 } 674 }
675 */
676 m_revert = m_channel.MakeCopy();
544 } 677 }
545 678
546 /// <summary> 679 /// <summary>
@@ -567,8 +700,8 @@ namespace OpenSim.Region.CoreModules.World.Terrain
567 { 700 {
568 ITerrainChannel channel = loader.Value.LoadFile(filename, offsetX, offsetY, 701 ITerrainChannel channel = loader.Value.LoadFile(filename, offsetX, offsetY,
569 fileWidth, fileHeight, 702 fileWidth, fileHeight,
570 (int) Constants.RegionSize, 703 (int) m_scene.RegionInfo.RegionSizeX,
571 (int) Constants.RegionSize); 704 (int) m_scene.RegionInfo.RegionSizeY);
572 m_scene.Heightmap = channel; 705 m_scene.Heightmap = channel;
573 m_channel = channel; 706 m_channel = channel;
574 UpdateRevertMap(); 707 UpdateRevertMap();
@@ -615,8 +748,8 @@ namespace OpenSim.Region.CoreModules.World.Terrain
615 { 748 {
616 loader.Value.SaveFile(m_channel, filename, offsetX, offsetY, 749 loader.Value.SaveFile(m_channel, filename, offsetX, offsetY,
617 fileWidth, fileHeight, 750 fileWidth, fileHeight,
618 (int)Constants.RegionSize, 751 (int)m_scene.RegionInfo.RegionSizeX,
619 (int)Constants.RegionSize); 752 (int)m_scene.RegionInfo.RegionSizeY);
620 753
621 MainConsole.Instance.OutputFormat( 754 MainConsole.Instance.OutputFormat(
622 "Saved terrain from ({0},{1}) to ({2},{3}) from {4} to {5}", 755 "Saved terrain from ({0},{1}) to ({2},{3}) from {4} to {5}",
@@ -634,7 +767,44 @@ namespace OpenSim.Region.CoreModules.World.Terrain
634 } 767 }
635 768
636 /// <summary> 769 /// <summary>
770 /// Called before processing of every simulation frame.
771 /// This is used to check to see of any of the terrain is tainted and, if so, schedule
772 /// updates for all the presences.
773 /// This also checks to see if there are updates that need to be sent for each presence.
774 /// This is where the logic is to send terrain updates to clients.
775 /// </summary>
776 private void EventManager_OnFrame()
777 {
778 TerrainData terrData = m_channel.GetTerrainData();
779
780 bool shouldTaint = false;
781 for (int x = 0; x < terrData.SizeX; x += Constants.TerrainPatchSize)
782 {
783 for (int y = 0; y < terrData.SizeY; y += Constants.TerrainPatchSize)
784 {
785 if (terrData.IsTaintedAt(x, y))
786 {
787 // Found a patch that was modified. Push this flag into the clients.
788 SendToClients(terrData, x, y);
789 shouldTaint = true;
790 }
791 }
792 }
793
794 // This event also causes changes to be sent to the clients
795 CheckSendingPatchesToClients();
796
797 // If things changes, generate some events
798 if (shouldTaint)
799 {
800 m_scene.EventManager.TriggerTerrainTainted();
801 m_tainted = true;
802 }
803 }
804
805 /// <summary>
637 /// Performs updates to the region periodically, synchronising physics and other heightmap aware sections 806 /// Performs updates to the region periodically, synchronising physics and other heightmap aware sections
807 /// Called infrequently (like every 5 seconds or so). Best used for storing terrain.
638 /// </summary> 808 /// </summary>
639 private void EventManager_OnTerrainTick() 809 private void EventManager_OnTerrainTick()
640 { 810 {
@@ -644,8 +814,6 @@ namespace OpenSim.Region.CoreModules.World.Terrain
644 m_scene.PhysicsScene.SetTerrain(m_channel.GetFloatsSerialised()); 814 m_scene.PhysicsScene.SetTerrain(m_channel.GetFloatsSerialised());
645 m_scene.SaveTerrain(); 815 m_scene.SaveTerrain();
646 816
647 m_scene.EventManager.TriggerTerrainUpdate();
648
649 // Clients who look at the map will never see changes after they looked at the map, so i've commented this out. 817 // Clients who look at the map will never see changes after they looked at the map, so i've commented this out.
650 //m_scene.CreateTerrainTexture(true); 818 //m_scene.CreateTerrainTexture(true);
651 } 819 }
@@ -687,54 +855,48 @@ namespace OpenSim.Region.CoreModules.World.Terrain
687 } 855 }
688 856
689 /// <summary> 857 /// <summary>
690 /// Checks to see if the terrain has been modified since last check 858 /// Installs terrain brush hook to IClientAPI
691 /// but won't attempt to limit those changes to the limits specified in the estate settings
692 /// currently invoked by the command line operations in the region server only
693 /// </summary> 859 /// </summary>
694 private void CheckForTerrainUpdates() 860 /// <param name="client"></param>
861 private void EventManager_OnClientClosed(UUID client, Scene scene)
695 { 862 {
696 CheckForTerrainUpdates(false); 863 ScenePresence presence = scene.GetScenePresence(client);
697 } 864 if (presence != null)
865 {
866 presence.ControllingClient.OnModifyTerrain -= client_OnModifyTerrain;
867 presence.ControllingClient.OnBakeTerrain -= client_OnBakeTerrain;
868 presence.ControllingClient.OnLandUndo -= client_OnLandUndo;
869 presence.ControllingClient.OnUnackedTerrain -= client_OnUnackedTerrain;
870 }
698 871
872 lock (m_perClientPatchUpdates)
873 m_perClientPatchUpdates.Remove(client);
874 }
875
699 /// <summary> 876 /// <summary>
700 /// Checks to see if the terrain has been modified since last check. 877 /// Scan over changes in the terrain and limit height changes. This enforces the
701 /// If it has been modified, every all the terrain patches are sent to the client. 878 /// non-estate owner limits on rate of terrain editting.
702 /// If the call is asked to respect the estate settings for terrain_raise_limit and 879 /// Returns 'true' if any heights were limited.
703 /// terrain_lower_limit, it will clamp terrain updates between these values
704 /// currently invoked by client_OnModifyTerrain only and not the Commander interfaces
705 /// <param name="respectEstateSettings">should height map deltas be limited to the estate settings limits</param>
706 /// </summary> 880 /// </summary>
707 private void CheckForTerrainUpdates(bool respectEstateSettings) 881 private bool EnforceEstateLimits()
708 { 882 {
709 bool shouldTaint = false; 883 TerrainData terrData = m_channel.GetTerrainData();
710 float[] serialised = m_channel.GetFloatsSerialised(); 884
711 int x; 885 bool wasLimited = false;
712 for (x = 0; x < m_channel.Width; x += Constants.TerrainPatchSize) 886 for (int x = 0; x < terrData.SizeX; x += Constants.TerrainPatchSize)
713 { 887 {
714 int y; 888 for (int y = 0; y < terrData.SizeY; y += Constants.TerrainPatchSize)
715 for (y = 0; y < m_channel.Height; y += Constants.TerrainPatchSize)
716 { 889 {
717 if (m_channel.Tainted(x, y)) 890 if (terrData.IsTaintedAt(x, y, false /* clearOnTest */))
718 { 891 {
719 // if we should respect the estate settings then 892 // If we should respect the estate settings then
720 // fixup and height deltas that don't respect them 893 // fixup and height deltas that don't respect them.
721 if (respectEstateSettings && LimitChannelChanges(x, y)) 894 // Note that LimitChannelChanges() modifies the TerrainChannel with the limited height values.
722 { 895 wasLimited |= LimitChannelChanges(terrData, x, y);
723 // this has been vetoed, so update
724 // what we are going to send to the client
725 serialised = m_channel.GetFloatsSerialised();
726 }
727
728 SendToClients(serialised, x, y);
729 shouldTaint = true;
730 } 896 }
731 } 897 }
732 } 898 }
733 if (shouldTaint) 899 return wasLimited;
734 {
735 m_scene.EventManager.TriggerTerrainTainted();
736 m_tainted = true;
737 }
738 } 900 }
739 901
740 /// <summary> 902 /// <summary>
@@ -742,11 +904,11 @@ namespace OpenSim.Region.CoreModules.World.Terrain
742 /// are all within the current estate limits 904 /// are all within the current estate limits
743 /// <returns>true if changes were limited, false otherwise</returns> 905 /// <returns>true if changes were limited, false otherwise</returns>
744 /// </summary> 906 /// </summary>
745 private bool LimitChannelChanges(int xStart, int yStart) 907 private bool LimitChannelChanges(TerrainData terrData, int xStart, int yStart)
746 { 908 {
747 bool changesLimited = false; 909 bool changesLimited = false;
748 double minDelta = m_scene.RegionInfo.RegionSettings.TerrainLowerLimit; 910 float minDelta = (float)m_scene.RegionInfo.RegionSettings.TerrainLowerLimit;
749 double maxDelta = m_scene.RegionInfo.RegionSettings.TerrainRaiseLimit; 911 float maxDelta = (float)m_scene.RegionInfo.RegionSettings.TerrainRaiseLimit;
750 912
751 // loop through the height map for this patch and compare it against 913 // loop through the height map for this patch and compare it against
752 // the revert map 914 // the revert map
@@ -754,19 +916,18 @@ namespace OpenSim.Region.CoreModules.World.Terrain
754 { 916 {
755 for (int y = yStart; y < yStart + Constants.TerrainPatchSize; y++) 917 for (int y = yStart; y < yStart + Constants.TerrainPatchSize; y++)
756 { 918 {
757 919 float requestedHeight = terrData[x, y];
758 double requestedHeight = m_channel[x, y]; 920 float bakedHeight = (float)m_revert[x, y];
759 double bakedHeight = m_revert[x, y]; 921 float requestedDelta = requestedHeight - bakedHeight;
760 double requestedDelta = requestedHeight - bakedHeight;
761 922
762 if (requestedDelta > maxDelta) 923 if (requestedDelta > maxDelta)
763 { 924 {
764 m_channel[x, y] = bakedHeight + maxDelta; 925 terrData[x, y] = bakedHeight + maxDelta;
765 changesLimited = true; 926 changesLimited = true;
766 } 927 }
767 else if (requestedDelta < minDelta) 928 else if (requestedDelta < minDelta)
768 { 929 {
769 m_channel[x, y] = bakedHeight + minDelta; //as lower is a -ve delta 930 terrData[x, y] = bakedHeight + minDelta; //as lower is a -ve delta
770 changesLimited = true; 931 changesLimited = true;
771 } 932 }
772 } 933 }
@@ -794,14 +955,154 @@ namespace OpenSim.Region.CoreModules.World.Terrain
794 /// <param name="serialised">A copy of the terrain as a 1D float array of size w*h</param> 955 /// <param name="serialised">A copy of the terrain as a 1D float array of size w*h</param>
795 /// <param name="x">The patch corner to send</param> 956 /// <param name="x">The patch corner to send</param>
796 /// <param name="y">The patch corner to send</param> 957 /// <param name="y">The patch corner to send</param>
797 private void SendToClients(float[] serialised, int x, int y) 958 private void SendToClients(TerrainData terrData, int x, int y)
959 {
960 if (m_sendTerrainUpdatesByViewDistance)
961 {
962 // Add that this patch needs to be sent to the accounting for each client.
963 lock (m_perClientPatchUpdates)
964 {
965 m_scene.ForEachScenePresence(presence =>
966 {
967 PatchUpdates thisClientUpdates;
968 if (!m_perClientPatchUpdates.TryGetValue(presence.UUID, out thisClientUpdates))
969 {
970 // There is a ScenePresence without a send patch map. Create one.
971 thisClientUpdates = new PatchUpdates(terrData, presence);
972 m_perClientPatchUpdates.Add(presence.UUID, thisClientUpdates);
973 }
974 thisClientUpdates.SetByXY(x, y, true);
975 }
976 );
977 }
978 }
979 else
980 {
981 // Legacy update sending where the update is sent out as soon as noticed
982 // We know the actual terrain data passed is ignored. This kludge saves changing IClientAPI.
983 //float[] heightMap = terrData.GetFloatsSerialized();
984 float[] heightMap = new float[10];
985 m_scene.ForEachClient(
986 delegate(IClientAPI controller)
987 {
988 controller.SendLayerData(x / Constants.TerrainPatchSize,
989 y / Constants.TerrainPatchSize,
990 heightMap);
991 }
992 );
993 }
994 }
995
996 private class PatchesToSend : IComparable<PatchesToSend>
798 { 997 {
799 m_scene.ForEachClient( 998 public int PatchX;
800 delegate(IClientAPI controller) 999 public int PatchY;
801 { controller.SendLayerData( 1000 public float Dist;
802 x / Constants.TerrainPatchSize, y / Constants.TerrainPatchSize, serialised); 1001 public PatchesToSend(int pX, int pY, float pDist)
1002 {
1003 PatchX = pX;
1004 PatchY = pY;
1005 Dist = pDist;
1006 }
1007 public int CompareTo(PatchesToSend other)
1008 {
1009 return Dist.CompareTo(other.Dist);
1010 }
1011 }
1012
1013 // Called each frame time to see if there are any patches to send to any of the
1014 // ScenePresences.
1015 // Loop through all the per-client info and send any patches necessary.
1016 private void CheckSendingPatchesToClients()
1017 {
1018 lock (m_perClientPatchUpdates)
1019 {
1020 foreach (PatchUpdates pups in m_perClientPatchUpdates.Values)
1021 {
1022 if (pups.HasUpdates())
1023 {
1024 // There is something that could be sent to this client.
1025 List<PatchesToSend> toSend = GetModifiedPatchesInViewDistance(pups);
1026 if (toSend.Count > 0)
1027 {
1028 // m_log.DebugFormat("{0} CheckSendingPatchesToClient: sending {1} patches to {2} in region {3}",
1029 // LogHeader, toSend.Count, pups.Presence.Name, m_scene.RegionInfo.RegionName);
1030 // Sort the patches to send by the distance from the presence
1031 toSend.Sort();
1032 /*
1033 foreach (PatchesToSend pts in toSend)
1034 {
1035 pups.Presence.ControllingClient.SendLayerData(pts.PatchX, pts.PatchY, null);
1036 // presence.ControllingClient.SendLayerData(xs.ToArray(), ys.ToArray(), null, TerrainPatch.LayerType.Land);
1037 }
1038 */
1039
1040 int[] xPieces = new int[toSend.Count];
1041 int[] yPieces = new int[toSend.Count];
1042 float[] patchPieces = new float[toSend.Count * 2];
1043 int pieceIndex = 0;
1044 foreach (PatchesToSend pts in toSend)
1045 {
1046 patchPieces[pieceIndex++] = pts.PatchX;
1047 patchPieces[pieceIndex++] = pts.PatchY;
1048 }
1049 pups.Presence.ControllingClient.SendLayerData(-toSend.Count, 0, patchPieces);
1050 }
1051 }
1052 }
1053 }
1054 }
1055
1056 private List<PatchesToSend> GetModifiedPatchesInViewDistance(PatchUpdates pups)
1057 {
1058 List<PatchesToSend> ret = new List<PatchesToSend>();
1059
1060 ScenePresence presence = pups.Presence;
1061 if (presence == null)
1062 return ret;
1063
1064 // Compute the area of patches within our draw distance
1065 int startX = (((int) (presence.AbsolutePosition.X - presence.DrawDistance))/Constants.TerrainPatchSize) - 2;
1066 startX = Math.Max(startX, 0);
1067 startX = Math.Min(startX, (int)m_scene.RegionInfo.RegionSizeX/Constants.TerrainPatchSize);
1068 int startY = (((int) (presence.AbsolutePosition.Y - presence.DrawDistance))/Constants.TerrainPatchSize) - 2;
1069 startY = Math.Max(startY, 0);
1070 startY = Math.Min(startY, (int)m_scene.RegionInfo.RegionSizeY/Constants.TerrainPatchSize);
1071 int endX = (((int) (presence.AbsolutePosition.X + presence.DrawDistance))/Constants.TerrainPatchSize) + 2;
1072 endX = Math.Max(endX, 0);
1073 endX = Math.Min(endX, (int)m_scene.RegionInfo.RegionSizeX/Constants.TerrainPatchSize);
1074 int endY = (((int) (presence.AbsolutePosition.Y + presence.DrawDistance))/Constants.TerrainPatchSize) + 2;
1075 endY = Math.Max(endY, 0);
1076 endY = Math.Min(endY, (int)m_scene.RegionInfo.RegionSizeY/Constants.TerrainPatchSize);
1077 // m_log.DebugFormat("{0} GetModifiedPatchesInViewDistance. rName={1}, ddist={2}, apos={3}, start=<{4},{5}>, end=<{6},{7}>",
1078 // LogHeader, m_scene.RegionInfo.RegionName,
1079 // presence.DrawDistance, presence.AbsolutePosition,
1080 // startX, startY, endX, endY);
1081 for (int x = startX; x < endX; x++)
1082 {
1083 for (int y = startY; y < endY; y++)
1084 {
1085 //Need to make sure we don't send the same ones over and over
1086 Vector3 presencePos = presence.AbsolutePosition;
1087 Vector3 patchPos = new Vector3(x * Constants.TerrainPatchSize, y * Constants.TerrainPatchSize, presencePos.Z);
1088 if (pups.GetByPatch(x, y))
1089 {
1090 //Check which has less distance, camera or avatar position, both have to be done.
1091 //Its not a radius, its a diameter and we add 50 so that it doesn't look like it cuts off
1092 if (Util.DistanceLessThan(presencePos, patchPos, presence.DrawDistance + 50)
1093 || Util.DistanceLessThan(presence.CameraPosition, patchPos, presence.DrawDistance + 50))
1094 {
1095 //They can see it, send it to them
1096 pups.SetByPatch(x, y, false);
1097 float dist = Vector3.DistanceSquared(presencePos, patchPos);
1098 ret.Add(new PatchesToSend(x, y, dist));
1099 //Wait and send them all at once
1100 // pups.client.SendLayerData(x, y, null);
1101 }
803 } 1102 }
804 ); 1103 }
1104 }
1105 return ret;
805 } 1106 }
806 1107
807 private void client_OnModifyTerrain(UUID user, float height, float seconds, byte size, byte action, 1108 private void client_OnModifyTerrain(UUID user, float height, float seconds, byte size, byte action,
@@ -846,7 +1147,9 @@ namespace OpenSim.Region.CoreModules.World.Terrain
846 m_painteffects[(StandardTerrainEffects) action].PaintEffect( 1147 m_painteffects[(StandardTerrainEffects) action].PaintEffect(
847 m_channel, allowMask, west, south, height, size, seconds); 1148 m_channel, allowMask, west, south, height, size, seconds);
848 1149
849 CheckForTerrainUpdates(!god); //revert changes outside estate limits 1150 //revert changes outside estate limits
1151 if (!god)
1152 EnforceEstateLimits();
850 } 1153 }
851 } 1154 }
852 else 1155 else
@@ -884,10 +1187,11 @@ namespace OpenSim.Region.CoreModules.World.Terrain
884 if (allowed) 1187 if (allowed)
885 { 1188 {
886 StoreUndoState(); 1189 StoreUndoState();
887 m_floodeffects[(StandardTerrainEffects) action].FloodEffect( 1190 m_floodeffects[(StandardTerrainEffects) action].FloodEffect(m_channel, fillArea, size);
888 m_channel, fillArea, size);
889 1191
890 CheckForTerrainUpdates(!god); //revert changes outside estate limits 1192 //revert changes outside estate limits
1193 if (!god)
1194 EnforceEstateLimits();
891 } 1195 }
892 } 1196 }
893 else 1197 else
@@ -911,7 +1215,9 @@ namespace OpenSim.Region.CoreModules.World.Terrain
911 protected void client_OnUnackedTerrain(IClientAPI client, int patchX, int patchY) 1215 protected void client_OnUnackedTerrain(IClientAPI client, int patchX, int patchY)
912 { 1216 {
913 //m_log.Debug("Terrain packet unacked, resending patch: " + patchX + " , " + patchY); 1217 //m_log.Debug("Terrain packet unacked, resending patch: " + patchX + " , " + patchY);
914 client.SendLayerData(patchX, patchY, m_scene.Heightmap.GetFloatsSerialised()); 1218 // SendLayerData does not use the heightmap parameter. This kludge is so as to not change IClientAPI.
1219 float[] heightMap = new float[10];
1220 client.SendLayerData(patchX, patchY, heightMap);
915 } 1221 }
916 1222
917 private void StoreUndoState() 1223 private void StoreUndoState()
@@ -938,7 +1244,6 @@ namespace OpenSim.Region.CoreModules.World.Terrain
938 private void InterfaceLoadFile(Object[] args) 1244 private void InterfaceLoadFile(Object[] args)
939 { 1245 {
940 LoadFromFile((string) args[0]); 1246 LoadFromFile((string) args[0]);
941 CheckForTerrainUpdates();
942 } 1247 }
943 1248
944 private void InterfaceLoadTileFile(Object[] args) 1249 private void InterfaceLoadTileFile(Object[] args)
@@ -948,7 +1253,6 @@ namespace OpenSim.Region.CoreModules.World.Terrain
948 (int) args[2], 1253 (int) args[2],
949 (int) args[3], 1254 (int) args[3],
950 (int) args[4]); 1255 (int) args[4]);
951 CheckForTerrainUpdates();
952 } 1256 }
953 1257
954 private void InterfaceSaveFile(Object[] args) 1258 private void InterfaceSaveFile(Object[] args)
@@ -977,7 +1281,6 @@ namespace OpenSim.Region.CoreModules.World.Terrain
977 for (y = 0; y < m_channel.Height; y++) 1281 for (y = 0; y < m_channel.Height; y++)
978 m_channel[x, y] = m_revert[x, y]; 1282 m_channel[x, y] = m_revert[x, y];
979 1283
980 CheckForTerrainUpdates();
981 } 1284 }
982 1285
983 private void InterfaceFlipTerrain(Object[] args) 1286 private void InterfaceFlipTerrain(Object[] args)
@@ -986,28 +1289,28 @@ namespace OpenSim.Region.CoreModules.World.Terrain
986 1289
987 if (direction.ToLower().StartsWith("y")) 1290 if (direction.ToLower().StartsWith("y"))
988 { 1291 {
989 for (int x = 0; x < Constants.RegionSize; x++) 1292 for (int x = 0; x < m_channel.Width; x++)
990 { 1293 {
991 for (int y = 0; y < Constants.RegionSize / 2; y++) 1294 for (int y = 0; y < m_channel.Height / 2; y++)
992 { 1295 {
993 double height = m_channel[x, y]; 1296 double height = m_channel[x, y];
994 double flippedHeight = m_channel[x, (int)Constants.RegionSize - 1 - y]; 1297 double flippedHeight = m_channel[x, (int)m_channel.Height - 1 - y];
995 m_channel[x, y] = flippedHeight; 1298 m_channel[x, y] = flippedHeight;
996 m_channel[x, (int)Constants.RegionSize - 1 - y] = height; 1299 m_channel[x, (int)m_channel.Height - 1 - y] = height;
997 1300
998 } 1301 }
999 } 1302 }
1000 } 1303 }
1001 else if (direction.ToLower().StartsWith("x")) 1304 else if (direction.ToLower().StartsWith("x"))
1002 { 1305 {
1003 for (int y = 0; y < Constants.RegionSize; y++) 1306 for (int y = 0; y < m_channel.Height; y++)
1004 { 1307 {
1005 for (int x = 0; x < Constants.RegionSize / 2; x++) 1308 for (int x = 0; x < m_channel.Width / 2; x++)
1006 { 1309 {
1007 double height = m_channel[x, y]; 1310 double height = m_channel[x, y];
1008 double flippedHeight = m_channel[(int)Constants.RegionSize - 1 - x, y]; 1311 double flippedHeight = m_channel[(int)m_channel.Width - 1 - x, y];
1009 m_channel[x, y] = flippedHeight; 1312 m_channel[x, y] = flippedHeight;
1010 m_channel[(int)Constants.RegionSize - 1 - x, y] = height; 1313 m_channel[(int)m_channel.Width - 1 - x, y] = height;
1011 1314
1012 } 1315 }
1013 } 1316 }
@@ -1016,9 +1319,6 @@ namespace OpenSim.Region.CoreModules.World.Terrain
1016 { 1319 {
1017 m_log.Error("Unrecognised direction - need x or y"); 1320 m_log.Error("Unrecognised direction - need x or y");
1018 } 1321 }
1019
1020
1021 CheckForTerrainUpdates();
1022 } 1322 }
1023 1323
1024 private void InterfaceRescaleTerrain(Object[] args) 1324 private void InterfaceRescaleTerrain(Object[] args)
@@ -1076,7 +1376,6 @@ namespace OpenSim.Region.CoreModules.World.Terrain
1076 } 1376 }
1077 } 1377 }
1078 1378
1079 CheckForTerrainUpdates();
1080 } 1379 }
1081 1380
1082 } 1381 }
@@ -1087,7 +1386,6 @@ namespace OpenSim.Region.CoreModules.World.Terrain
1087 for (x = 0; x < m_channel.Width; x++) 1386 for (x = 0; x < m_channel.Width; x++)
1088 for (y = 0; y < m_channel.Height; y++) 1387 for (y = 0; y < m_channel.Height; y++)
1089 m_channel[x, y] += (double) args[0]; 1388 m_channel[x, y] += (double) args[0];
1090 CheckForTerrainUpdates();
1091 } 1389 }
1092 1390
1093 private void InterfaceMultiplyTerrain(Object[] args) 1391 private void InterfaceMultiplyTerrain(Object[] args)
@@ -1096,7 +1394,6 @@ namespace OpenSim.Region.CoreModules.World.Terrain
1096 for (x = 0; x < m_channel.Width; x++) 1394 for (x = 0; x < m_channel.Width; x++)
1097 for (y = 0; y < m_channel.Height; y++) 1395 for (y = 0; y < m_channel.Height; y++)
1098 m_channel[x, y] *= (double) args[0]; 1396 m_channel[x, y] *= (double) args[0];
1099 CheckForTerrainUpdates();
1100 } 1397 }
1101 1398
1102 private void InterfaceLowerTerrain(Object[] args) 1399 private void InterfaceLowerTerrain(Object[] args)
@@ -1105,17 +1402,15 @@ namespace OpenSim.Region.CoreModules.World.Terrain
1105 for (x = 0; x < m_channel.Width; x++) 1402 for (x = 0; x < m_channel.Width; x++)
1106 for (y = 0; y < m_channel.Height; y++) 1403 for (y = 0; y < m_channel.Height; y++)
1107 m_channel[x, y] -= (double) args[0]; 1404 m_channel[x, y] -= (double) args[0];
1108 CheckForTerrainUpdates();
1109 } 1405 }
1110 1406
1111 private void InterfaceFillTerrain(Object[] args) 1407 public void InterfaceFillTerrain(Object[] args)
1112 { 1408 {
1113 int x, y; 1409 int x, y;
1114 1410
1115 for (x = 0; x < m_channel.Width; x++) 1411 for (x = 0; x < m_channel.Width; x++)
1116 for (y = 0; y < m_channel.Height; y++) 1412 for (y = 0; y < m_channel.Height; y++)
1117 m_channel[x, y] = (double) args[0]; 1413 m_channel[x, y] = (double) args[0];
1118 CheckForTerrainUpdates();
1119 } 1414 }
1120 1415
1121 private void InterfaceMinTerrain(Object[] args) 1416 private void InterfaceMinTerrain(Object[] args)
@@ -1128,7 +1423,6 @@ namespace OpenSim.Region.CoreModules.World.Terrain
1128 m_channel[x, y] = Math.Max((double)args[0], m_channel[x, y]); 1423 m_channel[x, y] = Math.Max((double)args[0], m_channel[x, y]);
1129 } 1424 }
1130 } 1425 }
1131 CheckForTerrainUpdates();
1132 } 1426 }
1133 1427
1134 private void InterfaceMaxTerrain(Object[] args) 1428 private void InterfaceMaxTerrain(Object[] args)
@@ -1141,7 +1435,6 @@ namespace OpenSim.Region.CoreModules.World.Terrain
1141 m_channel[x, y] = Math.Min((double)args[0], m_channel[x, y]); 1435 m_channel[x, y] = Math.Min((double)args[0], m_channel[x, y]);
1142 } 1436 }
1143 } 1437 }
1144 CheckForTerrainUpdates();
1145 } 1438 }
1146 1439
1147 private void InterfaceShowDebugStats(Object[] args) 1440 private void InterfaceShowDebugStats(Object[] args)
@@ -1204,7 +1497,6 @@ namespace OpenSim.Region.CoreModules.World.Terrain
1204 if (m_plugineffects.ContainsKey(firstArg)) 1497 if (m_plugineffects.ContainsKey(firstArg))
1205 { 1498 {
1206 m_plugineffects[firstArg].RunEffect(m_channel); 1499 m_plugineffects[firstArg].RunEffect(m_channel);
1207 CheckForTerrainUpdates();
1208 } 1500 }
1209 else 1501 else
1210 { 1502 {
diff --git a/OpenSim/Region/Framework/Interfaces/ISimulationDataService.cs b/OpenSim/Region/Framework/Interfaces/ISimulationDataService.cs
index 3e97a7a..13358cb 100644
--- a/OpenSim/Region/Framework/Interfaces/ISimulationDataService.cs
+++ b/OpenSim/Region/Framework/Interfaces/ISimulationDataService.cs
@@ -68,13 +68,22 @@ namespace OpenSim.Region.Framework.Interfaces
68 /// </summary> 68 /// </summary>
69 /// <param name="ter">HeightField data</param> 69 /// <param name="ter">HeightField data</param>
70 /// <param name="regionID">region UUID</param> 70 /// <param name="regionID">region UUID</param>
71 void StoreTerrain(TerrainData terrain, UUID regionID);
72
73 // Legacy version kept for downward compabibility
71 void StoreTerrain(double[,] terrain, UUID regionID); 74 void StoreTerrain(double[,] terrain, UUID regionID);
72 75
73 /// <summary> 76 /// <summary>
74 /// Load the latest terrain revision from region storage 77 /// Load the latest terrain revision from region storage
75 /// </summary> 78 /// </summary>
76 /// <param name="regionID">the region UUID</param> 79 /// <param name="regionID">the region UUID</param>
80 /// <param name="sizeX">the X dimension of the region being filled</param>
81 /// <param name="sizeY">the Y dimension of the region being filled</param>
82 /// <param name="sizeZ">the Z dimension of the region being filled</param>
77 /// <returns>Heightfield data</returns> 83 /// <returns>Heightfield data</returns>
84 TerrainData LoadTerrain(UUID regionID, int pSizeX, int pSizeY, int pSizeZ);
85
86 // Legacy version kept for downward compabibility
78 double[,] LoadTerrain(UUID regionID); 87 double[,] LoadTerrain(UUID regionID);
79 88
80 void StoreLandObject(ILandObject Parcel); 89 void StoreLandObject(ILandObject Parcel);
diff --git a/OpenSim/Region/Framework/Interfaces/ISimulationDataStore.cs b/OpenSim/Region/Framework/Interfaces/ISimulationDataStore.cs
index 17bd48b..e09f775 100644
--- a/OpenSim/Region/Framework/Interfaces/ISimulationDataStore.cs
+++ b/OpenSim/Region/Framework/Interfaces/ISimulationDataStore.cs
@@ -79,13 +79,22 @@ namespace OpenSim.Region.Framework.Interfaces
79 /// </summary> 79 /// </summary>
80 /// <param name="ter">HeightField data</param> 80 /// <param name="ter">HeightField data</param>
81 /// <param name="regionID">region UUID</param> 81 /// <param name="regionID">region UUID</param>
82 void StoreTerrain(TerrainData terrain, UUID regionID);
83
84 // Legacy version kept for downward compabibility
82 void StoreTerrain(double[,] terrain, UUID regionID); 85 void StoreTerrain(double[,] terrain, UUID regionID);
83 86
84 /// <summary> 87 /// <summary>
85 /// Load the latest terrain revision from region storage 88 /// Load the latest terrain revision from region storage
86 /// </summary> 89 /// </summary>
87 /// <param name="regionID">the region UUID</param> 90 /// <param name="regionID">the region UUID</param>
91 /// <param name="pSizeX">the X dimension of the terrain being filled</param>
92 /// <param name="pSizeY">the Y dimension of the terrain being filled</param>
93 /// <param name="pSizeZ">the Z dimension of the terrain being filled</param>
88 /// <returns>Heightfield data</returns> 94 /// <returns>Heightfield data</returns>
95 TerrainData LoadTerrain(UUID regionID, int pSizeX, int pSizeY, int pSizeZ);
96
97 // Legacy version kept for downward compabibility
89 double[,] LoadTerrain(UUID regionID); 98 double[,] LoadTerrain(UUID regionID);
90 99
91 void StoreLandObject(ILandObject Parcel); 100 void StoreLandObject(ILandObject Parcel);
@@ -136,4 +145,5 @@ namespace OpenSim.Region.Framework.Interfaces
136 145
137 void Shutdown(); 146 void Shutdown();
138 } 147 }
148
139} 149}
diff --git a/OpenSim/Region/Framework/Interfaces/ITerrainChannel.cs b/OpenSim/Region/Framework/Interfaces/ITerrainChannel.cs
index e467701..f660b8d 100644
--- a/OpenSim/Region/Framework/Interfaces/ITerrainChannel.cs
+++ b/OpenSim/Region/Framework/Interfaces/ITerrainChannel.cs
@@ -25,13 +25,23 @@
25 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */ 26 */
27 27
28using OpenSim.Framework;
29using OpenMetaverse;
30
28namespace OpenSim.Region.Framework.Interfaces 31namespace OpenSim.Region.Framework.Interfaces
29{ 32{
30 public interface ITerrainChannel 33 public interface ITerrainChannel
31 { 34 {
32 int Height { get; } 35 int Width { get;} // X dimension
36 int Height { get;} // Y dimension
37 int Altitude { get;} // Z dimension
38
33 double this[int x, int y] { get; set; } 39 double this[int x, int y] { get; set; }
34 int Width { get; } 40
41 float GetHeightAtXYZ(float x, float y, float z);
42
43 // Return the packaged terrain data for passing into lower levels of communication
44 TerrainData GetTerrainData();
35 45
36 /// <summary> 46 /// <summary>
37 /// Squash the entire heightmap into a single dimensioned array 47 /// Squash the entire heightmap into a single dimensioned array
@@ -40,9 +50,14 @@ namespace OpenSim.Region.Framework.Interfaces
40 float[] GetFloatsSerialised(); 50 float[] GetFloatsSerialised();
41 51
42 double[,] GetDoubles(); 52 double[,] GetDoubles();
53
54 // Check if a location has been updated. Clears the taint flag as a side effect.
43 bool Tainted(int x, int y); 55 bool Tainted(int x, int y);
56
44 ITerrainChannel MakeCopy(); 57 ITerrainChannel MakeCopy();
45 string SaveToXmlString(); 58 string SaveToXmlString();
46 void LoadFromXmlString(string data); 59 void LoadFromXmlString(string data);
60 // Merge some terrain into this channel
61 void Merge(ITerrainChannel newTerrain, Vector3 displacement, float radianRotation, Vector2 rotationDisplacement);
47 } 62 }
48} 63}
diff --git a/OpenSim/Region/Framework/Scenes/Scene.cs b/OpenSim/Region/Framework/Scenes/Scene.cs
index 03270d7..f5458c1 100644
--- a/OpenSim/Region/Framework/Scenes/Scene.cs
+++ b/OpenSim/Region/Framework/Scenes/Scene.cs
@@ -1935,7 +1935,7 @@ namespace OpenSim.Region.Framework.Scenes
1935 { 1935 {
1936 try 1936 try
1937 { 1937 {
1938 double[,] map = SimulationDataService.LoadTerrain(RegionInfo.RegionID); 1938 TerrainData map = SimulationDataService.LoadTerrain(RegionInfo.RegionID, (int)RegionInfo.RegionSizeX, (int)RegionInfo.RegionSizeY, (int)RegionInfo.RegionSizeZ);
1939 if (map == null) 1939 if (map == null)
1940 { 1940 {
1941 // This should be in the Terrain module, but it isn't because 1941 // This should be in the Terrain module, but it isn't because
@@ -1946,7 +1946,7 @@ namespace OpenSim.Region.Framework.Scenes
1946 m_InitialTerrain = terrainConfig.GetString("InitialTerrain", m_InitialTerrain); 1946 m_InitialTerrain = terrainConfig.GetString("InitialTerrain", m_InitialTerrain);
1947 1947
1948 m_log.InfoFormat("[TERRAIN]: No default terrain. Generating a new terrain {0}.", m_InitialTerrain); 1948 m_log.InfoFormat("[TERRAIN]: No default terrain. Generating a new terrain {0}.", m_InitialTerrain);
1949 Heightmap = new TerrainChannel(m_InitialTerrain); 1949 Heightmap = new TerrainChannel(m_InitialTerrain, (int)RegionInfo.RegionSizeX, (int)RegionInfo.RegionSizeY, (int)RegionInfo.RegionSizeZ);
1950 1950
1951 SimulationDataService.StoreTerrain(Heightmap.GetDoubles(), RegionInfo.RegionID); 1951 SimulationDataService.StoreTerrain(Heightmap.GetDoubles(), RegionInfo.RegionID);
1952 } 1952 }
diff --git a/OpenSim/Region/Framework/Scenes/TerrainChannel.cs b/OpenSim/Region/Framework/Scenes/TerrainChannel.cs
index b6e0a97..3d563a6 100644
--- a/OpenSim/Region/Framework/Scenes/TerrainChannel.cs
+++ b/OpenSim/Region/Framework/Scenes/TerrainChannel.cs
@@ -25,14 +25,21 @@
25 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */ 26 */
27 27
28using OpenSim.Framework;
29using OpenSim.Region.Framework.Interfaces;
30using System; 28using System;
29using System.IO;
31using System.Text; 30using System.Text;
31using System.Reflection;
32using System.Xml; 32using System.Xml;
33using System.IO;
34using System.Xml.Serialization; 33using System.Xml.Serialization;
35 34
35using OpenSim.Data;
36using OpenSim.Framework;
37using OpenSim.Region.Framework.Interfaces;
38
39using OpenMetaverse;
40
41using log4net;
42
36namespace OpenSim.Region.Framework.Scenes 43namespace OpenSim.Region.Framework.Scenes
37{ 44{
38 /// <summary> 45 /// <summary>
@@ -40,140 +47,136 @@ namespace OpenSim.Region.Framework.Scenes
40 /// </summary> 47 /// </summary>
41 public class TerrainChannel : ITerrainChannel 48 public class TerrainChannel : ITerrainChannel
42 { 49 {
43 private readonly bool[,] taint; 50 private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
44 private double[,] map; 51 private static string LogHeader = "[TERRAIN CHANNEL]";
45 52
53 protected TerrainData m_terrainData;
54
55 public int Width { get { return m_terrainData.SizeX; } } // X dimension
56 // Unfortunately, for historical reasons, in this module 'Width' is X and 'Height' is Y
57 public int Height { get { return m_terrainData.SizeY; } } // Y dimension
58 public int Altitude { get { return m_terrainData.SizeZ; } } // Y dimension
59
60 // Default, not-often-used builder
46 public TerrainChannel() 61 public TerrainChannel()
47 { 62 {
48 map = new double[Constants.RegionSize, Constants.RegionSize]; 63 m_terrainData = new HeightmapTerrainData((int)Constants.RegionSize, (int)Constants.RegionSize, (int)Constants.RegionHeight);
49 taint = new bool[Constants.RegionSize / 16, Constants.RegionSize / 16]; 64 FlatLand();
50 65 // PinHeadIsland();
51 PinHeadIsland();
52 } 66 }
53 67
54 public TerrainChannel(String type) 68 // Create terrain of given size
69 public TerrainChannel(int pX, int pY)
55 { 70 {
56 map = new double[Constants.RegionSize, Constants.RegionSize]; 71 m_terrainData = new HeightmapTerrainData(pX, pY, (int)Constants.RegionHeight);
57 taint = new bool[Constants.RegionSize / 16, Constants.RegionSize / 16]; 72 }
58 73
74 // Create terrain of specified size and initialize with specified terrain.
75 // TODO: join this with the terrain initializers.
76 public TerrainChannel(String type, int pX, int pY, int pZ)
77 {
78 m_terrainData = new HeightmapTerrainData(pX, pY, pZ);
59 if (type.Equals("flat")) 79 if (type.Equals("flat"))
60 FlatLand(); 80 FlatLand();
61 else 81 else
62 PinHeadIsland(); 82 PinHeadIsland();
63 } 83 }
64 84
65 public TerrainChannel(double[,] import) 85 // Create channel passed a heightmap and expected dimensions of the region.
86 // The heightmap might not fit the passed size so accomodations must be made.
87 public TerrainChannel(double[,] pM, int pSizeX, int pSizeY, int pAltitude)
66 { 88 {
67 map = import; 89 int hmSizeX = pM.GetLength(0);
68 taint = new bool[import.GetLength(0),import.GetLength(1)]; 90 int hmSizeY = pM.GetLength(1);
69 }
70 91
71 public TerrainChannel(bool createMap) 92 m_terrainData = new HeightmapTerrainData(pSizeX, pSizeY, pAltitude);
72 { 93
73 if (createMap) 94 for (int xx = 0; xx < pSizeX; xx++)
74 { 95 for (int yy = 0; yy < pSizeY; yy++)
75 map = new double[Constants.RegionSize,Constants.RegionSize]; 96 if (xx > hmSizeX || yy > hmSizeY)
76 taint = new bool[Constants.RegionSize / 16,Constants.RegionSize / 16]; 97 m_terrainData[xx, yy] = TerrainData.DefaultTerrainHeight;
77 } 98 else
99 m_terrainData[xx, yy] = (float)pM[xx, yy];
78 } 100 }
79 101
80 public TerrainChannel(int w, int h) 102 public TerrainChannel(TerrainData pTerrData)
81 { 103 {
82 map = new double[w,h]; 104 m_terrainData = pTerrData;
83 taint = new bool[w / 16,h / 16];
84 } 105 }
85 106
86 #region ITerrainChannel Members 107 #region ITerrainChannel Members
87 108
88 public int Width 109 // ITerrainChannel.MakeCopy()
110 public ITerrainChannel MakeCopy()
89 { 111 {
90 get { return map.GetLength(0); } 112 return this.Copy();
91 } 113 }
92 114
93 public int Height 115 // ITerrainChannel.GetTerrainData()
116 public TerrainData GetTerrainData()
94 { 117 {
95 get { return map.GetLength(1); } 118 return m_terrainData;
96 } 119 }
97 120
98 public ITerrainChannel MakeCopy() 121 // ITerrainChannel.GetFloatsSerialized()
122 // This one dimensional version is ordered so height = map[y*sizeX+x];
123 // DEPRECATED: don't use this function as it does not retain the dimensions of the terrain
124 // and the caller will probably do the wrong thing if the terrain is not the legacy 256x256.
125 public float[] GetFloatsSerialised()
99 { 126 {
100 TerrainChannel copy = new TerrainChannel(false); 127 return m_terrainData.GetFloatsSerialized();
101 copy.map = (double[,]) map.Clone();
102
103 return copy;
104 } 128 }
105 129
106 public float[] GetFloatsSerialised() 130 // ITerrainChannel.GetDoubles()
131 public double[,] GetDoubles()
107 { 132 {
108 // Move the member variables into local variables, calling 133 double[,] heights = new double[Width, Height];
109 // member variables 256*256 times gets expensive
110 int w = Width;
111 int h = Height;
112 float[] heights = new float[w * h];
113 134
114 int i, j; // map coordinates
115 int idx = 0; // index into serialized array 135 int idx = 0; // index into serialized array
116 for (i = 0; i < h; i++) 136 for (int ii = 0; ii < Width; ii++)
117 { 137 {
118 for (j = 0; j < w; j++) 138 for (int jj = 0; jj < Height; jj++)
119 { 139 {
120 heights[idx++] = (float)map[j, i]; 140 heights[ii, jj] = (double)m_terrainData[ii, jj];
141 idx++;
121 } 142 }
122 } 143 }
123 144
124 return heights; 145 return heights;
125 } 146 }
126 147
127 public double[,] GetDoubles() 148 // ITerrainChannel.this[x,y]
128 {
129 return map;
130 }
131
132 public double this[int x, int y] 149 public double this[int x, int y]
133 { 150 {
134 get 151 get {
135 { 152 if (x < 0 || x >= Width || y < 0 || y >= Height)
136 if (x < 0) x = 0; 153 return 0;
137 if (y < 0) y = 0; 154 return (double)m_terrainData[x, y];
138 if (x >= (int)Constants.RegionSize) x = (int)Constants.RegionSize - 1;
139 if (y >= (int)Constants.RegionSize) y = (int)Constants.RegionSize - 1;
140
141 return map[x, y];
142 } 155 }
143 set 156 set
144 { 157 {
145 // Will "fix" terrain hole problems. Although not fantastically.
146 if (Double.IsNaN(value) || Double.IsInfinity(value)) 158 if (Double.IsNaN(value) || Double.IsInfinity(value))
147 return; 159 return;
148 160
149 if (map[x, y] != value) 161 m_terrainData[x, y] = (float)value;
150 {
151 taint[x / 16, y / 16] = true;
152 map[x, y] = value;
153 }
154 } 162 }
155 } 163 }
156 164
157 public bool Tainted(int x, int y) 165 // ITerrainChannel.GetHieghtAtXYZ(x, y, z)
166 public float GetHeightAtXYZ(float x, float y, float z)
158 { 167 {
159 if (taint[x / 16, y / 16]) 168 if (x < 0 || x >= Width || y < 0 || y >= Height)
160 { 169 return 0;
161 taint[x / 16, y / 16] = false; 170 return m_terrainData[(int)x, (int)y];
162 return true;
163 }
164 return false;
165 } 171 }
166 172
167 #endregion 173 // ITerrainChannel.Tainted()
168 174 public bool Tainted(int x, int y)
169 public TerrainChannel Copy()
170 { 175 {
171 TerrainChannel copy = new TerrainChannel(false); 176 return m_terrainData.IsTaintedAt(x, y);
172 copy.map = (double[,]) map.Clone();
173
174 return copy;
175 } 177 }
176 178
179 // ITerrainChannel.SaveToXmlString()
177 public string SaveToXmlString() 180 public string SaveToXmlString()
178 { 181 {
179 XmlWriterSettings settings = new XmlWriterSettings(); 182 XmlWriterSettings settings = new XmlWriterSettings();
@@ -189,13 +192,7 @@ namespace OpenSim.Region.Framework.Scenes
189 } 192 }
190 } 193 }
191 194
192 private void WriteXml(XmlWriter writer) 195 // ITerrainChannel.LoadFromXmlString()
193 {
194 writer.WriteStartElement(String.Empty, "TerrainMap", String.Empty);
195 ToXml(writer);
196 writer.WriteEndElement();
197 }
198
199 public void LoadFromXmlString(string data) 196 public void LoadFromXmlString(string data)
200 { 197 {
201 StringReader sr = new StringReader(data); 198 StringReader sr = new StringReader(data);
@@ -207,12 +204,124 @@ namespace OpenSim.Region.Framework.Scenes
207 sr.Close(); 204 sr.Close();
208 } 205 }
209 206
207 // ITerrainChannel.Merge
208 public void Merge(ITerrainChannel newTerrain, Vector3 displacement, float radianRotation, Vector2 rotationDisplacement)
209 {
210 m_log.DebugFormat("{0} Merge. inSize=<{1},{2}>, disp={3}, rot={4}, rotDisp={5}, outSize=<{6},{7}>", LogHeader,
211 newTerrain.Width, newTerrain.Height,
212 displacement, radianRotation, rotationDisplacement,
213 m_terrainData.SizeX, m_terrainData.SizeY);
214 for (int xx = 0; xx < newTerrain.Width; xx++)
215 {
216 for (int yy = 0; yy < newTerrain.Height; yy++)
217 {
218 int dispX = (int)displacement.X;
219 int dispY = (int)displacement.Y;
220 float newHeight = (float)newTerrain[xx, yy] + displacement.Z;
221 if (radianRotation == 0)
222 {
223 // If no rotation, place the new height in the specified location
224 dispX += xx;
225 dispY += yy;
226 if (dispX >= 0 && dispX < m_terrainData.SizeX && dispY >= 0 && dispY < m_terrainData.SizeY)
227 {
228 m_terrainData[dispX, dispY] = newHeight;
229 }
230 }
231 else
232 {
233 // If rotating, we have to smooth the result because the conversion
234 // to ints will mean heightmap entries will not get changed
235 // First compute the rotation location for the new height.
236 dispX += (int)(rotationDisplacement.X
237 + ((float)xx - rotationDisplacement.X) * Math.Cos(radianRotation)
238 - ((float)yy - rotationDisplacement.Y) * Math.Sin(radianRotation) );
239
240 dispY += (int)(rotationDisplacement.Y
241 + ((float)xx - rotationDisplacement.X) * Math.Sin(radianRotation)
242 + ((float)yy - rotationDisplacement.Y) * Math.Cos(radianRotation) );
243
244 if (dispX >= 0 && dispX < m_terrainData.SizeX && dispY >= 0 && dispY < m_terrainData.SizeY)
245 {
246 float oldHeight = m_terrainData[dispX, dispY];
247 // Smooth the heights around this location if the old height is far from this one
248 for (int sxx = dispX - 2; sxx < dispX + 2; sxx++)
249 {
250 for (int syy = dispY - 2; syy < dispY + 2; syy++)
251 {
252 if (sxx >= 0 && sxx < m_terrainData.SizeX && syy >= 0 && syy < m_terrainData.SizeY)
253 {
254 if (sxx == dispX && syy == dispY)
255 {
256 // Set height for the exact rotated point
257 m_terrainData[dispX, dispY] = newHeight;
258 }
259 else
260 {
261 if (Math.Abs(m_terrainData[sxx, syy] - newHeight) > 1f)
262 {
263 // If the adjacent height is far off, force it to this height
264 m_terrainData[sxx, syy] = newHeight;
265 }
266 }
267 }
268 }
269 }
270 }
271
272 if (dispX >= 0 && dispX < m_terrainData.SizeX && dispY >= 0 && dispY < m_terrainData.SizeY)
273 {
274 m_terrainData[dispX, dispY] = (float)newTerrain[xx, yy];
275 }
276 }
277 }
278 }
279 }
280
281 #endregion
282
283 public TerrainChannel Copy()
284 {
285 TerrainChannel copy = new TerrainChannel();
286 copy.m_terrainData = m_terrainData.Clone();
287 return copy;
288 }
289
290 private void WriteXml(XmlWriter writer)
291 {
292 if (Width == Constants.RegionSize && Height == Constants.RegionSize)
293 {
294 // Downward compatibility for legacy region terrain maps.
295 // If region is exactly legacy size, return the old format XML.
296 writer.WriteStartElement(String.Empty, "TerrainMap", String.Empty);
297 ToXml(writer);
298 writer.WriteEndElement();
299 }
300 else
301 {
302 // New format XML that includes width and length.
303 writer.WriteStartElement(String.Empty, "TerrainMap2", String.Empty);
304 ToXml2(writer);
305 writer.WriteEndElement();
306 }
307 }
308
210 private void ReadXml(XmlReader reader) 309 private void ReadXml(XmlReader reader)
211 { 310 {
212 reader.ReadStartElement("TerrainMap"); 311 // Check the first element. If legacy element, use the legacy reader.
213 FromXml(reader); 312 if (reader.IsStartElement("TerrainMap"))
313 {
314 reader.ReadStartElement("TerrainMap");
315 FromXml(reader);
316 }
317 else
318 {
319 reader.ReadStartElement("TerrainMap2");
320 FromXml2(reader);
321 }
214 } 322 }
215 323
324 // Write legacy terrain map. Presumed to be 256x256 of data encoded as floats in a byte array.
216 private void ToXml(XmlWriter xmlWriter) 325 private void ToXml(XmlWriter xmlWriter)
217 { 326 {
218 float[] mapData = GetFloatsSerialised(); 327 float[] mapData = GetFloatsSerialised();
@@ -226,12 +335,15 @@ namespace OpenSim.Region.Framework.Scenes
226 serializer.Serialize(xmlWriter, buffer); 335 serializer.Serialize(xmlWriter, buffer);
227 } 336 }
228 337
338 // Read legacy terrain map. Presumed to be 256x256 of data encoded as floats in a byte array.
229 private void FromXml(XmlReader xmlReader) 339 private void FromXml(XmlReader xmlReader)
230 { 340 {
231 XmlSerializer serializer = new XmlSerializer(typeof(byte[])); 341 XmlSerializer serializer = new XmlSerializer(typeof(byte[]));
232 byte[] dataArray = (byte[])serializer.Deserialize(xmlReader); 342 byte[] dataArray = (byte[])serializer.Deserialize(xmlReader);
233 int index = 0; 343 int index = 0;
234 344
345 m_terrainData = new HeightmapTerrainData(Height, Width, (int)Constants.RegionHeight);
346
235 for (int y = 0; y < Height; y++) 347 for (int y = 0; y < Height; y++)
236 { 348 {
237 for (int x = 0; x < Width; x++) 349 for (int x = 0; x < Width; x++)
@@ -244,35 +356,63 @@ namespace OpenSim.Region.Framework.Scenes
244 } 356 }
245 } 357 }
246 358
359 private class TerrainChannelXMLPackage
360 {
361 public int Version;
362 public int SizeX;
363 public int SizeY;
364 public int SizeZ;
365 public float CompressionFactor;
366 public int[] Map;
367 public TerrainChannelXMLPackage(int pX, int pY, int pZ, float pCompressionFactor, int[] pMap)
368 {
369 Version = 1;
370 SizeX = pX;
371 SizeY = pY;
372 SizeZ = pZ;
373 CompressionFactor = pCompressionFactor;
374 Map = pMap;
375 }
376 }
377
378 // New terrain serialization format that includes the width and length.
379 private void ToXml2(XmlWriter xmlWriter)
380 {
381 TerrainChannelXMLPackage package = new TerrainChannelXMLPackage(Width, Height, Altitude, m_terrainData.CompressionFactor,
382 m_terrainData.GetCompressedMap());
383 XmlSerializer serializer = new XmlSerializer(typeof(TerrainChannelXMLPackage));
384 serializer.Serialize(xmlWriter, package);
385 }
386
387 // New terrain serialization format that includes the width and length.
388 private void FromXml2(XmlReader xmlReader)
389 {
390 XmlSerializer serializer = new XmlSerializer(typeof(TerrainChannelXMLPackage));
391 TerrainChannelXMLPackage package = (TerrainChannelXMLPackage)serializer.Deserialize(xmlReader);
392 m_terrainData = new HeightmapTerrainData(package.Map, package.CompressionFactor, package.SizeX, package.SizeY, package.SizeZ);
393 }
394
395 // Fill the heightmap with the center bump terrain
247 private void PinHeadIsland() 396 private void PinHeadIsland()
248 { 397 {
249 int x; 398 for (int x = 0; x < Width; x++)
250 for (x = 0; x < Constants.RegionSize; x++)
251 { 399 {
252 int y; 400 for (int y = 0; y < Height; y++)
253 for (y = 0; y < Constants.RegionSize; y++)
254 { 401 {
255 map[x, y] = TerrainUtil.PerlinNoise2D(x, y, 2, 0.125) * 10; 402 m_terrainData[x, y] = (float)TerrainUtil.PerlinNoise2D(x, y, 2, 0.125) * 10;
256 double spherFacA = TerrainUtil.SphericalFactor(x, y, Constants.RegionSize / 2.0, Constants.RegionSize / 2.0, 50) * 0.01; 403 float spherFacA = (float)(TerrainUtil.SphericalFactor(x, y, m_terrainData.SizeX / 2.0, m_terrainData.SizeY / 2.0, 50) * 0.01d);
257 double spherFacB = TerrainUtil.SphericalFactor(x, y, Constants.RegionSize / 2.0, Constants.RegionSize / 2.0, 100) * 0.001; 404 float spherFacB = (float)(TerrainUtil.SphericalFactor(x, y, m_terrainData.SizeX / 2.0, m_terrainData.SizeY / 2.0, 100) * 0.001d);
258 if (map[x, y] < spherFacA) 405 if (m_terrainData[x, y]< spherFacA)
259 map[x, y] = spherFacA; 406 m_terrainData[x, y]= spherFacA;
260 if (map[x, y] < spherFacB) 407 if (m_terrainData[x, y]< spherFacB)
261 map[x, y] = spherFacB; 408 m_terrainData[x, y] = spherFacB;
262 } 409 }
263 } 410 }
264 } 411 }
265 412
266 private void FlatLand() 413 private void FlatLand()
267 { 414 {
268 int x; 415 m_terrainData.ClearLand();
269 for (x = 0; x < Constants.RegionSize; x++)
270 {
271 int y;
272 for (y = 0; y < Constants.RegionSize; y++)
273 map[x, y] = 21;
274 }
275 } 416 }
276
277 } 417 }
278} 418}
diff --git a/OpenSim/Services/Connectors/Simulation/SimulationDataService.cs b/OpenSim/Services/Connectors/Simulation/SimulationDataService.cs
index 96c02d9..4759838 100644
--- a/OpenSim/Services/Connectors/Simulation/SimulationDataService.cs
+++ b/OpenSim/Services/Connectors/Simulation/SimulationDataService.cs
@@ -100,6 +100,11 @@ namespace OpenSim.Services.Connectors
100 return m_database.LoadObjects(regionUUID); 100 return m_database.LoadObjects(regionUUID);
101 } 101 }
102 102
103 public void StoreTerrain(TerrainData terrain, UUID regionID)
104 {
105 m_database.StoreTerrain(terrain, regionID);
106 }
107
103 public void StoreTerrain(double[,] terrain, UUID regionID) 108 public void StoreTerrain(double[,] terrain, UUID regionID)
104 { 109 {
105 m_database.StoreTerrain(terrain, regionID); 110 m_database.StoreTerrain(terrain, regionID);
@@ -110,6 +115,11 @@ namespace OpenSim.Services.Connectors
110 return m_database.LoadTerrain(regionID); 115 return m_database.LoadTerrain(regionID);
111 } 116 }
112 117
118 public TerrainData LoadTerrain(UUID regionID, int pSizeX, int pSizeY, int pSizeZ)
119 {
120 return m_database.LoadTerrain(regionID, pSizeX, pSizeY, pSizeZ);
121 }
122
113 public void StoreLandObject(ILandObject Parcel) 123 public void StoreLandObject(ILandObject Parcel)
114 { 124 {
115 m_database.StoreLandObject(Parcel); 125 m_database.StoreLandObject(Parcel);
diff --git a/OpenSim/Tests/Common/Mock/MockRegionDataPlugin.cs b/OpenSim/Tests/Common/Mock/MockRegionDataPlugin.cs
index 5c1ec0b..3ab9020 100644
--- a/OpenSim/Tests/Common/Mock/MockRegionDataPlugin.cs
+++ b/OpenSim/Tests/Common/Mock/MockRegionDataPlugin.cs
@@ -69,11 +69,21 @@ namespace OpenSim.Data.Null
69 m_store.StoreTerrain(terrain, regionID); 69 m_store.StoreTerrain(terrain, regionID);
70 } 70 }
71 71
72 public void StoreTerrain(TerrainData terrain, UUID regionID)
73 {
74 m_store.StoreTerrain(terrain, regionID);
75 }
76
72 public double[,] LoadTerrain(UUID regionID) 77 public double[,] LoadTerrain(UUID regionID)
73 { 78 {
74 return m_store.LoadTerrain(regionID); 79 return m_store.LoadTerrain(regionID);
75 } 80 }
76 81
82 public TerrainData LoadTerrain(UUID regionID, int pSizeX, int pSizeY, int pSizeZ)
83 {
84 return m_store.LoadTerrain(regionID, pSizeX, pSizeY, pSizeZ);
85 }
86
77 public void StoreLandObject(ILandObject Parcel) 87 public void StoreLandObject(ILandObject Parcel)
78 { 88 {
79 m_store.StoreLandObject(Parcel); 89 m_store.StoreLandObject(Parcel);
@@ -159,7 +169,7 @@ namespace OpenSim.Data.Null
159 protected Dictionary<UUID, SceneObjectPart> m_sceneObjectParts = new Dictionary<UUID, SceneObjectPart>(); 169 protected Dictionary<UUID, SceneObjectPart> m_sceneObjectParts = new Dictionary<UUID, SceneObjectPart>();
160 protected Dictionary<UUID, ICollection<TaskInventoryItem>> m_primItems 170 protected Dictionary<UUID, ICollection<TaskInventoryItem>> m_primItems
161 = new Dictionary<UUID, ICollection<TaskInventoryItem>>(); 171 = new Dictionary<UUID, ICollection<TaskInventoryItem>>();
162 protected Dictionary<UUID, double[,]> m_terrains = new Dictionary<UUID, double[,]>(); 172 protected Dictionary<UUID, TerrainData> m_terrains = new Dictionary<UUID, TerrainData>();
163 protected Dictionary<UUID, LandData> m_landData = new Dictionary<UUID, LandData>(); 173 protected Dictionary<UUID, LandData> m_landData = new Dictionary<UUID, LandData>();
164 174
165 public void Initialise(string dbfile) 175 public void Initialise(string dbfile)
@@ -304,12 +314,17 @@ namespace OpenSim.Data.Null
304 return new List<SceneObjectGroup>(objects.Values); 314 return new List<SceneObjectGroup>(objects.Values);
305 } 315 }
306 316
307 public void StoreTerrain(double[,] ter, UUID regionID) 317 public void StoreTerrain(TerrainData ter, UUID regionID)
308 { 318 {
309 m_terrains[regionID] = ter; 319 m_terrains[regionID] = ter;
310 } 320 }
311 321
312 public double[,] LoadTerrain(UUID regionID) 322 public void StoreTerrain(double[,] ter, UUID regionID)
323 {
324 m_terrains[regionID] = new HeightmapTerrainData(ter);
325 }
326
327 public TerrainData LoadTerrain(UUID regionID, int pSizeX, int pSizeY, int pSizeZ)
313 { 328 {
314 if (m_terrains.ContainsKey(regionID)) 329 if (m_terrains.ContainsKey(regionID))
315 return m_terrains[regionID]; 330 return m_terrains[regionID];
@@ -317,6 +332,14 @@ namespace OpenSim.Data.Null
317 return null; 332 return null;
318 } 333 }
319 334
335 public double[,] LoadTerrain(UUID regionID)
336 {
337 if (m_terrains.ContainsKey(regionID))
338 return m_terrains[regionID].GetDoubles();
339 else
340 return null;
341 }
342
320 public void RemoveLandObject(UUID globalID) 343 public void RemoveLandObject(UUID globalID)
321 { 344 {
322 if (m_landData.ContainsKey(globalID)) 345 if (m_landData.ContainsKey(globalID))