diff options
author | David Walter Seikel | 2016-11-03 21:44:39 +1000 |
---|---|---|
committer | David Walter Seikel | 2016-11-03 21:44:39 +1000 |
commit | 134f86e8d5c414409631b25b8c6f0ee45fbd8631 (patch) | |
tree | 216b89d3fb89acfb81be1e440c25c41ab09fa96d /OpenSim/Region/CoreModules/World/Terrain | |
parent | More changing to production grid. Double oops. (diff) | |
download | opensim-SC_OLD-134f86e8d5c414409631b25b8c6f0ee45fbd8631.zip opensim-SC_OLD-134f86e8d5c414409631b25b8c6f0ee45fbd8631.tar.gz opensim-SC_OLD-134f86e8d5c414409631b25b8c6f0ee45fbd8631.tar.bz2 opensim-SC_OLD-134f86e8d5c414409631b25b8c6f0ee45fbd8631.tar.xz |
Initial update to OpenSim 0.8.2.1 source code.
Diffstat (limited to 'OpenSim/Region/CoreModules/World/Terrain')
21 files changed, 2096 insertions, 366 deletions
diff --git a/OpenSim/Region/CoreModules/World/Terrain/Effects/DefaultTerrainGenerator.cs b/OpenSim/Region/CoreModules/World/Terrain/Effects/DefaultTerrainGenerator.cs index 7186dd7..89087b1 100644 --- a/OpenSim/Region/CoreModules/World/Terrain/Effects/DefaultTerrainGenerator.cs +++ b/OpenSim/Region/CoreModules/World/Terrain/Effects/DefaultTerrainGenerator.cs | |||
@@ -42,7 +42,7 @@ namespace OpenSim.Region.CoreModules.World.Terrain.Effects | |||
42 | for (y = 0; y < map.Height; y++) | 42 | for (y = 0; y < map.Height; y++) |
43 | { | 43 | { |
44 | map[x, y] = TerrainUtil.PerlinNoise2D(x, y, 3, 0.25) * 10; | 44 | map[x, y] = TerrainUtil.PerlinNoise2D(x, y, 3, 0.25) * 10; |
45 | double spherFac = TerrainUtil.SphericalFactor(x, y, Constants.RegionSize / 2, Constants.RegionSize / 2, 50) * 0.01; | 45 | double spherFac = TerrainUtil.SphericalFactor(x, y, map.Width / 2, map.Height / 2, 50) * 0.01; |
46 | if (map[x, y] < spherFac) | 46 | if (map[x, y] < spherFac) |
47 | { | 47 | { |
48 | map[x, y] = spherFac; | 48 | map[x, y] = spherFac; |
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/FileLoaders/LLRAW.cs b/OpenSim/Region/CoreModules/World/Terrain/FileLoaders/LLRAW.cs index 62d232e..be1fb24 100644 --- a/OpenSim/Region/CoreModules/World/Terrain/FileLoaders/LLRAW.cs +++ b/OpenSim/Region/CoreModules/World/Terrain/FileLoaders/LLRAW.cs | |||
@@ -27,6 +27,7 @@ | |||
27 | 27 | ||
28 | using System; | 28 | using System; |
29 | using System.IO; | 29 | using System.IO; |
30 | using OpenSim.Framework; | ||
30 | using OpenSim.Region.Framework.Interfaces; | 31 | using OpenSim.Region.Framework.Interfaces; |
31 | using OpenSim.Region.Framework.Scenes; | 32 | using OpenSim.Region.Framework.Scenes; |
32 | 33 | ||
@@ -73,12 +74,13 @@ namespace OpenSim.Region.CoreModules.World.Terrain.FileLoaders | |||
73 | public ITerrainChannel LoadFile(string filename) | 74 | public ITerrainChannel LoadFile(string filename) |
74 | { | 75 | { |
75 | FileInfo file = new FileInfo(filename); | 76 | FileInfo file = new FileInfo(filename); |
76 | FileStream s = file.Open(FileMode.Open, FileAccess.Read); | ||
77 | ITerrainChannel retval = LoadStream(s); | ||
78 | 77 | ||
79 | s.Close(); | 78 | ITerrainChannel channel; |
80 | 79 | ||
81 | return retval; | 80 | using (FileStream s = file.Open(FileMode.Open, FileAccess.Read)) |
81 | channel = LoadStream(s); | ||
82 | |||
83 | return channel; | ||
82 | } | 84 | } |
83 | 85 | ||
84 | public ITerrainChannel LoadFile(string filename, int offsetX, int offsetY, int fileWidth, int fileHeight, int sectionWidth, int sectionHeight) | 86 | public ITerrainChannel LoadFile(string filename, int offsetX, int offsetY, int fileWidth, int fileHeight, int sectionWidth, int sectionHeight) |
@@ -86,153 +88,159 @@ namespace OpenSim.Region.CoreModules.World.Terrain.FileLoaders | |||
86 | TerrainChannel retval = new TerrainChannel(sectionWidth, sectionHeight); | 88 | TerrainChannel retval = new TerrainChannel(sectionWidth, sectionHeight); |
87 | 89 | ||
88 | FileInfo file = new FileInfo(filename); | 90 | FileInfo file = new FileInfo(filename); |
89 | FileStream s = file.Open(FileMode.Open, FileAccess.Read); | ||
90 | BinaryReader bs = new BinaryReader(s); | ||
91 | 91 | ||
92 | int currFileYOffset = fileHeight - 1; | 92 | using (FileStream s = file.Open(FileMode.Open, FileAccess.Read)) |
93 | 93 | using (BinaryReader bs = new BinaryReader(s)) | |
94 | // if our region isn't on the first Y section of the areas to be landscaped, then | ||
95 | // advance to our section of the file | ||
96 | while (currFileYOffset > offsetY) | ||
97 | { | 94 | { |
98 | // read a whole strip of regions | 95 | int currFileYOffset = fileHeight - 1; |
99 | int heightsToRead = sectionHeight * (fileWidth * sectionWidth); | ||
100 | bs.ReadBytes(heightsToRead * 13); // because there are 13 fun channels | ||
101 | currFileYOffset--; | ||
102 | } | ||
103 | 96 | ||
104 | // got to the Y start offset within the file of our region | 97 | // if our region isn't on the first Y section of the areas to be landscaped, then |
105 | // so read the file bits associated with our region | 98 | // advance to our section of the file |
106 | int y; | 99 | while (currFileYOffset > offsetY) |
107 | // for each Y within our Y offset | ||
108 | for (y = sectionHeight - 1; y >= 0; y--) | ||
109 | { | ||
110 | int currFileXOffset = 0; | ||
111 | |||
112 | // if our region isn't the first X section of the areas to be landscaped, then | ||
113 | // advance the stream to the X start pos of our section in the file | ||
114 | // i.e. eat X upto where we start | ||
115 | while (currFileXOffset < offsetX) | ||
116 | { | 100 | { |
117 | bs.ReadBytes(sectionWidth * 13); | 101 | // read a whole strip of regions |
118 | currFileXOffset++; | 102 | int heightsToRead = sectionHeight * (fileWidth * sectionWidth); |
103 | bs.ReadBytes(heightsToRead * 13); // because there are 13 fun channels | ||
104 | currFileYOffset--; | ||
119 | } | 105 | } |
120 | 106 | ||
121 | // got to our X offset, so write our regions X line | 107 | // got to the Y start offset within the file of our region |
122 | int x; | 108 | // so read the file bits associated with our region |
123 | for (x = 0; x < sectionWidth; x++) | 109 | int y; |
124 | { | ||
125 | // Read a strip and continue | ||
126 | retval[x, y] = bs.ReadByte() * (bs.ReadByte() / 128.0); | ||
127 | bs.ReadBytes(11); | ||
128 | } | ||
129 | // record that we wrote it | ||
130 | currFileXOffset++; | ||
131 | 110 | ||
132 | // if our region isn't the last X section of the areas to be landscaped, then | 111 | // for each Y within our Y offset |
133 | // advance the stream to the end of this Y column | 112 | for (y = sectionHeight - 1; y >= 0; y--) |
134 | while (currFileXOffset < fileWidth) | ||
135 | { | 113 | { |
136 | // eat the next regions x line | 114 | int currFileXOffset = 0; |
137 | bs.ReadBytes(sectionWidth * 13); //The 13 channels again | 115 | |
116 | // if our region isn't the first X section of the areas to be landscaped, then | ||
117 | // advance the stream to the X start pos of our section in the file | ||
118 | // i.e. eat X upto where we start | ||
119 | while (currFileXOffset < offsetX) | ||
120 | { | ||
121 | bs.ReadBytes(sectionWidth * 13); | ||
122 | currFileXOffset++; | ||
123 | } | ||
124 | |||
125 | // got to our X offset, so write our regions X line | ||
126 | int x; | ||
127 | for (x = 0; x < sectionWidth; x++) | ||
128 | { | ||
129 | // Read a strip and continue | ||
130 | retval[x, y] = bs.ReadByte() * (bs.ReadByte() / 128.0); | ||
131 | bs.ReadBytes(11); | ||
132 | } | ||
133 | // record that we wrote it | ||
138 | currFileXOffset++; | 134 | currFileXOffset++; |
135 | |||
136 | // if our region isn't the last X section of the areas to be landscaped, then | ||
137 | // advance the stream to the end of this Y column | ||
138 | while (currFileXOffset < fileWidth) | ||
139 | { | ||
140 | // eat the next regions x line | ||
141 | bs.ReadBytes(sectionWidth * 13); //The 13 channels again | ||
142 | currFileXOffset++; | ||
143 | } | ||
139 | } | 144 | } |
140 | } | 145 | } |
141 | 146 | ||
142 | bs.Close(); | ||
143 | s.Close(); | ||
144 | |||
145 | return retval; | 147 | return retval; |
146 | } | 148 | } |
147 | 149 | ||
148 | public ITerrainChannel LoadStream(Stream s) | 150 | public ITerrainChannel LoadStream(Stream s) |
149 | { | 151 | { |
150 | TerrainChannel retval = new TerrainChannel(); | 152 | // The raw format doesn't contain any dimension information. |
153 | // Guess the square dimensions by using the length of the raw file. | ||
154 | double dimension = Math.Sqrt((double)(s.Length / 13)); | ||
155 | // Regions are always multiples of 256. | ||
156 | int trimmedDimension = (int)dimension - ((int)dimension % (int)Constants.RegionSize); | ||
157 | if (trimmedDimension < Constants.RegionSize) | ||
158 | trimmedDimension = (int)Constants.RegionSize; | ||
159 | |||
160 | TerrainChannel retval = new TerrainChannel(trimmedDimension, trimmedDimension); | ||
151 | 161 | ||
152 | BinaryReader bs = new BinaryReader(s); | 162 | using (BinaryReader bs = new BinaryReader(s)) |
153 | int y; | ||
154 | for (y = 0; y < retval.Height; y++) | ||
155 | { | 163 | { |
156 | int x; | 164 | int y; |
157 | for (x = 0; x < retval.Width; x++) | 165 | for (y = 0; y < retval.Height; y++) |
158 | { | 166 | { |
159 | retval[x, (retval.Height - 1) - y] = bs.ReadByte() * (bs.ReadByte() / 128.0); | 167 | int x; |
160 | bs.ReadBytes(11); // Advance the stream to next bytes. | 168 | for (x = 0; x < retval.Width; x++) |
169 | { | ||
170 | retval[x, (retval.Height - 1) - y] = bs.ReadByte() * (bs.ReadByte() / 128.0); | ||
171 | bs.ReadBytes(11); // Advance the stream to next bytes. | ||
172 | } | ||
161 | } | 173 | } |
162 | } | 174 | } |
163 | 175 | ||
164 | bs.Close(); | ||
165 | |||
166 | return retval; | 176 | return retval; |
167 | } | 177 | } |
168 | 178 | ||
169 | public void SaveFile(string filename, ITerrainChannel map) | 179 | public void SaveFile(string filename, ITerrainChannel map) |
170 | { | 180 | { |
171 | FileInfo file = new FileInfo(filename); | 181 | FileInfo file = new FileInfo(filename); |
172 | FileStream s = file.Open(FileMode.CreateNew, FileAccess.Write); | ||
173 | SaveStream(s, map); | ||
174 | 182 | ||
175 | s.Close(); | 183 | using (FileStream s = file.Open(FileMode.CreateNew, FileAccess.Write)) |
184 | SaveStream(s, map); | ||
176 | } | 185 | } |
177 | 186 | ||
178 | public void SaveStream(Stream s, ITerrainChannel map) | 187 | public void SaveStream(Stream s, ITerrainChannel map) |
179 | { | 188 | { |
180 | BinaryWriter binStream = new BinaryWriter(s); | 189 | using (BinaryWriter binStream = new BinaryWriter(s)) |
181 | |||
182 | // Output the calculated raw | ||
183 | for (int y = 0; y < map.Height; y++) | ||
184 | { | 190 | { |
185 | for (int x = 0; x < map.Width; x++) | 191 | // Output the calculated raw |
192 | for (int y = 0; y < map.Height; y++) | ||
186 | { | 193 | { |
187 | double t = map[x, (map.Height - 1) - y]; | 194 | for (int x = 0; x < map.Width; x++) |
188 | //if height is less than 0, set it to 0 as | ||
189 | //can't save -ve values in a LLRAW file | ||
190 | if (t < 0d) | ||
191 | { | 195 | { |
192 | t = 0d; | 196 | double t = map[x, (map.Height - 1) - y]; |
197 | //if height is less than 0, set it to 0 as | ||
198 | //can't save -ve values in a LLRAW file | ||
199 | if (t < 0d) | ||
200 | { | ||
201 | t = 0d; | ||
202 | } | ||
203 | |||
204 | int index = 0; | ||
205 | |||
206 | // The lookup table is pre-sorted, so we either find an exact match or | ||
207 | // the next closest (smaller) match with a binary search | ||
208 | index = Array.BinarySearch<HeightmapLookupValue>(LookupHeightTable, new HeightmapLookupValue(0, (float)t)); | ||
209 | if (index < 0) | ||
210 | index = ~index - 1; | ||
211 | |||
212 | index = LookupHeightTable[index].Index; | ||
213 | |||
214 | byte red = (byte) (index & 0xFF); | ||
215 | byte green = (byte) ((index >> 8) & 0xFF); | ||
216 | const byte blue = 20; | ||
217 | const byte alpha1 = 0; | ||
218 | const byte alpha2 = 0; | ||
219 | const byte alpha3 = 0; | ||
220 | const byte alpha4 = 0; | ||
221 | const byte alpha5 = 255; | ||
222 | const byte alpha6 = 255; | ||
223 | const byte alpha7 = 255; | ||
224 | const byte alpha8 = 255; | ||
225 | byte alpha9 = red; | ||
226 | byte alpha10 = green; | ||
227 | |||
228 | binStream.Write(red); | ||
229 | binStream.Write(green); | ||
230 | binStream.Write(blue); | ||
231 | binStream.Write(alpha1); | ||
232 | binStream.Write(alpha2); | ||
233 | binStream.Write(alpha3); | ||
234 | binStream.Write(alpha4); | ||
235 | binStream.Write(alpha5); | ||
236 | binStream.Write(alpha6); | ||
237 | binStream.Write(alpha7); | ||
238 | binStream.Write(alpha8); | ||
239 | binStream.Write(alpha9); | ||
240 | binStream.Write(alpha10); | ||
193 | } | 241 | } |
194 | |||
195 | int index = 0; | ||
196 | |||
197 | // The lookup table is pre-sorted, so we either find an exact match or | ||
198 | // the next closest (smaller) match with a binary search | ||
199 | index = Array.BinarySearch<HeightmapLookupValue>(LookupHeightTable, new HeightmapLookupValue(0, (float)t)); | ||
200 | if (index < 0) | ||
201 | index = ~index - 1; | ||
202 | |||
203 | index = LookupHeightTable[index].Index; | ||
204 | |||
205 | byte red = (byte) (index & 0xFF); | ||
206 | byte green = (byte) ((index >> 8) & 0xFF); | ||
207 | const byte blue = 20; | ||
208 | const byte alpha1 = 0; | ||
209 | const byte alpha2 = 0; | ||
210 | const byte alpha3 = 0; | ||
211 | const byte alpha4 = 0; | ||
212 | const byte alpha5 = 255; | ||
213 | const byte alpha6 = 255; | ||
214 | const byte alpha7 = 255; | ||
215 | const byte alpha8 = 255; | ||
216 | byte alpha9 = red; | ||
217 | byte alpha10 = green; | ||
218 | |||
219 | binStream.Write(red); | ||
220 | binStream.Write(green); | ||
221 | binStream.Write(blue); | ||
222 | binStream.Write(alpha1); | ||
223 | binStream.Write(alpha2); | ||
224 | binStream.Write(alpha3); | ||
225 | binStream.Write(alpha4); | ||
226 | binStream.Write(alpha5); | ||
227 | binStream.Write(alpha6); | ||
228 | binStream.Write(alpha7); | ||
229 | binStream.Write(alpha8); | ||
230 | binStream.Write(alpha9); | ||
231 | binStream.Write(alpha10); | ||
232 | } | 242 | } |
233 | } | 243 | } |
234 | |||
235 | binStream.Close(); | ||
236 | } | 244 | } |
237 | 245 | ||
238 | public string FileExtension | 246 | public string FileExtension |
@@ -259,7 +267,6 @@ namespace OpenSim.Region.CoreModules.World.Terrain.FileLoaders | |||
259 | public bool SupportsTileSave() | 267 | public bool SupportsTileSave() |
260 | { | 268 | { |
261 | return false; | 269 | return false; |
262 | } | 270 | } |
263 | |||
264 | } | 271 | } |
265 | } | 272 | } \ No newline at end of file |
diff --git a/OpenSim/Region/CoreModules/World/Terrain/FileLoaders/RAW32.cs b/OpenSim/Region/CoreModules/World/Terrain/FileLoaders/RAW32.cs index 9fb7ef7..d467abb 100644 --- a/OpenSim/Region/CoreModules/World/Terrain/FileLoaders/RAW32.cs +++ b/OpenSim/Region/CoreModules/World/Terrain/FileLoaders/RAW32.cs | |||
@@ -25,7 +25,10 @@ | |||
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 System; | ||
28 | using System.IO; | 29 | using System.IO; |
30 | |||
31 | using OpenSim.Framework; | ||
29 | using OpenSim.Region.Framework.Interfaces; | 32 | using OpenSim.Region.Framework.Interfaces; |
30 | using OpenSim.Region.Framework.Scenes; | 33 | using OpenSim.Region.Framework.Scenes; |
31 | 34 | ||
@@ -116,7 +119,15 @@ namespace OpenSim.Region.CoreModules.World.Terrain.FileLoaders | |||
116 | 119 | ||
117 | public ITerrainChannel LoadStream(Stream s) | 120 | public ITerrainChannel LoadStream(Stream s) |
118 | { | 121 | { |
119 | TerrainChannel retval = new TerrainChannel(); | 122 | // The raw format doesn't contain any dimension information. |
123 | // Guess the square dimensions by using the length of the raw file. | ||
124 | double dimension = Math.Sqrt((double)(s.Length / 4)); | ||
125 | // Regions are always multiples of 256. | ||
126 | int trimmedDimension = (int)dimension - ((int)dimension % (int)Constants.RegionSize); | ||
127 | if (trimmedDimension < Constants.RegionSize) | ||
128 | trimmedDimension = (int)Constants.RegionSize; | ||
129 | |||
130 | TerrainChannel retval = new TerrainChannel(trimmedDimension, trimmedDimension); | ||
120 | 131 | ||
121 | BinaryReader bs = new BinaryReader(s); | 132 | BinaryReader bs = new BinaryReader(s); |
122 | int y; | 133 | int y; |
diff --git a/OpenSim/Region/CoreModules/World/Terrain/FileLoaders/Terragen.cs b/OpenSim/Region/CoreModules/World/Terrain/FileLoaders/Terragen.cs index b5c7d33..219011e 100644 --- a/OpenSim/Region/CoreModules/World/Terrain/FileLoaders/Terragen.cs +++ b/OpenSim/Region/CoreModules/World/Terrain/FileLoaders/Terragen.cs | |||
@@ -65,7 +65,7 @@ namespace OpenSim.Region.CoreModules.World.Terrain.FileLoaders | |||
65 | bool eof = false; | 65 | bool eof = false; |
66 | 66 | ||
67 | int fileXPoints = 0; | 67 | int fileXPoints = 0; |
68 | // int fileYPoints = 0; | 68 | int fileYPoints = 0; |
69 | 69 | ||
70 | // Terragen file | 70 | // Terragen file |
71 | while (eof == false) | 71 | while (eof == false) |
@@ -75,7 +75,7 @@ namespace OpenSim.Region.CoreModules.World.Terrain.FileLoaders | |||
75 | { | 75 | { |
76 | case "SIZE": | 76 | case "SIZE": |
77 | fileXPoints = bs.ReadInt16() + 1; | 77 | fileXPoints = bs.ReadInt16() + 1; |
78 | // fileYPoints = fileXPoints; | 78 | fileYPoints = fileXPoints; |
79 | bs.ReadInt16(); | 79 | bs.ReadInt16(); |
80 | break; | 80 | break; |
81 | case "XPTS": | 81 | case "XPTS": |
@@ -83,8 +83,7 @@ namespace OpenSim.Region.CoreModules.World.Terrain.FileLoaders | |||
83 | bs.ReadInt16(); | 83 | bs.ReadInt16(); |
84 | break; | 84 | break; |
85 | case "YPTS": | 85 | case "YPTS": |
86 | // fileYPoints = bs.ReadInt16(); | 86 | fileYPoints = bs.ReadInt16(); |
87 | bs.ReadInt16(); | ||
88 | bs.ReadInt16(); | 87 | bs.ReadInt16(); |
89 | break; | 88 | break; |
90 | case "ALTW": | 89 | case "ALTW": |
@@ -138,7 +137,6 @@ namespace OpenSim.Region.CoreModules.World.Terrain.FileLoaders | |||
138 | bs.ReadInt16(); | 137 | bs.ReadInt16(); |
139 | } | 138 | } |
140 | 139 | ||
141 | |||
142 | break; | 140 | break; |
143 | default: | 141 | default: |
144 | bs.ReadInt32(); | 142 | bs.ReadInt32(); |
@@ -154,10 +152,11 @@ namespace OpenSim.Region.CoreModules.World.Terrain.FileLoaders | |||
154 | 152 | ||
155 | public ITerrainChannel LoadStream(Stream s) | 153 | public ITerrainChannel LoadStream(Stream s) |
156 | { | 154 | { |
157 | 155 | // Set to default size | |
158 | int w = (int)Constants.RegionSize; | 156 | int w = (int)Constants.RegionSize; |
159 | int h = (int)Constants.RegionSize; | 157 | int h = (int)Constants.RegionSize; |
160 | 158 | ||
159 | // create a dummy channel (in case data is bad) | ||
161 | TerrainChannel retval = new TerrainChannel(w, h); | 160 | TerrainChannel retval = new TerrainChannel(w, h); |
162 | 161 | ||
163 | BinaryReader bs = new BinaryReader(s); | 162 | BinaryReader bs = new BinaryReader(s); |
@@ -165,8 +164,6 @@ namespace OpenSim.Region.CoreModules.World.Terrain.FileLoaders | |||
165 | bool eof = false; | 164 | bool eof = false; |
166 | if (Encoding.ASCII.GetString(bs.ReadBytes(16)) == "TERRAGENTERRAIN ") | 165 | if (Encoding.ASCII.GetString(bs.ReadBytes(16)) == "TERRAGENTERRAIN ") |
167 | { | 166 | { |
168 | // int fileWidth = w; | ||
169 | // int fileHeight = h; | ||
170 | 167 | ||
171 | // Terragen file | 168 | // Terragen file |
172 | while (eof == false) | 169 | while (eof == false) |
@@ -175,31 +172,29 @@ namespace OpenSim.Region.CoreModules.World.Terrain.FileLoaders | |||
175 | switch (tmp) | 172 | switch (tmp) |
176 | { | 173 | { |
177 | case "SIZE": | 174 | case "SIZE": |
178 | // int sztmp = bs.ReadInt16() + 1; | 175 | w = bs.ReadInt16() + 1; |
179 | // fileWidth = sztmp; | 176 | h = w; |
180 | // fileHeight = sztmp; | ||
181 | bs.ReadInt16(); | ||
182 | bs.ReadInt16(); | 177 | bs.ReadInt16(); |
183 | break; | 178 | break; |
184 | case "XPTS": | 179 | case "XPTS": |
185 | // fileWidth = bs.ReadInt16(); | 180 | w = bs.ReadInt16(); |
186 | bs.ReadInt16(); | ||
187 | bs.ReadInt16(); | 181 | bs.ReadInt16(); |
188 | break; | 182 | break; |
189 | case "YPTS": | 183 | case "YPTS": |
190 | // fileHeight = bs.ReadInt16(); | 184 | h = bs.ReadInt16(); |
191 | bs.ReadInt16(); | ||
192 | bs.ReadInt16(); | 185 | bs.ReadInt16(); |
193 | break; | 186 | break; |
194 | case "ALTW": | 187 | case "ALTW": |
195 | eof = true; | 188 | eof = true; |
196 | Int16 heightScale = bs.ReadInt16(); | 189 | // create new channel of proper size (now that we know it) |
197 | Int16 baseHeight = bs.ReadInt16(); | 190 | retval = new TerrainChannel(w, h); |
191 | double heightScale = (double)bs.ReadInt16() / 65536.0; | ||
192 | double baseHeight = (double)bs.ReadInt16(); | ||
198 | for (int y = 0; y < h; y++) | 193 | for (int y = 0; y < h; y++) |
199 | { | 194 | { |
200 | for (int x = 0; x < w; x++) | 195 | for (int x = 0; x < w; x++) |
201 | { | 196 | { |
202 | retval[x, y] = baseHeight + bs.ReadInt16() * (double)heightScale / 65536.0; | 197 | retval[x, y] = baseHeight + (double)bs.ReadInt16() * heightScale; |
203 | } | 198 | } |
204 | } | 199 | } |
205 | break; | 200 | break; |
@@ -209,9 +204,7 @@ namespace OpenSim.Region.CoreModules.World.Terrain.FileLoaders | |||
209 | } | 204 | } |
210 | } | 205 | } |
211 | } | 206 | } |
212 | |||
213 | bs.Close(); | 207 | bs.Close(); |
214 | |||
215 | return retval; | 208 | return retval; |
216 | } | 209 | } |
217 | 210 | ||
@@ -257,17 +250,17 @@ namespace OpenSim.Region.CoreModules.World.Terrain.FileLoaders | |||
257 | bs.Write(enc.GetBytes("TERRAGENTERRAIN ")); | 250 | bs.Write(enc.GetBytes("TERRAGENTERRAIN ")); |
258 | 251 | ||
259 | bs.Write(enc.GetBytes("SIZE")); | 252 | bs.Write(enc.GetBytes("SIZE")); |
260 | bs.Write(Convert.ToInt16(Constants.RegionSize)); | 253 | bs.Write(Convert.ToInt16(map.Width)); |
261 | bs.Write(Convert.ToInt16(0)); // necessary padding | 254 | bs.Write(Convert.ToInt16(0)); // necessary padding |
262 | 255 | ||
263 | //The XPTS and YPTS chunks are not needed for square regions | 256 | //The XPTS and YPTS chunks are not needed for square regions |
264 | //but L3DT won't load the terrain file properly without them. | 257 | //but L3DT won't load the terrain file properly without them. |
265 | bs.Write(enc.GetBytes("XPTS")); | 258 | bs.Write(enc.GetBytes("XPTS")); |
266 | bs.Write(Convert.ToInt16(Constants.RegionSize)); | 259 | bs.Write(Convert.ToInt16(map.Width)); |
267 | bs.Write(Convert.ToInt16(0)); // necessary padding | 260 | bs.Write(Convert.ToInt16(0)); // necessary padding |
268 | 261 | ||
269 | bs.Write(enc.GetBytes("YPTS")); | 262 | bs.Write(enc.GetBytes("YPTS")); |
270 | bs.Write(Convert.ToInt16(Constants.RegionSize)); | 263 | bs.Write(Convert.ToInt16(map.Height)); |
271 | bs.Write(Convert.ToInt16(0)); // necessary padding | 264 | bs.Write(Convert.ToInt16(0)); // necessary padding |
272 | 265 | ||
273 | bs.Write(enc.GetBytes("SCAL")); | 266 | bs.Write(enc.GetBytes("SCAL")); |
@@ -283,11 +276,13 @@ namespace OpenSim.Region.CoreModules.World.Terrain.FileLoaders | |||
283 | bs.Write(Convert.ToInt16(horizontalScale)); // range between max and min | 276 | bs.Write(Convert.ToInt16(horizontalScale)); // range between max and min |
284 | bs.Write(Convert.ToInt16(baseHeight)); // base height or mid point | 277 | bs.Write(Convert.ToInt16(baseHeight)); // base height or mid point |
285 | 278 | ||
279 | double factor = 65536.0 / horizontalScale; // avoid computing this on each iteration | ||
280 | |||
286 | for (int y = 0; y < map.Height; y++) | 281 | for (int y = 0; y < map.Height; y++) |
287 | { | 282 | { |
288 | for (int x = 0; x < map.Width; x++) | 283 | for (int x = 0; x < map.Width; x++) |
289 | { | 284 | { |
290 | float elevation = (float)((map[x,y] - baseHeight) * 65536 ) / (float)horizontalScale; // see LoadStream for inverse | 285 | float elevation = (float)((map[x,y] - baseHeight) * factor); // see LoadStream for inverse |
291 | 286 | ||
292 | // clamp rounding issues | 287 | // clamp rounding issues |
293 | if (elevation > Int16.MaxValue) | 288 | if (elevation > Int16.MaxValue) |
@@ -299,7 +294,7 @@ namespace OpenSim.Region.CoreModules.World.Terrain.FileLoaders | |||
299 | } | 294 | } |
300 | } | 295 | } |
301 | 296 | ||
302 | //This is only necessary for older versions of Terragen. | 297 | //This is necessary for older versions of Terragen. |
303 | bs.Write(enc.GetBytes("EOF ")); | 298 | bs.Write(enc.GetBytes("EOF ")); |
304 | 299 | ||
305 | bs.Close(); | 300 | bs.Close(); |
@@ -343,7 +338,7 @@ namespace OpenSim.Region.CoreModules.World.Terrain.FileLoaders | |||
343 | if (BitConverter.IsLittleEndian == false) | 338 | if (BitConverter.IsLittleEndian == false) |
344 | { | 339 | { |
345 | byte[] tmp = new byte[4]; | 340 | byte[] tmp = new byte[4]; |
346 | for (int i = 0; i < 4; i++) | 341 | for (int i = 3; i >= 0; i--) |
347 | { | 342 | { |
348 | tmp[i] = retVal[3 - i]; | 343 | tmp[i] = retVal[3 - i]; |
349 | } | 344 | } |
diff --git a/OpenSim/Region/CoreModules/World/Terrain/FloodBrushes/NoiseArea.cs b/OpenSim/Region/CoreModules/World/Terrain/FloodBrushes/NoiseArea.cs index 630473e..b6c635c 100644 --- a/OpenSim/Region/CoreModules/World/Terrain/FloodBrushes/NoiseArea.cs +++ b/OpenSim/Region/CoreModules/World/Terrain/FloodBrushes/NoiseArea.cs | |||
@@ -45,7 +45,7 @@ namespace OpenSim.Region.CoreModules.World.Terrain.FloodBrushes | |||
45 | { | 45 | { |
46 | if (fillArea[x, y]) | 46 | if (fillArea[x, y]) |
47 | { | 47 | { |
48 | double noise = TerrainUtil.PerlinNoise2D((double) x / Constants.RegionSize, (double) y / Constants.RegionSize, 8, 1.0); | 48 | double noise = TerrainUtil.PerlinNoise2D((double) x / map.Width, (double) y / map.Height, 8, 1.0); |
49 | 49 | ||
50 | map[x, y] += noise * strength; | 50 | map[x, y] += noise * strength; |
51 | } | 51 | } |
diff --git a/OpenSim/Region/CoreModules/World/Terrain/ITerrainFeature.cs b/OpenSim/Region/CoreModules/World/Terrain/ITerrainFeature.cs new file mode 100644 index 0000000..78a43db --- /dev/null +++ b/OpenSim/Region/CoreModules/World/Terrain/ITerrainFeature.cs | |||
@@ -0,0 +1,60 @@ | |||
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 OpenSim.Region.Framework.Interfaces; | ||
30 | |||
31 | namespace OpenSim.Region.CoreModules.World.Terrain | ||
32 | { | ||
33 | public interface ITerrainFeature | ||
34 | { | ||
35 | /// <summary> | ||
36 | /// Creates the feature. | ||
37 | /// </summary> | ||
38 | /// <returns> | ||
39 | /// Empty string if successful, otherwise error message. | ||
40 | /// </returns> | ||
41 | /// <param name='map'> | ||
42 | /// ITerrainChannel holding terrain data. | ||
43 | /// </param> | ||
44 | /// <param name='args'> | ||
45 | /// command-line arguments from console. | ||
46 | /// </param> | ||
47 | string CreateFeature(ITerrainChannel map, string[] args); | ||
48 | |||
49 | /// <summary> | ||
50 | /// Gets a string describing the usage. | ||
51 | /// </summary> | ||
52 | /// <returns> | ||
53 | /// A string describing parameters for creating the feature. | ||
54 | /// Format is "feature-name <arg1> <arg2> ..." | ||
55 | /// </returns> | ||
56 | string GetUsage(); | ||
57 | } | ||
58 | |||
59 | } | ||
60 | |||
diff --git a/OpenSim/Region/CoreModules/World/Terrain/ITerrainModifier.cs b/OpenSim/Region/CoreModules/World/Terrain/ITerrainModifier.cs new file mode 100644 index 0000000..0e0a0e4 --- /dev/null +++ b/OpenSim/Region/CoreModules/World/Terrain/ITerrainModifier.cs | |||
@@ -0,0 +1,77 @@ | |||
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 OpenSim.Region.Framework.Interfaces; | ||
30 | |||
31 | namespace OpenSim.Region.CoreModules.World.Terrain | ||
32 | { | ||
33 | public interface ITerrainModifier | ||
34 | { | ||
35 | /// <summary> | ||
36 | /// Creates the feature. | ||
37 | /// </summary> | ||
38 | /// <returns> | ||
39 | /// Empty string if successful, otherwise error message. | ||
40 | /// </returns> | ||
41 | /// <param name='map'> | ||
42 | /// ITerrainChannel holding terrain data. | ||
43 | /// </param> | ||
44 | /// <param name='args'> | ||
45 | /// command-line arguments from console. | ||
46 | /// </param> | ||
47 | string ModifyTerrain(ITerrainChannel map, string[] args); | ||
48 | |||
49 | /// <summary> | ||
50 | /// Gets a string describing the usage. | ||
51 | /// </summary> | ||
52 | /// <returns> | ||
53 | /// A string describing parameters for creating the feature. | ||
54 | /// Format is "feature-name <arg1> <arg2> ..." | ||
55 | /// </returns> | ||
56 | string GetUsage(); | ||
57 | |||
58 | /// <summary> | ||
59 | /// Apply the appropriate operation on the specified map, at (x, y). | ||
60 | /// </summary> | ||
61 | /// <param name='map'> | ||
62 | /// Map. | ||
63 | /// </param> | ||
64 | /// <param name='data'> | ||
65 | /// Data. | ||
66 | /// </param> | ||
67 | /// <param name='x'> | ||
68 | /// X. | ||
69 | /// </param> | ||
70 | /// <param name='y'> | ||
71 | /// Y. | ||
72 | /// </param> | ||
73 | double operate(double[,] map, TerrainModifierData data, int x, int y); | ||
74 | } | ||
75 | |||
76 | } | ||
77 | |||
diff --git a/OpenSim/Region/CoreModules/World/Terrain/Modifiers/FillModifier.cs b/OpenSim/Region/CoreModules/World/Terrain/Modifiers/FillModifier.cs new file mode 100644 index 0000000..32f1de9 --- /dev/null +++ b/OpenSim/Region/CoreModules/World/Terrain/Modifiers/FillModifier.cs | |||
@@ -0,0 +1,93 @@ | |||
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 | using System; | ||
28 | |||
29 | using OpenSim.Region.CoreModules.World.Terrain; | ||
30 | using OpenSim.Region.Framework.Interfaces; | ||
31 | |||
32 | namespace OpenSim.Region.CoreModules.World.Terrain.Modifiers | ||
33 | { | ||
34 | public class FillModifier : TerrainModifier | ||
35 | { | ||
36 | |||
37 | public FillModifier(ITerrainModule module) : base(module) | ||
38 | { | ||
39 | } | ||
40 | |||
41 | public override string ModifyTerrain(ITerrainChannel map, string[] args) | ||
42 | { | ||
43 | string result; | ||
44 | if (args.Length < 3) | ||
45 | { | ||
46 | result = "Usage: " + GetUsage(); | ||
47 | } | ||
48 | else | ||
49 | { | ||
50 | TerrainModifierData data; | ||
51 | result = this.parseParameters(args, out data); | ||
52 | |||
53 | // Context-specific validation | ||
54 | if (result == String.Empty) | ||
55 | { | ||
56 | if (data.shape == String.Empty) | ||
57 | { | ||
58 | data.shape = "rectangle"; | ||
59 | data.x0 = 0; | ||
60 | data.y0 = 0; | ||
61 | data.dx = map.Width; | ||
62 | data.dy = map.Height; | ||
63 | } | ||
64 | } | ||
65 | |||
66 | // if it's all good, then do the work | ||
67 | if (result == String.Empty) | ||
68 | { | ||
69 | this.applyModification(map, data); | ||
70 | } | ||
71 | } | ||
72 | |||
73 | return result; | ||
74 | } | ||
75 | |||
76 | public override string GetUsage() | ||
77 | { | ||
78 | string val = "fill <height> [ -rec=x1,y1,dx[,dy] | -ell=x0,y0,rx[,ry] ] [-taper=<height2>]" | ||
79 | + "\nSets all points within the specified range to the specified value."; | ||
80 | return val; | ||
81 | } | ||
82 | |||
83 | public override double operate(double[,] map, TerrainModifierData data, int x, int y) | ||
84 | { | ||
85 | double factor = this.computeBevel(data, x, y); | ||
86 | double result = data.elevation - (data.elevation - data.bevelevation) * factor; | ||
87 | return result; | ||
88 | } | ||
89 | |||
90 | } | ||
91 | |||
92 | } | ||
93 | |||
diff --git a/OpenSim/Region/CoreModules/World/Terrain/Modifiers/LowerModifier.cs b/OpenSim/Region/CoreModules/World/Terrain/Modifiers/LowerModifier.cs new file mode 100644 index 0000000..2ab4bcc --- /dev/null +++ b/OpenSim/Region/CoreModules/World/Terrain/Modifiers/LowerModifier.cs | |||
@@ -0,0 +1,92 @@ | |||
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 | using System; | ||
28 | using OpenSim.Region.CoreModules.World.Terrain; | ||
29 | using OpenSim.Region.Framework.Interfaces; | ||
30 | |||
31 | namespace OpenSim.Region.CoreModules.World.Terrain.Modifiers | ||
32 | { | ||
33 | public class LowerModifier : TerrainModifier | ||
34 | { | ||
35 | public LowerModifier(ITerrainModule module) : base(module) | ||
36 | { | ||
37 | } | ||
38 | |||
39 | public override string ModifyTerrain(ITerrainChannel map, string[] args) | ||
40 | { | ||
41 | string result; | ||
42 | if (args.Length < 3) | ||
43 | { | ||
44 | result = "Usage: " + GetUsage(); | ||
45 | } | ||
46 | else | ||
47 | { | ||
48 | TerrainModifierData data; | ||
49 | result = this.parseParameters(args, out data); | ||
50 | |||
51 | // Context-specific validation | ||
52 | if (result == String.Empty) | ||
53 | { | ||
54 | if (data.shape == String.Empty) | ||
55 | { | ||
56 | data.shape = "rectangle"; | ||
57 | data.x0 = 0; | ||
58 | data.y0 = 0; | ||
59 | data.dx = map.Width; | ||
60 | data.dy = map.Height; | ||
61 | } | ||
62 | } | ||
63 | |||
64 | // if it's all good, then do the work | ||
65 | if (result == String.Empty) | ||
66 | { | ||
67 | this.applyModification(map, data); | ||
68 | } | ||
69 | } | ||
70 | |||
71 | return result; | ||
72 | } | ||
73 | |||
74 | public override string GetUsage() | ||
75 | { | ||
76 | string val = "lower <delta> [ -rec=x1,y1,dx[,dy] | -ell=x0,y0,rx[,ry] ] [-taper=<delta2>]" | ||
77 | + "\nLowers all points within the specified range by the specified amount."; | ||
78 | return val; | ||
79 | |||
80 | } | ||
81 | |||
82 | public override double operate(double[,] map, TerrainModifierData data, int x, int y) | ||
83 | { | ||
84 | double factor = this.computeBevel(data, x, y); | ||
85 | double result = map[x, y] - (data.elevation - (data.elevation - data.bevelevation) * factor); | ||
86 | return result; | ||
87 | } | ||
88 | |||
89 | } | ||
90 | |||
91 | } | ||
92 | |||
diff --git a/OpenSim/Region/CoreModules/World/Terrain/Modifiers/MaxModifier.cs b/OpenSim/Region/CoreModules/World/Terrain/Modifiers/MaxModifier.cs new file mode 100644 index 0000000..0939c0a --- /dev/null +++ b/OpenSim/Region/CoreModules/World/Terrain/Modifiers/MaxModifier.cs | |||
@@ -0,0 +1,92 @@ | |||
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 | using System; | ||
28 | using OpenSim.Region.CoreModules.World.Terrain; | ||
29 | using OpenSim.Region.Framework.Interfaces; | ||
30 | |||
31 | namespace OpenSim.Region.CoreModules.World.Terrain.Modifiers | ||
32 | { | ||
33 | public class MaxModifier : TerrainModifier | ||
34 | { | ||
35 | public MaxModifier(ITerrainModule module) : base(module) | ||
36 | { | ||
37 | } | ||
38 | |||
39 | public override string ModifyTerrain(ITerrainChannel map, string[] args) | ||
40 | { | ||
41 | string result; | ||
42 | if (args.Length < 3) | ||
43 | { | ||
44 | result = "Usage: " + GetUsage(); | ||
45 | } | ||
46 | else | ||
47 | { | ||
48 | TerrainModifierData data; | ||
49 | result = this.parseParameters(args, out data); | ||
50 | |||
51 | // Context-specific validation | ||
52 | if (result == String.Empty) | ||
53 | { | ||
54 | if (data.shape == String.Empty) | ||
55 | { | ||
56 | data.shape = "rectangle"; | ||
57 | data.x0 = 0; | ||
58 | data.y0 = 0; | ||
59 | data.dx = map.Width; | ||
60 | data.dy = map.Height; | ||
61 | } | ||
62 | } | ||
63 | |||
64 | // if it's all good, then do the work | ||
65 | if (result == String.Empty) | ||
66 | { | ||
67 | this.applyModification(map, data); | ||
68 | } | ||
69 | } | ||
70 | |||
71 | return result; | ||
72 | } | ||
73 | |||
74 | public override string GetUsage() | ||
75 | { | ||
76 | string val = "max <height> [ -rec=x1,y1,dx[,dy] | -ell=x0,y0,rx[,ry] ] [-taper=<height2>]" | ||
77 | + "\nEnsures that all points within the specified range are no higher than the specified value."; | ||
78 | return val; | ||
79 | |||
80 | } | ||
81 | |||
82 | public override double operate(double[,] map, TerrainModifierData data, int x, int y) | ||
83 | { | ||
84 | double factor = this.computeBevel(data, x, y); | ||
85 | double result = Math.Min(data.elevation - (data.elevation - data.bevelevation) * factor, map[x, y]); | ||
86 | return result; | ||
87 | } | ||
88 | |||
89 | } | ||
90 | |||
91 | } | ||
92 | |||
diff --git a/OpenSim/Region/CoreModules/World/Terrain/Modifiers/MinModifier.cs b/OpenSim/Region/CoreModules/World/Terrain/Modifiers/MinModifier.cs new file mode 100644 index 0000000..cbbccc0 --- /dev/null +++ b/OpenSim/Region/CoreModules/World/Terrain/Modifiers/MinModifier.cs | |||
@@ -0,0 +1,92 @@ | |||
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 | using System; | ||
28 | using OpenSim.Region.CoreModules.World.Terrain; | ||
29 | using OpenSim.Region.Framework.Interfaces; | ||
30 | |||
31 | namespace OpenSim.Region.CoreModules.World.Terrain.Modifiers | ||
32 | { | ||
33 | public class MinModifier : TerrainModifier | ||
34 | { | ||
35 | public MinModifier(ITerrainModule module) : base(module) | ||
36 | { | ||
37 | } | ||
38 | |||
39 | public override string ModifyTerrain(ITerrainChannel map, string[] args) | ||
40 | { | ||
41 | string result; | ||
42 | if (args.Length < 3) | ||
43 | { | ||
44 | result = "Usage: " + GetUsage(); | ||
45 | } | ||
46 | else | ||
47 | { | ||
48 | TerrainModifierData data; | ||
49 | result = this.parseParameters(args, out data); | ||
50 | |||
51 | // Context-specific validation | ||
52 | if (result == String.Empty) | ||
53 | { | ||
54 | if (data.shape == String.Empty) | ||
55 | { | ||
56 | data.shape = "rectangle"; | ||
57 | data.x0 = 0; | ||
58 | data.y0 = 0; | ||
59 | data.dx = map.Width; | ||
60 | data.dy = map.Height; | ||
61 | } | ||
62 | } | ||
63 | |||
64 | // if it's all good, then do the work | ||
65 | if (result == String.Empty) | ||
66 | { | ||
67 | this.applyModification(map, data); | ||
68 | } | ||
69 | } | ||
70 | |||
71 | return result; | ||
72 | } | ||
73 | |||
74 | public override string GetUsage() | ||
75 | { | ||
76 | string val = "min <height> [ -rec=x1,y1,dx[,dy] | -ell=x0,y0,rx[,ry] ] [-taper=<height2>]" | ||
77 | + "\nEnsures that all points within the specified range are no lower than the specified value."; | ||
78 | return val; | ||
79 | |||
80 | } | ||
81 | |||
82 | public override double operate(double[,] map, TerrainModifierData data, int x, int y) | ||
83 | { | ||
84 | double factor = this.computeBevel(data, x, y); | ||
85 | double result = Math.Max(data.elevation - (data.elevation - data.bevelevation) * factor, map[x, y]); | ||
86 | return result; | ||
87 | } | ||
88 | |||
89 | } | ||
90 | |||
91 | } | ||
92 | |||
diff --git a/OpenSim/Region/CoreModules/World/Terrain/Modifiers/NoiseModifier.cs b/OpenSim/Region/CoreModules/World/Terrain/Modifiers/NoiseModifier.cs new file mode 100644 index 0000000..d6b95d0 --- /dev/null +++ b/OpenSim/Region/CoreModules/World/Terrain/Modifiers/NoiseModifier.cs | |||
@@ -0,0 +1,108 @@ | |||
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 | using System; | ||
28 | using OpenSim.Region.CoreModules.World.Terrain; | ||
29 | using OpenSim.Region.Framework.Interfaces; | ||
30 | using OpenSim.Region.Framework.Scenes; | ||
31 | |||
32 | namespace OpenSim.Region.CoreModules.World.Terrain.Modifiers | ||
33 | { | ||
34 | public class NoiseModifier : TerrainModifier | ||
35 | { | ||
36 | public NoiseModifier(ITerrainModule module) : base(module) | ||
37 | { | ||
38 | } | ||
39 | |||
40 | public override string ModifyTerrain(ITerrainChannel map, string[] args) | ||
41 | { | ||
42 | string result; | ||
43 | if (args.Length < 3) | ||
44 | { | ||
45 | result = "Usage: " + GetUsage(); | ||
46 | } | ||
47 | else | ||
48 | { | ||
49 | TerrainModifierData data; | ||
50 | result = this.parseParameters(args, out data); | ||
51 | |||
52 | // Context-specific validation | ||
53 | if (result == String.Empty) | ||
54 | { | ||
55 | if (data.bevel == "taper") | ||
56 | { | ||
57 | if (data.bevelevation < 0.0 || data.bevelevation > 1.0) | ||
58 | { | ||
59 | result = String.Format("Taper must be 0.0 to 1.0: {0}", data.bevelevation); | ||
60 | } | ||
61 | } | ||
62 | else | ||
63 | { | ||
64 | data.bevelevation = 1.0f; | ||
65 | } | ||
66 | |||
67 | if (data.elevation < 0.0 || data.elevation > 1.0) | ||
68 | { | ||
69 | result = String.Format("Noise strength must be 0.0 to 1.0: {0}", data.elevation); | ||
70 | } | ||
71 | |||
72 | if (data.shape == String.Empty) | ||
73 | { | ||
74 | data.shape = "rectangle"; | ||
75 | data.x0 = 0; | ||
76 | data.y0 = 0; | ||
77 | data.dx = map.Width; | ||
78 | data.dy = map.Height; | ||
79 | } | ||
80 | } | ||
81 | |||
82 | // if it's all good, then do the work | ||
83 | if (result == String.Empty) | ||
84 | { | ||
85 | this.applyModification(map, data); | ||
86 | } | ||
87 | } | ||
88 | |||
89 | return result; | ||
90 | } | ||
91 | |||
92 | public override string GetUsage() | ||
93 | { | ||
94 | string val = "noise <delta> [ -rec=x1,y1,dx[,dy] | -ell=x0,y0,rx[,ry] ] [-taper=<delta2>]" | ||
95 | + "\nAdds noise to all points within the specified range."; | ||
96 | return val; | ||
97 | } | ||
98 | |||
99 | public override double operate(double[,] map, TerrainModifierData data, int x, int y) | ||
100 | { | ||
101 | double factor = this.computeBevel(data, x, y); | ||
102 | double noise = TerrainUtil.PerlinNoise2D((double)x / map.GetLength(0), (double)y / map.GetLength(1), 8, 1.0); | ||
103 | return map[x, y] + (data.elevation - (data.elevation - data.bevelevation) * factor) * (noise - .5); | ||
104 | } | ||
105 | |||
106 | } | ||
107 | |||
108 | } | ||
diff --git a/OpenSim/Region/CoreModules/World/Terrain/Modifiers/RaiseModifier.cs b/OpenSim/Region/CoreModules/World/Terrain/Modifiers/RaiseModifier.cs new file mode 100644 index 0000000..35fb9d6 --- /dev/null +++ b/OpenSim/Region/CoreModules/World/Terrain/Modifiers/RaiseModifier.cs | |||
@@ -0,0 +1,92 @@ | |||
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 | using System; | ||
28 | using OpenSim.Region.CoreModules.World.Terrain; | ||
29 | using OpenSim.Region.Framework.Interfaces; | ||
30 | |||
31 | namespace OpenSim.Region.CoreModules.World.Terrain.Modifiers | ||
32 | { | ||
33 | public class RaiseModifier : TerrainModifier | ||
34 | { | ||
35 | public RaiseModifier(ITerrainModule module) : base(module) | ||
36 | { | ||
37 | } | ||
38 | |||
39 | public override string ModifyTerrain(ITerrainChannel map, string[] args) | ||
40 | { | ||
41 | string result; | ||
42 | if (args.Length < 3) | ||
43 | { | ||
44 | result = "Usage: " + GetUsage(); | ||
45 | } | ||
46 | else | ||
47 | { | ||
48 | TerrainModifierData data; | ||
49 | result = this.parseParameters(args, out data); | ||
50 | |||
51 | // Context-specific validation | ||
52 | if (result == String.Empty) | ||
53 | { | ||
54 | if (data.shape == String.Empty) | ||
55 | { | ||
56 | data.shape = "rectangle"; | ||
57 | data.x0 = 0; | ||
58 | data.y0 = 0; | ||
59 | data.dx = map.Width; | ||
60 | data.dy = map.Height; | ||
61 | } | ||
62 | } | ||
63 | |||
64 | // if it's all good, then do the work | ||
65 | if (result == String.Empty) | ||
66 | { | ||
67 | this.applyModification(map, data); | ||
68 | } | ||
69 | } | ||
70 | |||
71 | return result; | ||
72 | } | ||
73 | |||
74 | public override string GetUsage() | ||
75 | { | ||
76 | string val = "raise <delta> [ -rec=x1,y1,dx[,dy] | -ell=x0,y0,rx[,ry] ] [-taper=<delta2>]" | ||
77 | + "\nRaises all points within the specified range by the specified amount."; | ||
78 | return val; | ||
79 | |||
80 | } | ||
81 | |||
82 | public override double operate(double[,] map, TerrainModifierData data, int x, int y) | ||
83 | { | ||
84 | double factor = this.computeBevel(data, x, y); | ||
85 | double result = map[x, y] + (data.elevation - (data.elevation - data.bevelevation) * factor); | ||
86 | return result; | ||
87 | } | ||
88 | |||
89 | } | ||
90 | |||
91 | } | ||
92 | |||
diff --git a/OpenSim/Region/CoreModules/World/Terrain/Modifiers/SmoothModifier.cs b/OpenSim/Region/CoreModules/World/Terrain/Modifiers/SmoothModifier.cs new file mode 100644 index 0000000..9f8d5b2 --- /dev/null +++ b/OpenSim/Region/CoreModules/World/Terrain/Modifiers/SmoothModifier.cs | |||
@@ -0,0 +1,131 @@ | |||
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 | using System; | ||
28 | using OpenSim.Region.CoreModules.World.Terrain; | ||
29 | using OpenSim.Region.Framework.Interfaces; | ||
30 | |||
31 | namespace OpenSim.Region.CoreModules.World.Terrain.Modifiers | ||
32 | { | ||
33 | public class SmoothModifier : TerrainModifier | ||
34 | { | ||
35 | public SmoothModifier(ITerrainModule module) : base(module) | ||
36 | { | ||
37 | } | ||
38 | |||
39 | public override string ModifyTerrain(ITerrainChannel map, string[] args) | ||
40 | { | ||
41 | string result; | ||
42 | if (args.Length < 3) | ||
43 | { | ||
44 | result = "Usage: " + GetUsage(); | ||
45 | } | ||
46 | else | ||
47 | { | ||
48 | TerrainModifierData data; | ||
49 | result = this.parseParameters(args, out data); | ||
50 | |||
51 | // Context-specific validation | ||
52 | if (result == String.Empty) | ||
53 | { | ||
54 | if (data.bevel == "taper") | ||
55 | { | ||
56 | if (data.bevelevation < 0.01 || data.bevelevation > 0.99) | ||
57 | { | ||
58 | result = String.Format("Taper must be 0.01 to 0.99: {0}", data.bevelevation); | ||
59 | } | ||
60 | } | ||
61 | else | ||
62 | { | ||
63 | data.bevelevation = 2.0f / 3.0f; | ||
64 | } | ||
65 | |||
66 | if (data.elevation < 0.0 || data.elevation > 1.0) | ||
67 | { | ||
68 | result = String.Format("Smoothing strength must be 0.0 to 1.0: {0}", data.elevation); | ||
69 | } | ||
70 | |||
71 | if (data.shape == String.Empty) | ||
72 | { | ||
73 | data.shape = "rectangle"; | ||
74 | data.x0 = 0; | ||
75 | data.y0 = 0; | ||
76 | data.dx = map.Width; | ||
77 | data.dy = map.Height; | ||
78 | } | ||
79 | } | ||
80 | |||
81 | // if it's all good, then do the work | ||
82 | if (result == String.Empty) | ||
83 | { | ||
84 | this.applyModification(map, data); | ||
85 | } | ||
86 | } | ||
87 | |||
88 | return result; | ||
89 | } | ||
90 | |||
91 | public override string GetUsage() | ||
92 | { | ||
93 | string val = "smooth <strength> [ -rec=x1,y1,dx[,dy] | -ell=x0,y0,rx[,ry] ] [-taper=<fraction>]" | ||
94 | + "\nSmooths all points within the specified range using a simple averaging algorithm."; | ||
95 | return val; | ||
96 | } | ||
97 | |||
98 | public override double operate(double[,] map, TerrainModifierData data, int x, int y) | ||
99 | { | ||
100 | double[] scale = new double[3]; | ||
101 | scale[0] = data.elevation; | ||
102 | scale[1] = ((1.0 - scale[0]) * data.bevelevation) / 8.0; | ||
103 | scale[2] = ((1.0 - scale[0]) * (1.0 - data.bevelevation)) / 16.0; | ||
104 | int xMax = map.GetLength(0); | ||
105 | int yMax = map.GetLength(1); | ||
106 | double result; | ||
107 | if ((x == 0) || (y == 0) || (x == (xMax - 1)) || (y == (yMax - 1))) | ||
108 | { | ||
109 | result = map[x, y]; | ||
110 | } | ||
111 | else | ||
112 | { | ||
113 | result = 0.0; | ||
114 | for(int yPos = (y - 2); yPos < (y + 3); yPos++) | ||
115 | { | ||
116 | int yVal = (yPos <= 0) ? 0 : ((yPos < yMax) ? yPos : yMax - 1); | ||
117 | for(int xPos = (x - 2); xPos < (x + 3); xPos++) | ||
118 | { | ||
119 | int xVal = (xPos <= 0) ? 0 : ((xPos < xMax) ? xPos : xMax - 1); | ||
120 | int dist = Math.Max(Math.Abs(x - xVal), Math.Abs(y - yVal)); | ||
121 | result += map[xVal, yVal] * scale[dist]; | ||
122 | } | ||
123 | } | ||
124 | } | ||
125 | return result; | ||
126 | } | ||
127 | |||
128 | } | ||
129 | |||
130 | } | ||
131 | |||
diff --git a/OpenSim/Region/CoreModules/World/Terrain/PaintBrushes/NoiseSphere.cs b/OpenSim/Region/CoreModules/World/Terrain/PaintBrushes/NoiseSphere.cs index 989b7d8..e7df3f8 100644 --- a/OpenSim/Region/CoreModules/World/Terrain/PaintBrushes/NoiseSphere.cs +++ b/OpenSim/Region/CoreModules/World/Terrain/PaintBrushes/NoiseSphere.cs | |||
@@ -53,7 +53,7 @@ namespace OpenSim.Region.CoreModules.World.Terrain.PaintBrushes | |||
53 | z *= z; | 53 | z *= z; |
54 | z -= ((x - rx) * (x - rx)) + ((y - ry) * (y - ry)); | 54 | z -= ((x - rx) * (x - rx)) + ((y - ry) * (y - ry)); |
55 | 55 | ||
56 | double noise = TerrainUtil.PerlinNoise2D(x / (double) Constants.RegionSize, y / (double) Constants.RegionSize, 8, 1.0); | 56 | double noise = TerrainUtil.PerlinNoise2D(x / (double) map.Width, y / (double) map.Height, 8, 1.0); |
57 | 57 | ||
58 | if (z > 0.0) | 58 | if (z > 0.0) |
59 | map[x, y] += noise * z * duration; | 59 | map[x, y] += noise * z * duration; |
diff --git a/OpenSim/Region/CoreModules/World/Terrain/TerrainModifier.cs b/OpenSim/Region/CoreModules/World/Terrain/TerrainModifier.cs new file mode 100644 index 0000000..7ebd08e --- /dev/null +++ b/OpenSim/Region/CoreModules/World/Terrain/TerrainModifier.cs | |||
@@ -0,0 +1,378 @@ | |||
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 | using System; | ||
28 | using System.Reflection; | ||
29 | using log4net; | ||
30 | |||
31 | using OpenSim.Region.Framework.Interfaces; | ||
32 | |||
33 | namespace OpenSim.Region.CoreModules.World.Terrain | ||
34 | { | ||
35 | public abstract class TerrainModifier : ITerrainModifier | ||
36 | { | ||
37 | protected ITerrainModule m_module; | ||
38 | protected static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); | ||
39 | |||
40 | protected TerrainModifier(ITerrainModule module) | ||
41 | { | ||
42 | m_module = module; | ||
43 | } | ||
44 | |||
45 | public abstract string ModifyTerrain(ITerrainChannel map, string[] args); | ||
46 | |||
47 | public abstract string GetUsage(); | ||
48 | |||
49 | public abstract double operate(double[,] map, TerrainModifierData data, int x, int y); | ||
50 | |||
51 | protected String parseParameters(string[] args, out TerrainModifierData data) | ||
52 | { | ||
53 | string val; | ||
54 | string arg; | ||
55 | string result; | ||
56 | data = new TerrainModifierData(); | ||
57 | data.shape = String.Empty; | ||
58 | data.bevel = String.Empty; | ||
59 | data.dx = 0; | ||
60 | data.dy = 0; | ||
61 | if (args.Length < 4) | ||
62 | { | ||
63 | result = "Usage: " + GetUsage(); | ||
64 | } | ||
65 | else | ||
66 | { | ||
67 | result = this.parseFloat(args[3], out data.elevation); | ||
68 | } | ||
69 | if (result == String.Empty) | ||
70 | { | ||
71 | int index = 3; | ||
72 | while(++index < args.Length && result == String.Empty) | ||
73 | { | ||
74 | arg = args[index]; | ||
75 | // check for shape | ||
76 | if (arg.StartsWith("-rec=") || arg.StartsWith("-ell=")) | ||
77 | { | ||
78 | if (data.shape != String.Empty) | ||
79 | { | ||
80 | result = "Only 1 '-rec' or '-ell' parameter is permitted."; | ||
81 | } | ||
82 | else | ||
83 | { | ||
84 | data.shape = arg.StartsWith("-ell=") ? "ellipse" : "rectangle"; | ||
85 | val = arg.Substring(arg.IndexOf("=") + 1); | ||
86 | string[] coords = val.Split(new char[] {','}); | ||
87 | if ((coords.Length < 3) || (coords.Length > 4)) | ||
88 | { | ||
89 | result = String.Format("Bad format for shape parameter {0}", arg); | ||
90 | } | ||
91 | else | ||
92 | { | ||
93 | result = this.parseInt(coords[0], out data.x0); | ||
94 | if (result == String.Empty) | ||
95 | { | ||
96 | result = this.parseInt(coords[1], out data.y0); | ||
97 | } | ||
98 | if (result == String.Empty) | ||
99 | { | ||
100 | result = this.parseInt(coords[2], out data.dx); | ||
101 | } | ||
102 | if (result == String.Empty) | ||
103 | { | ||
104 | if (coords.Length == 4) | ||
105 | { | ||
106 | result = this.parseInt(coords[3], out data.dy); | ||
107 | } | ||
108 | else | ||
109 | { | ||
110 | data.dy = data.dx; | ||
111 | } | ||
112 | } | ||
113 | if (result == String.Empty) | ||
114 | { | ||
115 | if ((data.dx <= 0) || (data.dy <= 0)) | ||
116 | { | ||
117 | result = "Shape sizes must be positive integers"; | ||
118 | } | ||
119 | } | ||
120 | else | ||
121 | { | ||
122 | result = String.Format("Bad value in shape parameters {0}", arg); | ||
123 | } | ||
124 | } | ||
125 | } | ||
126 | } | ||
127 | else if (arg.StartsWith("-taper=")) | ||
128 | { | ||
129 | if (data.bevel != String.Empty) | ||
130 | { | ||
131 | result = "Only 1 '-taper' parameter is permitted."; | ||
132 | } | ||
133 | else | ||
134 | { | ||
135 | data.bevel = "taper"; | ||
136 | val = arg.Substring(arg.IndexOf("=") + 1); | ||
137 | result = this.parseFloat(val, out data.bevelevation); | ||
138 | if (result != String.Empty) | ||
139 | { | ||
140 | result = String.Format("Bad format for taper parameter {0}", arg); | ||
141 | } | ||
142 | } | ||
143 | } | ||
144 | else | ||
145 | { | ||
146 | result = String.Format("Unrecognized parameter {0}", arg); | ||
147 | } | ||
148 | } | ||
149 | } | ||
150 | return result; | ||
151 | } | ||
152 | |||
153 | protected string parseFloat(String s, out float f) | ||
154 | { | ||
155 | string result; | ||
156 | double d; | ||
157 | if (Double.TryParse(s, out d)) | ||
158 | { | ||
159 | try | ||
160 | { | ||
161 | f = (float)d; | ||
162 | result = String.Empty; | ||
163 | } | ||
164 | catch(InvalidCastException) | ||
165 | { | ||
166 | result = String.Format("{0} is invalid", s); | ||
167 | f = -1.0f; | ||
168 | } | ||
169 | } | ||
170 | else | ||
171 | { | ||
172 | f = -1.0f; | ||
173 | result = String.Format("{0} is invalid", s); | ||
174 | } | ||
175 | return result; | ||
176 | } | ||
177 | |||
178 | protected string parseInt(String s, out int i) | ||
179 | { | ||
180 | string result; | ||
181 | if (Int32.TryParse(s, out i)) | ||
182 | { | ||
183 | result = String.Empty; | ||
184 | } | ||
185 | else | ||
186 | { | ||
187 | result = String.Format("{0} is invalid", s); | ||
188 | } | ||
189 | return result; | ||
190 | } | ||
191 | |||
192 | protected void applyModification(ITerrainChannel map, TerrainModifierData data) | ||
193 | { | ||
194 | bool[,] mask; | ||
195 | int xMax; | ||
196 | int yMax; | ||
197 | int xMid; | ||
198 | int yMid; | ||
199 | if (data.shape == "ellipse") | ||
200 | { | ||
201 | mask = this.ellipticalMask(data.dx, data.dy); | ||
202 | xMax = mask.GetLength(0); | ||
203 | yMax = mask.GetLength(1); | ||
204 | xMid = xMax / 2 + xMax % 2; | ||
205 | yMid = yMax / 2 + yMax % 2; | ||
206 | } | ||
207 | else | ||
208 | { | ||
209 | mask = this.rectangularMask(data.dx, data.dy); | ||
210 | xMax = mask.GetLength(0); | ||
211 | yMax = mask.GetLength(1); | ||
212 | xMid = 0; | ||
213 | yMid = 0; | ||
214 | } | ||
215 | // m_log.DebugFormat("Apply {0} mask {1}x{2} @ {3},{4}", data.shape, xMax, yMax, xMid, yMid); | ||
216 | double[,] buffer = map.GetDoubles(); | ||
217 | int yDim = yMax; | ||
218 | while(--yDim >= 0) | ||
219 | { | ||
220 | int yPos = data.y0 + yDim - yMid; | ||
221 | if ((yPos >= 0) && (yPos < map.Height)) | ||
222 | { | ||
223 | int xDim = xMax; | ||
224 | while(--xDim >= 0) | ||
225 | { | ||
226 | int xPos = data.x0 + xDim - xMid; | ||
227 | if ((xPos >= 0) && (xPos < map.Width) && (mask[xDim, yDim])) | ||
228 | { | ||
229 | double endElevation = this.operate(buffer, data, xPos, yPos); | ||
230 | map[xPos, yPos] = endElevation; | ||
231 | } | ||
232 | } | ||
233 | } | ||
234 | } | ||
235 | } | ||
236 | |||
237 | protected double computeBevel(TerrainModifierData data, int x, int y) | ||
238 | { | ||
239 | int deltaX; | ||
240 | int deltaY; | ||
241 | int xMax; | ||
242 | int yMax; | ||
243 | double factor; | ||
244 | if (data.bevel == "taper") | ||
245 | { | ||
246 | if (data.shape == "ellipse") | ||
247 | { | ||
248 | deltaX = x - data.x0; | ||
249 | deltaY = y - data.y0; | ||
250 | xMax = data.dx; | ||
251 | yMax = data.dy; | ||
252 | factor = (double)((deltaX * deltaX) + (deltaY * deltaY)); | ||
253 | factor /= ((xMax * xMax) + (yMax * yMax)); | ||
254 | } | ||
255 | else | ||
256 | { | ||
257 | // pyramid | ||
258 | xMax = data.dx / 2 + data.dx % 2; | ||
259 | yMax = data.dy / 2 + data.dy % 2; | ||
260 | deltaX = Math.Abs(data.x0 + xMax - x); | ||
261 | deltaY = Math.Abs(data.y0 + yMax - y); | ||
262 | factor = Math.Max(((double)(deltaY) / yMax), ((double)(deltaX) / xMax)); | ||
263 | } | ||
264 | } | ||
265 | else | ||
266 | { | ||
267 | factor = 0.0; | ||
268 | } | ||
269 | return factor; | ||
270 | } | ||
271 | |||
272 | private bool[,] rectangularMask(int xSize, int ySize) | ||
273 | { | ||
274 | bool[,] mask = new bool[xSize, ySize]; | ||
275 | int yPos = ySize; | ||
276 | while(--yPos >= 0) | ||
277 | { | ||
278 | int xPos = xSize; | ||
279 | while(--xPos >= 0) | ||
280 | { | ||
281 | mask[xPos, yPos] = true; | ||
282 | } | ||
283 | } | ||
284 | return mask; | ||
285 | } | ||
286 | |||
287 | /* | ||
288 | * Fast ellipse-based derivative of Bresenham algorithm. | ||
289 | * https://web.archive.org/web/20120225095359/http://homepage.smc.edu/kennedy_john/belipse.pdf | ||
290 | */ | ||
291 | private bool[,] ellipticalMask(int xRadius, int yRadius) | ||
292 | { | ||
293 | long twoASquared = 2L * xRadius * xRadius; | ||
294 | long twoBSquared = 2L * yRadius * yRadius; | ||
295 | |||
296 | bool[,] mask = new bool[2 * xRadius + 1, 2 * yRadius + 1]; | ||
297 | |||
298 | long ellipseError = 0L; | ||
299 | long stoppingX = twoBSquared * xRadius; | ||
300 | long stoppingY = 0L; | ||
301 | long xChange = yRadius * yRadius * (1L - 2L * xRadius); | ||
302 | long yChange = xRadius * xRadius; | ||
303 | |||
304 | int xPos = xRadius; | ||
305 | int yPos = 0; | ||
306 | |||
307 | // first set of points | ||
308 | while(stoppingX >= stoppingY) | ||
309 | { | ||
310 | int yUpper = yRadius + yPos; | ||
311 | int yLower = yRadius - yPos; | ||
312 | // fill in the mask | ||
313 | int xNow = xPos; | ||
314 | while(xNow >= 0) | ||
315 | { | ||
316 | mask[xRadius + xNow, yUpper] = true; | ||
317 | mask[xRadius - xNow, yUpper] = true; | ||
318 | mask[xRadius + xNow, yLower] = true; | ||
319 | mask[xRadius - xNow, yLower] = true; | ||
320 | --xNow; | ||
321 | } | ||
322 | yPos++; | ||
323 | stoppingY += twoASquared; | ||
324 | ellipseError += yChange; | ||
325 | yChange += twoASquared; | ||
326 | if ((2L * ellipseError + xChange) > 0L) | ||
327 | { | ||
328 | xPos--; | ||
329 | stoppingX -= twoBSquared; | ||
330 | ellipseError += xChange; | ||
331 | xChange += twoBSquared; | ||
332 | } | ||
333 | } | ||
334 | |||
335 | // second set of points | ||
336 | xPos = 0; | ||
337 | yPos = yRadius; | ||
338 | xChange = yRadius * yRadius; | ||
339 | yChange = xRadius * xRadius * (1L - 2L * yRadius); | ||
340 | |||
341 | ellipseError = 0L; | ||
342 | stoppingX = 0L; | ||
343 | stoppingY = twoASquared * yRadius; | ||
344 | |||
345 | while(stoppingX <= stoppingY) | ||
346 | { | ||
347 | int xUpper = xRadius + xPos; | ||
348 | int xLower = xRadius - xPos; | ||
349 | // fill in the mask | ||
350 | int yNow = yPos; | ||
351 | while(yNow >= 0) | ||
352 | { | ||
353 | mask[xUpper, yRadius + yNow] = true; | ||
354 | mask[xUpper, yRadius - yNow] = true; | ||
355 | mask[xLower, yRadius + yNow] = true; | ||
356 | mask[xLower, yRadius - yNow] = true; | ||
357 | --yNow; | ||
358 | } | ||
359 | xPos++; | ||
360 | stoppingX += twoBSquared; | ||
361 | ellipseError += xChange; | ||
362 | xChange += twoBSquared; | ||
363 | if ((2L * ellipseError + yChange) > 0L) | ||
364 | { | ||
365 | yPos--; | ||
366 | stoppingY -= twoASquared; | ||
367 | ellipseError += yChange; | ||
368 | yChange += twoASquared; | ||
369 | } | ||
370 | } | ||
371 | return mask; | ||
372 | } | ||
373 | |||
374 | |||
375 | } | ||
376 | |||
377 | } | ||
378 | |||
diff --git a/OpenSim/Region/CoreModules/World/Terrain/TerrainModifierData.cs b/OpenSim/Region/CoreModules/World/Terrain/TerrainModifierData.cs new file mode 100644 index 0000000..4e0f8d7 --- /dev/null +++ b/OpenSim/Region/CoreModules/World/Terrain/TerrainModifierData.cs | |||
@@ -0,0 +1,17 @@ | |||
1 | using System; | ||
2 | |||
3 | namespace OpenSim.Region.CoreModules.World.Terrain | ||
4 | { | ||
5 | public struct TerrainModifierData | ||
6 | { | ||
7 | public float elevation; | ||
8 | public string shape; | ||
9 | public int x0; | ||
10 | public int y0; | ||
11 | public int dx; | ||
12 | public int dy; | ||
13 | public string bevel; | ||
14 | public float bevelevation; | ||
15 | } | ||
16 | } | ||
17 | |||
diff --git a/OpenSim/Region/CoreModules/World/Terrain/TerrainModule.cs b/OpenSim/Region/CoreModules/World/Terrain/TerrainModule.cs index fd30c46..932652c 100644 --- a/OpenSim/Region/CoreModules/World/Terrain/TerrainModule.cs +++ b/OpenSim/Region/CoreModules/World/Terrain/TerrainModule.cs | |||
@@ -24,19 +24,24 @@ | |||
24 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | 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. | 25 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
26 | */ | 26 | */ |
27 | |||
28 | using System; | 27 | using System; |
29 | using System.Collections.Generic; | 28 | using System.Collections.Generic; |
30 | using System.IO; | 29 | using System.IO; |
31 | using System.Reflection; | 30 | using System.Reflection; |
32 | using System.Net; | 31 | using System.Net; |
32 | |||
33 | using log4net; | 33 | using log4net; |
34 | using Nini.Config; | 34 | using Nini.Config; |
35 | |||
35 | using OpenMetaverse; | 36 | using OpenMetaverse; |
36 | using Mono.Addins; | 37 | using Mono.Addins; |
38 | |||
39 | using OpenSim.Data; | ||
37 | using OpenSim.Framework; | 40 | using OpenSim.Framework; |
41 | using OpenSim.Framework.Console; | ||
38 | using OpenSim.Region.CoreModules.Framework.InterfaceCommander; | 42 | using OpenSim.Region.CoreModules.Framework.InterfaceCommander; |
39 | using OpenSim.Region.CoreModules.World.Terrain.FileLoaders; | 43 | using OpenSim.Region.CoreModules.World.Terrain.FileLoaders; |
44 | using OpenSim.Region.CoreModules.World.Terrain.Modifiers; | ||
40 | using OpenSim.Region.CoreModules.World.Terrain.FloodBrushes; | 45 | using OpenSim.Region.CoreModules.World.Terrain.FloodBrushes; |
41 | using OpenSim.Region.CoreModules.World.Terrain.PaintBrushes; | 46 | using OpenSim.Region.CoreModules.World.Terrain.PaintBrushes; |
42 | using OpenSim.Region.Framework.Interfaces; | 47 | using OpenSim.Region.Framework.Interfaces; |
@@ -70,26 +75,112 @@ namespace OpenSim.Region.CoreModules.World.Terrain | |||
70 | #endregion | 75 | #endregion |
71 | 76 | ||
72 | private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); | 77 | private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); |
73 | |||
74 | private readonly Commander m_commander = new Commander("terrain"); | ||
75 | 78 | ||
79 | #pragma warning disable 414 | ||
80 | private static readonly string LogHeader = "[TERRAIN MODULE]"; | ||
81 | #pragma warning restore 414 | ||
82 | |||
83 | private readonly Commander m_commander = new Commander("terrain"); | ||
76 | private readonly Dictionary<StandardTerrainEffects, ITerrainFloodEffect> m_floodeffects = | 84 | private readonly Dictionary<StandardTerrainEffects, ITerrainFloodEffect> m_floodeffects = |
77 | new Dictionary<StandardTerrainEffects, ITerrainFloodEffect>(); | 85 | new Dictionary<StandardTerrainEffects, ITerrainFloodEffect>(); |
78 | |||
79 | private readonly Dictionary<string, ITerrainLoader> m_loaders = new Dictionary<string, ITerrainLoader>(); | 86 | private readonly Dictionary<string, ITerrainLoader> m_loaders = new Dictionary<string, ITerrainLoader>(); |
80 | |||
81 | private readonly Dictionary<StandardTerrainEffects, ITerrainPaintableEffect> m_painteffects = | 87 | private readonly Dictionary<StandardTerrainEffects, ITerrainPaintableEffect> m_painteffects = |
82 | new Dictionary<StandardTerrainEffects, ITerrainPaintableEffect>(); | 88 | new Dictionary<StandardTerrainEffects, ITerrainPaintableEffect>(); |
83 | |||
84 | private ITerrainChannel m_channel; | ||
85 | private Dictionary<string, ITerrainEffect> m_plugineffects; | 89 | private Dictionary<string, ITerrainEffect> m_plugineffects; |
90 | private Dictionary<string, ITerrainModifier> m_modifyOperations = | ||
91 | new Dictionary<string, ITerrainModifier>(); | ||
92 | private ITerrainChannel m_channel; | ||
86 | private ITerrainChannel m_revert; | 93 | private ITerrainChannel m_revert; |
87 | private Scene m_scene; | 94 | private Scene m_scene; |
88 | private volatile bool m_tainted; | 95 | private volatile bool m_tainted; |
89 | private readonly Stack<LandUndoState> m_undo = new Stack<LandUndoState>(5); | 96 | private readonly Stack<LandUndoState> m_undo = new Stack<LandUndoState>(5); |
90 | |||
91 | private String m_InitialTerrain = "pinhead-island"; | 97 | private String m_InitialTerrain = "pinhead-island"; |
92 | 98 | ||
99 | // If true, send terrain patch updates to clients based on their view distance | ||
100 | private bool m_sendTerrainUpdatesByViewDistance = true; | ||
101 | |||
102 | // Class to keep the per client collection of terrain patches that must be sent. | ||
103 | // A patch is set to 'true' meaning it should be sent to the client. Once the | ||
104 | // patch packet is queued to the client, the bit for that patch is set to 'false'. | ||
105 | private class PatchUpdates | ||
106 | { | ||
107 | private bool[,] updated; // for each patch, whether it needs to be sent to this client | ||
108 | private int updateCount; // number of patches that need to be sent | ||
109 | public ScenePresence Presence; // a reference to the client to send to | ||
110 | public TerrainData Terrain; // reference to the underlying terrain | ||
111 | public PatchUpdates(TerrainData terrData, ScenePresence pPresence) | ||
112 | { | ||
113 | updated = new bool[terrData.SizeX / Constants.TerrainPatchSize, terrData.SizeY / Constants.TerrainPatchSize]; | ||
114 | updateCount = 0; | ||
115 | Presence = pPresence; | ||
116 | Terrain = terrData; | ||
117 | // Initially, send all patches to the client | ||
118 | SetAll(true); | ||
119 | } | ||
120 | // Returns 'true' if there are any patches marked for sending | ||
121 | public bool HasUpdates() | ||
122 | { | ||
123 | return (updateCount > 0); | ||
124 | } | ||
125 | |||
126 | public void SetByXY(int x, int y, bool state) | ||
127 | { | ||
128 | this.SetByPatch(x / Constants.TerrainPatchSize, y / Constants.TerrainPatchSize, state); | ||
129 | } | ||
130 | |||
131 | public bool GetByPatch(int patchX, int patchY) | ||
132 | { | ||
133 | return updated[patchX, patchY]; | ||
134 | } | ||
135 | |||
136 | public void SetByPatch(int patchX, int patchY, bool state) | ||
137 | { | ||
138 | bool prevState = updated[patchX, patchY]; | ||
139 | if (!prevState && state) | ||
140 | updateCount++; | ||
141 | if (prevState && !state) | ||
142 | updateCount--; | ||
143 | updated[patchX, patchY] = state; | ||
144 | } | ||
145 | |||
146 | public void SetAll(bool state) | ||
147 | { | ||
148 | updateCount = 0; | ||
149 | for(int xx = 0; xx < updated.GetLength(0); xx++) | ||
150 | for(int yy = 0; yy < updated.GetLength(1); yy++) | ||
151 | updated[xx, yy] = state; | ||
152 | if (state) | ||
153 | updateCount = updated.GetLength(0) * updated.GetLength(1); | ||
154 | } | ||
155 | // Logically OR's the terrain data's patch taint map into this client's update map. | ||
156 | public void SetAll(TerrainData terrData) | ||
157 | { | ||
158 | if (updated.GetLength(0) != (terrData.SizeX / Constants.TerrainPatchSize) | ||
159 | || updated.GetLength(1) != (terrData.SizeY / Constants.TerrainPatchSize)) | ||
160 | { | ||
161 | throw new Exception( | ||
162 | String.Format("{0} PatchUpdates.SetAll: patch array not same size as terrain. arr=<{1},{2}>, terr=<{3},{4}>", | ||
163 | LogHeader, updated.GetLength(0), updated.GetLength(1), | ||
164 | terrData.SizeX / Constants.TerrainPatchSize, terrData.SizeY / Constants.TerrainPatchSize) | ||
165 | ); | ||
166 | } | ||
167 | for(int xx = 0; xx < terrData.SizeX; xx += Constants.TerrainPatchSize) | ||
168 | { | ||
169 | for(int yy = 0; yy < terrData.SizeY; yy += Constants.TerrainPatchSize) | ||
170 | { | ||
171 | // Only set tainted. The patch bit may be set if the patch was to be sent later. | ||
172 | if (terrData.IsTaintedAt(xx, yy, false)) | ||
173 | { | ||
174 | this.SetByXY(xx, yy, true); | ||
175 | } | ||
176 | } | ||
177 | } | ||
178 | } | ||
179 | } | ||
180 | |||
181 | // The flags of which terrain patches to send for each of the ScenePresence's | ||
182 | private Dictionary<UUID, PatchUpdates> m_perClientPatchUpdates = new Dictionary<UUID, PatchUpdates>(); | ||
183 | |||
93 | /// <summary> | 184 | /// <summary> |
94 | /// Human readable list of terrain file extensions that are supported. | 185 | /// Human readable list of terrain file extensions that are supported. |
95 | /// </summary> | 186 | /// </summary> |
@@ -100,8 +191,7 @@ namespace OpenSim.Region.CoreModules.World.Terrain | |||
100 | 191 | ||
101 | #region ICommandableModule Members | 192 | #region ICommandableModule Members |
102 | 193 | ||
103 | public ICommander CommandInterface | 194 | public ICommander CommandInterface { |
104 | { | ||
105 | get { return m_commander; } | 195 | get { return m_commander; } |
106 | } | 196 | } |
107 | 197 | ||
@@ -118,7 +208,10 @@ namespace OpenSim.Region.CoreModules.World.Terrain | |||
118 | { | 208 | { |
119 | IConfig terrainConfig = config.Configs["Terrain"]; | 209 | IConfig terrainConfig = config.Configs["Terrain"]; |
120 | if (terrainConfig != null) | 210 | if (terrainConfig != null) |
211 | { | ||
121 | m_InitialTerrain = terrainConfig.GetString("InitialTerrain", m_InitialTerrain); | 212 | m_InitialTerrain = terrainConfig.GetString("InitialTerrain", m_InitialTerrain); |
213 | m_sendTerrainUpdatesByViewDistance = terrainConfig.GetBoolean("SendTerrainUpdatesByViewDistance", m_sendTerrainUpdatesByViewDistance); | ||
214 | } | ||
122 | } | 215 | } |
123 | 216 | ||
124 | public void AddRegion(Scene scene) | 217 | public void AddRegion(Scene scene) |
@@ -126,26 +219,28 @@ namespace OpenSim.Region.CoreModules.World.Terrain | |||
126 | m_scene = scene; | 219 | m_scene = scene; |
127 | 220 | ||
128 | // Install terrain module in the simulator | 221 | // Install terrain module in the simulator |
129 | lock (m_scene) | 222 | lock(m_scene) |
130 | { | 223 | { |
131 | if (m_scene.Heightmap == null) | 224 | if (m_scene.Heightmap == null) |
132 | { | 225 | { |
133 | m_channel = new TerrainChannel(m_InitialTerrain); | 226 | m_channel = new TerrainChannel(m_InitialTerrain, (int)m_scene.RegionInfo.RegionSizeX, |
227 | (int)m_scene.RegionInfo.RegionSizeY, | ||
228 | (int)m_scene.RegionInfo.RegionSizeZ); | ||
134 | m_scene.Heightmap = m_channel; | 229 | m_scene.Heightmap = m_channel; |
135 | m_revert = new TerrainChannel(); | ||
136 | UpdateRevertMap(); | 230 | UpdateRevertMap(); |
137 | } | 231 | } |
138 | else | 232 | else |
139 | { | 233 | { |
140 | m_channel = m_scene.Heightmap; | 234 | m_channel = m_scene.Heightmap; |
141 | m_revert = new TerrainChannel(); | ||
142 | UpdateRevertMap(); | 235 | UpdateRevertMap(); |
143 | } | 236 | } |
144 | 237 | ||
145 | m_scene.RegisterModuleInterface<ITerrainModule>(this); | 238 | m_scene.RegisterModuleInterface<ITerrainModule>(this); |
146 | m_scene.EventManager.OnNewClient += EventManager_OnNewClient; | 239 | m_scene.EventManager.OnNewClient += EventManager_OnNewClient; |
240 | m_scene.EventManager.OnClientClosed += EventManager_OnClientClosed; | ||
147 | m_scene.EventManager.OnPluginConsole += EventManager_OnPluginConsole; | 241 | m_scene.EventManager.OnPluginConsole += EventManager_OnPluginConsole; |
148 | m_scene.EventManager.OnTerrainTick += EventManager_OnTerrainTick; | 242 | m_scene.EventManager.OnTerrainTick += EventManager_OnTerrainTick; |
243 | m_scene.EventManager.OnFrame += EventManager_OnFrame; | ||
149 | } | 244 | } |
150 | 245 | ||
151 | InstallDefaultEffects(); | 246 | InstallDefaultEffects(); |
@@ -156,7 +251,7 @@ namespace OpenSim.Region.CoreModules.World.Terrain | |||
156 | string supportedFilesSeparatorForTileSave = ""; | 251 | string supportedFilesSeparatorForTileSave = ""; |
157 | 252 | ||
158 | m_supportFileExtensionsForTileSave = ""; | 253 | m_supportFileExtensionsForTileSave = ""; |
159 | foreach (KeyValuePair<string, ITerrainLoader> loader in m_loaders) | 254 | foreach(KeyValuePair<string, ITerrainLoader> loader in m_loaders) |
160 | { | 255 | { |
161 | m_supportedFileExtensions += supportedFilesSeparator + loader.Key + " (" + loader.Value + ")"; | 256 | m_supportedFileExtensions += supportedFilesSeparator + loader.Key + " (" + loader.Value + ")"; |
162 | supportedFilesSeparator = ", "; | 257 | supportedFilesSeparator = ", "; |
@@ -179,13 +274,15 @@ namespace OpenSim.Region.CoreModules.World.Terrain | |||
179 | 274 | ||
180 | public void RemoveRegion(Scene scene) | 275 | public void RemoveRegion(Scene scene) |
181 | { | 276 | { |
182 | lock (m_scene) | 277 | lock(m_scene) |
183 | { | 278 | { |
184 | // remove the commands | 279 | // remove the commands |
185 | m_scene.UnregisterModuleCommander(m_commander.Name); | 280 | m_scene.UnregisterModuleCommander(m_commander.Name); |
186 | // remove the event-handlers | 281 | // remove the event-handlers |
282 | m_scene.EventManager.OnFrame -= EventManager_OnFrame; | ||
187 | m_scene.EventManager.OnTerrainTick -= EventManager_OnTerrainTick; | 283 | m_scene.EventManager.OnTerrainTick -= EventManager_OnTerrainTick; |
188 | m_scene.EventManager.OnPluginConsole -= EventManager_OnPluginConsole; | 284 | m_scene.EventManager.OnPluginConsole -= EventManager_OnPluginConsole; |
285 | m_scene.EventManager.OnClientClosed -= EventManager_OnClientClosed; | ||
189 | m_scene.EventManager.OnNewClient -= EventManager_OnNewClient; | 286 | m_scene.EventManager.OnNewClient -= EventManager_OnNewClient; |
190 | // remove the interface | 287 | // remove the interface |
191 | m_scene.UnregisterModuleInterface<ITerrainModule>(this); | 288 | m_scene.UnregisterModuleInterface<ITerrainModule>(this); |
@@ -196,13 +293,11 @@ namespace OpenSim.Region.CoreModules.World.Terrain | |||
196 | { | 293 | { |
197 | } | 294 | } |
198 | 295 | ||
199 | public Type ReplaceableInterface | 296 | public Type ReplaceableInterface { |
200 | { | ||
201 | get { return null; } | 297 | get { return null; } |
202 | } | 298 | } |
203 | 299 | ||
204 | public string Name | 300 | public string Name { |
205 | { | ||
206 | get { return "TerrainModule"; } | 301 | get { return "TerrainModule"; } |
207 | } | 302 | } |
208 | 303 | ||
@@ -221,47 +316,46 @@ namespace OpenSim.Region.CoreModules.World.Terrain | |||
221 | /// <param name="filename">Filename to terrain file. Type is determined by extension.</param> | 316 | /// <param name="filename">Filename to terrain file. Type is determined by extension.</param> |
222 | public void LoadFromFile(string filename) | 317 | public void LoadFromFile(string filename) |
223 | { | 318 | { |
224 | foreach (KeyValuePair<string, ITerrainLoader> loader in m_loaders) | 319 | foreach(KeyValuePair<string, ITerrainLoader> loader in m_loaders) |
225 | { | 320 | { |
226 | if (filename.EndsWith(loader.Key)) | 321 | if (filename.EndsWith(loader.Key)) |
227 | { | 322 | { |
228 | lock (m_scene) | 323 | lock(m_scene) |
229 | { | 324 | { |
230 | try | 325 | try |
231 | { | 326 | { |
232 | ITerrainChannel channel = loader.Value.LoadFile(filename); | 327 | ITerrainChannel channel = loader.Value.LoadFile(filename); |
233 | if (channel.Width != Constants.RegionSize || channel.Height != Constants.RegionSize) | 328 | if (channel.Width != m_scene.RegionInfo.RegionSizeX || channel.Height != m_scene.RegionInfo.RegionSizeY) |
234 | { | 329 | { |
235 | // TerrainChannel expects a RegionSize x RegionSize map, currently | 330 | // TerrainChannel expects a RegionSize x RegionSize map, currently |
236 | throw new ArgumentException(String.Format("wrong size, use a file with size {0} x {1}", | 331 | throw new ArgumentException(String.Format("wrong size, use a file with size {0} x {1}", |
237 | Constants.RegionSize, Constants.RegionSize)); | 332 | m_scene.RegionInfo.RegionSizeX, m_scene.RegionInfo.RegionSizeY)); |
238 | } | 333 | } |
239 | m_log.DebugFormat("[TERRAIN]: Loaded terrain, wd/ht: {0}/{1}", channel.Width, channel.Height); | 334 | m_log.DebugFormat("[TERRAIN]: Loaded terrain, wd/ht: {0}/{1}", channel.Width, channel.Height); |
240 | m_scene.Heightmap = channel; | 335 | m_scene.Heightmap = channel; |
241 | m_channel = channel; | 336 | m_channel = channel; |
242 | UpdateRevertMap(); | 337 | UpdateRevertMap(); |
243 | } | 338 | } |
244 | catch (NotImplementedException) | 339 | catch(NotImplementedException) |
245 | { | 340 | { |
246 | m_log.Error("[TERRAIN]: Unable to load heightmap, the " + loader.Value + | 341 | m_log.Error("[TERRAIN]: Unable to load heightmap, the " + loader.Value + |
247 | " parser does not support file loading. (May be save only)"); | 342 | " parser does not support file loading. (May be save only)"); |
248 | throw new TerrainException(String.Format("unable to load heightmap: parser {0} does not support loading", loader.Value)); | 343 | throw new TerrainException(String.Format("unable to load heightmap: parser {0} does not support loading", loader.Value)); |
249 | } | 344 | } |
250 | catch (FileNotFoundException) | 345 | catch(FileNotFoundException) |
251 | { | 346 | { |
252 | m_log.Error( | 347 | m_log.Error( |
253 | "[TERRAIN]: Unable to load heightmap, file not found. (A directory permissions error may also cause this)"); | 348 | "[TERRAIN]: Unable to load heightmap, file not found. (A directory permissions error may also cause this)"); |
254 | throw new TerrainException( | 349 | throw new TerrainException( |
255 | String.Format("unable to load heightmap: file {0} not found (or permissions do not allow access", filename)); | 350 | String.Format("unable to load heightmap: file {0} not found (or permissions do not allow access", filename)); |
256 | } | 351 | } |
257 | catch (ArgumentException e) | 352 | catch(ArgumentException e) |
258 | { | 353 | { |
259 | m_log.ErrorFormat("[TERRAIN]: Unable to load heightmap: {0}", e.Message); | 354 | m_log.ErrorFormat("[TERRAIN]: Unable to load heightmap: {0}", e.Message); |
260 | throw new TerrainException( | 355 | throw new TerrainException( |
261 | String.Format("Unable to load heightmap: {0}", e.Message)); | 356 | String.Format("Unable to load heightmap: {0}", e.Message)); |
262 | } | 357 | } |
263 | } | 358 | } |
264 | CheckForTerrainUpdates(); | ||
265 | m_log.Info("[TERRAIN]: File (" + filename + ") loaded successfully"); | 359 | m_log.Info("[TERRAIN]: File (" + filename + ") loaded successfully"); |
266 | return; | 360 | return; |
267 | } | 361 | } |
@@ -279,7 +373,7 @@ namespace OpenSim.Region.CoreModules.World.Terrain | |||
279 | { | 373 | { |
280 | try | 374 | try |
281 | { | 375 | { |
282 | foreach (KeyValuePair<string, ITerrainLoader> loader in m_loaders) | 376 | foreach(KeyValuePair<string, ITerrainLoader> loader in m_loaders) |
283 | { | 377 | { |
284 | if (filename.EndsWith(loader.Key)) | 378 | if (filename.EndsWith(loader.Key)) |
285 | { | 379 | { |
@@ -289,7 +383,7 @@ namespace OpenSim.Region.CoreModules.World.Terrain | |||
289 | } | 383 | } |
290 | } | 384 | } |
291 | } | 385 | } |
292 | catch (IOException ioe) | 386 | catch(IOException ioe) |
293 | { | 387 | { |
294 | m_log.Error(String.Format("[TERRAIN]: Unable to save to {0}, {1}", filename, ioe.Message)); | 388 | m_log.Error(String.Format("[TERRAIN]: Unable to save to {0}, {1}", filename, ioe.Message)); |
295 | } | 389 | } |
@@ -309,27 +403,32 @@ namespace OpenSim.Region.CoreModules.World.Terrain | |||
309 | LoadFromStream(filename, URIFetch(pathToTerrainHeightmap)); | 403 | LoadFromStream(filename, URIFetch(pathToTerrainHeightmap)); |
310 | } | 404 | } |
311 | 405 | ||
406 | public void LoadFromStream(string filename, Stream stream) | ||
407 | { | ||
408 | LoadFromStream(filename, Vector3.Zero, 0f, Vector2.Zero, stream); | ||
409 | } | ||
410 | |||
312 | /// <summary> | 411 | /// <summary> |
313 | /// Loads a terrain file from a stream and installs it in the scene. | 412 | /// Loads a terrain file from a stream and installs it in the scene. |
314 | /// </summary> | 413 | /// </summary> |
315 | /// <param name="filename">Filename to terrain file. Type is determined by extension.</param> | 414 | /// <param name="filename">Filename to terrain file. Type is determined by extension.</param> |
316 | /// <param name="stream"></param> | 415 | /// <param name="stream"></param> |
317 | public void LoadFromStream(string filename, Stream stream) | 416 | public void LoadFromStream(string filename, Vector3 displacement, |
417 | float radianRotation, Vector2 rotationDisplacement, Stream stream) | ||
318 | { | 418 | { |
319 | foreach (KeyValuePair<string, ITerrainLoader> loader in m_loaders) | 419 | foreach(KeyValuePair<string, ITerrainLoader> loader in m_loaders) |
320 | { | 420 | { |
321 | if (filename.EndsWith(loader.Key)) | 421 | if (filename.EndsWith(loader.Key)) |
322 | { | 422 | { |
323 | lock (m_scene) | 423 | lock(m_scene) |
324 | { | 424 | { |
325 | try | 425 | try |
326 | { | 426 | { |
327 | ITerrainChannel channel = loader.Value.LoadStream(stream); | 427 | ITerrainChannel channel = loader.Value.LoadStream(stream); |
328 | m_scene.Heightmap = channel; | 428 | m_channel.Merge(channel, displacement, radianRotation, rotationDisplacement); |
329 | m_channel = channel; | ||
330 | UpdateRevertMap(); | 429 | UpdateRevertMap(); |
331 | } | 430 | } |
332 | catch (NotImplementedException) | 431 | catch(NotImplementedException) |
333 | { | 432 | { |
334 | m_log.Error("[TERRAIN]: Unable to load heightmap, the " + loader.Value + | 433 | m_log.Error("[TERRAIN]: Unable to load heightmap, the " + loader.Value + |
335 | " parser does not support file loading. (May be save only)"); | 434 | " parser does not support file loading. (May be save only)"); |
@@ -337,7 +436,6 @@ namespace OpenSim.Region.CoreModules.World.Terrain | |||
337 | } | 436 | } |
338 | } | 437 | } |
339 | 438 | ||
340 | CheckForTerrainUpdates(); | ||
341 | m_log.Info("[TERRAIN]: File (" + filename + ") loaded successfully"); | 439 | m_log.Info("[TERRAIN]: File (" + filename + ") loaded successfully"); |
342 | return; | 440 | return; |
343 | } | 441 | } |
@@ -390,7 +488,7 @@ namespace OpenSim.Region.CoreModules.World.Terrain | |||
390 | { | 488 | { |
391 | try | 489 | try |
392 | { | 490 | { |
393 | foreach (KeyValuePair<string, ITerrainLoader> loader in m_loaders) | 491 | foreach(KeyValuePair<string, ITerrainLoader> loader in m_loaders) |
394 | { | 492 | { |
395 | if (filename.EndsWith(loader.Key)) | 493 | if (filename.EndsWith(loader.Key)) |
396 | { | 494 | { |
@@ -399,18 +497,56 @@ namespace OpenSim.Region.CoreModules.World.Terrain | |||
399 | } | 497 | } |
400 | } | 498 | } |
401 | } | 499 | } |
402 | catch (NotImplementedException) | 500 | catch(NotImplementedException) |
403 | { | 501 | { |
404 | m_log.Error("Unable to save to " + filename + ", saving of this file format has not been implemented."); | 502 | m_log.Error("Unable to save to " + filename + ", saving of this file format has not been implemented."); |
405 | throw new TerrainException(String.Format("Unable to save heightmap: saving of this file format not implemented")); | 503 | throw new TerrainException(String.Format("Unable to save heightmap: saving of this file format not implemented")); |
406 | } | 504 | } |
407 | } | 505 | } |
408 | 506 | ||
409 | public void TaintTerrain () | 507 | // Someone diddled terrain outside the normal code paths. Set the taintedness for all clients. |
508 | // ITerrainModule.TaintTerrain() | ||
509 | public void TaintTerrain() | ||
410 | { | 510 | { |
411 | CheckForTerrainUpdates(); | 511 | lock(m_perClientPatchUpdates) |
512 | { | ||
513 | // Set the flags for all clients so the tainted patches will be sent out | ||
514 | foreach(PatchUpdates pups in m_perClientPatchUpdates.Values) | ||
515 | { | ||
516 | pups.SetAll(m_scene.Heightmap.GetTerrainData()); | ||
517 | } | ||
518 | } | ||
412 | } | 519 | } |
413 | 520 | ||
521 | // ITerrainModule.PushTerrain() | ||
522 | public void PushTerrain(IClientAPI pClient) | ||
523 | { | ||
524 | // If view distance based, set the modified patch bits and the frame event will send the updates | ||
525 | if (m_sendTerrainUpdatesByViewDistance) | ||
526 | { | ||
527 | ScenePresence presence = m_scene.GetScenePresence(pClient.AgentId); | ||
528 | if (presence != null) | ||
529 | { | ||
530 | lock(m_perClientPatchUpdates) | ||
531 | { | ||
532 | PatchUpdates pups; | ||
533 | if (!m_perClientPatchUpdates.TryGetValue(pClient.AgentId, out pups)) | ||
534 | { | ||
535 | // There is a ScenePresence without a send patch map. Create one. | ||
536 | pups = new PatchUpdates(m_scene.Heightmap.GetTerrainData(), presence); | ||
537 | m_perClientPatchUpdates.Add(presence.UUID, pups); | ||
538 | } | ||
539 | // By setting all to modified, the next update tick will send the patches | ||
540 | pups.SetAll(true); | ||
541 | } | ||
542 | } | ||
543 | } | ||
544 | else | ||
545 | { | ||
546 | // The traditional way is to call into the protocol stack to send them all. | ||
547 | pClient.SendLayerData(new float[10]); | ||
548 | } | ||
549 | } | ||
414 | #region Plugin Loading Methods | 550 | #region Plugin Loading Methods |
415 | 551 | ||
416 | private void LoadPlugins() | 552 | private void LoadPlugins() |
@@ -418,13 +554,13 @@ namespace OpenSim.Region.CoreModules.World.Terrain | |||
418 | m_plugineffects = new Dictionary<string, ITerrainEffect>(); | 554 | m_plugineffects = new Dictionary<string, ITerrainEffect>(); |
419 | LoadPlugins(Assembly.GetCallingAssembly()); | 555 | LoadPlugins(Assembly.GetCallingAssembly()); |
420 | string plugineffectsPath = "Terrain"; | 556 | string plugineffectsPath = "Terrain"; |
421 | 557 | ||
422 | // Load the files in the Terrain/ dir | 558 | // Load the files in the Terrain/ dir |
423 | if (!Directory.Exists(plugineffectsPath)) | 559 | if (!Directory.Exists(plugineffectsPath)) |
424 | return; | 560 | return; |
425 | 561 | ||
426 | string[] files = Directory.GetFiles(plugineffectsPath); | 562 | string[] files = Directory.GetFiles(plugineffectsPath); |
427 | foreach (string file in files) | 563 | foreach(string file in files) |
428 | { | 564 | { |
429 | m_log.Info("Loading effects in " + file); | 565 | m_log.Info("Loading effects in " + file); |
430 | try | 566 | try |
@@ -432,7 +568,7 @@ namespace OpenSim.Region.CoreModules.World.Terrain | |||
432 | Assembly library = Assembly.LoadFrom(file); | 568 | Assembly library = Assembly.LoadFrom(file); |
433 | LoadPlugins(library); | 569 | LoadPlugins(library); |
434 | } | 570 | } |
435 | catch (BadImageFormatException) | 571 | catch(BadImageFormatException) |
436 | { | 572 | { |
437 | } | 573 | } |
438 | } | 574 | } |
@@ -440,7 +576,7 @@ namespace OpenSim.Region.CoreModules.World.Terrain | |||
440 | 576 | ||
441 | private void LoadPlugins(Assembly library) | 577 | private void LoadPlugins(Assembly library) |
442 | { | 578 | { |
443 | foreach (Type pluginType in library.GetTypes()) | 579 | foreach(Type pluginType in library.GetTypes()) |
444 | { | 580 | { |
445 | try | 581 | try |
446 | { | 582 | { |
@@ -462,7 +598,7 @@ namespace OpenSim.Region.CoreModules.World.Terrain | |||
462 | m_log.Info("L ... " + typeName); | 598 | m_log.Info("L ... " + typeName); |
463 | } | 599 | } |
464 | } | 600 | } |
465 | catch (AmbiguousMatchException) | 601 | catch(AmbiguousMatchException) |
466 | { | 602 | { |
467 | } | 603 | } |
468 | } | 604 | } |
@@ -470,7 +606,7 @@ namespace OpenSim.Region.CoreModules.World.Terrain | |||
470 | 606 | ||
471 | public void InstallPlugin(string pluginName, ITerrainEffect effect) | 607 | public void InstallPlugin(string pluginName, ITerrainEffect effect) |
472 | { | 608 | { |
473 | lock (m_plugineffects) | 609 | lock(m_plugineffects) |
474 | { | 610 | { |
475 | if (!m_plugineffects.ContainsKey(pluginName)) | 611 | if (!m_plugineffects.ContainsKey(pluginName)) |
476 | { | 612 | { |
@@ -513,6 +649,15 @@ namespace OpenSim.Region.CoreModules.World.Terrain | |||
513 | m_floodeffects[StandardTerrainEffects.Flatten] = new FlattenArea(); | 649 | m_floodeffects[StandardTerrainEffects.Flatten] = new FlattenArea(); |
514 | m_floodeffects[StandardTerrainEffects.Revert] = new RevertArea(m_revert); | 650 | m_floodeffects[StandardTerrainEffects.Revert] = new RevertArea(m_revert); |
515 | 651 | ||
652 | // Terrain Modifier operations | ||
653 | m_modifyOperations["min"] = new MinModifier(this); | ||
654 | m_modifyOperations["max"] = new MaxModifier(this); | ||
655 | m_modifyOperations["raise"] = new RaiseModifier(this); | ||
656 | m_modifyOperations["lower"] = new LowerModifier(this); | ||
657 | m_modifyOperations["fill"] = new FillModifier(this); | ||
658 | m_modifyOperations["smooth"] = new SmoothModifier(this); | ||
659 | m_modifyOperations["noise"] = new NoiseModifier(this); | ||
660 | |||
516 | // Filesystem load/save loaders | 661 | // Filesystem load/save loaders |
517 | m_loaders[".r32"] = new RAW32(); | 662 | m_loaders[".r32"] = new RAW32(); |
518 | m_loaders[".f32"] = m_loaders[".r32"]; | 663 | m_loaders[".f32"] = m_loaders[".r32"]; |
@@ -532,6 +677,7 @@ namespace OpenSim.Region.CoreModules.World.Terrain | |||
532 | /// </summary> | 677 | /// </summary> |
533 | public void UpdateRevertMap() | 678 | public void UpdateRevertMap() |
534 | { | 679 | { |
680 | /* | ||
535 | int x; | 681 | int x; |
536 | for (x = 0; x < m_channel.Width; x++) | 682 | for (x = 0; x < m_channel.Width; x++) |
537 | { | 683 | { |
@@ -541,6 +687,8 @@ namespace OpenSim.Region.CoreModules.World.Terrain | |||
541 | m_revert[x, y] = m_channel[x, y]; | 687 | m_revert[x, y] = m_channel[x, y]; |
542 | } | 688 | } |
543 | } | 689 | } |
690 | */ | ||
691 | m_revert = m_channel.MakeCopy(); | ||
544 | } | 692 | } |
545 | 693 | ||
546 | /// <summary> | 694 | /// <summary> |
@@ -553,22 +701,22 @@ namespace OpenSim.Region.CoreModules.World.Terrain | |||
553 | /// <param name="fileStartY">Where to begin our slice</param> | 701 | /// <param name="fileStartY">Where to begin our slice</param> |
554 | public void LoadFromFile(string filename, int fileWidth, int fileHeight, int fileStartX, int fileStartY) | 702 | public void LoadFromFile(string filename, int fileWidth, int fileHeight, int fileStartX, int fileStartY) |
555 | { | 703 | { |
556 | int offsetX = (int) m_scene.RegionInfo.RegionLocX - fileStartX; | 704 | int offsetX = (int)m_scene.RegionInfo.RegionLocX - fileStartX; |
557 | int offsetY = (int) m_scene.RegionInfo.RegionLocY - fileStartY; | 705 | int offsetY = (int)m_scene.RegionInfo.RegionLocY - fileStartY; |
558 | 706 | ||
559 | if (offsetX >= 0 && offsetX < fileWidth && offsetY >= 0 && offsetY < fileHeight) | 707 | if (offsetX >= 0 && offsetX < fileWidth && offsetY >= 0 && offsetY < fileHeight) |
560 | { | 708 | { |
561 | // this region is included in the tile request | 709 | // this region is included in the tile request |
562 | foreach (KeyValuePair<string, ITerrainLoader> loader in m_loaders) | 710 | foreach(KeyValuePair<string, ITerrainLoader> loader in m_loaders) |
563 | { | 711 | { |
564 | if (filename.EndsWith(loader.Key)) | 712 | if (filename.EndsWith(loader.Key)) |
565 | { | 713 | { |
566 | lock (m_scene) | 714 | lock(m_scene) |
567 | { | 715 | { |
568 | ITerrainChannel channel = loader.Value.LoadFile(filename, offsetX, offsetY, | 716 | ITerrainChannel channel = loader.Value.LoadFile(filename, offsetX, offsetY, |
569 | fileWidth, fileHeight, | 717 | fileWidth, fileHeight, |
570 | (int) Constants.RegionSize, | 718 | (int)m_scene.RegionInfo.RegionSizeX, |
571 | (int) Constants.RegionSize); | 719 | (int)m_scene.RegionInfo.RegionSizeY); |
572 | m_scene.Heightmap = channel; | 720 | m_scene.Heightmap = channel; |
573 | m_channel = channel; | 721 | m_channel = channel; |
574 | UpdateRevertMap(); | 722 | UpdateRevertMap(); |
@@ -607,23 +755,23 @@ namespace OpenSim.Region.CoreModules.World.Terrain | |||
607 | } | 755 | } |
608 | 756 | ||
609 | // this region is included in the tile request | 757 | // this region is included in the tile request |
610 | foreach (KeyValuePair<string, ITerrainLoader> loader in m_loaders) | 758 | foreach(KeyValuePair<string, ITerrainLoader> loader in m_loaders) |
611 | { | 759 | { |
612 | if (filename.EndsWith(loader.Key) && loader.Value.SupportsTileSave()) | 760 | if (filename.EndsWith(loader.Key) && loader.Value.SupportsTileSave()) |
613 | { | 761 | { |
614 | lock (m_scene) | 762 | lock(m_scene) |
615 | { | 763 | { |
616 | loader.Value.SaveFile(m_channel, filename, offsetX, offsetY, | 764 | loader.Value.SaveFile(m_channel, filename, offsetX, offsetY, |
617 | fileWidth, fileHeight, | 765 | fileWidth, fileHeight, |
618 | (int)Constants.RegionSize, | 766 | (int)m_scene.RegionInfo.RegionSizeX, |
619 | (int)Constants.RegionSize); | 767 | (int)m_scene.RegionInfo.RegionSizeY); |
620 | 768 | ||
621 | MainConsole.Instance.OutputFormat( | 769 | MainConsole.Instance.OutputFormat( |
622 | "Saved terrain from ({0},{1}) to ({2},{3}) from {4} to {5}", | 770 | "Saved terrain from ({0},{1}) to ({2},{3}) from {4} to {5}", |
623 | fileStartX, fileStartY, fileStartX + fileWidth - 1, fileStartY + fileHeight - 1, | 771 | fileStartX, fileStartY, fileStartX + fileWidth - 1, fileStartY + fileHeight - 1, |
624 | m_scene.RegionInfo.RegionName, filename); | 772 | m_scene.RegionInfo.RegionName, filename); |
625 | } | 773 | } |
626 | 774 | ||
627 | return; | 775 | return; |
628 | } | 776 | } |
629 | } | 777 | } |
@@ -634,7 +782,44 @@ namespace OpenSim.Region.CoreModules.World.Terrain | |||
634 | } | 782 | } |
635 | 783 | ||
636 | /// <summary> | 784 | /// <summary> |
785 | /// Called before processing of every simulation frame. | ||
786 | /// This is used to check to see of any of the terrain is tainted and, if so, schedule | ||
787 | /// updates for all the presences. | ||
788 | /// This also checks to see if there are updates that need to be sent for each presence. | ||
789 | /// This is where the logic is to send terrain updates to clients. | ||
790 | /// </summary> | ||
791 | private void EventManager_OnFrame() | ||
792 | { | ||
793 | TerrainData terrData = m_channel.GetTerrainData(); | ||
794 | |||
795 | bool shouldTaint = false; | ||
796 | for(int x = 0; x < terrData.SizeX; x += Constants.TerrainPatchSize) | ||
797 | { | ||
798 | for(int y = 0; y < terrData.SizeY; y += Constants.TerrainPatchSize) | ||
799 | { | ||
800 | if (terrData.IsTaintedAt(x, y)) | ||
801 | { | ||
802 | // Found a patch that was modified. Push this flag into the clients. | ||
803 | SendToClients(terrData, x, y); | ||
804 | shouldTaint = true; | ||
805 | } | ||
806 | } | ||
807 | } | ||
808 | |||
809 | // This event also causes changes to be sent to the clients | ||
810 | CheckSendingPatchesToClients(); | ||
811 | |||
812 | // If things changes, generate some events | ||
813 | if (shouldTaint) | ||
814 | { | ||
815 | m_scene.EventManager.TriggerTerrainTainted(); | ||
816 | m_tainted = true; | ||
817 | } | ||
818 | } | ||
819 | |||
820 | /// <summary> | ||
637 | /// Performs updates to the region periodically, synchronising physics and other heightmap aware sections | 821 | /// Performs updates to the region periodically, synchronising physics and other heightmap aware sections |
822 | /// Called infrequently (like every 5 seconds or so). Best used for storing terrain. | ||
638 | /// </summary> | 823 | /// </summary> |
639 | private void EventManager_OnTerrainTick() | 824 | private void EventManager_OnTerrainTick() |
640 | { | 825 | { |
@@ -665,7 +850,7 @@ namespace OpenSim.Region.CoreModules.World.Terrain | |||
665 | 850 | ||
666 | string[] tmpArgs = new string[args.Length - 2]; | 851 | string[] tmpArgs = new string[args.Length - 2]; |
667 | int i; | 852 | int i; |
668 | for (i = 2; i < args.Length; i++) | 853 | for(i = 2; i < args.Length; i++) |
669 | tmpArgs[i - 2] = args[i]; | 854 | tmpArgs[i - 2] = args[i]; |
670 | 855 | ||
671 | m_commander.ProcessConsoleCommand(args[1], tmpArgs); | 856 | m_commander.ProcessConsoleCommand(args[1], tmpArgs); |
@@ -683,56 +868,50 @@ namespace OpenSim.Region.CoreModules.World.Terrain | |||
683 | client.OnLandUndo += client_OnLandUndo; | 868 | client.OnLandUndo += client_OnLandUndo; |
684 | client.OnUnackedTerrain += client_OnUnackedTerrain; | 869 | client.OnUnackedTerrain += client_OnUnackedTerrain; |
685 | } | 870 | } |
686 | 871 | ||
687 | /// <summary> | 872 | /// <summary> |
688 | /// Checks to see if the terrain has been modified since last check | 873 | /// Installs terrain brush hook to IClientAPI |
689 | /// but won't attempt to limit those changes to the limits specified in the estate settings | ||
690 | /// currently invoked by the command line operations in the region server only | ||
691 | /// </summary> | 874 | /// </summary> |
692 | private void CheckForTerrainUpdates() | 875 | /// <param name="client"></param> |
876 | private void EventManager_OnClientClosed(UUID client, Scene scene) | ||
693 | { | 877 | { |
694 | CheckForTerrainUpdates(false); | 878 | ScenePresence presence = scene.GetScenePresence(client); |
879 | if (presence != null) | ||
880 | { | ||
881 | presence.ControllingClient.OnModifyTerrain -= client_OnModifyTerrain; | ||
882 | presence.ControllingClient.OnBakeTerrain -= client_OnBakeTerrain; | ||
883 | presence.ControllingClient.OnLandUndo -= client_OnLandUndo; | ||
884 | presence.ControllingClient.OnUnackedTerrain -= client_OnUnackedTerrain; | ||
885 | } | ||
886 | |||
887 | lock(m_perClientPatchUpdates) | ||
888 | m_perClientPatchUpdates.Remove(client); | ||
695 | } | 889 | } |
696 | 890 | ||
697 | /// <summary> | 891 | /// <summary> |
698 | /// Checks to see if the terrain has been modified since last check. | 892 | /// Scan over changes in the terrain and limit height changes. This enforces the |
699 | /// If it has been modified, every all the terrain patches are sent to the client. | 893 | /// non-estate owner limits on rate of terrain editting. |
700 | /// If the call is asked to respect the estate settings for terrain_raise_limit and | 894 | /// Returns 'true' if any heights were limited. |
701 | /// terrain_lower_limit, it will clamp terrain updates between these values | ||
702 | /// currently invoked by client_OnModifyTerrain only and not the Commander interfaces | ||
703 | /// <param name="respectEstateSettings">should height map deltas be limited to the estate settings limits</param> | ||
704 | /// </summary> | 895 | /// </summary> |
705 | private void CheckForTerrainUpdates(bool respectEstateSettings) | 896 | private bool EnforceEstateLimits() |
706 | { | 897 | { |
707 | bool shouldTaint = false; | 898 | TerrainData terrData = m_channel.GetTerrainData(); |
708 | float[] serialised = m_channel.GetFloatsSerialised(); | 899 | |
709 | int x; | 900 | bool wasLimited = false; |
710 | for (x = 0; x < m_channel.Width; x += Constants.TerrainPatchSize) | 901 | for(int x = 0; x < terrData.SizeX; x += Constants.TerrainPatchSize) |
711 | { | 902 | { |
712 | int y; | 903 | for(int y = 0; y < terrData.SizeY; y += Constants.TerrainPatchSize) |
713 | for (y = 0; y < m_channel.Height; y += Constants.TerrainPatchSize) | ||
714 | { | 904 | { |
715 | if (m_channel.Tainted(x, y)) | 905 | if (terrData.IsTaintedAt(x, y, false /* clearOnTest */)) |
716 | { | 906 | { |
717 | // if we should respect the estate settings then | 907 | // If we should respect the estate settings then |
718 | // fixup and height deltas that don't respect them | 908 | // fixup and height deltas that don't respect them. |
719 | if (respectEstateSettings && LimitChannelChanges(x, y)) | 909 | // Note that LimitChannelChanges() modifies the TerrainChannel with the limited height values. |
720 | { | 910 | wasLimited |= LimitChannelChanges(terrData, x, y); |
721 | // this has been vetoed, so update | ||
722 | // what we are going to send to the client | ||
723 | serialised = m_channel.GetFloatsSerialised(); | ||
724 | } | ||
725 | |||
726 | SendToClients(serialised, x, y); | ||
727 | shouldTaint = true; | ||
728 | } | 911 | } |
729 | } | 912 | } |
730 | } | 913 | } |
731 | if (shouldTaint) | 914 | return wasLimited; |
732 | { | ||
733 | m_scene.EventManager.TriggerTerrainTainted(); | ||
734 | m_tainted = true; | ||
735 | } | ||
736 | } | 915 | } |
737 | 916 | ||
738 | /// <summary> | 917 | /// <summary> |
@@ -740,31 +919,30 @@ namespace OpenSim.Region.CoreModules.World.Terrain | |||
740 | /// are all within the current estate limits | 919 | /// are all within the current estate limits |
741 | /// <returns>true if changes were limited, false otherwise</returns> | 920 | /// <returns>true if changes were limited, false otherwise</returns> |
742 | /// </summary> | 921 | /// </summary> |
743 | private bool LimitChannelChanges(int xStart, int yStart) | 922 | private bool LimitChannelChanges(TerrainData terrData, int xStart, int yStart) |
744 | { | 923 | { |
745 | bool changesLimited = false; | 924 | bool changesLimited = false; |
746 | double minDelta = m_scene.RegionInfo.RegionSettings.TerrainLowerLimit; | 925 | float minDelta = (float)m_scene.RegionInfo.RegionSettings.TerrainLowerLimit; |
747 | double maxDelta = m_scene.RegionInfo.RegionSettings.TerrainRaiseLimit; | 926 | float maxDelta = (float)m_scene.RegionInfo.RegionSettings.TerrainRaiseLimit; |
748 | 927 | ||
749 | // loop through the height map for this patch and compare it against | 928 | // loop through the height map for this patch and compare it against |
750 | // the revert map | 929 | // the revert map |
751 | for (int x = xStart; x < xStart + Constants.TerrainPatchSize; x++) | 930 | for(int x = xStart; x < xStart + Constants.TerrainPatchSize; x++) |
752 | { | 931 | { |
753 | for (int y = yStart; y < yStart + Constants.TerrainPatchSize; y++) | 932 | for(int y = yStart; y < yStart + Constants.TerrainPatchSize; y++) |
754 | { | 933 | { |
755 | 934 | float requestedHeight = terrData[x, y]; | |
756 | double requestedHeight = m_channel[x, y]; | 935 | float bakedHeight = (float)m_revert[x, y]; |
757 | double bakedHeight = m_revert[x, y]; | 936 | float requestedDelta = requestedHeight - bakedHeight; |
758 | double requestedDelta = requestedHeight - bakedHeight; | ||
759 | 937 | ||
760 | if (requestedDelta > maxDelta) | 938 | if (requestedDelta > maxDelta) |
761 | { | 939 | { |
762 | m_channel[x, y] = bakedHeight + maxDelta; | 940 | terrData[x, y] = bakedHeight + maxDelta; |
763 | changesLimited = true; | 941 | changesLimited = true; |
764 | } | 942 | } |
765 | else if (requestedDelta < minDelta) | 943 | else if (requestedDelta < minDelta) |
766 | { | 944 | { |
767 | m_channel[x, y] = bakedHeight + minDelta; //as lower is a -ve delta | 945 | terrData[x, y] = bakedHeight + minDelta; //as lower is a -ve delta |
768 | changesLimited = true; | 946 | changesLimited = true; |
769 | } | 947 | } |
770 | } | 948 | } |
@@ -775,7 +953,7 @@ namespace OpenSim.Region.CoreModules.World.Terrain | |||
775 | 953 | ||
776 | private void client_OnLandUndo(IClientAPI client) | 954 | private void client_OnLandUndo(IClientAPI client) |
777 | { | 955 | { |
778 | lock (m_undo) | 956 | lock(m_undo) |
779 | { | 957 | { |
780 | if (m_undo.Count > 0) | 958 | if (m_undo.Count > 0) |
781 | { | 959 | { |
@@ -792,14 +970,177 @@ namespace OpenSim.Region.CoreModules.World.Terrain | |||
792 | /// <param name="serialised">A copy of the terrain as a 1D float array of size w*h</param> | 970 | /// <param name="serialised">A copy of the terrain as a 1D float array of size w*h</param> |
793 | /// <param name="x">The patch corner to send</param> | 971 | /// <param name="x">The patch corner to send</param> |
794 | /// <param name="y">The patch corner to send</param> | 972 | /// <param name="y">The patch corner to send</param> |
795 | private void SendToClients(float[] serialised, int x, int y) | 973 | private void SendToClients(TerrainData terrData, int x, int y) |
796 | { | 974 | { |
797 | m_scene.ForEachClient( | 975 | if (m_sendTerrainUpdatesByViewDistance) |
798 | delegate(IClientAPI controller) | 976 | { |
799 | { controller.SendLayerData( | 977 | // Add that this patch needs to be sent to the accounting for each client. |
800 | x / Constants.TerrainPatchSize, y / Constants.TerrainPatchSize, serialised); | 978 | lock(m_perClientPatchUpdates) |
979 | { | ||
980 | m_scene.ForEachScenePresence(presence => | ||
981 | { | ||
982 | PatchUpdates thisClientUpdates; | ||
983 | if (!m_perClientPatchUpdates.TryGetValue(presence.UUID, out thisClientUpdates)) | ||
984 | { | ||
985 | // There is a ScenePresence without a send patch map. Create one. | ||
986 | thisClientUpdates = new PatchUpdates(terrData, presence); | ||
987 | m_perClientPatchUpdates.Add(presence.UUID, thisClientUpdates); | ||
988 | } | ||
989 | thisClientUpdates.SetByXY(x, y, true); | ||
801 | } | 990 | } |
802 | ); | 991 | ); |
992 | } | ||
993 | } | ||
994 | else | ||
995 | { | ||
996 | // Legacy update sending where the update is sent out as soon as noticed | ||
997 | // We know the actual terrain data that is passed is ignored so this passes a dummy heightmap. | ||
998 | //float[] heightMap = terrData.GetFloatsSerialized(); | ||
999 | float[] heightMap = new float[10]; | ||
1000 | m_scene.ForEachClient( | ||
1001 | delegate(IClientAPI controller) | ||
1002 | { | ||
1003 | controller.SendLayerData(x / Constants.TerrainPatchSize, | ||
1004 | y / Constants.TerrainPatchSize, | ||
1005 | heightMap); | ||
1006 | } | ||
1007 | ); | ||
1008 | } | ||
1009 | } | ||
1010 | |||
1011 | private class PatchesToSend : IComparable<PatchesToSend> | ||
1012 | { | ||
1013 | public int PatchX; | ||
1014 | public int PatchY; | ||
1015 | public float Dist; | ||
1016 | |||
1017 | public PatchesToSend(int pX, int pY, float pDist) | ||
1018 | { | ||
1019 | PatchX = pX; | ||
1020 | PatchY = pY; | ||
1021 | Dist = pDist; | ||
1022 | } | ||
1023 | |||
1024 | public int CompareTo(PatchesToSend other) | ||
1025 | { | ||
1026 | return Dist.CompareTo(other.Dist); | ||
1027 | } | ||
1028 | } | ||
1029 | |||
1030 | // Called each frame time to see if there are any patches to send to any of the | ||
1031 | // ScenePresences. | ||
1032 | // We know this is only called if we are doing view distance patch sending so some | ||
1033 | // tests are not made. | ||
1034 | // Loop through all the per-client info and send any patches necessary. | ||
1035 | private void CheckSendingPatchesToClients() | ||
1036 | { | ||
1037 | lock(m_perClientPatchUpdates) | ||
1038 | { | ||
1039 | foreach(PatchUpdates pups in m_perClientPatchUpdates.Values) | ||
1040 | { | ||
1041 | if (pups.HasUpdates()) | ||
1042 | { | ||
1043 | // There is something that could be sent to this client. | ||
1044 | List<PatchesToSend> toSend = GetModifiedPatchesInViewDistance(pups); | ||
1045 | if (toSend.Count > 0) | ||
1046 | { | ||
1047 | // m_log.DebugFormat("{0} CheckSendingPatchesToClient: sending {1} patches to {2} in region {3}", | ||
1048 | // LogHeader, toSend.Count, pups.Presence.Name, m_scene.RegionInfo.RegionName); | ||
1049 | // Sort the patches to send by the distance from the presence | ||
1050 | toSend.Sort(); | ||
1051 | /* old way that sent individual patches | ||
1052 | foreach (PatchesToSend pts in toSend) | ||
1053 | { | ||
1054 | pups.Presence.ControllingClient.SendLayerData(pts.PatchX, pts.PatchY, null); | ||
1055 | // presence.ControllingClient.SendLayerData(xs.ToArray(), ys.ToArray(), null, TerrainPatch.LayerType.Land); | ||
1056 | } | ||
1057 | */ | ||
1058 | |||
1059 | // new way that sends all patches to the protocol so they can be sent in one block | ||
1060 | int[] xPieces = new int[toSend.Count]; | ||
1061 | int[] yPieces = new int[toSend.Count]; | ||
1062 | float[] patchPieces = new float[toSend.Count * 2]; | ||
1063 | int pieceIndex = 0; | ||
1064 | foreach(PatchesToSend pts in toSend) | ||
1065 | { | ||
1066 | patchPieces[pieceIndex++] = pts.PatchX; | ||
1067 | patchPieces[pieceIndex++] = pts.PatchY; | ||
1068 | } | ||
1069 | pups.Presence.ControllingClient.SendLayerData(-toSend.Count, 0, patchPieces); | ||
1070 | } | ||
1071 | } | ||
1072 | } | ||
1073 | } | ||
1074 | } | ||
1075 | |||
1076 | // Compute a list of modified patches that are within our view distance. | ||
1077 | private List<PatchesToSend> GetModifiedPatchesInViewDistance(PatchUpdates pups) | ||
1078 | { | ||
1079 | List<PatchesToSend> ret = new List<PatchesToSend>(); | ||
1080 | |||
1081 | ScenePresence presence = pups.Presence; | ||
1082 | if (presence == null) | ||
1083 | return ret; | ||
1084 | |||
1085 | Vector3 presencePos = presence.AbsolutePosition; | ||
1086 | |||
1087 | // Before this distance check, the whole region just showed up. Adding the distance | ||
1088 | // check causes different things to happen for the current and adjacent regions. | ||
1089 | // So, to keep legacy views, if the region is legacy sized, don't do distance check. | ||
1090 | bool isLegacySizedRegion = pups.Terrain.SizeX == Constants.RegionSize && pups.Terrain.SizeY == Constants.RegionSize; | ||
1091 | bool shouldCheckViewDistance = m_sendTerrainUpdatesByViewDistance && !isLegacySizedRegion; | ||
1092 | |||
1093 | int startX = 0; | ||
1094 | int endX = (int)m_scene.RegionInfo.RegionSizeX / Constants.TerrainPatchSize; | ||
1095 | int startY = 0; | ||
1096 | int endY = (int)m_scene.RegionInfo.RegionSizeY / Constants.TerrainPatchSize; | ||
1097 | |||
1098 | // The following only reduces the size of area scanned for updates. Only significant for very large varregions. | ||
1099 | if (shouldCheckViewDistance) | ||
1100 | { | ||
1101 | // Compute the area of patches within our draw distance | ||
1102 | startX = (((int)(presencePos.X - presence.DrawDistance)) / Constants.TerrainPatchSize) - 2; | ||
1103 | startX = Math.Max(startX, 0); | ||
1104 | startX = Math.Min(startX, (int)m_scene.RegionInfo.RegionSizeX / Constants.TerrainPatchSize); | ||
1105 | startY = (((int)(presencePos.Y - presence.DrawDistance)) / Constants.TerrainPatchSize) - 2; | ||
1106 | startY = Math.Max(startY, 0); | ||
1107 | startY = Math.Min(startY, (int)m_scene.RegionInfo.RegionSizeY / Constants.TerrainPatchSize); | ||
1108 | endX = (((int)(presencePos.X + presence.DrawDistance)) / Constants.TerrainPatchSize) + 2; | ||
1109 | endX = Math.Max(endX, 0); | ||
1110 | endX = Math.Min(endX, (int)m_scene.RegionInfo.RegionSizeX / Constants.TerrainPatchSize); | ||
1111 | endY = (((int)(presencePos.Y + presence.DrawDistance)) / Constants.TerrainPatchSize) + 2; | ||
1112 | endY = Math.Max(endY, 0); | ||
1113 | endY = Math.Min(endY, (int)m_scene.RegionInfo.RegionSizeY / Constants.TerrainPatchSize); | ||
1114 | } | ||
1115 | |||
1116 | // m_log.DebugFormat("{0} GetModifiedPatchesInViewDistance. rName={1}, ddist={2}, apos={3}, cpos={4}, isChild={5}, start=<{6},{7}>, end=<{8},{9}>", | ||
1117 | // LogHeader, m_scene.RegionInfo.RegionName, | ||
1118 | // presence.DrawDistance, presencePos, presence.CameraPosition, | ||
1119 | // isLegacySizeChildRegion, | ||
1120 | // startX, startY, endX, endY); | ||
1121 | for(int x = startX; x < endX; x++) | ||
1122 | { | ||
1123 | for(int y = startY; y < endY; y++) | ||
1124 | { | ||
1125 | //Need to make sure we don't send the same ones over and over | ||
1126 | Vector3 patchPos = new Vector3(x * Constants.TerrainPatchSize, y * Constants.TerrainPatchSize, presencePos.Z); | ||
1127 | if (pups.GetByPatch(x, y)) | ||
1128 | { | ||
1129 | //Check which has less distance, camera or avatar position, both have to be done. | ||
1130 | //Its not a radius, its a diameter and we add 50 so that it doesn't look like it cuts off | ||
1131 | if (!shouldCheckViewDistance | ||
1132 | || Util.DistanceLessThan(presencePos, patchPos, presence.DrawDistance + 50) | ||
1133 | || Util.DistanceLessThan(presence.CameraPosition, patchPos, presence.DrawDistance + 50)) | ||
1134 | { | ||
1135 | //They can see it, send it to them | ||
1136 | pups.SetByPatch(x, y, false); | ||
1137 | float dist = Vector3.DistanceSquared(presencePos, patchPos); | ||
1138 | ret.Add(new PatchesToSend(x, y, dist)); | ||
1139 | } | ||
1140 | } | ||
1141 | } | ||
1142 | } | ||
1143 | return ret; | ||
803 | } | 1144 | } |
804 | 1145 | ||
805 | private void client_OnModifyTerrain(UUID user, float height, float seconds, byte size, byte action, | 1146 | private void client_OnModifyTerrain(UUID user, float height, float seconds, byte size, byte action, |
@@ -809,28 +1150,28 @@ namespace OpenSim.Region.CoreModules.World.Terrain | |||
809 | bool allowed = false; | 1150 | bool allowed = false; |
810 | if (north == south && east == west) | 1151 | if (north == south && east == west) |
811 | { | 1152 | { |
812 | if (m_painteffects.ContainsKey((StandardTerrainEffects) action)) | 1153 | if (m_painteffects.ContainsKey((StandardTerrainEffects)action)) |
813 | { | 1154 | { |
814 | bool[,] allowMask = new bool[m_channel.Width,m_channel.Height]; | 1155 | bool[,] allowMask = new bool[m_channel.Width, m_channel.Height]; |
815 | allowMask.Initialize(); | 1156 | allowMask.Initialize(); |
816 | int n = size + 1; | 1157 | int n = size + 1; |
817 | if (n > 2) | 1158 | if (n > 2) |
818 | n = 4; | 1159 | n = 4; |
819 | 1160 | ||
820 | int zx = (int) (west + 0.5); | 1161 | int zx = (int)(west + 0.5); |
821 | int zy = (int) (north + 0.5); | 1162 | int zy = (int)(north + 0.5); |
822 | 1163 | ||
823 | int dx; | 1164 | int dx; |
824 | for (dx=-n; dx<=n; dx++) | 1165 | for(dx=-n; dx<=n; dx++) |
825 | { | 1166 | { |
826 | int dy; | 1167 | int dy; |
827 | for (dy=-n; dy<=n; dy++) | 1168 | for(dy=-n; dy<=n; dy++) |
828 | { | 1169 | { |
829 | int x = zx + dx; | 1170 | int x = zx + dx; |
830 | int y = zy + dy; | 1171 | int y = zy + dy; |
831 | if (x>=0 && y>=0 && x<m_channel.Width && y<m_channel.Height) | 1172 | if (x >= 0 && y >= 0 && x < m_channel.Width && y < m_channel.Height) |
832 | { | 1173 | { |
833 | if (m_scene.Permissions.CanTerraformLand(agentId, new Vector3(x,y,0))) | 1174 | if (m_scene.Permissions.CanTerraformLand(agentId, new Vector3(x, y, 0))) |
834 | { | 1175 | { |
835 | allowMask[x, y] = true; | 1176 | allowMask[x, y] = true; |
836 | allowed = true; | 1177 | allowed = true; |
@@ -841,10 +1182,12 @@ namespace OpenSim.Region.CoreModules.World.Terrain | |||
841 | if (allowed) | 1182 | if (allowed) |
842 | { | 1183 | { |
843 | StoreUndoState(); | 1184 | StoreUndoState(); |
844 | m_painteffects[(StandardTerrainEffects) action].PaintEffect( | 1185 | m_painteffects[(StandardTerrainEffects)action].PaintEffect( |
845 | m_channel, allowMask, west, south, height, size, seconds); | 1186 | m_channel, allowMask, west, south, height, size, seconds); |
846 | 1187 | ||
847 | CheckForTerrainUpdates(!god); //revert changes outside estate limits | 1188 | //revert changes outside estate limits |
1189 | if (!god) | ||
1190 | EnforceEstateLimits(); | ||
848 | } | 1191 | } |
849 | } | 1192 | } |
850 | else | 1193 | else |
@@ -854,22 +1197,22 @@ namespace OpenSim.Region.CoreModules.World.Terrain | |||
854 | } | 1197 | } |
855 | else | 1198 | else |
856 | { | 1199 | { |
857 | if (m_floodeffects.ContainsKey((StandardTerrainEffects) action)) | 1200 | if (m_floodeffects.ContainsKey((StandardTerrainEffects)action)) |
858 | { | 1201 | { |
859 | bool[,] fillArea = new bool[m_channel.Width,m_channel.Height]; | 1202 | bool[,] fillArea = new bool[m_channel.Width, m_channel.Height]; |
860 | fillArea.Initialize(); | 1203 | fillArea.Initialize(); |
861 | 1204 | ||
862 | int x; | 1205 | int x; |
863 | for (x = 0; x < m_channel.Width; x++) | 1206 | for(x = 0; x < m_channel.Width; x++) |
864 | { | 1207 | { |
865 | int y; | 1208 | int y; |
866 | for (y = 0; y < m_channel.Height; y++) | 1209 | for(y = 0; y < m_channel.Height; y++) |
867 | { | 1210 | { |
868 | if (x < east && x > west) | 1211 | if (x < east && x > west) |
869 | { | 1212 | { |
870 | if (y < north && y > south) | 1213 | if (y < north && y > south) |
871 | { | 1214 | { |
872 | if (m_scene.Permissions.CanTerraformLand(agentId, new Vector3(x,y,0))) | 1215 | if (m_scene.Permissions.CanTerraformLand(agentId, new Vector3(x, y, 0))) |
873 | { | 1216 | { |
874 | fillArea[x, y] = true; | 1217 | fillArea[x, y] = true; |
875 | allowed = true; | 1218 | allowed = true; |
@@ -882,10 +1225,11 @@ namespace OpenSim.Region.CoreModules.World.Terrain | |||
882 | if (allowed) | 1225 | if (allowed) |
883 | { | 1226 | { |
884 | StoreUndoState(); | 1227 | StoreUndoState(); |
885 | m_floodeffects[(StandardTerrainEffects) action].FloodEffect( | 1228 | m_floodeffects[(StandardTerrainEffects)action].FloodEffect(m_channel, fillArea, size); |
886 | m_channel, fillArea, size); | ||
887 | 1229 | ||
888 | CheckForTerrainUpdates(!god); //revert changes outside estate limits | 1230 | //revert changes outside estate limits |
1231 | if (!god) | ||
1232 | EnforceEstateLimits(); | ||
889 | } | 1233 | } |
890 | } | 1234 | } |
891 | else | 1235 | else |
@@ -905,16 +1249,18 @@ namespace OpenSim.Region.CoreModules.World.Terrain | |||
905 | InterfaceBakeTerrain(null); //bake terrain does not use the passed in parameter | 1249 | InterfaceBakeTerrain(null); //bake terrain does not use the passed in parameter |
906 | } | 1250 | } |
907 | } | 1251 | } |
908 | 1252 | ||
909 | protected void client_OnUnackedTerrain(IClientAPI client, int patchX, int patchY) | 1253 | protected void client_OnUnackedTerrain(IClientAPI client, int patchX, int patchY) |
910 | { | 1254 | { |
911 | //m_log.Debug("Terrain packet unacked, resending patch: " + patchX + " , " + patchY); | 1255 | //m_log.Debug("Terrain packet unacked, resending patch: " + patchX + " , " + patchY); |
912 | client.SendLayerData(patchX, patchY, m_scene.Heightmap.GetFloatsSerialised()); | 1256 | // SendLayerData does not use the heightmap parameter. This kludge is so as to not change IClientAPI. |
1257 | float[] heightMap = new float[10]; | ||
1258 | client.SendLayerData(patchX, patchY, heightMap); | ||
913 | } | 1259 | } |
914 | 1260 | ||
915 | private void StoreUndoState() | 1261 | private void StoreUndoState() |
916 | { | 1262 | { |
917 | lock (m_undo) | 1263 | lock(m_undo) |
918 | { | 1264 | { |
919 | if (m_undo.Count > 0) | 1265 | if (m_undo.Count > 0) |
920 | { | 1266 | { |
@@ -935,23 +1281,21 @@ namespace OpenSim.Region.CoreModules.World.Terrain | |||
935 | 1281 | ||
936 | private void InterfaceLoadFile(Object[] args) | 1282 | private void InterfaceLoadFile(Object[] args) |
937 | { | 1283 | { |
938 | LoadFromFile((string) args[0]); | 1284 | LoadFromFile((string)args[0]); |
939 | CheckForTerrainUpdates(); | ||
940 | } | 1285 | } |
941 | 1286 | ||
942 | private void InterfaceLoadTileFile(Object[] args) | 1287 | private void InterfaceLoadTileFile(Object[] args) |
943 | { | 1288 | { |
944 | LoadFromFile((string) args[0], | 1289 | LoadFromFile((string)args[0], |
945 | (int) args[1], | 1290 | (int)args[1], |
946 | (int) args[2], | 1291 | (int)args[2], |
947 | (int) args[3], | 1292 | (int)args[3], |
948 | (int) args[4]); | 1293 | (int)args[4]); |
949 | CheckForTerrainUpdates(); | ||
950 | } | 1294 | } |
951 | 1295 | ||
952 | private void InterfaceSaveFile(Object[] args) | 1296 | private void InterfaceSaveFile(Object[] args) |
953 | { | 1297 | { |
954 | SaveToFile((string) args[0]); | 1298 | SaveToFile((string)args[0]); |
955 | } | 1299 | } |
956 | 1300 | ||
957 | private void InterfaceSaveTileFile(Object[] args) | 1301 | private void InterfaceSaveTileFile(Object[] args) |
@@ -971,11 +1315,10 @@ namespace OpenSim.Region.CoreModules.World.Terrain | |||
971 | private void InterfaceRevertTerrain(Object[] args) | 1315 | private void InterfaceRevertTerrain(Object[] args) |
972 | { | 1316 | { |
973 | int x, y; | 1317 | int x, y; |
974 | for (x = 0; x < m_channel.Width; x++) | 1318 | for(x = 0; x < m_channel.Width; x++) |
975 | for (y = 0; y < m_channel.Height; y++) | 1319 | for(y = 0; y < m_channel.Height; y++) |
976 | m_channel[x, y] = m_revert[x, y]; | 1320 | m_channel[x, y] = m_revert[x, y]; |
977 | 1321 | ||
978 | CheckForTerrainUpdates(); | ||
979 | } | 1322 | } |
980 | 1323 | ||
981 | private void InterfaceFlipTerrain(Object[] args) | 1324 | private void InterfaceFlipTerrain(Object[] args) |
@@ -984,39 +1327,36 @@ namespace OpenSim.Region.CoreModules.World.Terrain | |||
984 | 1327 | ||
985 | if (direction.ToLower().StartsWith("y")) | 1328 | if (direction.ToLower().StartsWith("y")) |
986 | { | 1329 | { |
987 | for (int x = 0; x < Constants.RegionSize; x++) | 1330 | for(int x = 0; x < m_channel.Width; x++) |
988 | { | 1331 | { |
989 | for (int y = 0; y < Constants.RegionSize / 2; y++) | 1332 | for(int y = 0; y < m_channel.Height / 2; y++) |
990 | { | 1333 | { |
991 | double height = m_channel[x, y]; | 1334 | double height = m_channel[x, y]; |
992 | double flippedHeight = m_channel[x, (int)Constants.RegionSize - 1 - y]; | 1335 | double flippedHeight = m_channel[x, (int)m_channel.Height - 1 - y]; |
993 | m_channel[x, y] = flippedHeight; | 1336 | m_channel[x, y] = flippedHeight; |
994 | m_channel[x, (int)Constants.RegionSize - 1 - y] = height; | 1337 | m_channel[x, (int)m_channel.Height - 1 - y] = height; |
995 | 1338 | ||
996 | } | 1339 | } |
997 | } | 1340 | } |
998 | } | 1341 | } |
999 | else if (direction.ToLower().StartsWith("x")) | 1342 | else if (direction.ToLower().StartsWith("x")) |
1000 | { | 1343 | { |
1001 | for (int y = 0; y < Constants.RegionSize; y++) | 1344 | for(int y = 0; y < m_channel.Height; y++) |
1002 | { | 1345 | { |
1003 | for (int x = 0; x < Constants.RegionSize / 2; x++) | 1346 | for(int x = 0; x < m_channel.Width / 2; x++) |
1004 | { | 1347 | { |
1005 | double height = m_channel[x, y]; | 1348 | double height = m_channel[x, y]; |
1006 | double flippedHeight = m_channel[(int)Constants.RegionSize - 1 - x, y]; | 1349 | double flippedHeight = m_channel[(int)m_channel.Width - 1 - x, y]; |
1007 | m_channel[x, y] = flippedHeight; | 1350 | m_channel[x, y] = flippedHeight; |
1008 | m_channel[(int)Constants.RegionSize - 1 - x, y] = height; | 1351 | m_channel[(int)m_channel.Width - 1 - x, y] = height; |
1009 | 1352 | ||
1010 | } | 1353 | } |
1011 | } | 1354 | } |
1012 | } | 1355 | } |
1013 | else | 1356 | else |
1014 | { | 1357 | { |
1015 | m_log.Error("Unrecognised direction - need x or y"); | 1358 | MainConsole.Instance.OutputFormat("ERROR: Unrecognised direction {0} - need x or y", direction); |
1016 | } | 1359 | } |
1017 | |||
1018 | |||
1019 | CheckForTerrainUpdates(); | ||
1020 | } | 1360 | } |
1021 | 1361 | ||
1022 | private void InterfaceRescaleTerrain(Object[] args) | 1362 | private void InterfaceRescaleTerrain(Object[] args) |
@@ -1042,9 +1382,9 @@ namespace OpenSim.Region.CoreModules.World.Terrain | |||
1042 | int width = m_channel.Width; | 1382 | int width = m_channel.Width; |
1043 | int height = m_channel.Height; | 1383 | int height = m_channel.Height; |
1044 | 1384 | ||
1045 | for (int x = 0; x < width; x++) | 1385 | for(int x = 0; x < width; x++) |
1046 | { | 1386 | { |
1047 | for (int y = 0; y < height; y++) | 1387 | for(int y = 0; y < height; y++) |
1048 | { | 1388 | { |
1049 | double currHeight = m_channel[x, y]; | 1389 | double currHeight = m_channel[x, y]; |
1050 | if (currHeight < currMin) | 1390 | if (currHeight < currMin) |
@@ -1065,16 +1405,15 @@ namespace OpenSim.Region.CoreModules.World.Terrain | |||
1065 | //m_log.InfoFormat("Scale = {0}", scale); | 1405 | //m_log.InfoFormat("Scale = {0}", scale); |
1066 | 1406 | ||
1067 | // scale the heightmap accordingly | 1407 | // scale the heightmap accordingly |
1068 | for (int x = 0; x < width; x++) | 1408 | for(int x = 0; x < width; x++) |
1069 | { | 1409 | { |
1070 | for (int y = 0; y < height; y++) | 1410 | for(int y = 0; y < height; y++) |
1071 | { | 1411 | { |
1072 | double currHeight = m_channel[x, y] - currMin; | 1412 | double currHeight = m_channel[x, y] - currMin; |
1073 | m_channel[x, y] = desiredMin + (currHeight * scale); | 1413 | m_channel[x, y] = desiredMin + (currHeight * scale); |
1074 | } | 1414 | } |
1075 | } | 1415 | } |
1076 | 1416 | ||
1077 | CheckForTerrainUpdates(); | ||
1078 | } | 1417 | } |
1079 | 1418 | ||
1080 | } | 1419 | } |
@@ -1082,64 +1421,73 @@ namespace OpenSim.Region.CoreModules.World.Terrain | |||
1082 | private void InterfaceElevateTerrain(Object[] args) | 1421 | private void InterfaceElevateTerrain(Object[] args) |
1083 | { | 1422 | { |
1084 | int x, y; | 1423 | int x, y; |
1085 | for (x = 0; x < m_channel.Width; x++) | 1424 | for(x = 0; x < m_channel.Width; x++) |
1086 | for (y = 0; y < m_channel.Height; y++) | 1425 | for(y = 0; y < m_channel.Height; y++) |
1087 | m_channel[x, y] += (double) args[0]; | 1426 | m_channel[x, y] += (double)args[0]; |
1088 | CheckForTerrainUpdates(); | ||
1089 | } | 1427 | } |
1090 | 1428 | ||
1091 | private void InterfaceMultiplyTerrain(Object[] args) | 1429 | private void InterfaceMultiplyTerrain(Object[] args) |
1092 | { | 1430 | { |
1093 | int x, y; | 1431 | int x, y; |
1094 | for (x = 0; x < m_channel.Width; x++) | 1432 | for(x = 0; x < m_channel.Width; x++) |
1095 | for (y = 0; y < m_channel.Height; y++) | 1433 | for(y = 0; y < m_channel.Height; y++) |
1096 | m_channel[x, y] *= (double) args[0]; | 1434 | m_channel[x, y] *= (double)args[0]; |
1097 | CheckForTerrainUpdates(); | ||
1098 | } | 1435 | } |
1099 | 1436 | ||
1100 | private void InterfaceLowerTerrain(Object[] args) | 1437 | private void InterfaceLowerTerrain(Object[] args) |
1101 | { | 1438 | { |
1102 | int x, y; | 1439 | int x, y; |
1103 | for (x = 0; x < m_channel.Width; x++) | 1440 | for(x = 0; x < m_channel.Width; x++) |
1104 | for (y = 0; y < m_channel.Height; y++) | 1441 | for(y = 0; y < m_channel.Height; y++) |
1105 | m_channel[x, y] -= (double) args[0]; | 1442 | m_channel[x, y] -= (double)args[0]; |
1106 | CheckForTerrainUpdates(); | ||
1107 | } | 1443 | } |
1108 | 1444 | ||
1109 | private void InterfaceFillTerrain(Object[] args) | 1445 | public void InterfaceFillTerrain(Object[] args) |
1110 | { | 1446 | { |
1111 | int x, y; | 1447 | int x, y; |
1112 | 1448 | ||
1113 | for (x = 0; x < m_channel.Width; x++) | 1449 | for(x = 0; x < m_channel.Width; x++) |
1114 | for (y = 0; y < m_channel.Height; y++) | 1450 | for(y = 0; y < m_channel.Height; y++) |
1115 | m_channel[x, y] = (double) args[0]; | 1451 | m_channel[x, y] = (double)args[0]; |
1116 | CheckForTerrainUpdates(); | ||
1117 | } | 1452 | } |
1118 | 1453 | ||
1119 | private void InterfaceMinTerrain(Object[] args) | 1454 | private void InterfaceMinTerrain(Object[] args) |
1120 | { | 1455 | { |
1121 | int x, y; | 1456 | int x, y; |
1122 | for (x = 0; x < m_channel.Width; x++) | 1457 | for(x = 0; x < m_channel.Width; x++) |
1123 | { | 1458 | { |
1124 | for (y = 0; y < m_channel.Height; y++) | 1459 | for(y = 0; y < m_channel.Height; y++) |
1125 | { | 1460 | { |
1126 | m_channel[x, y] = Math.Max((double)args[0], m_channel[x, y]); | 1461 | m_channel[x, y] = Math.Max((double)args[0], m_channel[x, y]); |
1127 | } | 1462 | } |
1128 | } | 1463 | } |
1129 | CheckForTerrainUpdates(); | ||
1130 | } | 1464 | } |
1131 | 1465 | ||
1132 | private void InterfaceMaxTerrain(Object[] args) | 1466 | private void InterfaceMaxTerrain(Object[] args) |
1133 | { | 1467 | { |
1134 | int x, y; | 1468 | int x, y; |
1135 | for (x = 0; x < m_channel.Width; x++) | 1469 | for(x = 0; x < m_channel.Width; x++) |
1136 | { | 1470 | { |
1137 | for (y = 0; y < m_channel.Height; y++) | 1471 | for(y = 0; y < m_channel.Height; y++) |
1138 | { | 1472 | { |
1139 | m_channel[x, y] = Math.Min((double)args[0], m_channel[x, y]); | 1473 | m_channel[x, y] = Math.Min((double)args[0], m_channel[x, y]); |
1140 | } | 1474 | } |
1141 | } | 1475 | } |
1142 | CheckForTerrainUpdates(); | 1476 | } |
1477 | |||
1478 | private void InterfaceShow(Object[] args) | ||
1479 | { | ||
1480 | Vector2 point; | ||
1481 | |||
1482 | if (!ConsoleUtil.TryParseConsole2DVector((string)args[0], null, out point)) | ||
1483 | { | ||
1484 | Console.WriteLine("ERROR: {0} is not a valid vector", args[0]); | ||
1485 | return; | ||
1486 | } | ||
1487 | |||
1488 | double height = m_channel[(int)point.X, (int)point.Y]; | ||
1489 | |||
1490 | Console.WriteLine("Terrain height at {0} is {1}", point, height); | ||
1143 | } | 1491 | } |
1144 | 1492 | ||
1145 | private void InterfaceShowDebugStats(Object[] args) | 1493 | private void InterfaceShowDebugStats(Object[] args) |
@@ -1149,10 +1497,10 @@ namespace OpenSim.Region.CoreModules.World.Terrain | |||
1149 | double sum = 0; | 1497 | double sum = 0; |
1150 | 1498 | ||
1151 | int x; | 1499 | int x; |
1152 | for (x = 0; x < m_channel.Width; x++) | 1500 | for(x = 0; x < m_channel.Width; x++) |
1153 | { | 1501 | { |
1154 | int y; | 1502 | int y; |
1155 | for (y = 0; y < m_channel.Height; y++) | 1503 | for(y = 0; y < m_channel.Height; y++) |
1156 | { | 1504 | { |
1157 | sum += m_channel[x, y]; | 1505 | sum += m_channel[x, y]; |
1158 | if (max < m_channel[x, y]) | 1506 | if (max < m_channel[x, y]) |
@@ -1164,13 +1512,13 @@ namespace OpenSim.Region.CoreModules.World.Terrain | |||
1164 | 1512 | ||
1165 | double avg = sum / (m_channel.Height * m_channel.Width); | 1513 | double avg = sum / (m_channel.Height * m_channel.Width); |
1166 | 1514 | ||
1167 | m_log.Info("Channel " + m_channel.Width + "x" + m_channel.Height); | 1515 | MainConsole.Instance.OutputFormat("Channel {0}x{1}", m_channel.Width, m_channel.Height); |
1168 | m_log.Info("max/min/avg/sum: " + max + "/" + min + "/" + avg + "/" + sum); | 1516 | MainConsole.Instance.OutputFormat("max/min/avg/sum: {0}/{1}/{2}/{3}", max, min, avg, sum); |
1169 | } | 1517 | } |
1170 | 1518 | ||
1171 | private void InterfaceEnableExperimentalBrushes(Object[] args) | 1519 | private void InterfaceEnableExperimentalBrushes(Object[] args) |
1172 | { | 1520 | { |
1173 | if ((bool) args[0]) | 1521 | if ((bool)args[0]) |
1174 | { | 1522 | { |
1175 | m_painteffects[StandardTerrainEffects.Revert] = new WeatherSphere(); | 1523 | m_painteffects[StandardTerrainEffects.Revert] = new WeatherSphere(); |
1176 | m_painteffects[StandardTerrainEffects.Flatten] = new OlsenSphere(); | 1524 | m_painteffects[StandardTerrainEffects.Flatten] = new OlsenSphere(); |
@@ -1185,28 +1533,30 @@ namespace OpenSim.Region.CoreModules.World.Terrain | |||
1185 | private void InterfaceRunPluginEffect(Object[] args) | 1533 | private void InterfaceRunPluginEffect(Object[] args) |
1186 | { | 1534 | { |
1187 | string firstArg = (string)args[0]; | 1535 | string firstArg = (string)args[0]; |
1536 | |||
1188 | if (firstArg == "list") | 1537 | if (firstArg == "list") |
1189 | { | 1538 | { |
1190 | m_log.Info("List of loaded plugins"); | 1539 | MainConsole.Instance.Output("List of loaded plugins"); |
1191 | foreach (KeyValuePair<string, ITerrainEffect> kvp in m_plugineffects) | 1540 | foreach(KeyValuePair<string, ITerrainEffect> kvp in m_plugineffects) |
1192 | { | 1541 | { |
1193 | m_log.Info(kvp.Key); | 1542 | MainConsole.Instance.Output(kvp.Key); |
1194 | } | 1543 | } |
1195 | return; | 1544 | return; |
1196 | } | 1545 | } |
1546 | |||
1197 | if (firstArg == "reload") | 1547 | if (firstArg == "reload") |
1198 | { | 1548 | { |
1199 | LoadPlugins(); | 1549 | LoadPlugins(); |
1200 | return; | 1550 | return; |
1201 | } | 1551 | } |
1552 | |||
1202 | if (m_plugineffects.ContainsKey(firstArg)) | 1553 | if (m_plugineffects.ContainsKey(firstArg)) |
1203 | { | 1554 | { |
1204 | m_plugineffects[firstArg].RunEffect(m_channel); | 1555 | m_plugineffects[firstArg].RunEffect(m_channel); |
1205 | CheckForTerrainUpdates(); | ||
1206 | } | 1556 | } |
1207 | else | 1557 | else |
1208 | { | 1558 | { |
1209 | m_log.Warn("No such plugin effect loaded."); | 1559 | MainConsole.Instance.Output("WARNING: No such plugin effect {0} loaded.", firstArg); |
1210 | } | 1560 | } |
1211 | } | 1561 | } |
1212 | 1562 | ||
@@ -1295,12 +1645,17 @@ namespace OpenSim.Region.CoreModules.World.Terrain | |||
1295 | new Command("stats", CommandIntentions.COMMAND_STATISTICAL, InterfaceShowDebugStats, | 1645 | new Command("stats", CommandIntentions.COMMAND_STATISTICAL, InterfaceShowDebugStats, |
1296 | "Shows some information about the regions heightmap for debugging purposes."); | 1646 | "Shows some information about the regions heightmap for debugging purposes."); |
1297 | 1647 | ||
1648 | Command showCommand = | ||
1649 | new Command("show", CommandIntentions.COMMAND_NON_HAZARDOUS, InterfaceShow, | ||
1650 | "Shows terrain height at a given co-ordinate."); | ||
1651 | showCommand.AddArgument("point", "point in <x>,<y> format with no spaces (e.g. 45,45)", "String"); | ||
1652 | |||
1298 | Command experimentalBrushesCommand = | 1653 | Command experimentalBrushesCommand = |
1299 | new Command("newbrushes", CommandIntentions.COMMAND_HAZARDOUS, InterfaceEnableExperimentalBrushes, | 1654 | new Command("newbrushes", CommandIntentions.COMMAND_HAZARDOUS, InterfaceEnableExperimentalBrushes, |
1300 | "Enables experimental brushes which replace the standard terrain brushes. WARNING: This is a debug setting and may be removed at any time."); | 1655 | "Enables experimental brushes which replace the standard terrain brushes. WARNING: This is a debug setting and may be removed at any time."); |
1301 | experimentalBrushesCommand.AddArgument("Enabled?", "true / false - Enable new brushes", "Boolean"); | 1656 | experimentalBrushesCommand.AddArgument("Enabled?", "true / false - Enable new brushes", "Boolean"); |
1302 | 1657 | ||
1303 | //Plugins | 1658 | // Plugins |
1304 | Command pluginRunCommand = | 1659 | Command pluginRunCommand = |
1305 | new Command("effect", CommandIntentions.COMMAND_HAZARDOUS, InterfaceRunPluginEffect, "Runs a specified plugin effect"); | 1660 | new Command("effect", CommandIntentions.COMMAND_HAZARDOUS, InterfaceRunPluginEffect, "Runs a specified plugin effect"); |
1306 | pluginRunCommand.AddArgument("name", "The plugin effect you wish to run, or 'list' to see all plugins", "String"); | 1661 | pluginRunCommand.AddArgument("name", "The plugin effect you wish to run, or 'list' to see all plugins", "String"); |
@@ -1316,6 +1671,7 @@ namespace OpenSim.Region.CoreModules.World.Terrain | |||
1316 | m_commander.RegisterCommand("bake", bakeRegionCommand); | 1671 | m_commander.RegisterCommand("bake", bakeRegionCommand); |
1317 | m_commander.RegisterCommand("revert", revertRegionCommand); | 1672 | m_commander.RegisterCommand("revert", revertRegionCommand); |
1318 | m_commander.RegisterCommand("newbrushes", experimentalBrushesCommand); | 1673 | m_commander.RegisterCommand("newbrushes", experimentalBrushesCommand); |
1674 | m_commander.RegisterCommand("show", showCommand); | ||
1319 | m_commander.RegisterCommand("stats", showDebugStatsCommand); | 1675 | m_commander.RegisterCommand("stats", showDebugStatsCommand); |
1320 | m_commander.RegisterCommand("effect", pluginRunCommand); | 1676 | m_commander.RegisterCommand("effect", pluginRunCommand); |
1321 | m_commander.RegisterCommand("flip", flipCommand); | 1677 | m_commander.RegisterCommand("flip", flipCommand); |
@@ -1325,10 +1681,66 @@ namespace OpenSim.Region.CoreModules.World.Terrain | |||
1325 | 1681 | ||
1326 | // Add this to our scene so scripts can call these functions | 1682 | // Add this to our scene so scripts can call these functions |
1327 | m_scene.RegisterModuleCommander(m_commander); | 1683 | m_scene.RegisterModuleCommander(m_commander); |
1684 | |||
1685 | // Add Modify command to Scene, since Command object requires fixed-length arglists | ||
1686 | m_scene.AddCommand("Terrain", this, "terrain modify", | ||
1687 | "terrain modify <operation> <value> [<area>] [<taper>]", | ||
1688 | "Modifies the terrain as instructed." + | ||
1689 | "\nEach operation can be limited to an area of effect:" + | ||
1690 | "\n * -ell=x,y,rx[,ry] constrains the operation to an ellipse centred at x,y" + | ||
1691 | "\n * -rec=x,y,dx[,dy] constrains the operation to a rectangle based at x,y" + | ||
1692 | "\nEach operation can have its effect tapered based on distance from centre:" + | ||
1693 | "\n * elliptical operations taper as cones" + | ||
1694 | "\n * rectangular operations taper as pyramids" | ||
1695 | , | ||
1696 | ModifyCommand); | ||
1697 | |||
1328 | } | 1698 | } |
1329 | 1699 | ||
1700 | public void ModifyCommand(string module, string[] cmd) | ||
1701 | { | ||
1702 | string result; | ||
1703 | Scene scene = SceneManager.Instance.CurrentScene; | ||
1704 | if ((scene != null) && (scene != m_scene)) | ||
1705 | { | ||
1706 | result = String.Empty; | ||
1707 | } | ||
1708 | else if (cmd.Length > 2) | ||
1709 | { | ||
1710 | string operationType = cmd[2]; | ||
1330 | 1711 | ||
1331 | #endregion | 1712 | |
1713 | ITerrainModifier operation; | ||
1714 | if (!m_modifyOperations.TryGetValue(operationType, out operation)) | ||
1715 | { | ||
1716 | result = String.Format("Terrain Modify \"{0}\" not found.", operationType); | ||
1717 | } | ||
1718 | else if ((cmd.Length > 3) && (cmd[3] == "usage")) | ||
1719 | { | ||
1720 | result = "Usage: " + operation.GetUsage(); | ||
1721 | } | ||
1722 | else | ||
1723 | { | ||
1724 | result = operation.ModifyTerrain(m_channel, cmd); | ||
1725 | } | ||
1726 | |||
1727 | if (result == String.Empty) | ||
1728 | { | ||
1729 | result = "Modified terrain"; | ||
1730 | m_log.DebugFormat("Performed terrain operation {0}", operationType); | ||
1731 | } | ||
1732 | } | ||
1733 | else | ||
1734 | { | ||
1735 | result = "Usage: <operation-name> <arg1> <arg2>..."; | ||
1736 | } | ||
1737 | if (result != String.Empty) | ||
1738 | { | ||
1739 | MainConsole.Instance.Output(result); | ||
1740 | } | ||
1741 | } | ||
1742 | |||
1743 | #endregion | ||
1332 | 1744 | ||
1333 | } | 1745 | } |
1334 | } | 1746 | } |
diff --git a/OpenSim/Region/CoreModules/World/Terrain/Tests/TerrainModuleTests.cs b/OpenSim/Region/CoreModules/World/Terrain/Tests/TerrainModuleTests.cs new file mode 100644 index 0000000..0563ad0 --- /dev/null +++ b/OpenSim/Region/CoreModules/World/Terrain/Tests/TerrainModuleTests.cs | |||
@@ -0,0 +1,75 @@ | |||
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 NUnit.Framework; | ||
30 | using OpenSim.Framework; | ||
31 | using OpenSim.Region.CoreModules.World.Terrain; | ||
32 | using OpenSim.Region.Framework.Scenes; | ||
33 | using OpenSim.Tests.Common; | ||
34 | |||
35 | namespace OpenSim.Region.CoreModules.Terrain.Tests | ||
36 | { | ||
37 | public class TerrainModuleTests : OpenSimTestCase | ||
38 | { | ||
39 | [Test] | ||
40 | public void TestTerrainFill() | ||
41 | { | ||
42 | TestHelpers.InMethod(); | ||
43 | // TestHelpers.EnableLogging(); | ||
44 | |||
45 | //UUID userId = TestHelpers.ParseTail(0x1); | ||
46 | |||
47 | TerrainModule tm = new TerrainModule(); | ||
48 | Scene scene = new SceneHelpers().SetupScene(); | ||
49 | SceneHelpers.SetupSceneModules(scene, tm); | ||
50 | |||
51 | // Fillheight of 30 | ||
52 | { | ||
53 | double fillHeight = 30; | ||
54 | |||
55 | tm.InterfaceFillTerrain(new object[] { fillHeight }); | ||
56 | |||
57 | double height = scene.Heightmap[128, 128]; | ||
58 | |||
59 | Assert.AreEqual(fillHeight, height); | ||
60 | } | ||
61 | |||
62 | // Max fillheight of 30 | ||
63 | // According to http://wiki.secondlife.com/wiki/Tips_for_Creating_Heightfields_and_Details_on_Terrain_RAW_Files#Notes_for_Creating_Height_Field_Maps_for_Second_Life | ||
64 | { | ||
65 | double fillHeight = 508; | ||
66 | |||
67 | tm.InterfaceFillTerrain(new object[] { fillHeight }); | ||
68 | |||
69 | double height = scene.Heightmap[128, 128]; | ||
70 | |||
71 | Assert.AreEqual(fillHeight, height); | ||
72 | } | ||
73 | } | ||
74 | } | ||
75 | } \ No newline at end of file | ||
diff --git a/OpenSim/Region/CoreModules/World/Terrain/Tests/TerrainTest.cs b/OpenSim/Region/CoreModules/World/Terrain/Tests/TerrainTest.cs index be719ea..29e80ef 100644 --- a/OpenSim/Region/CoreModules/World/Terrain/Tests/TerrainTest.cs +++ b/OpenSim/Region/CoreModules/World/Terrain/Tests/TerrainTest.cs | |||
@@ -40,10 +40,13 @@ namespace OpenSim.Region.CoreModules.World.Terrain.Tests | |||
40 | [Test] | 40 | [Test] |
41 | public void BrushTest() | 41 | public void BrushTest() |
42 | { | 42 | { |
43 | int midRegion = (int)Constants.RegionSize / 2; | ||
44 | |||
45 | // Create a mask that covers only the left half of the region | ||
43 | bool[,] allowMask = new bool[(int)Constants.RegionSize, 256]; | 46 | bool[,] allowMask = new bool[(int)Constants.RegionSize, 256]; |
44 | int x; | 47 | int x; |
45 | int y; | 48 | int y; |
46 | for (x = 0; x < (int)((int)Constants.RegionSize * 0.5f); x++) | 49 | for (x = 0; x < midRegion; x++) |
47 | { | 50 | { |
48 | for (y = 0; y < (int)Constants.RegionSize; y++) | 51 | for (y = 0; y < (int)Constants.RegionSize; y++) |
49 | { | 52 | { |
@@ -57,13 +60,12 @@ namespace OpenSim.Region.CoreModules.World.Terrain.Tests | |||
57 | TerrainChannel map = new TerrainChannel((int)Constants.RegionSize, (int)Constants.RegionSize); | 60 | TerrainChannel map = new TerrainChannel((int)Constants.RegionSize, (int)Constants.RegionSize); |
58 | ITerrainPaintableEffect effect = new RaiseSphere(); | 61 | ITerrainPaintableEffect effect = new RaiseSphere(); |
59 | 62 | ||
60 | effect.PaintEffect(map, allowMask, (int)Constants.RegionSize * 0.5f, (int)Constants.RegionSize * 0.5f, -1.0, 2, 0.1); | 63 | effect.PaintEffect(map, allowMask, midRegion, midRegion, -1.0, 2, 6.0); |
61 | Assert.That(map[127, (int)((int)Constants.RegionSize * 0.5f)] > 0.0, "Raise brush should raising value at this point (127,128)."); | 64 | Assert.That(map[127, midRegion] > 0.0, "Raise brush should raising value at this point (127,128)."); |
62 | Assert.That(map[124, (int)((int)Constants.RegionSize * 0.5f)] > 0.0, "Raise brush should raising value at this point (124,128)."); | 65 | Assert.That(map[125, midRegion] > 0.0, "Raise brush should raising value at this point (124,128)."); |
63 | Assert.That(map[123, (int)((int)Constants.RegionSize * 0.5f)] == 0.0, "Raise brush should not change value at this point (123,128)."); | 66 | Assert.That(map[120, midRegion] == 0.0, "Raise brush should not change value at this point (120,128)."); |
64 | Assert.That(map[128, (int)((int)Constants.RegionSize * 0.5f)] == 0.0, "Raise brush should not change value at this point (128,128)."); | 67 | Assert.That(map[128, midRegion] == 0.0, "Raise brush should not change value at this point (128,128)."); |
65 | Assert.That(map[0, (int)((int)Constants.RegionSize * 0.5f)] == 0.0, "Raise brush should not change value at this point (0,128)."); | 68 | Assert.That(map[0, midRegion] == 0.0, "Raise brush should not change value at this point (0,128)."); |
66 | |||
67 | // | 69 | // |
68 | // Test LowerSphere | 70 | // Test LowerSphere |
69 | // | 71 | // |
@@ -77,13 +79,13 @@ namespace OpenSim.Region.CoreModules.World.Terrain.Tests | |||
77 | } | 79 | } |
78 | effect = new LowerSphere(); | 80 | effect = new LowerSphere(); |
79 | 81 | ||
80 | effect.PaintEffect(map, allowMask, ((int)Constants.RegionSize * 0.5f), ((int)Constants.RegionSize * 0.5f), -1.0, 2, 6.0); | 82 | effect.PaintEffect(map, allowMask, midRegion, midRegion, -1.0, 2, 6.0); |
81 | Assert.That(map[127, (int)((int)Constants.RegionSize * 0.5f)] >= 0.0, "Lower should not lowering value below 0.0 at this point (127,128)."); | 83 | Assert.That(map[127, midRegion] >= 0.0, "Lower should not lowering value below 0.0 at this point (127,128)."); |
82 | Assert.That(map[127, (int)((int)Constants.RegionSize * 0.5f)] == 0.0, "Lower brush should lowering value to 0.0 at this point (127,128)."); | 84 | Assert.That(map[127, midRegion] == 0.0, "Lower brush should lowering value to 0.0 at this point (127,128)."); |
83 | Assert.That(map[124, (int)((int)Constants.RegionSize * 0.5f)] < 1.0, "Lower brush should lowering value at this point (124,128)."); | 85 | Assert.That(map[125, midRegion] < 1.0, "Lower brush should lowering value at this point (124,128)."); |
84 | Assert.That(map[123, (int)((int)Constants.RegionSize * 0.5f)] == 1.0, "Lower brush should not change value at this point (123,128)."); | 86 | Assert.That(map[120, midRegion] == 1.0, "Lower brush should not change value at this point (120,128)."); |
85 | Assert.That(map[128, (int)((int)Constants.RegionSize * 0.5f)] == 1.0, "Lower brush should not change value at this point (128,128)."); | 87 | Assert.That(map[128, midRegion] == 1.0, "Lower brush should not change value at this point (128,128)."); |
86 | Assert.That(map[0, (int)((int)Constants.RegionSize * 0.5f)] == 1.0, "Lower brush should not change value at this point (0,128)."); | 88 | Assert.That(map[0, midRegion] == 1.0, "Lower brush should not change value at this point (0,128)."); |
87 | } | 89 | } |
88 | 90 | ||
89 | [Test] | 91 | [Test] |
@@ -100,10 +102,6 @@ namespace OpenSim.Region.CoreModules.World.Terrain.Tests | |||
100 | x[0, 0] -= 1.0; | 102 | x[0, 0] -= 1.0; |
101 | Assert.That(x[0, 0] == 4.0, "Terrain addition/subtraction error."); | 103 | Assert.That(x[0, 0] == 4.0, "Terrain addition/subtraction error."); |
102 | 104 | ||
103 | x[0, 0] = Math.PI; | ||
104 | double[,] doublesExport = x.GetDoubles(); | ||
105 | Assert.That(doublesExport[0, 0] == Math.PI, "Export to double[,] array not working correctly."); | ||
106 | |||
107 | x[0, 0] = 1.0; | 105 | x[0, 0] = 1.0; |
108 | float[] floatsExport = x.GetFloatsSerialised(); | 106 | float[] floatsExport = x.GetFloatsSerialised(); |
109 | Assert.That(floatsExport[0] == 1.0f, "Export to float[] not working correctly."); | 107 | Assert.That(floatsExport[0] == 1.0f, "Export to float[] not working correctly."); |