diff options
author | Robert Adams | 2015-03-27 19:32:50 -0700 |
---|---|---|
committer | Robert Adams | 2015-03-27 19:32:50 -0700 |
commit | bedafb8fae9898ef0c5fc6470236ee7244e616a9 (patch) | |
tree | 618728d92dad56dd587c243892d966b9ff9ea89b | |
parent | varregion: restore checkAgentAccessToRegion routine in EntityTransferModule. (diff) | |
download | opensim-SC-bedafb8fae9898ef0c5fc6470236ee7244e616a9.zip opensim-SC-bedafb8fae9898ef0c5fc6470236ee7244e616a9.tar.gz opensim-SC-bedafb8fae9898ef0c5fc6470236ee7244e616a9.tar.bz2 opensim-SC-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.cs | 83 | ||||
-rw-r--r-- | OpenSim/Data/MySQL/MySQLSimulationData.cs | 66 | ||||
-rw-r--r-- | OpenSim/Data/Null/NullSimulationData.cs | 22 | ||||
-rw-r--r-- | OpenSim/Data/PGSQL/PGSQLSimulationData.cs | 72 | ||||
-rw-r--r-- | OpenSim/Data/SQLite/SQLiteSimulationData.cs | 54 | ||||
-rw-r--r-- | OpenSim/Framework/TerrainData.cs | 464 | ||||
-rw-r--r-- | OpenSim/Region/CoreModules/World/Terrain/FileLoaders/GenericSystemDrawing.cs | 2 | ||||
-rw-r--r-- | OpenSim/Region/CoreModules/World/Terrain/TerrainModule.cs | 488 | ||||
-rw-r--r-- | OpenSim/Region/Framework/Interfaces/ISimulationDataService.cs | 9 | ||||
-rw-r--r-- | OpenSim/Region/Framework/Interfaces/ISimulationDataStore.cs | 10 | ||||
-rw-r--r-- | OpenSim/Region/Framework/Interfaces/ITerrainChannel.cs | 19 | ||||
-rw-r--r-- | OpenSim/Region/Framework/Scenes/Scene.cs | 4 | ||||
-rw-r--r-- | OpenSim/Region/Framework/Scenes/TerrainChannel.cs | 352 | ||||
-rw-r--r-- | OpenSim/Services/Connectors/Simulation/SimulationDataService.cs | 10 | ||||
-rw-r--r-- | OpenSim/Tests/Common/Mock/MockRegionDataPlugin.cs | 29 |
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 | |||
28 | using System; | ||
29 | using System.Collections.Generic; | ||
30 | using System.IO; | ||
31 | using System.Reflection; | ||
32 | |||
33 | using OpenMetaverse; | ||
34 | |||
35 | using log4net; | ||
36 | |||
37 | namespace 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 | ||
28 | using OpenSim.Framework; | ||
29 | using OpenMetaverse; | ||
30 | |||
28 | namespace OpenSim.Region.Framework.Interfaces | 31 | namespace 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 | ||
28 | using OpenSim.Framework; | ||
29 | using OpenSim.Region.Framework.Interfaces; | ||
30 | using System; | 28 | using System; |
29 | using System.IO; | ||
31 | using System.Text; | 30 | using System.Text; |
31 | using System.Reflection; | ||
32 | using System.Xml; | 32 | using System.Xml; |
33 | using System.IO; | ||
34 | using System.Xml.Serialization; | 33 | using System.Xml.Serialization; |
35 | 34 | ||
35 | using OpenSim.Data; | ||
36 | using OpenSim.Framework; | ||
37 | using OpenSim.Region.Framework.Interfaces; | ||
38 | |||
39 | using OpenMetaverse; | ||
40 | |||
41 | using log4net; | ||
42 | |||
36 | namespace OpenSim.Region.Framework.Scenes | 43 | namespace 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)) |