aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/OpenSim/Framework
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--OpenSim/Framework/Constants.cs9
-rw-r--r--OpenSim/Framework/RegionInfo.cs210
-rw-r--r--OpenSim/Framework/TerrainData.cs416
3 files changed, 593 insertions, 42 deletions
diff --git a/OpenSim/Framework/Constants.cs b/OpenSim/Framework/Constants.cs
index a2eb5ee..d18b32e 100644
--- a/OpenSim/Framework/Constants.cs
+++ b/OpenSim/Framework/Constants.cs
@@ -30,9 +30,16 @@ namespace OpenSim.Framework
30{ 30{
31 public class Constants 31 public class Constants
32 { 32 {
33 // 'RegionSize' captures the legacy region size.
34 // DO NOT USE THIS FOR ANY NEW CODE. Use Scene.RegionSize[XYZ] as a region might not
35 // be the legacy region size.
33 public const uint RegionSize = 256; 36 public const uint RegionSize = 256;
34 public const uint RegionHeight = 4096; 37 public const uint RegionHeight = 4096;
35 public const byte TerrainPatchSize = 16; 38
39 // Since terrain is stored in 16x16 heights, regions must be a multiple of this number and that is the minimum
40 public const int MinRegionSize = 16;
41 public const int TerrainPatchSize = 16;
42
36 public const string DefaultTexture = "89556747-24cb-43ed-920b-47caed15465f"; 43 public const string DefaultTexture = "89556747-24cb-43ed-920b-47caed15465f";
37 44
38 public enum EstateAccessCodex : uint 45 public enum EstateAccessCodex : uint
diff --git a/OpenSim/Framework/RegionInfo.cs b/OpenSim/Framework/RegionInfo.cs
index 24b9c89..cf909cd 100644
--- a/OpenSim/Framework/RegionInfo.cs
+++ b/OpenSim/Framework/RegionInfo.cs
@@ -99,6 +99,7 @@ namespace OpenSim.Framework
99 public class RegionInfo 99 public class RegionInfo
100 { 100 {
101 private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); 101 private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
102 private static readonly string LogHeader = "[REGION INFO]";
102 103
103 public bool commFailTF = false; 104 public bool commFailTF = false;
104 public ConfigurationMember configMember; 105 public ConfigurationMember configMember;
@@ -137,16 +138,20 @@ namespace OpenSim.Framework
137 public bool m_allow_alternate_ports; 138 public bool m_allow_alternate_ports;
138 protected string m_externalHostName; 139 protected string m_externalHostName;
139 protected IPEndPoint m_internalEndPoint; 140 protected IPEndPoint m_internalEndPoint;
140 protected uint? m_regionLocX;
141 protected uint? m_regionLocY;
142 protected uint m_remotingPort; 141 protected uint m_remotingPort;
143 public UUID RegionID = UUID.Zero; 142 public UUID RegionID = UUID.Zero;
144 public string RemotingAddress; 143 public string RemotingAddress;
145 public UUID ScopeID = UUID.Zero; 144 public UUID ScopeID = UUID.Zero;
146 private UUID m_maptileStaticUUID = UUID.Zero; 145 private UUID m_maptileStaticUUID = UUID.Zero;
147 146
148 private Dictionary<String, String> m_otherSettings = new Dictionary<string, string>(); 147 public uint RegionWorldLocX = 0;
148 public uint RegionWorldLocY = 0;
149 public uint RegionWorldLocZ = 0;
150 public uint RegionSizeX = Constants.RegionSize;
151 public uint RegionSizeY = Constants.RegionSize;
152 public uint RegionSizeZ = Constants.RegionHeight;
149 153
154 private Dictionary<String, String> m_otherSettings = new Dictionary<string, string>();
150 155
151 // Apparently, we're applying the same estatesettings regardless of whether it's local or remote. 156 // Apparently, we're applying the same estatesettings regardless of whether it's local or remote.
152 157
@@ -229,11 +234,8 @@ namespace OpenSim.Framework
229 m_serverURI = string.Empty; 234 m_serverURI = string.Empty;
230 } 235 }
231 236
232 public RegionInfo(uint regionLocX, uint regionLocY, IPEndPoint internalEndPoint, string externalUri) 237 public RegionInfo(uint legacyRegionLocX, uint legacyRegionLocY, IPEndPoint internalEndPoint, string externalUri)
233 { 238 {
234 m_regionLocX = regionLocX;
235 m_regionLocY = regionLocY;
236
237 m_internalEndPoint = internalEndPoint; 239 m_internalEndPoint = internalEndPoint;
238 m_externalHostName = externalUri; 240 m_externalHostName = externalUri;
239 m_serverURI = string.Empty; 241 m_serverURI = string.Empty;
@@ -447,25 +449,66 @@ namespace OpenSim.Framework
447 449
448 /// <summary> 450 /// <summary>
449 /// The x co-ordinate of this region in map tiles (e.g. 1000). 451 /// The x co-ordinate of this region in map tiles (e.g. 1000).
452 /// Coordinate is scaled as world coordinates divided by the legacy region size
453 /// and is thus is the number of legacy regions.
454 /// </summary>
455 public uint LegacyRegionLocX
456 {
457 get { return RegionWorldLocX / Constants.RegionSize; }
458 set { RegionWorldLocX = value * Constants.RegionSize; }
459 }
460
461 /// <summary>
462 /// The y co-ordinate of this region in map tiles (e.g. 1000).
463 /// Coordinate is scaled as world coordinates divided by the legacy region size
464 /// and is thus is the number of legacy regions.
465 /// </summary>
466 public uint LegacyRegionLocY
467 {
468 get { return RegionWorldLocY / Constants.RegionSize; }
469 set { RegionWorldLocY = value * Constants.RegionSize; }
470 }
471
472 /// <summary>
473 /// The x co-ordinate of this region in map tiles (e.g. 1000).
474 /// Coordinate is scaled as world coordinates divided by the legacy region size
475 /// and is thus is the number of legacy regions.
476 /// DO NOT USE FOR NEW CODE! This entrypoint exists for downward compatability with external modules.
450 /// </summary> 477 /// </summary>
451 public uint RegionLocX 478 public uint RegionLocX
452 { 479 {
453 get { return m_regionLocX.Value; } 480 get { return LegacyRegionLocX; }
454 set { m_regionLocX = value; } 481 set { LegacyRegionLocX = value; }
455 } 482 }
456 483
457 /// <summary> 484 /// <summary>
458 /// The y co-ordinate of this region in map tiles (e.g. 1000). 485 /// The y co-ordinate of this region in map tiles (e.g. 1000).
486 /// Coordinate is scaled as world coordinates divided by the legacy region size
487 /// and is thus is the number of legacy regions.
488 /// DO NOT USE FOR NEW CODE! This entrypoint exists for downward compatability with external modules.
459 /// </summary> 489 /// </summary>
460 public uint RegionLocY 490 public uint RegionLocY
461 { 491 {
462 get { return m_regionLocY.Value; } 492 get { return LegacyRegionLocY; }
463 set { m_regionLocY = value; } 493 set { LegacyRegionLocY = value; }
494 }
495
496 public void SetDefaultRegionSize()
497 {
498 RegionWorldLocX = 0;
499 RegionWorldLocY = 0;
500 RegionWorldLocZ = 0;
501 RegionSizeX = Constants.RegionSize;
502 RegionSizeY = Constants.RegionSize;
503 RegionSizeZ = Constants.RegionHeight;
464 } 504 }
465 505
506 // A unique region handle is created from the region's world coordinates.
507 // This cannot be changed because some code expects to receive the region handle and then
508 // compute the region coordinates from it.
466 public ulong RegionHandle 509 public ulong RegionHandle
467 { 510 {
468 get { return Util.UIntsToLong((RegionLocX * (uint) Constants.RegionSize), (RegionLocY * (uint) Constants.RegionSize)); } 511 get { return Util.UIntsToLong(RegionWorldLocX, RegionWorldLocY); }
469 } 512 }
470 513
471 public void SetEndPoint(string ipaddr, int port) 514 public void SetEndPoint(string ipaddr, int port)
@@ -572,8 +615,25 @@ namespace OpenSim.Framework
572 615
573 string[] locationElements = location.Split(new char[] {','}); 616 string[] locationElements = location.Split(new char[] {','});
574 617
575 m_regionLocX = Convert.ToUInt32(locationElements[0]); 618 LegacyRegionLocX = Convert.ToUInt32(locationElements[0]);
576 m_regionLocY = Convert.ToUInt32(locationElements[1]); 619 LegacyRegionLocY = Convert.ToUInt32(locationElements[1]);
620
621 // Region size
622 // Default to legacy region size if not specified.
623 allKeys.Remove("SizeX");
624 string configSizeX = config.GetString("SizeX", Constants.RegionSize.ToString());
625 config.Set("SizeX", configSizeX);
626 RegionSizeX = Convert.ToUInt32(configSizeX);
627 allKeys.Remove("SizeY");
628 string configSizeY = config.GetString("SizeY", Constants.RegionSize.ToString());
629 config.Set("SizeY", configSizeX);
630 RegionSizeY = Convert.ToUInt32(configSizeY);
631 allKeys.Remove("SizeZ");
632 string configSizeZ = config.GetString("SizeZ", Constants.RegionHeight.ToString());
633 config.Set("SizeZ", configSizeX);
634 RegionSizeZ = Convert.ToUInt32(configSizeZ);
635
636 DoRegionSizeSanityChecks();
577 637
578 // InternalAddress 638 // InternalAddress
579 // 639 //
@@ -693,6 +753,48 @@ namespace OpenSim.Framework
693 } 753 }
694 } 754 }
695 755
756 // Make sure user specified region sizes are sane.
757 // Must be multiples of legacy region size (256).
758 private void DoRegionSizeSanityChecks()
759 {
760 if (RegionSizeX != Constants.RegionSize || RegionSizeY != Constants.RegionSize)
761 {
762 // Doing non-legacy region sizes.
763 // Enforce region size to be multiples of the legacy region size (256)
764 uint partial = RegionSizeX % Constants.RegionSize;
765 if (partial != 0)
766 {
767 RegionSizeX -= partial;
768 if (RegionSizeX == 0)
769 RegionSizeX = Constants.RegionSize;
770 m_log.ErrorFormat("{0} Region size must be multiple of {1}. Enforcing {2}.RegionSizeX={3} instead of specified {4}",
771 LogHeader, Constants.RegionSize, m_regionName, RegionSizeX, RegionSizeX + partial);
772 }
773 partial = RegionSizeY % Constants.RegionSize;
774 if (partial != 0)
775 {
776 RegionSizeY -= partial;
777 if (RegionSizeY == 0)
778 RegionSizeY = Constants.RegionSize;
779 m_log.ErrorFormat("{0} Region size must be multiple of {1}. Enforcing {2}.RegionSizeY={3} instead of specified {4}",
780 LogHeader, Constants.RegionSize, m_regionName, RegionSizeY, RegionSizeY + partial);
781 }
782
783 // Because of things in the viewer, regions MUST be square.
784 // Remove this check when viewers have been updated.
785 if (RegionSizeX != RegionSizeY)
786 {
787 uint minSize = Math.Min(RegionSizeX, RegionSizeY);
788 RegionSizeX = minSize;
789 RegionSizeY = minSize;
790 m_log.ErrorFormat("{0} Regions must be square until viewers are updated. Forcing region {1} size to <{2},{3}>",
791 LogHeader, m_regionName, RegionSizeX, RegionSizeY);
792 }
793
794 m_log.InfoFormat("{0} Region {1} size set to <{2},{3}>", LogHeader, m_regionName, RegionSizeX, RegionSizeY);
795 }
796 }
797
696 private void WriteNiniConfig(IConfigSource source) 798 private void WriteNiniConfig(IConfigSource source)
697 { 799 {
698 IConfig config = source.Configs[RegionName]; 800 IConfig config = source.Configs[RegionName];
@@ -704,9 +806,16 @@ namespace OpenSim.Framework
704 806
705 config.Set("RegionUUID", RegionID.ToString()); 807 config.Set("RegionUUID", RegionID.ToString());
706 808
707 string location = String.Format("{0},{1}", m_regionLocX, m_regionLocY); 809 string location = String.Format("{0},{1}", LegacyRegionLocX, LegacyRegionLocY);
708 config.Set("Location", location); 810 config.Set("Location", location);
709 811
812 if (RegionSizeX != Constants.RegionSize || RegionSizeY != Constants.RegionSize)
813 {
814 config.Set("SizeX", RegionSizeX);
815 config.Set("SizeY", RegionSizeY);
816 config.Set("SizeZ", RegionSizeZ);
817 }
818
710 config.Set("InternalAddress", m_internalEndPoint.Address.ToString()); 819 config.Set("InternalAddress", m_internalEndPoint.Address.ToString());
711 config.Set("InternalPort", m_internalEndPoint.Port); 820 config.Set("InternalPort", m_internalEndPoint.Port);
712 821
@@ -789,10 +898,18 @@ namespace OpenSim.Framework
789 RegionID.ToString(), true); 898 RegionID.ToString(), true);
790 configMember.addConfigurationOption("sim_name", ConfigurationOption.ConfigurationTypes.TYPE_STRING_NOT_EMPTY, 899 configMember.addConfigurationOption("sim_name", ConfigurationOption.ConfigurationTypes.TYPE_STRING_NOT_EMPTY,
791 "Region Name", RegionName, true); 900 "Region Name", RegionName, true);
901
792 configMember.addConfigurationOption("sim_location_x", ConfigurationOption.ConfigurationTypes.TYPE_UINT32, 902 configMember.addConfigurationOption("sim_location_x", ConfigurationOption.ConfigurationTypes.TYPE_UINT32,
793 "Grid Location (X Axis)", m_regionLocX.ToString(), true); 903 "Grid Location (X Axis)", LegacyRegionLocX.ToString(), true);
794 configMember.addConfigurationOption("sim_location_y", ConfigurationOption.ConfigurationTypes.TYPE_UINT32, 904 configMember.addConfigurationOption("sim_location_y", ConfigurationOption.ConfigurationTypes.TYPE_UINT32,
795 "Grid Location (Y Axis)", m_regionLocY.ToString(), true); 905 "Grid Location (Y Axis)", LegacyRegionLocY.ToString(), true);
906 configMember.addConfigurationOption("sim_size_x", ConfigurationOption.ConfigurationTypes.TYPE_UINT32,
907 "Size of region in X dimension", RegionSizeX.ToString(), true);
908 configMember.addConfigurationOption("sim_size_y", ConfigurationOption.ConfigurationTypes.TYPE_UINT32,
909 "Size of region in Y dimension", RegionSizeY.ToString(), true);
910 configMember.addConfigurationOption("sim_size_z", ConfigurationOption.ConfigurationTypes.TYPE_UINT32,
911 "Size of region in Z dimension", RegionSizeZ.ToString(), true);
912
796 //m_configMember.addConfigurationOption("datastore", ConfigurationOption.ConfigurationTypes.TYPE_STRING_NOT_EMPTY, "Filename for local storage", "OpenSim.db", false); 913 //m_configMember.addConfigurationOption("datastore", ConfigurationOption.ConfigurationTypes.TYPE_STRING_NOT_EMPTY, "Filename for local storage", "OpenSim.db", false);
797 configMember.addConfigurationOption("internal_ip_address", 914 configMember.addConfigurationOption("internal_ip_address",
798 ConfigurationOption.ConfigurationTypes.TYPE_IP_ADDRESS, 915 ConfigurationOption.ConfigurationTypes.TYPE_IP_ADDRESS,
@@ -855,10 +972,18 @@ namespace OpenSim.Framework
855 UUID.Random().ToString(), true); 972 UUID.Random().ToString(), true);
856 configMember.addConfigurationOption("sim_name", ConfigurationOption.ConfigurationTypes.TYPE_STRING_NOT_EMPTY, 973 configMember.addConfigurationOption("sim_name", ConfigurationOption.ConfigurationTypes.TYPE_STRING_NOT_EMPTY,
857 "Region Name", "OpenSim Test", false); 974 "Region Name", "OpenSim Test", false);
975
858 configMember.addConfigurationOption("sim_location_x", ConfigurationOption.ConfigurationTypes.TYPE_UINT32, 976 configMember.addConfigurationOption("sim_location_x", ConfigurationOption.ConfigurationTypes.TYPE_UINT32,
859 "Grid Location (X Axis)", "1000", false); 977 "Grid Location (X Axis)", "1000", false);
860 configMember.addConfigurationOption("sim_location_y", ConfigurationOption.ConfigurationTypes.TYPE_UINT32, 978 configMember.addConfigurationOption("sim_location_y", ConfigurationOption.ConfigurationTypes.TYPE_UINT32,
861 "Grid Location (Y Axis)", "1000", false); 979 "Grid Location (Y Axis)", "1000", false);
980 configMember.addConfigurationOption("sim_size_x", ConfigurationOption.ConfigurationTypes.TYPE_UINT32,
981 "Size of region in X dimension", Constants.RegionSize.ToString(), false);
982 configMember.addConfigurationOption("sim_size_y", ConfigurationOption.ConfigurationTypes.TYPE_UINT32,
983 "Size of region in Y dimension", Constants.RegionSize.ToString(), false);
984 configMember.addConfigurationOption("sim_size_z", ConfigurationOption.ConfigurationTypes.TYPE_UINT32,
985 "Size of region in Z dimension", Constants.RegionHeight.ToString(), false);
986
862 //m_configMember.addConfigurationOption("datastore", ConfigurationOption.ConfigurationTypes.TYPE_STRING_NOT_EMPTY, "Filename for local storage", "OpenSim.db", false); 987 //m_configMember.addConfigurationOption("datastore", ConfigurationOption.ConfigurationTypes.TYPE_STRING_NOT_EMPTY, "Filename for local storage", "OpenSim.db", false);
863 configMember.addConfigurationOption("internal_ip_address", 988 configMember.addConfigurationOption("internal_ip_address",
864 ConfigurationOption.ConfigurationTypes.TYPE_IP_ADDRESS, 989 ConfigurationOption.ConfigurationTypes.TYPE_IP_ADDRESS,
@@ -916,10 +1041,19 @@ namespace OpenSim.Framework
916 RegionName = (string) configuration_result; 1041 RegionName = (string) configuration_result;
917 break; 1042 break;
918 case "sim_location_x": 1043 case "sim_location_x":
919 m_regionLocX = (uint) configuration_result; 1044 LegacyRegionLocX = (uint) configuration_result;
920 break; 1045 break;
921 case "sim_location_y": 1046 case "sim_location_y":
922 m_regionLocY = (uint) configuration_result; 1047 LegacyRegionLocY = (uint) configuration_result;
1048 break;
1049 case "sim_size_x":
1050 RegionSizeX = (uint) configuration_result;
1051 break;
1052 case "sim_size_y":
1053 RegionSizeY = (uint) configuration_result;
1054 break;
1055 case "sim_size_z":
1056 RegionSizeZ = (uint) configuration_result;
923 break; 1057 break;
924 case "internal_ip_address": 1058 case "internal_ip_address":
925 IPAddress address = (IPAddress) configuration_result; 1059 IPAddress address = (IPAddress) configuration_result;
@@ -1000,8 +1134,13 @@ namespace OpenSim.Framework
1000 args["external_host_name"] = OSD.FromString(ExternalHostName); 1134 args["external_host_name"] = OSD.FromString(ExternalHostName);
1001 args["http_port"] = OSD.FromString(HttpPort.ToString()); 1135 args["http_port"] = OSD.FromString(HttpPort.ToString());
1002 args["server_uri"] = OSD.FromString(ServerURI); 1136 args["server_uri"] = OSD.FromString(ServerURI);
1003 args["region_xloc"] = OSD.FromString(RegionLocX.ToString()); 1137
1004 args["region_yloc"] = OSD.FromString(RegionLocY.ToString()); 1138 args["region_xloc"] = OSD.FromString(LegacyRegionLocX.ToString());
1139 args["region_yloc"] = OSD.FromString(LegacyRegionLocY.ToString());
1140 args["region_size_x"] = OSD.FromString(RegionSizeX.ToString());
1141 args["region_size_y"] = OSD.FromString(RegionSizeY.ToString());
1142 args["region_size_z"] = OSD.FromString(RegionSizeZ.ToString());
1143
1005 args["internal_ep_address"] = OSD.FromString(InternalEndPoint.Address.ToString()); 1144 args["internal_ep_address"] = OSD.FromString(InternalEndPoint.Address.ToString());
1006 args["internal_ep_port"] = OSD.FromString(InternalEndPoint.Port.ToString()); 1145 args["internal_ep_port"] = OSD.FromString(InternalEndPoint.Port.ToString());
1007 if ((RemotingAddress != null) && !RemotingAddress.Equals("")) 1146 if ((RemotingAddress != null) && !RemotingAddress.Equals(""))
@@ -1032,14 +1171,21 @@ namespace OpenSim.Framework
1032 { 1171 {
1033 uint locx; 1172 uint locx;
1034 UInt32.TryParse(args["region_xloc"].AsString(), out locx); 1173 UInt32.TryParse(args["region_xloc"].AsString(), out locx);
1035 RegionLocX = locx; 1174 LegacyRegionLocX = locx;
1036 } 1175 }
1037 if (args["region_yloc"] != null) 1176 if (args["region_yloc"] != null)
1038 { 1177 {
1039 uint locy; 1178 uint locy;
1040 UInt32.TryParse(args["region_yloc"].AsString(), out locy); 1179 UInt32.TryParse(args["region_yloc"].AsString(), out locy);
1041 RegionLocY = locy; 1180 LegacyRegionLocY = locy;
1042 } 1181 }
1182 if (args.ContainsKey("region_size_x"))
1183 RegionSizeX = (uint)args["region_size_x"].AsInteger();
1184 if (args.ContainsKey("region_size_y"))
1185 RegionSizeY = (uint)args["region_size_y"].AsInteger();
1186 if (args.ContainsKey("region_size_z"))
1187 RegionSizeZ = (uint)args["region_size_z"].AsInteger();
1188
1043 IPAddress ip_addr = null; 1189 IPAddress ip_addr = null;
1044 if (args["internal_ep_address"] != null) 1190 if (args["internal_ep_address"] != null)
1045 { 1191 {
@@ -1076,23 +1222,5 @@ namespace OpenSim.Framework
1076 regionInfo.ServerURI = serverURI; 1222 regionInfo.ServerURI = serverURI;
1077 return regionInfo; 1223 return regionInfo;
1078 } 1224 }
1079
1080 public Dictionary<string, object> ToKeyValuePairs()
1081 {
1082 Dictionary<string, object> kvp = new Dictionary<string, object>();
1083 kvp["uuid"] = RegionID.ToString();
1084 kvp["locX"] = RegionLocX.ToString();
1085 kvp["locY"] = RegionLocY.ToString();
1086 kvp["external_ip_address"] = ExternalEndPoint.Address.ToString();
1087 kvp["external_port"] = ExternalEndPoint.Port.ToString();
1088 kvp["external_host_name"] = ExternalHostName;
1089 kvp["http_port"] = HttpPort.ToString();
1090 kvp["internal_ip_address"] = InternalEndPoint.Address.ToString();
1091 kvp["internal_port"] = InternalEndPoint.Port.ToString();
1092 kvp["alternate_ports"] = m_allow_alternate_ports.ToString();
1093 kvp["server_uri"] = ServerURI;
1094
1095 return kvp;
1096 }
1097 } 1225 }
1098} 1226}
diff --git a/OpenSim/Framework/TerrainData.cs b/OpenSim/Framework/TerrainData.cs
new file mode 100644
index 0000000..d5dad8f
--- /dev/null
+++ b/OpenSim/Framework/TerrainData.cs
@@ -0,0 +1,416 @@
1/*
2 * Copyright (c) Contributors, http://opensimulator.org/
3 * See CONTRIBUTORS.TXT for a full list of copyright holders.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 * * Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * * Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 * * Neither the name of the OpenSimulator Project nor the
13 * names of its contributors may be used to endorse or promote products
14 * derived from this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
17 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19 * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
20 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27
28using System;
29using System.Collections.Generic;
30using System.IO;
31using System.Reflection;
32
33using OpenMetaverse;
34
35using log4net;
36
37namespace OpenSim.Framework
38{
39 public abstract class TerrainData
40 {
41 // Terrain always is a square
42 public int SizeX { get; protected set; }
43 public int SizeY { get; protected set; }
44 public int SizeZ { get; protected set; }
45
46 // A height used when the user doesn't specify anything
47 public const float DefaultTerrainHeight = 21f;
48
49 public abstract float this[int x, int y] { get; set; }
50 // Someday terrain will have caves
51 public abstract float this[int x, int y, int z] { get; set; }
52
53 public bool IsTainted { get; protected set; }
54 public abstract bool IsTaintedAt(int xx, int yy);
55 public abstract void ClearTaint();
56
57 public abstract void ClearLand();
58 public abstract void ClearLand(float height);
59
60 // Return a representation of this terrain for storing as a blob in the database.
61 // Returns 'true' to say blob was stored in the 'out' locations.
62 public abstract bool GetDatabaseBlob(out int DBFormatRevisionCode, out Array blob);
63
64 // Given a revision code and a blob from the database, create and return the right type of TerrainData.
65 // The sizes passed are the expected size of the region. The database info will be used to
66 // initialize the heightmap of that sized region with as much data is in the blob.
67 // Return created TerrainData or 'null' if unsuccessful.
68 public static TerrainData CreateFromDatabaseBlobFactory(int pSizeX, int pSizeY, int pSizeZ, int pFormatCode, byte[] pBlob)
69 {
70 // For the moment, there is only one implementation class
71 return new HeightmapTerrainData(pSizeX, pSizeY, pSizeZ, pFormatCode, pBlob);
72 }
73
74 // return a special compressed representation of the heightmap in shorts
75 public abstract short[] GetCompressedMap();
76 public abstract float CompressionFactor { get; }
77
78 public abstract double[,] GetDoubles();
79 public abstract TerrainData Clone();
80 }
81
82 // The terrain is stored in the database as a blob with a 'revision' field.
83 // Some implementations of terrain storage would fill the revision field with
84 // the time the terrain was stored. When real revisions were added and this
85 // feature removed, that left some old entries with the time in the revision
86 // field.
87 // Thus, if revision is greater than 'RevisionHigh' then terrain db entry is
88 // left over and it is presumed to be 'Legacy256'.
89 // Numbers are arbitrary and are chosen to to reduce possible mis-interpretation.
90 // If a revision does not match any of these, it is assumed to be Legacy256.
91 public enum DBTerrainRevision
92 {
93 // Terrain is 'double[256,256]'
94 Legacy256 = 11,
95 // Terrain is 'int32, int32, float[,]' where the ints are X and Y dimensions
96 // The dimensions are presumed to be multiples of 16 and, more likely, multiples of 256.
97 Variable2D = 22,
98 // Terrain is 'int32, int32, int32, int16[]' where the ints are X and Y dimensions
99 // and third int is the 'compression factor'. The heights are compressed as
100 // "short compressedHeight = (short)(height * compressionFactor);"
101 // The dimensions are presumed to be multiples of 16 and, more likely, multiples of 256.
102 Compressed2D = 27,
103 // A revision that is not listed above or any revision greater than this value is 'Legacy256'.
104 RevisionHigh = 1234
105 }
106
107 // Version of terrain that is a heightmap.
108 // This should really be 'LLOptimizedHeightmapTerrainData' as it includes knowledge
109 // of 'patches' which are 16x16 terrain areas which can be sent separately to the viewer.
110 // The heighmap is kept as an array of short integers. The integer values are converted to
111 // and from floats by TerrainCompressionFactor. Shorts are used to limit storage used.
112 public class HeightmapTerrainData : TerrainData
113 {
114 private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
115 private static string LogHeader = "[TERRAIN DATA]";
116
117 // TerrainData.this[x, y]
118 public override float this[int x, int y]
119 {
120 get { return FromCompressedHeight(m_heightmap[x, y]); }
121 set {
122 short newVal = ToCompressedHeight(value);
123 if (m_heightmap[x, y] != newVal)
124 {
125 m_heightmap[x, y] = newVal;
126 m_taint[x / Constants.TerrainPatchSize, y / Constants.TerrainPatchSize] = true;
127 // m_log.DebugFormat("{0} set[{1},{2}] to {3} ({4})", LogHeader, x, y, value, newVal);
128 }
129 }
130 }
131
132 // TerrainData.this[x, y, z]
133 public override float this[int x, int y, int z]
134 {
135 get { return this[x, y]; }
136 set { this[x, y] = value; }
137 }
138
139 // TerrainData.ClearTaint
140 public override void ClearTaint()
141 {
142 IsTainted = false;
143 for (int ii = 0; ii < m_taint.GetLength(0); ii++)
144 for (int jj = 0; jj < m_taint.GetLength(1); jj++)
145 m_taint[ii, jj] = false;
146 }
147
148 // TerrainData.ClearLand
149 public override void ClearLand()
150 {
151 ClearLand(DefaultTerrainHeight);
152 }
153 // TerrainData.ClearLand(float)
154 public override void ClearLand(float pHeight)
155 {
156 short flatHeight = ToCompressedHeight(pHeight);
157 for (int xx = 0; xx < SizeX; xx++)
158 for (int yy = 0; yy < SizeY; yy++)
159 m_heightmap[xx, yy] = flatHeight;
160 }
161
162 public override bool IsTaintedAt(int xx, int yy)
163 {
164 return m_taint[xx / Constants.TerrainPatchSize, yy / Constants.TerrainPatchSize];
165 }
166
167 // TerrainData.GetDatabaseBlob
168 // The user wants something to store in the database.
169 public override bool GetDatabaseBlob(out int DBRevisionCode, out Array blob)
170 {
171 bool ret = false;
172 if (SizeX == Constants.RegionSize && SizeY == Constants.RegionSize)
173 {
174 DBRevisionCode = (int)DBTerrainRevision.Legacy256;
175 blob = ToLegacyTerrainSerialization();
176 ret = true;
177 }
178 else
179 {
180 DBRevisionCode = (int)DBTerrainRevision.Compressed2D;
181 blob = ToCompressedTerrainSerialization();
182 ret = true;
183 }
184 return ret;
185 }
186
187 // TerrainData.CompressionFactor
188 private float m_compressionFactor = 100.0f;
189 public override float CompressionFactor { get { return m_compressionFactor; } }
190
191 // TerrainData.GetCompressedMap
192 public override short[] GetCompressedMap()
193 {
194 short[] newMap = new short[SizeX * SizeY];
195
196 int ind = 0;
197 for (int xx = 0; xx < SizeX; xx++)
198 for (int yy = 0; yy < SizeY; yy++)
199 newMap[ind++] = m_heightmap[xx, yy];
200
201 return newMap;
202
203 }
204 // TerrainData.Clone
205 public override TerrainData Clone()
206 {
207 HeightmapTerrainData ret = new HeightmapTerrainData(SizeX, SizeY, SizeZ);
208 ret.m_heightmap = (short[,])this.m_heightmap.Clone();
209 return ret;
210 }
211
212 // TerrainData.GetDoubles
213 public override double[,] GetDoubles()
214 {
215 double[,] ret = new double[SizeX, SizeY];
216 for (int xx = 0; xx < SizeX; xx++)
217 for (int yy = 0; yy < SizeY; yy++)
218 ret[xx, yy] = FromCompressedHeight(m_heightmap[xx, yy]);
219
220 return ret;
221 }
222
223
224 // =============================================================
225
226 private short[,] m_heightmap;
227 // Remember subregions of the heightmap that has changed.
228 private bool[,] m_taint;
229
230 // To save space (especially for large regions), keep the height as a short integer
231 // that is coded as the float height times the compression factor (usually '100'
232 // to make for two decimal points).
233 public short ToCompressedHeight(double pHeight)
234 {
235 return (short)(pHeight * CompressionFactor);
236 }
237
238 public float FromCompressedHeight(short pHeight)
239 {
240 return ((float)pHeight) / CompressionFactor;
241 }
242
243 // To keep with the legacy theme, create an instance of this class based on the
244 // way terrain used to be passed around.
245 public HeightmapTerrainData(double[,] pTerrain)
246 {
247 SizeX = pTerrain.GetLength(0);
248 SizeY = pTerrain.GetLength(1);
249 SizeZ = (int)Constants.RegionHeight;
250 m_compressionFactor = 100.0f;
251
252 m_heightmap = new short[SizeX, SizeY];
253 for (int ii = 0; ii < SizeX; ii++)
254 {
255 for (int jj = 0; jj < SizeY; jj++)
256 {
257 m_heightmap[ii, jj] = ToCompressedHeight(pTerrain[ii, jj]);
258
259 }
260 }
261
262 m_taint = new bool[SizeX / Constants.TerrainPatchSize, SizeY / Constants.TerrainPatchSize];
263 ClearTaint();
264 }
265
266 // Create underlying structures but don't initialize the heightmap assuming the caller will immediately do that
267 public HeightmapTerrainData(int pX, int pY, int pZ)
268 {
269 SizeX = pX;
270 SizeY = pY;
271 SizeZ = pZ;
272 m_compressionFactor = 100.0f;
273 m_heightmap = new short[SizeX, SizeY];
274 m_taint = new bool[SizeX / Constants.TerrainPatchSize, SizeY / Constants.TerrainPatchSize];
275 ClearTaint();
276 }
277
278 public HeightmapTerrainData(short[] cmap, float pCompressionFactor, int pX, int pY, int pZ) : this(pX, pY, pZ)
279 {
280 m_compressionFactor = pCompressionFactor;
281 int ind = 0;
282 for (int xx = 0; xx < SizeX; xx++)
283 for (int yy = 0; yy < SizeY; yy++)
284 m_heightmap[xx, yy] = cmap[ind++];
285 }
286
287 // Create a heighmap from a database blob
288 public HeightmapTerrainData(int pSizeX, int pSizeY, int pSizeZ, int pFormatCode, byte[] pBlob) : this(pSizeX, pSizeY, pSizeZ)
289 {
290 switch ((DBTerrainRevision)pFormatCode)
291 {
292 case DBTerrainRevision.Compressed2D:
293 FromCompressedTerrainSerialization(pBlob);
294 m_log.DebugFormat("{0} HeightmapTerrainData create from Compressed2D serialization. Size=<{1},{2}>", LogHeader, SizeX, SizeY);
295 break;
296 default:
297 FromLegacyTerrainSerialization(pBlob);
298 m_log.DebugFormat("{0} HeightmapTerrainData create from legacy serialization. Size=<{1},{2}>", LogHeader, SizeX, SizeY);
299 break;
300 }
301 }
302
303 // Just create an array of doubles. Presumes the caller implicitly knows the size.
304 public Array ToLegacyTerrainSerialization()
305 {
306 Array ret = null;
307
308 using (MemoryStream str = new MemoryStream((int)Constants.RegionSize * (int)Constants.RegionSize * sizeof(double)))
309 {
310 using (BinaryWriter bw = new BinaryWriter(str))
311 {
312 for (int xx = 0; xx < Constants.RegionSize; xx++)
313 {
314 for (int yy = 0; yy < Constants.RegionSize; yy++)
315 {
316 double height = this[xx, yy];
317 if (height == 0.0)
318 height = double.Epsilon;
319 bw.Write(height);
320 }
321 }
322 }
323 ret = str.ToArray();
324 }
325 return ret;
326 }
327
328 // Just create an array of doubles. Presumes the caller implicitly knows the size.
329 public void FromLegacyTerrainSerialization(byte[] pBlob)
330 {
331 // In case database info doesn't match real terrain size, initialize the whole terrain.
332 ClearLand();
333
334 using (MemoryStream mstr = new MemoryStream(pBlob))
335 {
336 using (BinaryReader br = new BinaryReader(mstr))
337 {
338 for (int xx = 0; xx < (int)Constants.RegionSize; xx++)
339 {
340 for (int yy = 0; yy < (int)Constants.RegionSize; yy++)
341 {
342 float val = (float)br.ReadDouble();
343 if (xx < SizeX && yy < SizeY)
344 m_heightmap[xx, yy] = ToCompressedHeight(val);
345 }
346 }
347 }
348 ClearTaint();
349 }
350 }
351
352 // See the reader below.
353 public Array ToCompressedTerrainSerialization()
354 {
355 Array ret = null;
356 using (MemoryStream str = new MemoryStream((3 * sizeof(Int32)) + (SizeX * SizeY * sizeof(Int16))))
357 {
358 using (BinaryWriter bw = new BinaryWriter(str))
359 {
360 bw.Write((Int32)DBTerrainRevision.Compressed2D);
361 bw.Write((Int32)SizeX);
362 bw.Write((Int32)SizeY);
363 bw.Write((Int32)CompressionFactor);
364 for (int yy = 0; yy < SizeY; yy++)
365 for (int xx = 0; xx < SizeX; xx++)
366 {
367 bw.Write((Int16)m_heightmap[xx, yy]);
368 }
369 }
370 ret = str.ToArray();
371 }
372 return ret;
373 }
374
375 // Initialize heightmap from blob consisting of:
376 // int32, int32, int32, int32, int16[]
377 // where the first int32 is format code, next two int32s are the X and y of heightmap data and
378 // the forth int is the compression factor for the following int16s
379 // This is just sets heightmap info. The actual size of the region was set on this instance's
380 // creation and any heights not initialized by theis blob are set to the default height.
381 public void FromCompressedTerrainSerialization(byte[] pBlob)
382 {
383 Int32 hmFormatCode, hmSizeX, hmSizeY, hmCompressionFactor;
384
385 using (MemoryStream mstr = new MemoryStream(pBlob))
386 {
387 using (BinaryReader br = new BinaryReader(mstr))
388 {
389 hmFormatCode = br.ReadInt32();
390 hmSizeX = br.ReadInt32();
391 hmSizeY = br.ReadInt32();
392 hmCompressionFactor = br.ReadInt32();
393
394 m_compressionFactor = hmCompressionFactor;
395
396 // In case database info doesn't match real terrain size, initialize the whole terrain.
397 ClearLand();
398
399 for (int yy = 0; yy < hmSizeY; yy++)
400 {
401 for (int xx = 0; xx < hmSizeX; xx++)
402 {
403 Int16 val = br.ReadInt16();
404 if (xx < SizeX && yy < SizeY)
405 m_heightmap[xx, yy] = val;
406 }
407 }
408 }
409 ClearTaint();
410
411 m_log.InfoFormat("{0} Read compressed 2d heightmap. Heightmap size=<{1},{2}>. Region size={<{3},{4}>. CompFact={5}", LogHeader,
412 hmSizeX, hmSizeY, SizeX, SizeY, hmCompressionFactor);
413 }
414 }
415 }
416}