aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/OpenSim/Region/CoreModules/World/Terrain
diff options
context:
space:
mode:
authorDavid Walter Seikel2016-11-03 21:44:39 +1000
committerDavid Walter Seikel2016-11-03 21:44:39 +1000
commit134f86e8d5c414409631b25b8c6f0ee45fbd8631 (patch)
tree216b89d3fb89acfb81be1e440c25c41ab09fa96d /OpenSim/Region/CoreModules/World/Terrain
parentMore changing to production grid. Double oops. (diff)
downloadopensim-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')
-rw-r--r--OpenSim/Region/CoreModules/World/Terrain/Effects/DefaultTerrainGenerator.cs2
-rw-r--r--OpenSim/Region/CoreModules/World/Terrain/FileLoaders/GenericSystemDrawing.cs2
-rw-r--r--OpenSim/Region/CoreModules/World/Terrain/FileLoaders/LLRAW.cs235
-rw-r--r--OpenSim/Region/CoreModules/World/Terrain/FileLoaders/RAW32.cs13
-rw-r--r--OpenSim/Region/CoreModules/World/Terrain/FileLoaders/Terragen.cs49
-rw-r--r--OpenSim/Region/CoreModules/World/Terrain/FloodBrushes/NoiseArea.cs2
-rw-r--r--OpenSim/Region/CoreModules/World/Terrain/ITerrainFeature.cs60
-rw-r--r--OpenSim/Region/CoreModules/World/Terrain/ITerrainModifier.cs77
-rw-r--r--OpenSim/Region/CoreModules/World/Terrain/Modifiers/FillModifier.cs93
-rw-r--r--OpenSim/Region/CoreModules/World/Terrain/Modifiers/LowerModifier.cs92
-rw-r--r--OpenSim/Region/CoreModules/World/Terrain/Modifiers/MaxModifier.cs92
-rw-r--r--OpenSim/Region/CoreModules/World/Terrain/Modifiers/MinModifier.cs92
-rw-r--r--OpenSim/Region/CoreModules/World/Terrain/Modifiers/NoiseModifier.cs108
-rw-r--r--OpenSim/Region/CoreModules/World/Terrain/Modifiers/RaiseModifier.cs92
-rw-r--r--OpenSim/Region/CoreModules/World/Terrain/Modifiers/SmoothModifier.cs131
-rw-r--r--OpenSim/Region/CoreModules/World/Terrain/PaintBrushes/NoiseSphere.cs2
-rw-r--r--OpenSim/Region/CoreModules/World/Terrain/TerrainModifier.cs378
-rw-r--r--OpenSim/Region/CoreModules/World/Terrain/TerrainModifierData.cs17
-rw-r--r--OpenSim/Region/CoreModules/World/Terrain/TerrainModule.cs814
-rw-r--r--OpenSim/Region/CoreModules/World/Terrain/Tests/TerrainModuleTests.cs75
-rw-r--r--OpenSim/Region/CoreModules/World/Terrain/Tests/TerrainTest.cs36
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
28using System; 28using System;
29using System.IO; 29using System.IO;
30using OpenSim.Framework;
30using OpenSim.Region.Framework.Interfaces; 31using OpenSim.Region.Framework.Interfaces;
31using OpenSim.Region.Framework.Scenes; 32using 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
28using System;
28using System.IO; 29using System.IO;
30
31using OpenSim.Framework;
29using OpenSim.Region.Framework.Interfaces; 32using OpenSim.Region.Framework.Interfaces;
30using OpenSim.Region.Framework.Scenes; 33using 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
28using System;
29using OpenSim.Region.Framework.Interfaces;
30
31namespace 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
28using System;
29using OpenSim.Region.Framework.Interfaces;
30
31namespace 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 */
27using System;
28
29using OpenSim.Region.CoreModules.World.Terrain;
30using OpenSim.Region.Framework.Interfaces;
31
32namespace 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 */
27using System;
28using OpenSim.Region.CoreModules.World.Terrain;
29using OpenSim.Region.Framework.Interfaces;
30
31namespace 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 */
27using System;
28using OpenSim.Region.CoreModules.World.Terrain;
29using OpenSim.Region.Framework.Interfaces;
30
31namespace 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 */
27using System;
28using OpenSim.Region.CoreModules.World.Terrain;
29using OpenSim.Region.Framework.Interfaces;
30
31namespace 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 */
27using System;
28using OpenSim.Region.CoreModules.World.Terrain;
29using OpenSim.Region.Framework.Interfaces;
30using OpenSim.Region.Framework.Scenes;
31
32namespace 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 */
27using System;
28using OpenSim.Region.CoreModules.World.Terrain;
29using OpenSim.Region.Framework.Interfaces;
30
31namespace 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 */
27using System;
28using OpenSim.Region.CoreModules.World.Terrain;
29using OpenSim.Region.Framework.Interfaces;
30
31namespace 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 */
27using System;
28using System.Reflection;
29using log4net;
30
31using OpenSim.Region.Framework.Interfaces;
32
33namespace 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 @@
1using System;
2
3namespace 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
28using System; 27using System;
29using System.Collections.Generic; 28using System.Collections.Generic;
30using System.IO; 29using System.IO;
31using System.Reflection; 30using System.Reflection;
32using System.Net; 31using System.Net;
32
33using log4net; 33using log4net;
34using Nini.Config; 34using Nini.Config;
35
35using OpenMetaverse; 36using OpenMetaverse;
36using Mono.Addins; 37using Mono.Addins;
38
39using OpenSim.Data;
37using OpenSim.Framework; 40using OpenSim.Framework;
41using OpenSim.Framework.Console;
38using OpenSim.Region.CoreModules.Framework.InterfaceCommander; 42using OpenSim.Region.CoreModules.Framework.InterfaceCommander;
39using OpenSim.Region.CoreModules.World.Terrain.FileLoaders; 43using OpenSim.Region.CoreModules.World.Terrain.FileLoaders;
44using OpenSim.Region.CoreModules.World.Terrain.Modifiers;
40using OpenSim.Region.CoreModules.World.Terrain.FloodBrushes; 45using OpenSim.Region.CoreModules.World.Terrain.FloodBrushes;
41using OpenSim.Region.CoreModules.World.Terrain.PaintBrushes; 46using OpenSim.Region.CoreModules.World.Terrain.PaintBrushes;
42using OpenSim.Region.Framework.Interfaces; 47using 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
28using System;
29using NUnit.Framework;
30using OpenSim.Framework;
31using OpenSim.Region.CoreModules.World.Terrain;
32using OpenSim.Region.Framework.Scenes;
33using OpenSim.Tests.Common;
34
35namespace 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.");