From 134f86e8d5c414409631b25b8c6f0ee45fbd8631 Mon Sep 17 00:00:00 2001 From: David Walter Seikel Date: Thu, 3 Nov 2016 21:44:39 +1000 Subject: Initial update to OpenSim 0.8.2.1 source code. --- .../Terrain/Effects/DefaultTerrainGenerator.cs | 2 +- .../Terrain/FileLoaders/GenericSystemDrawing.cs | 2 +- .../CoreModules/World/Terrain/FileLoaders/LLRAW.cs | 235 +++--- .../CoreModules/World/Terrain/FileLoaders/RAW32.cs | 13 +- .../World/Terrain/FileLoaders/Terragen.cs | 49 +- .../World/Terrain/FloodBrushes/NoiseArea.cs | 2 +- .../CoreModules/World/Terrain/ITerrainFeature.cs | 60 ++ .../CoreModules/World/Terrain/ITerrainModifier.cs | 77 ++ .../World/Terrain/Modifiers/FillModifier.cs | 93 +++ .../World/Terrain/Modifiers/LowerModifier.cs | 92 +++ .../World/Terrain/Modifiers/MaxModifier.cs | 92 +++ .../World/Terrain/Modifiers/MinModifier.cs | 92 +++ .../World/Terrain/Modifiers/NoiseModifier.cs | 108 +++ .../World/Terrain/Modifiers/RaiseModifier.cs | 92 +++ .../World/Terrain/Modifiers/SmoothModifier.cs | 131 ++++ .../World/Terrain/PaintBrushes/NoiseSphere.cs | 2 +- .../CoreModules/World/Terrain/TerrainModifier.cs | 378 ++++++++++ .../World/Terrain/TerrainModifierData.cs | 17 + .../CoreModules/World/Terrain/TerrainModule.cs | 814 ++++++++++++++++----- .../World/Terrain/Tests/TerrainModuleTests.cs | 75 ++ .../CoreModules/World/Terrain/Tests/TerrainTest.cs | 36 +- 21 files changed, 2096 insertions(+), 366 deletions(-) create mode 100644 OpenSim/Region/CoreModules/World/Terrain/ITerrainFeature.cs create mode 100644 OpenSim/Region/CoreModules/World/Terrain/ITerrainModifier.cs create mode 100644 OpenSim/Region/CoreModules/World/Terrain/Modifiers/FillModifier.cs create mode 100644 OpenSim/Region/CoreModules/World/Terrain/Modifiers/LowerModifier.cs create mode 100644 OpenSim/Region/CoreModules/World/Terrain/Modifiers/MaxModifier.cs create mode 100644 OpenSim/Region/CoreModules/World/Terrain/Modifiers/MinModifier.cs create mode 100644 OpenSim/Region/CoreModules/World/Terrain/Modifiers/NoiseModifier.cs create mode 100644 OpenSim/Region/CoreModules/World/Terrain/Modifiers/RaiseModifier.cs create mode 100644 OpenSim/Region/CoreModules/World/Terrain/Modifiers/SmoothModifier.cs create mode 100644 OpenSim/Region/CoreModules/World/Terrain/TerrainModifier.cs create mode 100644 OpenSim/Region/CoreModules/World/Terrain/TerrainModifierData.cs create mode 100644 OpenSim/Region/CoreModules/World/Terrain/Tests/TerrainModuleTests.cs (limited to 'OpenSim/Region/CoreModules/World/Terrain') 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 for (y = 0; y < map.Height; y++) { map[x, y] = TerrainUtil.PerlinNoise2D(x, y, 3, 0.25) * 10; - double spherFac = TerrainUtil.SphericalFactor(x, y, Constants.RegionSize / 2, Constants.RegionSize / 2, 50) * 0.01; + double spherFac = TerrainUtil.SphericalFactor(x, y, map.Width / 2, map.Height / 2, 50) * 0.01; if (map[x, y] < spherFac) { 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 { using (Bitmap bitmap = new Bitmap(filename)) { - ITerrainChannel retval = new TerrainChannel(true); + ITerrainChannel retval = new TerrainChannel(w, h); for (int x = 0; x < retval.Width; x++) { 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 @@ using System; using System.IO; +using OpenSim.Framework; using OpenSim.Region.Framework.Interfaces; using OpenSim.Region.Framework.Scenes; @@ -73,12 +74,13 @@ namespace OpenSim.Region.CoreModules.World.Terrain.FileLoaders public ITerrainChannel LoadFile(string filename) { FileInfo file = new FileInfo(filename); - FileStream s = file.Open(FileMode.Open, FileAccess.Read); - ITerrainChannel retval = LoadStream(s); - s.Close(); + ITerrainChannel channel; - return retval; + using (FileStream s = file.Open(FileMode.Open, FileAccess.Read)) + channel = LoadStream(s); + + return channel; } 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 TerrainChannel retval = new TerrainChannel(sectionWidth, sectionHeight); FileInfo file = new FileInfo(filename); - FileStream s = file.Open(FileMode.Open, FileAccess.Read); - BinaryReader bs = new BinaryReader(s); - int currFileYOffset = fileHeight - 1; - - // if our region isn't on the first Y section of the areas to be landscaped, then - // advance to our section of the file - while (currFileYOffset > offsetY) + using (FileStream s = file.Open(FileMode.Open, FileAccess.Read)) + using (BinaryReader bs = new BinaryReader(s)) { - // read a whole strip of regions - int heightsToRead = sectionHeight * (fileWidth * sectionWidth); - bs.ReadBytes(heightsToRead * 13); // because there are 13 fun channels - currFileYOffset--; - } + int currFileYOffset = fileHeight - 1; - // got to the Y start offset within the file of our region - // so read the file bits associated with our region - int y; - // for each Y within our Y offset - for (y = sectionHeight - 1; y >= 0; y--) - { - int currFileXOffset = 0; - - // if our region isn't the first X section of the areas to be landscaped, then - // advance the stream to the X start pos of our section in the file - // i.e. eat X upto where we start - while (currFileXOffset < offsetX) + // if our region isn't on the first Y section of the areas to be landscaped, then + // advance to our section of the file + while (currFileYOffset > offsetY) { - bs.ReadBytes(sectionWidth * 13); - currFileXOffset++; + // read a whole strip of regions + int heightsToRead = sectionHeight * (fileWidth * sectionWidth); + bs.ReadBytes(heightsToRead * 13); // because there are 13 fun channels + currFileYOffset--; } - // got to our X offset, so write our regions X line - int x; - for (x = 0; x < sectionWidth; x++) - { - // Read a strip and continue - retval[x, y] = bs.ReadByte() * (bs.ReadByte() / 128.0); - bs.ReadBytes(11); - } - // record that we wrote it - currFileXOffset++; + // got to the Y start offset within the file of our region + // so read the file bits associated with our region + int y; - // if our region isn't the last X section of the areas to be landscaped, then - // advance the stream to the end of this Y column - while (currFileXOffset < fileWidth) + // for each Y within our Y offset + for (y = sectionHeight - 1; y >= 0; y--) { - // eat the next regions x line - bs.ReadBytes(sectionWidth * 13); //The 13 channels again + int currFileXOffset = 0; + + // if our region isn't the first X section of the areas to be landscaped, then + // advance the stream to the X start pos of our section in the file + // i.e. eat X upto where we start + while (currFileXOffset < offsetX) + { + bs.ReadBytes(sectionWidth * 13); + currFileXOffset++; + } + + // got to our X offset, so write our regions X line + int x; + for (x = 0; x < sectionWidth; x++) + { + // Read a strip and continue + retval[x, y] = bs.ReadByte() * (bs.ReadByte() / 128.0); + bs.ReadBytes(11); + } + // record that we wrote it currFileXOffset++; + + // if our region isn't the last X section of the areas to be landscaped, then + // advance the stream to the end of this Y column + while (currFileXOffset < fileWidth) + { + // eat the next regions x line + bs.ReadBytes(sectionWidth * 13); //The 13 channels again + currFileXOffset++; + } } } - bs.Close(); - s.Close(); - return retval; } public ITerrainChannel LoadStream(Stream s) { - TerrainChannel retval = new TerrainChannel(); + // The raw format doesn't contain any dimension information. + // Guess the square dimensions by using the length of the raw file. + double dimension = Math.Sqrt((double)(s.Length / 13)); + // Regions are always multiples of 256. + int trimmedDimension = (int)dimension - ((int)dimension % (int)Constants.RegionSize); + if (trimmedDimension < Constants.RegionSize) + trimmedDimension = (int)Constants.RegionSize; + + TerrainChannel retval = new TerrainChannel(trimmedDimension, trimmedDimension); - BinaryReader bs = new BinaryReader(s); - int y; - for (y = 0; y < retval.Height; y++) + using (BinaryReader bs = new BinaryReader(s)) { - int x; - for (x = 0; x < retval.Width; x++) + int y; + for (y = 0; y < retval.Height; y++) { - retval[x, (retval.Height - 1) - y] = bs.ReadByte() * (bs.ReadByte() / 128.0); - bs.ReadBytes(11); // Advance the stream to next bytes. + int x; + for (x = 0; x < retval.Width; x++) + { + retval[x, (retval.Height - 1) - y] = bs.ReadByte() * (bs.ReadByte() / 128.0); + bs.ReadBytes(11); // Advance the stream to next bytes. + } } } - bs.Close(); - return retval; } public void SaveFile(string filename, ITerrainChannel map) { FileInfo file = new FileInfo(filename); - FileStream s = file.Open(FileMode.CreateNew, FileAccess.Write); - SaveStream(s, map); - s.Close(); + using (FileStream s = file.Open(FileMode.CreateNew, FileAccess.Write)) + SaveStream(s, map); } public void SaveStream(Stream s, ITerrainChannel map) { - BinaryWriter binStream = new BinaryWriter(s); - - // Output the calculated raw - for (int y = 0; y < map.Height; y++) + using (BinaryWriter binStream = new BinaryWriter(s)) { - for (int x = 0; x < map.Width; x++) + // Output the calculated raw + for (int y = 0; y < map.Height; y++) { - double t = map[x, (map.Height - 1) - y]; - //if height is less than 0, set it to 0 as - //can't save -ve values in a LLRAW file - if (t < 0d) + for (int x = 0; x < map.Width; x++) { - t = 0d; + double t = map[x, (map.Height - 1) - y]; + //if height is less than 0, set it to 0 as + //can't save -ve values in a LLRAW file + if (t < 0d) + { + t = 0d; + } + + int index = 0; + + // The lookup table is pre-sorted, so we either find an exact match or + // the next closest (smaller) match with a binary search + index = Array.BinarySearch(LookupHeightTable, new HeightmapLookupValue(0, (float)t)); + if (index < 0) + index = ~index - 1; + + index = LookupHeightTable[index].Index; + + byte red = (byte) (index & 0xFF); + byte green = (byte) ((index >> 8) & 0xFF); + const byte blue = 20; + const byte alpha1 = 0; + const byte alpha2 = 0; + const byte alpha3 = 0; + const byte alpha4 = 0; + const byte alpha5 = 255; + const byte alpha6 = 255; + const byte alpha7 = 255; + const byte alpha8 = 255; + byte alpha9 = red; + byte alpha10 = green; + + binStream.Write(red); + binStream.Write(green); + binStream.Write(blue); + binStream.Write(alpha1); + binStream.Write(alpha2); + binStream.Write(alpha3); + binStream.Write(alpha4); + binStream.Write(alpha5); + binStream.Write(alpha6); + binStream.Write(alpha7); + binStream.Write(alpha8); + binStream.Write(alpha9); + binStream.Write(alpha10); } - - int index = 0; - - // The lookup table is pre-sorted, so we either find an exact match or - // the next closest (smaller) match with a binary search - index = Array.BinarySearch(LookupHeightTable, new HeightmapLookupValue(0, (float)t)); - if (index < 0) - index = ~index - 1; - - index = LookupHeightTable[index].Index; - - byte red = (byte) (index & 0xFF); - byte green = (byte) ((index >> 8) & 0xFF); - const byte blue = 20; - const byte alpha1 = 0; - const byte alpha2 = 0; - const byte alpha3 = 0; - const byte alpha4 = 0; - const byte alpha5 = 255; - const byte alpha6 = 255; - const byte alpha7 = 255; - const byte alpha8 = 255; - byte alpha9 = red; - byte alpha10 = green; - - binStream.Write(red); - binStream.Write(green); - binStream.Write(blue); - binStream.Write(alpha1); - binStream.Write(alpha2); - binStream.Write(alpha3); - binStream.Write(alpha4); - binStream.Write(alpha5); - binStream.Write(alpha6); - binStream.Write(alpha7); - binStream.Write(alpha8); - binStream.Write(alpha9); - binStream.Write(alpha10); } } - - binStream.Close(); } public string FileExtension @@ -259,7 +267,6 @@ namespace OpenSim.Region.CoreModules.World.Terrain.FileLoaders public bool SupportsTileSave() { return false; - } - + } } -} +} \ 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 @@ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +using System; using System.IO; + +using OpenSim.Framework; using OpenSim.Region.Framework.Interfaces; using OpenSim.Region.Framework.Scenes; @@ -116,7 +119,15 @@ namespace OpenSim.Region.CoreModules.World.Terrain.FileLoaders public ITerrainChannel LoadStream(Stream s) { - TerrainChannel retval = new TerrainChannel(); + // The raw format doesn't contain any dimension information. + // Guess the square dimensions by using the length of the raw file. + double dimension = Math.Sqrt((double)(s.Length / 4)); + // Regions are always multiples of 256. + int trimmedDimension = (int)dimension - ((int)dimension % (int)Constants.RegionSize); + if (trimmedDimension < Constants.RegionSize) + trimmedDimension = (int)Constants.RegionSize; + + TerrainChannel retval = new TerrainChannel(trimmedDimension, trimmedDimension); BinaryReader bs = new BinaryReader(s); 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 bool eof = false; int fileXPoints = 0; -// int fileYPoints = 0; + int fileYPoints = 0; // Terragen file while (eof == false) @@ -75,7 +75,7 @@ namespace OpenSim.Region.CoreModules.World.Terrain.FileLoaders { case "SIZE": fileXPoints = bs.ReadInt16() + 1; -// fileYPoints = fileXPoints; + fileYPoints = fileXPoints; bs.ReadInt16(); break; case "XPTS": @@ -83,8 +83,7 @@ namespace OpenSim.Region.CoreModules.World.Terrain.FileLoaders bs.ReadInt16(); break; case "YPTS": -// fileYPoints = bs.ReadInt16(); - bs.ReadInt16(); + fileYPoints = bs.ReadInt16(); bs.ReadInt16(); break; case "ALTW": @@ -138,7 +137,6 @@ namespace OpenSim.Region.CoreModules.World.Terrain.FileLoaders bs.ReadInt16(); } - break; default: bs.ReadInt32(); @@ -154,10 +152,11 @@ namespace OpenSim.Region.CoreModules.World.Terrain.FileLoaders public ITerrainChannel LoadStream(Stream s) { - + // Set to default size int w = (int)Constants.RegionSize; int h = (int)Constants.RegionSize; + // create a dummy channel (in case data is bad) TerrainChannel retval = new TerrainChannel(w, h); BinaryReader bs = new BinaryReader(s); @@ -165,8 +164,6 @@ namespace OpenSim.Region.CoreModules.World.Terrain.FileLoaders bool eof = false; if (Encoding.ASCII.GetString(bs.ReadBytes(16)) == "TERRAGENTERRAIN ") { -// int fileWidth = w; -// int fileHeight = h; // Terragen file while (eof == false) @@ -175,31 +172,29 @@ namespace OpenSim.Region.CoreModules.World.Terrain.FileLoaders switch (tmp) { case "SIZE": -// int sztmp = bs.ReadInt16() + 1; -// fileWidth = sztmp; -// fileHeight = sztmp; - bs.ReadInt16(); + w = bs.ReadInt16() + 1; + h = w; bs.ReadInt16(); break; case "XPTS": -// fileWidth = bs.ReadInt16(); - bs.ReadInt16(); + w = bs.ReadInt16(); bs.ReadInt16(); break; case "YPTS": -// fileHeight = bs.ReadInt16(); - bs.ReadInt16(); + h = bs.ReadInt16(); bs.ReadInt16(); break; case "ALTW": eof = true; - Int16 heightScale = bs.ReadInt16(); - Int16 baseHeight = bs.ReadInt16(); + // create new channel of proper size (now that we know it) + retval = new TerrainChannel(w, h); + double heightScale = (double)bs.ReadInt16() / 65536.0; + double baseHeight = (double)bs.ReadInt16(); for (int y = 0; y < h; y++) { for (int x = 0; x < w; x++) { - retval[x, y] = baseHeight + bs.ReadInt16() * (double)heightScale / 65536.0; + retval[x, y] = baseHeight + (double)bs.ReadInt16() * heightScale; } } break; @@ -209,9 +204,7 @@ namespace OpenSim.Region.CoreModules.World.Terrain.FileLoaders } } } - bs.Close(); - return retval; } @@ -257,17 +250,17 @@ namespace OpenSim.Region.CoreModules.World.Terrain.FileLoaders bs.Write(enc.GetBytes("TERRAGENTERRAIN ")); bs.Write(enc.GetBytes("SIZE")); - bs.Write(Convert.ToInt16(Constants.RegionSize)); + bs.Write(Convert.ToInt16(map.Width)); bs.Write(Convert.ToInt16(0)); // necessary padding //The XPTS and YPTS chunks are not needed for square regions //but L3DT won't load the terrain file properly without them. bs.Write(enc.GetBytes("XPTS")); - bs.Write(Convert.ToInt16(Constants.RegionSize)); + bs.Write(Convert.ToInt16(map.Width)); bs.Write(Convert.ToInt16(0)); // necessary padding bs.Write(enc.GetBytes("YPTS")); - bs.Write(Convert.ToInt16(Constants.RegionSize)); + bs.Write(Convert.ToInt16(map.Height)); bs.Write(Convert.ToInt16(0)); // necessary padding bs.Write(enc.GetBytes("SCAL")); @@ -283,11 +276,13 @@ namespace OpenSim.Region.CoreModules.World.Terrain.FileLoaders bs.Write(Convert.ToInt16(horizontalScale)); // range between max and min bs.Write(Convert.ToInt16(baseHeight)); // base height or mid point + double factor = 65536.0 / horizontalScale; // avoid computing this on each iteration + for (int y = 0; y < map.Height; y++) { for (int x = 0; x < map.Width; x++) { - float elevation = (float)((map[x,y] - baseHeight) * 65536 ) / (float)horizontalScale; // see LoadStream for inverse + float elevation = (float)((map[x,y] - baseHeight) * factor); // see LoadStream for inverse // clamp rounding issues if (elevation > Int16.MaxValue) @@ -299,7 +294,7 @@ namespace OpenSim.Region.CoreModules.World.Terrain.FileLoaders } } - //This is only necessary for older versions of Terragen. + //This is necessary for older versions of Terragen. bs.Write(enc.GetBytes("EOF ")); bs.Close(); @@ -343,7 +338,7 @@ namespace OpenSim.Region.CoreModules.World.Terrain.FileLoaders if (BitConverter.IsLittleEndian == false) { byte[] tmp = new byte[4]; - for (int i = 0; i < 4; i++) + for (int i = 3; i >= 0; i--) { tmp[i] = retVal[3 - i]; } 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 { if (fillArea[x, y]) { - double noise = TerrainUtil.PerlinNoise2D((double) x / Constants.RegionSize, (double) y / Constants.RegionSize, 8, 1.0); + double noise = TerrainUtil.PerlinNoise2D((double) x / map.Width, (double) y / map.Height, 8, 1.0); map[x, y] += noise * strength; } 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 @@ +/* + * Copyright (c) Contributors, http://opensimulator.org/ + * See CONTRIBUTORS.TXT for a full list of copyright holders. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the OpenSimulator Project nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +using System; +using OpenSim.Region.Framework.Interfaces; + +namespace OpenSim.Region.CoreModules.World.Terrain +{ + public interface ITerrainFeature + { + /// + /// Creates the feature. + /// + /// + /// Empty string if successful, otherwise error message. + /// + /// + /// ITerrainChannel holding terrain data. + /// + /// + /// command-line arguments from console. + /// + string CreateFeature(ITerrainChannel map, string[] args); + + /// + /// Gets a string describing the usage. + /// + /// + /// A string describing parameters for creating the feature. + /// Format is "feature-name ..." + /// + string GetUsage(); + } + +} + 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 @@ +/* + * Copyright (c) Contributors, http://opensimulator.org/ + * See CONTRIBUTORS.TXT for a full list of copyright holders. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the OpenSimulator Project nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +using System; +using OpenSim.Region.Framework.Interfaces; + +namespace OpenSim.Region.CoreModules.World.Terrain +{ + public interface ITerrainModifier + { + /// + /// Creates the feature. + /// + /// + /// Empty string if successful, otherwise error message. + /// + /// + /// ITerrainChannel holding terrain data. + /// + /// + /// command-line arguments from console. + /// + string ModifyTerrain(ITerrainChannel map, string[] args); + + /// + /// Gets a string describing the usage. + /// + /// + /// A string describing parameters for creating the feature. + /// Format is "feature-name ..." + /// + string GetUsage(); + + /// + /// Apply the appropriate operation on the specified map, at (x, y). + /// + /// + /// Map. + /// + /// + /// Data. + /// + /// + /// X. + /// + /// + /// Y. + /// + double operate(double[,] map, TerrainModifierData data, int x, int y); + } + +} + 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 @@ +/* + * Copyright (c) Contributors, http://opensimulator.org/ + * See CONTRIBUTORS.TXT for a full list of copyright holders. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the OpenSimulator Project nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +using System; + +using OpenSim.Region.CoreModules.World.Terrain; +using OpenSim.Region.Framework.Interfaces; + +namespace OpenSim.Region.CoreModules.World.Terrain.Modifiers +{ + public class FillModifier : TerrainModifier + { + + public FillModifier(ITerrainModule module) : base(module) + { + } + + public override string ModifyTerrain(ITerrainChannel map, string[] args) + { + string result; + if (args.Length < 3) + { + result = "Usage: " + GetUsage(); + } + else + { + TerrainModifierData data; + result = this.parseParameters(args, out data); + + // Context-specific validation + if (result == String.Empty) + { + if (data.shape == String.Empty) + { + data.shape = "rectangle"; + data.x0 = 0; + data.y0 = 0; + data.dx = map.Width; + data.dy = map.Height; + } + } + + // if it's all good, then do the work + if (result == String.Empty) + { + this.applyModification(map, data); + } + } + + return result; + } + + public override string GetUsage() + { + string val = "fill [ -rec=x1,y1,dx[,dy] | -ell=x0,y0,rx[,ry] ] [-taper=]" + + "\nSets all points within the specified range to the specified value."; + return val; + } + + public override double operate(double[,] map, TerrainModifierData data, int x, int y) + { + double factor = this.computeBevel(data, x, y); + double result = data.elevation - (data.elevation - data.bevelevation) * factor; + return result; + } + + } + +} + 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 @@ +/* + * Copyright (c) Contributors, http://opensimulator.org/ + * See CONTRIBUTORS.TXT for a full list of copyright holders. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the OpenSimulator Project nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +using System; +using OpenSim.Region.CoreModules.World.Terrain; +using OpenSim.Region.Framework.Interfaces; + +namespace OpenSim.Region.CoreModules.World.Terrain.Modifiers +{ + public class LowerModifier : TerrainModifier + { + public LowerModifier(ITerrainModule module) : base(module) + { + } + + public override string ModifyTerrain(ITerrainChannel map, string[] args) + { + string result; + if (args.Length < 3) + { + result = "Usage: " + GetUsage(); + } + else + { + TerrainModifierData data; + result = this.parseParameters(args, out data); + + // Context-specific validation + if (result == String.Empty) + { + if (data.shape == String.Empty) + { + data.shape = "rectangle"; + data.x0 = 0; + data.y0 = 0; + data.dx = map.Width; + data.dy = map.Height; + } + } + + // if it's all good, then do the work + if (result == String.Empty) + { + this.applyModification(map, data); + } + } + + return result; + } + + public override string GetUsage() + { + string val = "lower [ -rec=x1,y1,dx[,dy] | -ell=x0,y0,rx[,ry] ] [-taper=]" + + "\nLowers all points within the specified range by the specified amount."; + return val; + + } + + public override double operate(double[,] map, TerrainModifierData data, int x, int y) + { + double factor = this.computeBevel(data, x, y); + double result = map[x, y] - (data.elevation - (data.elevation - data.bevelevation) * factor); + return result; + } + + } + +} + 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 @@ +/* + * Copyright (c) Contributors, http://opensimulator.org/ + * See CONTRIBUTORS.TXT for a full list of copyright holders. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the OpenSimulator Project nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +using System; +using OpenSim.Region.CoreModules.World.Terrain; +using OpenSim.Region.Framework.Interfaces; + +namespace OpenSim.Region.CoreModules.World.Terrain.Modifiers +{ + public class MaxModifier : TerrainModifier + { + public MaxModifier(ITerrainModule module) : base(module) + { + } + + public override string ModifyTerrain(ITerrainChannel map, string[] args) + { + string result; + if (args.Length < 3) + { + result = "Usage: " + GetUsage(); + } + else + { + TerrainModifierData data; + result = this.parseParameters(args, out data); + + // Context-specific validation + if (result == String.Empty) + { + if (data.shape == String.Empty) + { + data.shape = "rectangle"; + data.x0 = 0; + data.y0 = 0; + data.dx = map.Width; + data.dy = map.Height; + } + } + + // if it's all good, then do the work + if (result == String.Empty) + { + this.applyModification(map, data); + } + } + + return result; + } + + public override string GetUsage() + { + string val = "max [ -rec=x1,y1,dx[,dy] | -ell=x0,y0,rx[,ry] ] [-taper=]" + + "\nEnsures that all points within the specified range are no higher than the specified value."; + return val; + + } + + public override double operate(double[,] map, TerrainModifierData data, int x, int y) + { + double factor = this.computeBevel(data, x, y); + double result = Math.Min(data.elevation - (data.elevation - data.bevelevation) * factor, map[x, y]); + return result; + } + + } + +} + 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 @@ +/* + * Copyright (c) Contributors, http://opensimulator.org/ + * See CONTRIBUTORS.TXT for a full list of copyright holders. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the OpenSimulator Project nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +using System; +using OpenSim.Region.CoreModules.World.Terrain; +using OpenSim.Region.Framework.Interfaces; + +namespace OpenSim.Region.CoreModules.World.Terrain.Modifiers +{ + public class MinModifier : TerrainModifier + { + public MinModifier(ITerrainModule module) : base(module) + { + } + + public override string ModifyTerrain(ITerrainChannel map, string[] args) + { + string result; + if (args.Length < 3) + { + result = "Usage: " + GetUsage(); + } + else + { + TerrainModifierData data; + result = this.parseParameters(args, out data); + + // Context-specific validation + if (result == String.Empty) + { + if (data.shape == String.Empty) + { + data.shape = "rectangle"; + data.x0 = 0; + data.y0 = 0; + data.dx = map.Width; + data.dy = map.Height; + } + } + + // if it's all good, then do the work + if (result == String.Empty) + { + this.applyModification(map, data); + } + } + + return result; + } + + public override string GetUsage() + { + string val = "min [ -rec=x1,y1,dx[,dy] | -ell=x0,y0,rx[,ry] ] [-taper=]" + + "\nEnsures that all points within the specified range are no lower than the specified value."; + return val; + + } + + public override double operate(double[,] map, TerrainModifierData data, int x, int y) + { + double factor = this.computeBevel(data, x, y); + double result = Math.Max(data.elevation - (data.elevation - data.bevelevation) * factor, map[x, y]); + return result; + } + + } + +} + 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 @@ +/* + * Copyright (c) Contributors, http://opensimulator.org/ + * See CONTRIBUTORS.TXT for a full list of copyright holders. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the OpenSimulator Project nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +using System; +using OpenSim.Region.CoreModules.World.Terrain; +using OpenSim.Region.Framework.Interfaces; +using OpenSim.Region.Framework.Scenes; + +namespace OpenSim.Region.CoreModules.World.Terrain.Modifiers +{ + public class NoiseModifier : TerrainModifier + { + public NoiseModifier(ITerrainModule module) : base(module) + { + } + + public override string ModifyTerrain(ITerrainChannel map, string[] args) + { + string result; + if (args.Length < 3) + { + result = "Usage: " + GetUsage(); + } + else + { + TerrainModifierData data; + result = this.parseParameters(args, out data); + + // Context-specific validation + if (result == String.Empty) + { + if (data.bevel == "taper") + { + if (data.bevelevation < 0.0 || data.bevelevation > 1.0) + { + result = String.Format("Taper must be 0.0 to 1.0: {0}", data.bevelevation); + } + } + else + { + data.bevelevation = 1.0f; + } + + if (data.elevation < 0.0 || data.elevation > 1.0) + { + result = String.Format("Noise strength must be 0.0 to 1.0: {0}", data.elevation); + } + + if (data.shape == String.Empty) + { + data.shape = "rectangle"; + data.x0 = 0; + data.y0 = 0; + data.dx = map.Width; + data.dy = map.Height; + } + } + + // if it's all good, then do the work + if (result == String.Empty) + { + this.applyModification(map, data); + } + } + + return result; + } + + public override string GetUsage() + { + string val = "noise [ -rec=x1,y1,dx[,dy] | -ell=x0,y0,rx[,ry] ] [-taper=]" + + "\nAdds noise to all points within the specified range."; + return val; + } + + public override double operate(double[,] map, TerrainModifierData data, int x, int y) + { + double factor = this.computeBevel(data, x, y); + double noise = TerrainUtil.PerlinNoise2D((double)x / map.GetLength(0), (double)y / map.GetLength(1), 8, 1.0); + return map[x, y] + (data.elevation - (data.elevation - data.bevelevation) * factor) * (noise - .5); + } + + } + +} 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 @@ +/* + * Copyright (c) Contributors, http://opensimulator.org/ + * See CONTRIBUTORS.TXT for a full list of copyright holders. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the OpenSimulator Project nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +using System; +using OpenSim.Region.CoreModules.World.Terrain; +using OpenSim.Region.Framework.Interfaces; + +namespace OpenSim.Region.CoreModules.World.Terrain.Modifiers +{ + public class RaiseModifier : TerrainModifier + { + public RaiseModifier(ITerrainModule module) : base(module) + { + } + + public override string ModifyTerrain(ITerrainChannel map, string[] args) + { + string result; + if (args.Length < 3) + { + result = "Usage: " + GetUsage(); + } + else + { + TerrainModifierData data; + result = this.parseParameters(args, out data); + + // Context-specific validation + if (result == String.Empty) + { + if (data.shape == String.Empty) + { + data.shape = "rectangle"; + data.x0 = 0; + data.y0 = 0; + data.dx = map.Width; + data.dy = map.Height; + } + } + + // if it's all good, then do the work + if (result == String.Empty) + { + this.applyModification(map, data); + } + } + + return result; + } + + public override string GetUsage() + { + string val = "raise [ -rec=x1,y1,dx[,dy] | -ell=x0,y0,rx[,ry] ] [-taper=]" + + "\nRaises all points within the specified range by the specified amount."; + return val; + + } + + public override double operate(double[,] map, TerrainModifierData data, int x, int y) + { + double factor = this.computeBevel(data, x, y); + double result = map[x, y] + (data.elevation - (data.elevation - data.bevelevation) * factor); + return result; + } + + } + +} + 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 @@ +/* + * Copyright (c) Contributors, http://opensimulator.org/ + * See CONTRIBUTORS.TXT for a full list of copyright holders. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the OpenSimulator Project nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +using System; +using OpenSim.Region.CoreModules.World.Terrain; +using OpenSim.Region.Framework.Interfaces; + +namespace OpenSim.Region.CoreModules.World.Terrain.Modifiers +{ + public class SmoothModifier : TerrainModifier + { + public SmoothModifier(ITerrainModule module) : base(module) + { + } + + public override string ModifyTerrain(ITerrainChannel map, string[] args) + { + string result; + if (args.Length < 3) + { + result = "Usage: " + GetUsage(); + } + else + { + TerrainModifierData data; + result = this.parseParameters(args, out data); + + // Context-specific validation + if (result == String.Empty) + { + if (data.bevel == "taper") + { + if (data.bevelevation < 0.01 || data.bevelevation > 0.99) + { + result = String.Format("Taper must be 0.01 to 0.99: {0}", data.bevelevation); + } + } + else + { + data.bevelevation = 2.0f / 3.0f; + } + + if (data.elevation < 0.0 || data.elevation > 1.0) + { + result = String.Format("Smoothing strength must be 0.0 to 1.0: {0}", data.elevation); + } + + if (data.shape == String.Empty) + { + data.shape = "rectangle"; + data.x0 = 0; + data.y0 = 0; + data.dx = map.Width; + data.dy = map.Height; + } + } + + // if it's all good, then do the work + if (result == String.Empty) + { + this.applyModification(map, data); + } + } + + return result; + } + + public override string GetUsage() + { + string val = "smooth [ -rec=x1,y1,dx[,dy] | -ell=x0,y0,rx[,ry] ] [-taper=]" + + "\nSmooths all points within the specified range using a simple averaging algorithm."; + return val; + } + + public override double operate(double[,] map, TerrainModifierData data, int x, int y) + { + double[] scale = new double[3]; + scale[0] = data.elevation; + scale[1] = ((1.0 - scale[0]) * data.bevelevation) / 8.0; + scale[2] = ((1.0 - scale[0]) * (1.0 - data.bevelevation)) / 16.0; + int xMax = map.GetLength(0); + int yMax = map.GetLength(1); + double result; + if ((x == 0) || (y == 0) || (x == (xMax - 1)) || (y == (yMax - 1))) + { + result = map[x, y]; + } + else + { + result = 0.0; + for(int yPos = (y - 2); yPos < (y + 3); yPos++) + { + int yVal = (yPos <= 0) ? 0 : ((yPos < yMax) ? yPos : yMax - 1); + for(int xPos = (x - 2); xPos < (x + 3); xPos++) + { + int xVal = (xPos <= 0) ? 0 : ((xPos < xMax) ? xPos : xMax - 1); + int dist = Math.Max(Math.Abs(x - xVal), Math.Abs(y - yVal)); + result += map[xVal, yVal] * scale[dist]; + } + } + } + return result; + } + + } + +} + 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 z *= z; z -= ((x - rx) * (x - rx)) + ((y - ry) * (y - ry)); - double noise = TerrainUtil.PerlinNoise2D(x / (double) Constants.RegionSize, y / (double) Constants.RegionSize, 8, 1.0); + double noise = TerrainUtil.PerlinNoise2D(x / (double) map.Width, y / (double) map.Height, 8, 1.0); if (z > 0.0) 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 @@ +/* + * Copyright (c) Contributors, http://opensimulator.org/ + * See CONTRIBUTORS.TXT for a full list of copyright holders. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the OpenSimulator Project nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +using System; +using System.Reflection; +using log4net; + +using OpenSim.Region.Framework.Interfaces; + +namespace OpenSim.Region.CoreModules.World.Terrain +{ + public abstract class TerrainModifier : ITerrainModifier + { + protected ITerrainModule m_module; + protected static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + protected TerrainModifier(ITerrainModule module) + { + m_module = module; + } + + public abstract string ModifyTerrain(ITerrainChannel map, string[] args); + + public abstract string GetUsage(); + + public abstract double operate(double[,] map, TerrainModifierData data, int x, int y); + + protected String parseParameters(string[] args, out TerrainModifierData data) + { + string val; + string arg; + string result; + data = new TerrainModifierData(); + data.shape = String.Empty; + data.bevel = String.Empty; + data.dx = 0; + data.dy = 0; + if (args.Length < 4) + { + result = "Usage: " + GetUsage(); + } + else + { + result = this.parseFloat(args[3], out data.elevation); + } + if (result == String.Empty) + { + int index = 3; + while(++index < args.Length && result == String.Empty) + { + arg = args[index]; + // check for shape + if (arg.StartsWith("-rec=") || arg.StartsWith("-ell=")) + { + if (data.shape != String.Empty) + { + result = "Only 1 '-rec' or '-ell' parameter is permitted."; + } + else + { + data.shape = arg.StartsWith("-ell=") ? "ellipse" : "rectangle"; + val = arg.Substring(arg.IndexOf("=") + 1); + string[] coords = val.Split(new char[] {','}); + if ((coords.Length < 3) || (coords.Length > 4)) + { + result = String.Format("Bad format for shape parameter {0}", arg); + } + else + { + result = this.parseInt(coords[0], out data.x0); + if (result == String.Empty) + { + result = this.parseInt(coords[1], out data.y0); + } + if (result == String.Empty) + { + result = this.parseInt(coords[2], out data.dx); + } + if (result == String.Empty) + { + if (coords.Length == 4) + { + result = this.parseInt(coords[3], out data.dy); + } + else + { + data.dy = data.dx; + } + } + if (result == String.Empty) + { + if ((data.dx <= 0) || (data.dy <= 0)) + { + result = "Shape sizes must be positive integers"; + } + } + else + { + result = String.Format("Bad value in shape parameters {0}", arg); + } + } + } + } + else if (arg.StartsWith("-taper=")) + { + if (data.bevel != String.Empty) + { + result = "Only 1 '-taper' parameter is permitted."; + } + else + { + data.bevel = "taper"; + val = arg.Substring(arg.IndexOf("=") + 1); + result = this.parseFloat(val, out data.bevelevation); + if (result != String.Empty) + { + result = String.Format("Bad format for taper parameter {0}", arg); + } + } + } + else + { + result = String.Format("Unrecognized parameter {0}", arg); + } + } + } + return result; + } + + protected string parseFloat(String s, out float f) + { + string result; + double d; + if (Double.TryParse(s, out d)) + { + try + { + f = (float)d; + result = String.Empty; + } + catch(InvalidCastException) + { + result = String.Format("{0} is invalid", s); + f = -1.0f; + } + } + else + { + f = -1.0f; + result = String.Format("{0} is invalid", s); + } + return result; + } + + protected string parseInt(String s, out int i) + { + string result; + if (Int32.TryParse(s, out i)) + { + result = String.Empty; + } + else + { + result = String.Format("{0} is invalid", s); + } + return result; + } + + protected void applyModification(ITerrainChannel map, TerrainModifierData data) + { + bool[,] mask; + int xMax; + int yMax; + int xMid; + int yMid; + if (data.shape == "ellipse") + { + mask = this.ellipticalMask(data.dx, data.dy); + xMax = mask.GetLength(0); + yMax = mask.GetLength(1); + xMid = xMax / 2 + xMax % 2; + yMid = yMax / 2 + yMax % 2; + } + else + { + mask = this.rectangularMask(data.dx, data.dy); + xMax = mask.GetLength(0); + yMax = mask.GetLength(1); + xMid = 0; + yMid = 0; + } +// m_log.DebugFormat("Apply {0} mask {1}x{2} @ {3},{4}", data.shape, xMax, yMax, xMid, yMid); + double[,] buffer = map.GetDoubles(); + int yDim = yMax; + while(--yDim >= 0) + { + int yPos = data.y0 + yDim - yMid; + if ((yPos >= 0) && (yPos < map.Height)) + { + int xDim = xMax; + while(--xDim >= 0) + { + int xPos = data.x0 + xDim - xMid; + if ((xPos >= 0) && (xPos < map.Width) && (mask[xDim, yDim])) + { + double endElevation = this.operate(buffer, data, xPos, yPos); + map[xPos, yPos] = endElevation; + } + } + } + } + } + + protected double computeBevel(TerrainModifierData data, int x, int y) + { + int deltaX; + int deltaY; + int xMax; + int yMax; + double factor; + if (data.bevel == "taper") + { + if (data.shape == "ellipse") + { + deltaX = x - data.x0; + deltaY = y - data.y0; + xMax = data.dx; + yMax = data.dy; + factor = (double)((deltaX * deltaX) + (deltaY * deltaY)); + factor /= ((xMax * xMax) + (yMax * yMax)); + } + else + { + // pyramid + xMax = data.dx / 2 + data.dx % 2; + yMax = data.dy / 2 + data.dy % 2; + deltaX = Math.Abs(data.x0 + xMax - x); + deltaY = Math.Abs(data.y0 + yMax - y); + factor = Math.Max(((double)(deltaY) / yMax), ((double)(deltaX) / xMax)); + } + } + else + { + factor = 0.0; + } + return factor; + } + + private bool[,] rectangularMask(int xSize, int ySize) + { + bool[,] mask = new bool[xSize, ySize]; + int yPos = ySize; + while(--yPos >= 0) + { + int xPos = xSize; + while(--xPos >= 0) + { + mask[xPos, yPos] = true; + } + } + return mask; + } + + /* + * Fast ellipse-based derivative of Bresenham algorithm. + * https://web.archive.org/web/20120225095359/http://homepage.smc.edu/kennedy_john/belipse.pdf + */ + private bool[,] ellipticalMask(int xRadius, int yRadius) + { + long twoASquared = 2L * xRadius * xRadius; + long twoBSquared = 2L * yRadius * yRadius; + + bool[,] mask = new bool[2 * xRadius + 1, 2 * yRadius + 1]; + + long ellipseError = 0L; + long stoppingX = twoBSquared * xRadius; + long stoppingY = 0L; + long xChange = yRadius * yRadius * (1L - 2L * xRadius); + long yChange = xRadius * xRadius; + + int xPos = xRadius; + int yPos = 0; + + // first set of points + while(stoppingX >= stoppingY) + { + int yUpper = yRadius + yPos; + int yLower = yRadius - yPos; + // fill in the mask + int xNow = xPos; + while(xNow >= 0) + { + mask[xRadius + xNow, yUpper] = true; + mask[xRadius - xNow, yUpper] = true; + mask[xRadius + xNow, yLower] = true; + mask[xRadius - xNow, yLower] = true; + --xNow; + } + yPos++; + stoppingY += twoASquared; + ellipseError += yChange; + yChange += twoASquared; + if ((2L * ellipseError + xChange) > 0L) + { + xPos--; + stoppingX -= twoBSquared; + ellipseError += xChange; + xChange += twoBSquared; + } + } + + // second set of points + xPos = 0; + yPos = yRadius; + xChange = yRadius * yRadius; + yChange = xRadius * xRadius * (1L - 2L * yRadius); + + ellipseError = 0L; + stoppingX = 0L; + stoppingY = twoASquared * yRadius; + + while(stoppingX <= stoppingY) + { + int xUpper = xRadius + xPos; + int xLower = xRadius - xPos; + // fill in the mask + int yNow = yPos; + while(yNow >= 0) + { + mask[xUpper, yRadius + yNow] = true; + mask[xUpper, yRadius - yNow] = true; + mask[xLower, yRadius + yNow] = true; + mask[xLower, yRadius - yNow] = true; + --yNow; + } + xPos++; + stoppingX += twoBSquared; + ellipseError += xChange; + xChange += twoBSquared; + if ((2L * ellipseError + yChange) > 0L) + { + yPos--; + stoppingY -= twoASquared; + ellipseError += yChange; + yChange += twoASquared; + } + } + return mask; + } + + + } + +} + 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 @@ +using System; + +namespace OpenSim.Region.CoreModules.World.Terrain +{ + public struct TerrainModifierData + { + public float elevation; + public string shape; + public int x0; + public int y0; + public int dx; + public int dy; + public string bevel; + public float bevelevation; + } +} + 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 @@ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ - using System; using System.Collections.Generic; using System.IO; using System.Reflection; using System.Net; + using log4net; using Nini.Config; + using OpenMetaverse; using Mono.Addins; + +using OpenSim.Data; using OpenSim.Framework; +using OpenSim.Framework.Console; using OpenSim.Region.CoreModules.Framework.InterfaceCommander; using OpenSim.Region.CoreModules.World.Terrain.FileLoaders; +using OpenSim.Region.CoreModules.World.Terrain.Modifiers; using OpenSim.Region.CoreModules.World.Terrain.FloodBrushes; using OpenSim.Region.CoreModules.World.Terrain.PaintBrushes; using OpenSim.Region.Framework.Interfaces; @@ -70,26 +75,112 @@ namespace OpenSim.Region.CoreModules.World.Terrain #endregion private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); - - private readonly Commander m_commander = new Commander("terrain"); +#pragma warning disable 414 + private static readonly string LogHeader = "[TERRAIN MODULE]"; +#pragma warning restore 414 + + private readonly Commander m_commander = new Commander("terrain"); private readonly Dictionary m_floodeffects = new Dictionary(); - private readonly Dictionary m_loaders = new Dictionary(); - private readonly Dictionary m_painteffects = new Dictionary(); - - private ITerrainChannel m_channel; private Dictionary m_plugineffects; + private Dictionary m_modifyOperations = + new Dictionary(); + private ITerrainChannel m_channel; private ITerrainChannel m_revert; private Scene m_scene; private volatile bool m_tainted; private readonly Stack m_undo = new Stack(5); - private String m_InitialTerrain = "pinhead-island"; + // If true, send terrain patch updates to clients based on their view distance + private bool m_sendTerrainUpdatesByViewDistance = true; + + // Class to keep the per client collection of terrain patches that must be sent. + // A patch is set to 'true' meaning it should be sent to the client. Once the + // patch packet is queued to the client, the bit for that patch is set to 'false'. + private class PatchUpdates + { + private bool[,] updated; // for each patch, whether it needs to be sent to this client + private int updateCount; // number of patches that need to be sent + public ScenePresence Presence; // a reference to the client to send to + public TerrainData Terrain; // reference to the underlying terrain + public PatchUpdates(TerrainData terrData, ScenePresence pPresence) + { + updated = new bool[terrData.SizeX / Constants.TerrainPatchSize, terrData.SizeY / Constants.TerrainPatchSize]; + updateCount = 0; + Presence = pPresence; + Terrain = terrData; + // Initially, send all patches to the client + SetAll(true); + } + // Returns 'true' if there are any patches marked for sending + public bool HasUpdates() + { + return (updateCount > 0); + } + + public void SetByXY(int x, int y, bool state) + { + this.SetByPatch(x / Constants.TerrainPatchSize, y / Constants.TerrainPatchSize, state); + } + + public bool GetByPatch(int patchX, int patchY) + { + return updated[patchX, patchY]; + } + + public void SetByPatch(int patchX, int patchY, bool state) + { + bool prevState = updated[patchX, patchY]; + if (!prevState && state) + updateCount++; + if (prevState && !state) + updateCount--; + updated[patchX, patchY] = state; + } + + public void SetAll(bool state) + { + updateCount = 0; + for(int xx = 0; xx < updated.GetLength(0); xx++) + for(int yy = 0; yy < updated.GetLength(1); yy++) + updated[xx, yy] = state; + if (state) + updateCount = updated.GetLength(0) * updated.GetLength(1); + } + // Logically OR's the terrain data's patch taint map into this client's update map. + public void SetAll(TerrainData terrData) + { + if (updated.GetLength(0) != (terrData.SizeX / Constants.TerrainPatchSize) + || updated.GetLength(1) != (terrData.SizeY / Constants.TerrainPatchSize)) + { + throw new Exception( + String.Format("{0} PatchUpdates.SetAll: patch array not same size as terrain. arr=<{1},{2}>, terr=<{3},{4}>", + LogHeader, updated.GetLength(0), updated.GetLength(1), + terrData.SizeX / Constants.TerrainPatchSize, terrData.SizeY / Constants.TerrainPatchSize) + ); + } + for(int xx = 0; xx < terrData.SizeX; xx += Constants.TerrainPatchSize) + { + for(int yy = 0; yy < terrData.SizeY; yy += Constants.TerrainPatchSize) + { + // Only set tainted. The patch bit may be set if the patch was to be sent later. + if (terrData.IsTaintedAt(xx, yy, false)) + { + this.SetByXY(xx, yy, true); + } + } + } + } + } + + // The flags of which terrain patches to send for each of the ScenePresence's + private Dictionary m_perClientPatchUpdates = new Dictionary(); + /// /// Human readable list of terrain file extensions that are supported. /// @@ -100,8 +191,7 @@ namespace OpenSim.Region.CoreModules.World.Terrain #region ICommandableModule Members - public ICommander CommandInterface - { + public ICommander CommandInterface { get { return m_commander; } } @@ -118,7 +208,10 @@ namespace OpenSim.Region.CoreModules.World.Terrain { IConfig terrainConfig = config.Configs["Terrain"]; if (terrainConfig != null) + { m_InitialTerrain = terrainConfig.GetString("InitialTerrain", m_InitialTerrain); + m_sendTerrainUpdatesByViewDistance = terrainConfig.GetBoolean("SendTerrainUpdatesByViewDistance", m_sendTerrainUpdatesByViewDistance); + } } public void AddRegion(Scene scene) @@ -126,26 +219,28 @@ namespace OpenSim.Region.CoreModules.World.Terrain m_scene = scene; // Install terrain module in the simulator - lock (m_scene) + lock(m_scene) { if (m_scene.Heightmap == null) { - m_channel = new TerrainChannel(m_InitialTerrain); + m_channel = new TerrainChannel(m_InitialTerrain, (int)m_scene.RegionInfo.RegionSizeX, + (int)m_scene.RegionInfo.RegionSizeY, + (int)m_scene.RegionInfo.RegionSizeZ); m_scene.Heightmap = m_channel; - m_revert = new TerrainChannel(); UpdateRevertMap(); } else { m_channel = m_scene.Heightmap; - m_revert = new TerrainChannel(); UpdateRevertMap(); } m_scene.RegisterModuleInterface(this); m_scene.EventManager.OnNewClient += EventManager_OnNewClient; + m_scene.EventManager.OnClientClosed += EventManager_OnClientClosed; m_scene.EventManager.OnPluginConsole += EventManager_OnPluginConsole; m_scene.EventManager.OnTerrainTick += EventManager_OnTerrainTick; + m_scene.EventManager.OnFrame += EventManager_OnFrame; } InstallDefaultEffects(); @@ -156,7 +251,7 @@ namespace OpenSim.Region.CoreModules.World.Terrain string supportedFilesSeparatorForTileSave = ""; m_supportFileExtensionsForTileSave = ""; - foreach (KeyValuePair loader in m_loaders) + foreach(KeyValuePair loader in m_loaders) { m_supportedFileExtensions += supportedFilesSeparator + loader.Key + " (" + loader.Value + ")"; supportedFilesSeparator = ", "; @@ -179,13 +274,15 @@ namespace OpenSim.Region.CoreModules.World.Terrain public void RemoveRegion(Scene scene) { - lock (m_scene) + lock(m_scene) { // remove the commands m_scene.UnregisterModuleCommander(m_commander.Name); // remove the event-handlers + m_scene.EventManager.OnFrame -= EventManager_OnFrame; m_scene.EventManager.OnTerrainTick -= EventManager_OnTerrainTick; m_scene.EventManager.OnPluginConsole -= EventManager_OnPluginConsole; + m_scene.EventManager.OnClientClosed -= EventManager_OnClientClosed; m_scene.EventManager.OnNewClient -= EventManager_OnNewClient; // remove the interface m_scene.UnregisterModuleInterface(this); @@ -196,13 +293,11 @@ namespace OpenSim.Region.CoreModules.World.Terrain { } - public Type ReplaceableInterface - { + public Type ReplaceableInterface { get { return null; } } - public string Name - { + public string Name { get { return "TerrainModule"; } } @@ -221,47 +316,46 @@ namespace OpenSim.Region.CoreModules.World.Terrain /// Filename to terrain file. Type is determined by extension. public void LoadFromFile(string filename) { - foreach (KeyValuePair loader in m_loaders) + foreach(KeyValuePair loader in m_loaders) { if (filename.EndsWith(loader.Key)) { - lock (m_scene) + lock(m_scene) { try { ITerrainChannel channel = loader.Value.LoadFile(filename); - if (channel.Width != Constants.RegionSize || channel.Height != Constants.RegionSize) + if (channel.Width != m_scene.RegionInfo.RegionSizeX || channel.Height != m_scene.RegionInfo.RegionSizeY) { // TerrainChannel expects a RegionSize x RegionSize map, currently throw new ArgumentException(String.Format("wrong size, use a file with size {0} x {1}", - Constants.RegionSize, Constants.RegionSize)); + m_scene.RegionInfo.RegionSizeX, m_scene.RegionInfo.RegionSizeY)); } m_log.DebugFormat("[TERRAIN]: Loaded terrain, wd/ht: {0}/{1}", channel.Width, channel.Height); m_scene.Heightmap = channel; m_channel = channel; UpdateRevertMap(); } - catch (NotImplementedException) + catch(NotImplementedException) { m_log.Error("[TERRAIN]: Unable to load heightmap, the " + loader.Value + " parser does not support file loading. (May be save only)"); throw new TerrainException(String.Format("unable to load heightmap: parser {0} does not support loading", loader.Value)); } - catch (FileNotFoundException) + catch(FileNotFoundException) { m_log.Error( "[TERRAIN]: Unable to load heightmap, file not found. (A directory permissions error may also cause this)"); throw new TerrainException( String.Format("unable to load heightmap: file {0} not found (or permissions do not allow access", filename)); } - catch (ArgumentException e) + catch(ArgumentException e) { m_log.ErrorFormat("[TERRAIN]: Unable to load heightmap: {0}", e.Message); throw new TerrainException( String.Format("Unable to load heightmap: {0}", e.Message)); } } - CheckForTerrainUpdates(); m_log.Info("[TERRAIN]: File (" + filename + ") loaded successfully"); return; } @@ -279,7 +373,7 @@ namespace OpenSim.Region.CoreModules.World.Terrain { try { - foreach (KeyValuePair loader in m_loaders) + foreach(KeyValuePair loader in m_loaders) { if (filename.EndsWith(loader.Key)) { @@ -289,7 +383,7 @@ namespace OpenSim.Region.CoreModules.World.Terrain } } } - catch (IOException ioe) + catch(IOException ioe) { m_log.Error(String.Format("[TERRAIN]: Unable to save to {0}, {1}", filename, ioe.Message)); } @@ -309,27 +403,32 @@ namespace OpenSim.Region.CoreModules.World.Terrain LoadFromStream(filename, URIFetch(pathToTerrainHeightmap)); } + public void LoadFromStream(string filename, Stream stream) + { + LoadFromStream(filename, Vector3.Zero, 0f, Vector2.Zero, stream); + } + /// /// Loads a terrain file from a stream and installs it in the scene. /// /// Filename to terrain file. Type is determined by extension. /// - public void LoadFromStream(string filename, Stream stream) + public void LoadFromStream(string filename, Vector3 displacement, + float radianRotation, Vector2 rotationDisplacement, Stream stream) { - foreach (KeyValuePair loader in m_loaders) + foreach(KeyValuePair loader in m_loaders) { if (filename.EndsWith(loader.Key)) { - lock (m_scene) + lock(m_scene) { try { ITerrainChannel channel = loader.Value.LoadStream(stream); - m_scene.Heightmap = channel; - m_channel = channel; + m_channel.Merge(channel, displacement, radianRotation, rotationDisplacement); UpdateRevertMap(); } - catch (NotImplementedException) + catch(NotImplementedException) { m_log.Error("[TERRAIN]: Unable to load heightmap, the " + loader.Value + " parser does not support file loading. (May be save only)"); @@ -337,7 +436,6 @@ namespace OpenSim.Region.CoreModules.World.Terrain } } - CheckForTerrainUpdates(); m_log.Info("[TERRAIN]: File (" + filename + ") loaded successfully"); return; } @@ -390,7 +488,7 @@ namespace OpenSim.Region.CoreModules.World.Terrain { try { - foreach (KeyValuePair loader in m_loaders) + foreach(KeyValuePair loader in m_loaders) { if (filename.EndsWith(loader.Key)) { @@ -399,18 +497,56 @@ namespace OpenSim.Region.CoreModules.World.Terrain } } } - catch (NotImplementedException) + catch(NotImplementedException) { m_log.Error("Unable to save to " + filename + ", saving of this file format has not been implemented."); throw new TerrainException(String.Format("Unable to save heightmap: saving of this file format not implemented")); } } - public void TaintTerrain () + // Someone diddled terrain outside the normal code paths. Set the taintedness for all clients. + // ITerrainModule.TaintTerrain() + public void TaintTerrain() { - CheckForTerrainUpdates(); + lock(m_perClientPatchUpdates) + { + // Set the flags for all clients so the tainted patches will be sent out + foreach(PatchUpdates pups in m_perClientPatchUpdates.Values) + { + pups.SetAll(m_scene.Heightmap.GetTerrainData()); + } + } } + // ITerrainModule.PushTerrain() + public void PushTerrain(IClientAPI pClient) + { + // If view distance based, set the modified patch bits and the frame event will send the updates + if (m_sendTerrainUpdatesByViewDistance) + { + ScenePresence presence = m_scene.GetScenePresence(pClient.AgentId); + if (presence != null) + { + lock(m_perClientPatchUpdates) + { + PatchUpdates pups; + if (!m_perClientPatchUpdates.TryGetValue(pClient.AgentId, out pups)) + { + // There is a ScenePresence without a send patch map. Create one. + pups = new PatchUpdates(m_scene.Heightmap.GetTerrainData(), presence); + m_perClientPatchUpdates.Add(presence.UUID, pups); + } + // By setting all to modified, the next update tick will send the patches + pups.SetAll(true); + } + } + } + else + { + // The traditional way is to call into the protocol stack to send them all. + pClient.SendLayerData(new float[10]); + } + } #region Plugin Loading Methods private void LoadPlugins() @@ -418,13 +554,13 @@ namespace OpenSim.Region.CoreModules.World.Terrain m_plugineffects = new Dictionary(); LoadPlugins(Assembly.GetCallingAssembly()); string plugineffectsPath = "Terrain"; - + // Load the files in the Terrain/ dir if (!Directory.Exists(plugineffectsPath)) return; - + string[] files = Directory.GetFiles(plugineffectsPath); - foreach (string file in files) + foreach(string file in files) { m_log.Info("Loading effects in " + file); try @@ -432,7 +568,7 @@ namespace OpenSim.Region.CoreModules.World.Terrain Assembly library = Assembly.LoadFrom(file); LoadPlugins(library); } - catch (BadImageFormatException) + catch(BadImageFormatException) { } } @@ -440,7 +576,7 @@ namespace OpenSim.Region.CoreModules.World.Terrain private void LoadPlugins(Assembly library) { - foreach (Type pluginType in library.GetTypes()) + foreach(Type pluginType in library.GetTypes()) { try { @@ -462,7 +598,7 @@ namespace OpenSim.Region.CoreModules.World.Terrain m_log.Info("L ... " + typeName); } } - catch (AmbiguousMatchException) + catch(AmbiguousMatchException) { } } @@ -470,7 +606,7 @@ namespace OpenSim.Region.CoreModules.World.Terrain public void InstallPlugin(string pluginName, ITerrainEffect effect) { - lock (m_plugineffects) + lock(m_plugineffects) { if (!m_plugineffects.ContainsKey(pluginName)) { @@ -513,6 +649,15 @@ namespace OpenSim.Region.CoreModules.World.Terrain m_floodeffects[StandardTerrainEffects.Flatten] = new FlattenArea(); m_floodeffects[StandardTerrainEffects.Revert] = new RevertArea(m_revert); + // Terrain Modifier operations + m_modifyOperations["min"] = new MinModifier(this); + m_modifyOperations["max"] = new MaxModifier(this); + m_modifyOperations["raise"] = new RaiseModifier(this); + m_modifyOperations["lower"] = new LowerModifier(this); + m_modifyOperations["fill"] = new FillModifier(this); + m_modifyOperations["smooth"] = new SmoothModifier(this); + m_modifyOperations["noise"] = new NoiseModifier(this); + // Filesystem load/save loaders m_loaders[".r32"] = new RAW32(); m_loaders[".f32"] = m_loaders[".r32"]; @@ -532,6 +677,7 @@ namespace OpenSim.Region.CoreModules.World.Terrain /// public void UpdateRevertMap() { + /* int x; for (x = 0; x < m_channel.Width; x++) { @@ -541,6 +687,8 @@ namespace OpenSim.Region.CoreModules.World.Terrain m_revert[x, y] = m_channel[x, y]; } } + */ + m_revert = m_channel.MakeCopy(); } /// @@ -553,22 +701,22 @@ namespace OpenSim.Region.CoreModules.World.Terrain /// Where to begin our slice public void LoadFromFile(string filename, int fileWidth, int fileHeight, int fileStartX, int fileStartY) { - int offsetX = (int) m_scene.RegionInfo.RegionLocX - fileStartX; - int offsetY = (int) m_scene.RegionInfo.RegionLocY - fileStartY; + int offsetX = (int)m_scene.RegionInfo.RegionLocX - fileStartX; + int offsetY = (int)m_scene.RegionInfo.RegionLocY - fileStartY; if (offsetX >= 0 && offsetX < fileWidth && offsetY >= 0 && offsetY < fileHeight) { // this region is included in the tile request - foreach (KeyValuePair loader in m_loaders) + foreach(KeyValuePair loader in m_loaders) { if (filename.EndsWith(loader.Key)) { - lock (m_scene) + lock(m_scene) { ITerrainChannel channel = loader.Value.LoadFile(filename, offsetX, offsetY, fileWidth, fileHeight, - (int) Constants.RegionSize, - (int) Constants.RegionSize); + (int)m_scene.RegionInfo.RegionSizeX, + (int)m_scene.RegionInfo.RegionSizeY); m_scene.Heightmap = channel; m_channel = channel; UpdateRevertMap(); @@ -607,23 +755,23 @@ namespace OpenSim.Region.CoreModules.World.Terrain } // this region is included in the tile request - foreach (KeyValuePair loader in m_loaders) + foreach(KeyValuePair loader in m_loaders) { if (filename.EndsWith(loader.Key) && loader.Value.SupportsTileSave()) { - lock (m_scene) + lock(m_scene) { loader.Value.SaveFile(m_channel, filename, offsetX, offsetY, fileWidth, fileHeight, - (int)Constants.RegionSize, - (int)Constants.RegionSize); + (int)m_scene.RegionInfo.RegionSizeX, + (int)m_scene.RegionInfo.RegionSizeY); MainConsole.Instance.OutputFormat( "Saved terrain from ({0},{1}) to ({2},{3}) from {4} to {5}", fileStartX, fileStartY, fileStartX + fileWidth - 1, fileStartY + fileHeight - 1, m_scene.RegionInfo.RegionName, filename); } - + return; } } @@ -634,7 +782,44 @@ namespace OpenSim.Region.CoreModules.World.Terrain } /// + /// Called before processing of every simulation frame. + /// This is used to check to see of any of the terrain is tainted and, if so, schedule + /// updates for all the presences. + /// This also checks to see if there are updates that need to be sent for each presence. + /// This is where the logic is to send terrain updates to clients. + /// + private void EventManager_OnFrame() + { + TerrainData terrData = m_channel.GetTerrainData(); + + bool shouldTaint = false; + for(int x = 0; x < terrData.SizeX; x += Constants.TerrainPatchSize) + { + for(int y = 0; y < terrData.SizeY; y += Constants.TerrainPatchSize) + { + if (terrData.IsTaintedAt(x, y)) + { + // Found a patch that was modified. Push this flag into the clients. + SendToClients(terrData, x, y); + shouldTaint = true; + } + } + } + + // This event also causes changes to be sent to the clients + CheckSendingPatchesToClients(); + + // If things changes, generate some events + if (shouldTaint) + { + m_scene.EventManager.TriggerTerrainTainted(); + m_tainted = true; + } + } + + /// /// Performs updates to the region periodically, synchronising physics and other heightmap aware sections + /// Called infrequently (like every 5 seconds or so). Best used for storing terrain. /// private void EventManager_OnTerrainTick() { @@ -665,7 +850,7 @@ namespace OpenSim.Region.CoreModules.World.Terrain string[] tmpArgs = new string[args.Length - 2]; int i; - for (i = 2; i < args.Length; i++) + for(i = 2; i < args.Length; i++) tmpArgs[i - 2] = args[i]; m_commander.ProcessConsoleCommand(args[1], tmpArgs); @@ -683,56 +868,50 @@ namespace OpenSim.Region.CoreModules.World.Terrain client.OnLandUndo += client_OnLandUndo; client.OnUnackedTerrain += client_OnUnackedTerrain; } - + /// - /// Checks to see if the terrain has been modified since last check - /// but won't attempt to limit those changes to the limits specified in the estate settings - /// currently invoked by the command line operations in the region server only + /// Installs terrain brush hook to IClientAPI /// - private void CheckForTerrainUpdates() + /// + private void EventManager_OnClientClosed(UUID client, Scene scene) { - CheckForTerrainUpdates(false); + ScenePresence presence = scene.GetScenePresence(client); + if (presence != null) + { + presence.ControllingClient.OnModifyTerrain -= client_OnModifyTerrain; + presence.ControllingClient.OnBakeTerrain -= client_OnBakeTerrain; + presence.ControllingClient.OnLandUndo -= client_OnLandUndo; + presence.ControllingClient.OnUnackedTerrain -= client_OnUnackedTerrain; + } + + lock(m_perClientPatchUpdates) + m_perClientPatchUpdates.Remove(client); } /// - /// Checks to see if the terrain has been modified since last check. - /// If it has been modified, every all the terrain patches are sent to the client. - /// If the call is asked to respect the estate settings for terrain_raise_limit and - /// terrain_lower_limit, it will clamp terrain updates between these values - /// currently invoked by client_OnModifyTerrain only and not the Commander interfaces - /// should height map deltas be limited to the estate settings limits + /// Scan over changes in the terrain and limit height changes. This enforces the + /// non-estate owner limits on rate of terrain editting. + /// Returns 'true' if any heights were limited. /// - private void CheckForTerrainUpdates(bool respectEstateSettings) + private bool EnforceEstateLimits() { - bool shouldTaint = false; - float[] serialised = m_channel.GetFloatsSerialised(); - int x; - for (x = 0; x < m_channel.Width; x += Constants.TerrainPatchSize) + TerrainData terrData = m_channel.GetTerrainData(); + + bool wasLimited = false; + for(int x = 0; x < terrData.SizeX; x += Constants.TerrainPatchSize) { - int y; - for (y = 0; y < m_channel.Height; y += Constants.TerrainPatchSize) + for(int y = 0; y < terrData.SizeY; y += Constants.TerrainPatchSize) { - if (m_channel.Tainted(x, y)) + if (terrData.IsTaintedAt(x, y, false /* clearOnTest */)) { - // if we should respect the estate settings then - // fixup and height deltas that don't respect them - if (respectEstateSettings && LimitChannelChanges(x, y)) - { - // this has been vetoed, so update - // what we are going to send to the client - serialised = m_channel.GetFloatsSerialised(); - } - - SendToClients(serialised, x, y); - shouldTaint = true; + // If we should respect the estate settings then + // fixup and height deltas that don't respect them. + // Note that LimitChannelChanges() modifies the TerrainChannel with the limited height values. + wasLimited |= LimitChannelChanges(terrData, x, y); } } } - if (shouldTaint) - { - m_scene.EventManager.TriggerTerrainTainted(); - m_tainted = true; - } + return wasLimited; } /// @@ -740,31 +919,30 @@ namespace OpenSim.Region.CoreModules.World.Terrain /// are all within the current estate limits /// true if changes were limited, false otherwise /// - private bool LimitChannelChanges(int xStart, int yStart) + private bool LimitChannelChanges(TerrainData terrData, int xStart, int yStart) { bool changesLimited = false; - double minDelta = m_scene.RegionInfo.RegionSettings.TerrainLowerLimit; - double maxDelta = m_scene.RegionInfo.RegionSettings.TerrainRaiseLimit; + float minDelta = (float)m_scene.RegionInfo.RegionSettings.TerrainLowerLimit; + float maxDelta = (float)m_scene.RegionInfo.RegionSettings.TerrainRaiseLimit; // loop through the height map for this patch and compare it against // the revert map - for (int x = xStart; x < xStart + Constants.TerrainPatchSize; x++) + for(int x = xStart; x < xStart + Constants.TerrainPatchSize; x++) { - for (int y = yStart; y < yStart + Constants.TerrainPatchSize; y++) + for(int y = yStart; y < yStart + Constants.TerrainPatchSize; y++) { - - double requestedHeight = m_channel[x, y]; - double bakedHeight = m_revert[x, y]; - double requestedDelta = requestedHeight - bakedHeight; + float requestedHeight = terrData[x, y]; + float bakedHeight = (float)m_revert[x, y]; + float requestedDelta = requestedHeight - bakedHeight; if (requestedDelta > maxDelta) { - m_channel[x, y] = bakedHeight + maxDelta; + terrData[x, y] = bakedHeight + maxDelta; changesLimited = true; } else if (requestedDelta < minDelta) { - m_channel[x, y] = bakedHeight + minDelta; //as lower is a -ve delta + terrData[x, y] = bakedHeight + minDelta; //as lower is a -ve delta changesLimited = true; } } @@ -775,7 +953,7 @@ namespace OpenSim.Region.CoreModules.World.Terrain private void client_OnLandUndo(IClientAPI client) { - lock (m_undo) + lock(m_undo) { if (m_undo.Count > 0) { @@ -792,14 +970,177 @@ namespace OpenSim.Region.CoreModules.World.Terrain /// A copy of the terrain as a 1D float array of size w*h /// The patch corner to send /// The patch corner to send - private void SendToClients(float[] serialised, int x, int y) + private void SendToClients(TerrainData terrData, int x, int y) { - m_scene.ForEachClient( - delegate(IClientAPI controller) - { controller.SendLayerData( - x / Constants.TerrainPatchSize, y / Constants.TerrainPatchSize, serialised); + if (m_sendTerrainUpdatesByViewDistance) + { + // Add that this patch needs to be sent to the accounting for each client. + lock(m_perClientPatchUpdates) + { + m_scene.ForEachScenePresence(presence => + { + PatchUpdates thisClientUpdates; + if (!m_perClientPatchUpdates.TryGetValue(presence.UUID, out thisClientUpdates)) + { + // There is a ScenePresence without a send patch map. Create one. + thisClientUpdates = new PatchUpdates(terrData, presence); + m_perClientPatchUpdates.Add(presence.UUID, thisClientUpdates); + } + thisClientUpdates.SetByXY(x, y, true); } - ); + ); + } + } + else + { + // Legacy update sending where the update is sent out as soon as noticed + // We know the actual terrain data that is passed is ignored so this passes a dummy heightmap. + //float[] heightMap = terrData.GetFloatsSerialized(); + float[] heightMap = new float[10]; + m_scene.ForEachClient( + delegate(IClientAPI controller) + { + controller.SendLayerData(x / Constants.TerrainPatchSize, + y / Constants.TerrainPatchSize, + heightMap); + } + ); + } + } + + private class PatchesToSend : IComparable + { + public int PatchX; + public int PatchY; + public float Dist; + + public PatchesToSend(int pX, int pY, float pDist) + { + PatchX = pX; + PatchY = pY; + Dist = pDist; + } + + public int CompareTo(PatchesToSend other) + { + return Dist.CompareTo(other.Dist); + } + } + + // Called each frame time to see if there are any patches to send to any of the + // ScenePresences. + // We know this is only called if we are doing view distance patch sending so some + // tests are not made. + // Loop through all the per-client info and send any patches necessary. + private void CheckSendingPatchesToClients() + { + lock(m_perClientPatchUpdates) + { + foreach(PatchUpdates pups in m_perClientPatchUpdates.Values) + { + if (pups.HasUpdates()) + { + // There is something that could be sent to this client. + List toSend = GetModifiedPatchesInViewDistance(pups); + if (toSend.Count > 0) + { + // m_log.DebugFormat("{0} CheckSendingPatchesToClient: sending {1} patches to {2} in region {3}", + // LogHeader, toSend.Count, pups.Presence.Name, m_scene.RegionInfo.RegionName); + // Sort the patches to send by the distance from the presence + toSend.Sort(); + /* old way that sent individual patches + foreach (PatchesToSend pts in toSend) + { + pups.Presence.ControllingClient.SendLayerData(pts.PatchX, pts.PatchY, null); + // presence.ControllingClient.SendLayerData(xs.ToArray(), ys.ToArray(), null, TerrainPatch.LayerType.Land); + } + */ + + // new way that sends all patches to the protocol so they can be sent in one block + int[] xPieces = new int[toSend.Count]; + int[] yPieces = new int[toSend.Count]; + float[] patchPieces = new float[toSend.Count * 2]; + int pieceIndex = 0; + foreach(PatchesToSend pts in toSend) + { + patchPieces[pieceIndex++] = pts.PatchX; + patchPieces[pieceIndex++] = pts.PatchY; + } + pups.Presence.ControllingClient.SendLayerData(-toSend.Count, 0, patchPieces); + } + } + } + } + } + + // Compute a list of modified patches that are within our view distance. + private List GetModifiedPatchesInViewDistance(PatchUpdates pups) + { + List ret = new List(); + + ScenePresence presence = pups.Presence; + if (presence == null) + return ret; + + Vector3 presencePos = presence.AbsolutePosition; + + // Before this distance check, the whole region just showed up. Adding the distance + // check causes different things to happen for the current and adjacent regions. + // So, to keep legacy views, if the region is legacy sized, don't do distance check. + bool isLegacySizedRegion = pups.Terrain.SizeX == Constants.RegionSize && pups.Terrain.SizeY == Constants.RegionSize; + bool shouldCheckViewDistance = m_sendTerrainUpdatesByViewDistance && !isLegacySizedRegion; + + int startX = 0; + int endX = (int)m_scene.RegionInfo.RegionSizeX / Constants.TerrainPatchSize; + int startY = 0; + int endY = (int)m_scene.RegionInfo.RegionSizeY / Constants.TerrainPatchSize; + + // The following only reduces the size of area scanned for updates. Only significant for very large varregions. + if (shouldCheckViewDistance) + { + // Compute the area of patches within our draw distance + startX = (((int)(presencePos.X - presence.DrawDistance)) / Constants.TerrainPatchSize) - 2; + startX = Math.Max(startX, 0); + startX = Math.Min(startX, (int)m_scene.RegionInfo.RegionSizeX / Constants.TerrainPatchSize); + startY = (((int)(presencePos.Y - presence.DrawDistance)) / Constants.TerrainPatchSize) - 2; + startY = Math.Max(startY, 0); + startY = Math.Min(startY, (int)m_scene.RegionInfo.RegionSizeY / Constants.TerrainPatchSize); + endX = (((int)(presencePos.X + presence.DrawDistance)) / Constants.TerrainPatchSize) + 2; + endX = Math.Max(endX, 0); + endX = Math.Min(endX, (int)m_scene.RegionInfo.RegionSizeX / Constants.TerrainPatchSize); + endY = (((int)(presencePos.Y + presence.DrawDistance)) / Constants.TerrainPatchSize) + 2; + endY = Math.Max(endY, 0); + endY = Math.Min(endY, (int)m_scene.RegionInfo.RegionSizeY / Constants.TerrainPatchSize); + } + + // m_log.DebugFormat("{0} GetModifiedPatchesInViewDistance. rName={1}, ddist={2}, apos={3}, cpos={4}, isChild={5}, start=<{6},{7}>, end=<{8},{9}>", + // LogHeader, m_scene.RegionInfo.RegionName, + // presence.DrawDistance, presencePos, presence.CameraPosition, + // isLegacySizeChildRegion, + // startX, startY, endX, endY); + for(int x = startX; x < endX; x++) + { + for(int y = startY; y < endY; y++) + { + //Need to make sure we don't send the same ones over and over + Vector3 patchPos = new Vector3(x * Constants.TerrainPatchSize, y * Constants.TerrainPatchSize, presencePos.Z); + if (pups.GetByPatch(x, y)) + { + //Check which has less distance, camera or avatar position, both have to be done. + //Its not a radius, its a diameter and we add 50 so that it doesn't look like it cuts off + if (!shouldCheckViewDistance + || Util.DistanceLessThan(presencePos, patchPos, presence.DrawDistance + 50) + || Util.DistanceLessThan(presence.CameraPosition, patchPos, presence.DrawDistance + 50)) + { + //They can see it, send it to them + pups.SetByPatch(x, y, false); + float dist = Vector3.DistanceSquared(presencePos, patchPos); + ret.Add(new PatchesToSend(x, y, dist)); + } + } + } + } + return ret; } private void client_OnModifyTerrain(UUID user, float height, float seconds, byte size, byte action, @@ -809,28 +1150,28 @@ namespace OpenSim.Region.CoreModules.World.Terrain bool allowed = false; if (north == south && east == west) { - if (m_painteffects.ContainsKey((StandardTerrainEffects) action)) + if (m_painteffects.ContainsKey((StandardTerrainEffects)action)) { - bool[,] allowMask = new bool[m_channel.Width,m_channel.Height]; + bool[,] allowMask = new bool[m_channel.Width, m_channel.Height]; allowMask.Initialize(); int n = size + 1; if (n > 2) n = 4; - int zx = (int) (west + 0.5); - int zy = (int) (north + 0.5); + int zx = (int)(west + 0.5); + int zy = (int)(north + 0.5); int dx; - for (dx=-n; dx<=n; dx++) + for(dx=-n; dx<=n; dx++) { int dy; - for (dy=-n; dy<=n; dy++) + for(dy=-n; dy<=n; dy++) { int x = zx + dx; int y = zy + dy; - if (x>=0 && y>=0 && x= 0 && y >= 0 && x < m_channel.Width && y < m_channel.Height) { - if (m_scene.Permissions.CanTerraformLand(agentId, new Vector3(x,y,0))) + if (m_scene.Permissions.CanTerraformLand(agentId, new Vector3(x, y, 0))) { allowMask[x, y] = true; allowed = true; @@ -841,10 +1182,12 @@ namespace OpenSim.Region.CoreModules.World.Terrain if (allowed) { StoreUndoState(); - m_painteffects[(StandardTerrainEffects) action].PaintEffect( + m_painteffects[(StandardTerrainEffects)action].PaintEffect( m_channel, allowMask, west, south, height, size, seconds); - CheckForTerrainUpdates(!god); //revert changes outside estate limits + //revert changes outside estate limits + if (!god) + EnforceEstateLimits(); } } else @@ -854,22 +1197,22 @@ namespace OpenSim.Region.CoreModules.World.Terrain } else { - if (m_floodeffects.ContainsKey((StandardTerrainEffects) action)) + if (m_floodeffects.ContainsKey((StandardTerrainEffects)action)) { - bool[,] fillArea = new bool[m_channel.Width,m_channel.Height]; + bool[,] fillArea = new bool[m_channel.Width, m_channel.Height]; fillArea.Initialize(); int x; - for (x = 0; x < m_channel.Width; x++) + for(x = 0; x < m_channel.Width; x++) { int y; - for (y = 0; y < m_channel.Height; y++) + for(y = 0; y < m_channel.Height; y++) { if (x < east && x > west) { if (y < north && y > south) { - if (m_scene.Permissions.CanTerraformLand(agentId, new Vector3(x,y,0))) + if (m_scene.Permissions.CanTerraformLand(agentId, new Vector3(x, y, 0))) { fillArea[x, y] = true; allowed = true; @@ -882,10 +1225,11 @@ namespace OpenSim.Region.CoreModules.World.Terrain if (allowed) { StoreUndoState(); - m_floodeffects[(StandardTerrainEffects) action].FloodEffect( - m_channel, fillArea, size); + m_floodeffects[(StandardTerrainEffects)action].FloodEffect(m_channel, fillArea, size); - CheckForTerrainUpdates(!god); //revert changes outside estate limits + //revert changes outside estate limits + if (!god) + EnforceEstateLimits(); } } else @@ -905,16 +1249,18 @@ namespace OpenSim.Region.CoreModules.World.Terrain InterfaceBakeTerrain(null); //bake terrain does not use the passed in parameter } } - + protected void client_OnUnackedTerrain(IClientAPI client, int patchX, int patchY) { //m_log.Debug("Terrain packet unacked, resending patch: " + patchX + " , " + patchY); - client.SendLayerData(patchX, patchY, m_scene.Heightmap.GetFloatsSerialised()); + // SendLayerData does not use the heightmap parameter. This kludge is so as to not change IClientAPI. + float[] heightMap = new float[10]; + client.SendLayerData(patchX, patchY, heightMap); } private void StoreUndoState() { - lock (m_undo) + lock(m_undo) { if (m_undo.Count > 0) { @@ -935,23 +1281,21 @@ namespace OpenSim.Region.CoreModules.World.Terrain private void InterfaceLoadFile(Object[] args) { - LoadFromFile((string) args[0]); - CheckForTerrainUpdates(); + LoadFromFile((string)args[0]); } private void InterfaceLoadTileFile(Object[] args) { - LoadFromFile((string) args[0], - (int) args[1], - (int) args[2], - (int) args[3], - (int) args[4]); - CheckForTerrainUpdates(); + LoadFromFile((string)args[0], + (int)args[1], + (int)args[2], + (int)args[3], + (int)args[4]); } private void InterfaceSaveFile(Object[] args) { - SaveToFile((string) args[0]); + SaveToFile((string)args[0]); } private void InterfaceSaveTileFile(Object[] args) @@ -971,11 +1315,10 @@ namespace OpenSim.Region.CoreModules.World.Terrain private void InterfaceRevertTerrain(Object[] args) { int x, y; - for (x = 0; x < m_channel.Width; x++) - for (y = 0; y < m_channel.Height; y++) + for(x = 0; x < m_channel.Width; x++) + for(y = 0; y < m_channel.Height; y++) m_channel[x, y] = m_revert[x, y]; - CheckForTerrainUpdates(); } private void InterfaceFlipTerrain(Object[] args) @@ -984,39 +1327,36 @@ namespace OpenSim.Region.CoreModules.World.Terrain if (direction.ToLower().StartsWith("y")) { - for (int x = 0; x < Constants.RegionSize; x++) + for(int x = 0; x < m_channel.Width; x++) { - for (int y = 0; y < Constants.RegionSize / 2; y++) + for(int y = 0; y < m_channel.Height / 2; y++) { double height = m_channel[x, y]; - double flippedHeight = m_channel[x, (int)Constants.RegionSize - 1 - y]; + double flippedHeight = m_channel[x, (int)m_channel.Height - 1 - y]; m_channel[x, y] = flippedHeight; - m_channel[x, (int)Constants.RegionSize - 1 - y] = height; + m_channel[x, (int)m_channel.Height - 1 - y] = height; } } } else if (direction.ToLower().StartsWith("x")) { - for (int y = 0; y < Constants.RegionSize; y++) + for(int y = 0; y < m_channel.Height; y++) { - for (int x = 0; x < Constants.RegionSize / 2; x++) + for(int x = 0; x < m_channel.Width / 2; x++) { double height = m_channel[x, y]; - double flippedHeight = m_channel[(int)Constants.RegionSize - 1 - x, y]; + double flippedHeight = m_channel[(int)m_channel.Width - 1 - x, y]; m_channel[x, y] = flippedHeight; - m_channel[(int)Constants.RegionSize - 1 - x, y] = height; + m_channel[(int)m_channel.Width - 1 - x, y] = height; } } } else { - m_log.Error("Unrecognised direction - need x or y"); + MainConsole.Instance.OutputFormat("ERROR: Unrecognised direction {0} - need x or y", direction); } - - - CheckForTerrainUpdates(); } private void InterfaceRescaleTerrain(Object[] args) @@ -1042,9 +1382,9 @@ namespace OpenSim.Region.CoreModules.World.Terrain int width = m_channel.Width; int height = m_channel.Height; - for (int x = 0; x < width; x++) + for(int x = 0; x < width; x++) { - for (int y = 0; y < height; y++) + for(int y = 0; y < height; y++) { double currHeight = m_channel[x, y]; if (currHeight < currMin) @@ -1065,16 +1405,15 @@ namespace OpenSim.Region.CoreModules.World.Terrain //m_log.InfoFormat("Scale = {0}", scale); // scale the heightmap accordingly - for (int x = 0; x < width; x++) + for(int x = 0; x < width; x++) { - for (int y = 0; y < height; y++) + for(int y = 0; y < height; y++) { - double currHeight = m_channel[x, y] - currMin; - m_channel[x, y] = desiredMin + (currHeight * scale); + double currHeight = m_channel[x, y] - currMin; + m_channel[x, y] = desiredMin + (currHeight * scale); } } - CheckForTerrainUpdates(); } } @@ -1082,64 +1421,73 @@ namespace OpenSim.Region.CoreModules.World.Terrain private void InterfaceElevateTerrain(Object[] args) { int x, y; - for (x = 0; x < m_channel.Width; x++) - for (y = 0; y < m_channel.Height; y++) - m_channel[x, y] += (double) args[0]; - CheckForTerrainUpdates(); + for(x = 0; x < m_channel.Width; x++) + for(y = 0; y < m_channel.Height; y++) + m_channel[x, y] += (double)args[0]; } private void InterfaceMultiplyTerrain(Object[] args) { int x, y; - for (x = 0; x < m_channel.Width; x++) - for (y = 0; y < m_channel.Height; y++) - m_channel[x, y] *= (double) args[0]; - CheckForTerrainUpdates(); + for(x = 0; x < m_channel.Width; x++) + for(y = 0; y < m_channel.Height; y++) + m_channel[x, y] *= (double)args[0]; } private void InterfaceLowerTerrain(Object[] args) { int x, y; - for (x = 0; x < m_channel.Width; x++) - for (y = 0; y < m_channel.Height; y++) - m_channel[x, y] -= (double) args[0]; - CheckForTerrainUpdates(); + for(x = 0; x < m_channel.Width; x++) + for(y = 0; y < m_channel.Height; y++) + m_channel[x, y] -= (double)args[0]; } - private void InterfaceFillTerrain(Object[] args) + public void InterfaceFillTerrain(Object[] args) { int x, y; - for (x = 0; x < m_channel.Width; x++) - for (y = 0; y < m_channel.Height; y++) - m_channel[x, y] = (double) args[0]; - CheckForTerrainUpdates(); + for(x = 0; x < m_channel.Width; x++) + for(y = 0; y < m_channel.Height; y++) + m_channel[x, y] = (double)args[0]; } private void InterfaceMinTerrain(Object[] args) { int x, y; - for (x = 0; x < m_channel.Width; x++) + for(x = 0; x < m_channel.Width; x++) { - for (y = 0; y < m_channel.Height; y++) + for(y = 0; y < m_channel.Height; y++) { m_channel[x, y] = Math.Max((double)args[0], m_channel[x, y]); } } - CheckForTerrainUpdates(); } private void InterfaceMaxTerrain(Object[] args) { int x, y; - for (x = 0; x < m_channel.Width; x++) + for(x = 0; x < m_channel.Width; x++) { - for (y = 0; y < m_channel.Height; y++) + for(y = 0; y < m_channel.Height; y++) { m_channel[x, y] = Math.Min((double)args[0], m_channel[x, y]); } } - CheckForTerrainUpdates(); + } + + private void InterfaceShow(Object[] args) + { + Vector2 point; + + if (!ConsoleUtil.TryParseConsole2DVector((string)args[0], null, out point)) + { + Console.WriteLine("ERROR: {0} is not a valid vector", args[0]); + return; + } + + double height = m_channel[(int)point.X, (int)point.Y]; + + Console.WriteLine("Terrain height at {0} is {1}", point, height); } private void InterfaceShowDebugStats(Object[] args) @@ -1149,10 +1497,10 @@ namespace OpenSim.Region.CoreModules.World.Terrain double sum = 0; int x; - for (x = 0; x < m_channel.Width; x++) + for(x = 0; x < m_channel.Width; x++) { int y; - for (y = 0; y < m_channel.Height; y++) + for(y = 0; y < m_channel.Height; y++) { sum += m_channel[x, y]; if (max < m_channel[x, y]) @@ -1164,13 +1512,13 @@ namespace OpenSim.Region.CoreModules.World.Terrain double avg = sum / (m_channel.Height * m_channel.Width); - m_log.Info("Channel " + m_channel.Width + "x" + m_channel.Height); - m_log.Info("max/min/avg/sum: " + max + "/" + min + "/" + avg + "/" + sum); + MainConsole.Instance.OutputFormat("Channel {0}x{1}", m_channel.Width, m_channel.Height); + MainConsole.Instance.OutputFormat("max/min/avg/sum: {0}/{1}/{2}/{3}", max, min, avg, sum); } private void InterfaceEnableExperimentalBrushes(Object[] args) { - if ((bool) args[0]) + if ((bool)args[0]) { m_painteffects[StandardTerrainEffects.Revert] = new WeatherSphere(); m_painteffects[StandardTerrainEffects.Flatten] = new OlsenSphere(); @@ -1185,28 +1533,30 @@ namespace OpenSim.Region.CoreModules.World.Terrain private void InterfaceRunPluginEffect(Object[] args) { string firstArg = (string)args[0]; + if (firstArg == "list") { - m_log.Info("List of loaded plugins"); - foreach (KeyValuePair kvp in m_plugineffects) + MainConsole.Instance.Output("List of loaded plugins"); + foreach(KeyValuePair kvp in m_plugineffects) { - m_log.Info(kvp.Key); + MainConsole.Instance.Output(kvp.Key); } return; } + if (firstArg == "reload") { LoadPlugins(); return; } + if (m_plugineffects.ContainsKey(firstArg)) { m_plugineffects[firstArg].RunEffect(m_channel); - CheckForTerrainUpdates(); } else { - m_log.Warn("No such plugin effect loaded."); + MainConsole.Instance.Output("WARNING: No such plugin effect {0} loaded.", firstArg); } } @@ -1295,12 +1645,17 @@ namespace OpenSim.Region.CoreModules.World.Terrain new Command("stats", CommandIntentions.COMMAND_STATISTICAL, InterfaceShowDebugStats, "Shows some information about the regions heightmap for debugging purposes."); + Command showCommand = + new Command("show", CommandIntentions.COMMAND_NON_HAZARDOUS, InterfaceShow, + "Shows terrain height at a given co-ordinate."); + showCommand.AddArgument("point", "point in , format with no spaces (e.g. 45,45)", "String"); + Command experimentalBrushesCommand = new Command("newbrushes", CommandIntentions.COMMAND_HAZARDOUS, InterfaceEnableExperimentalBrushes, "Enables experimental brushes which replace the standard terrain brushes. WARNING: This is a debug setting and may be removed at any time."); experimentalBrushesCommand.AddArgument("Enabled?", "true / false - Enable new brushes", "Boolean"); - //Plugins + // Plugins Command pluginRunCommand = new Command("effect", CommandIntentions.COMMAND_HAZARDOUS, InterfaceRunPluginEffect, "Runs a specified plugin effect"); 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 m_commander.RegisterCommand("bake", bakeRegionCommand); m_commander.RegisterCommand("revert", revertRegionCommand); m_commander.RegisterCommand("newbrushes", experimentalBrushesCommand); + m_commander.RegisterCommand("show", showCommand); m_commander.RegisterCommand("stats", showDebugStatsCommand); m_commander.RegisterCommand("effect", pluginRunCommand); m_commander.RegisterCommand("flip", flipCommand); @@ -1325,10 +1681,66 @@ namespace OpenSim.Region.CoreModules.World.Terrain // Add this to our scene so scripts can call these functions m_scene.RegisterModuleCommander(m_commander); + + // Add Modify command to Scene, since Command object requires fixed-length arglists + m_scene.AddCommand("Terrain", this, "terrain modify", + "terrain modify [] []", + "Modifies the terrain as instructed." + + "\nEach operation can be limited to an area of effect:" + + "\n * -ell=x,y,rx[,ry] constrains the operation to an ellipse centred at x,y" + + "\n * -rec=x,y,dx[,dy] constrains the operation to a rectangle based at x,y" + + "\nEach operation can have its effect tapered based on distance from centre:" + + "\n * elliptical operations taper as cones" + + "\n * rectangular operations taper as pyramids" + , + ModifyCommand); + } + public void ModifyCommand(string module, string[] cmd) + { + string result; + Scene scene = SceneManager.Instance.CurrentScene; + if ((scene != null) && (scene != m_scene)) + { + result = String.Empty; + } + else if (cmd.Length > 2) + { + string operationType = cmd[2]; - #endregion + + ITerrainModifier operation; + if (!m_modifyOperations.TryGetValue(operationType, out operation)) + { + result = String.Format("Terrain Modify \"{0}\" not found.", operationType); + } + else if ((cmd.Length > 3) && (cmd[3] == "usage")) + { + result = "Usage: " + operation.GetUsage(); + } + else + { + result = operation.ModifyTerrain(m_channel, cmd); + } + + if (result == String.Empty) + { + result = "Modified terrain"; + m_log.DebugFormat("Performed terrain operation {0}", operationType); + } + } + else + { + result = "Usage: ..."; + } + if (result != String.Empty) + { + MainConsole.Instance.Output(result); + } + } + +#endregion } } 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 @@ +/* + * Copyright (c) Contributors, http://opensimulator.org/ + * See CONTRIBUTORS.TXT for a full list of copyright holders. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the OpenSimulator Project nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +using System; +using NUnit.Framework; +using OpenSim.Framework; +using OpenSim.Region.CoreModules.World.Terrain; +using OpenSim.Region.Framework.Scenes; +using OpenSim.Tests.Common; + +namespace OpenSim.Region.CoreModules.Terrain.Tests +{ + public class TerrainModuleTests : OpenSimTestCase + { + [Test] + public void TestTerrainFill() + { + TestHelpers.InMethod(); +// TestHelpers.EnableLogging(); + + //UUID userId = TestHelpers.ParseTail(0x1); + + TerrainModule tm = new TerrainModule(); + Scene scene = new SceneHelpers().SetupScene(); + SceneHelpers.SetupSceneModules(scene, tm); + + // Fillheight of 30 + { + double fillHeight = 30; + + tm.InterfaceFillTerrain(new object[] { fillHeight }); + + double height = scene.Heightmap[128, 128]; + + Assert.AreEqual(fillHeight, height); + } + + // Max fillheight of 30 + // 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 + { + double fillHeight = 508; + + tm.InterfaceFillTerrain(new object[] { fillHeight }); + + double height = scene.Heightmap[128, 128]; + + Assert.AreEqual(fillHeight, height); + } + } + } +} \ 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 [Test] public void BrushTest() { + int midRegion = (int)Constants.RegionSize / 2; + + // Create a mask that covers only the left half of the region bool[,] allowMask = new bool[(int)Constants.RegionSize, 256]; int x; int y; - for (x = 0; x < (int)((int)Constants.RegionSize * 0.5f); x++) + for (x = 0; x < midRegion; x++) { for (y = 0; y < (int)Constants.RegionSize; y++) { @@ -57,13 +60,12 @@ namespace OpenSim.Region.CoreModules.World.Terrain.Tests TerrainChannel map = new TerrainChannel((int)Constants.RegionSize, (int)Constants.RegionSize); ITerrainPaintableEffect effect = new RaiseSphere(); - effect.PaintEffect(map, allowMask, (int)Constants.RegionSize * 0.5f, (int)Constants.RegionSize * 0.5f, -1.0, 2, 0.1); - Assert.That(map[127, (int)((int)Constants.RegionSize * 0.5f)] > 0.0, "Raise brush should raising value at this point (127,128)."); - Assert.That(map[124, (int)((int)Constants.RegionSize * 0.5f)] > 0.0, "Raise brush should raising value at this point (124,128)."); - Assert.That(map[123, (int)((int)Constants.RegionSize * 0.5f)] == 0.0, "Raise brush should not change value at this point (123,128)."); - Assert.That(map[128, (int)((int)Constants.RegionSize * 0.5f)] == 0.0, "Raise brush should not change value at this point (128,128)."); - Assert.That(map[0, (int)((int)Constants.RegionSize * 0.5f)] == 0.0, "Raise brush should not change value at this point (0,128)."); - + effect.PaintEffect(map, allowMask, midRegion, midRegion, -1.0, 2, 6.0); + Assert.That(map[127, midRegion] > 0.0, "Raise brush should raising value at this point (127,128)."); + Assert.That(map[125, midRegion] > 0.0, "Raise brush should raising value at this point (124,128)."); + Assert.That(map[120, midRegion] == 0.0, "Raise brush should not change value at this point (120,128)."); + Assert.That(map[128, midRegion] == 0.0, "Raise brush should not change value at this point (128,128)."); + Assert.That(map[0, midRegion] == 0.0, "Raise brush should not change value at this point (0,128)."); // // Test LowerSphere // @@ -77,13 +79,13 @@ namespace OpenSim.Region.CoreModules.World.Terrain.Tests } effect = new LowerSphere(); - effect.PaintEffect(map, allowMask, ((int)Constants.RegionSize * 0.5f), ((int)Constants.RegionSize * 0.5f), -1.0, 2, 6.0); - 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)."); - 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)."); - Assert.That(map[124, (int)((int)Constants.RegionSize * 0.5f)] < 1.0, "Lower brush should lowering value at this point (124,128)."); - Assert.That(map[123, (int)((int)Constants.RegionSize * 0.5f)] == 1.0, "Lower brush should not change value at this point (123,128)."); - Assert.That(map[128, (int)((int)Constants.RegionSize * 0.5f)] == 1.0, "Lower brush should not change value at this point (128,128)."); - Assert.That(map[0, (int)((int)Constants.RegionSize * 0.5f)] == 1.0, "Lower brush should not change value at this point (0,128)."); + effect.PaintEffect(map, allowMask, midRegion, midRegion, -1.0, 2, 6.0); + Assert.That(map[127, midRegion] >= 0.0, "Lower should not lowering value below 0.0 at this point (127,128)."); + Assert.That(map[127, midRegion] == 0.0, "Lower brush should lowering value to 0.0 at this point (127,128)."); + Assert.That(map[125, midRegion] < 1.0, "Lower brush should lowering value at this point (124,128)."); + Assert.That(map[120, midRegion] == 1.0, "Lower brush should not change value at this point (120,128)."); + Assert.That(map[128, midRegion] == 1.0, "Lower brush should not change value at this point (128,128)."); + Assert.That(map[0, midRegion] == 1.0, "Lower brush should not change value at this point (0,128)."); } [Test] @@ -100,10 +102,6 @@ namespace OpenSim.Region.CoreModules.World.Terrain.Tests x[0, 0] -= 1.0; Assert.That(x[0, 0] == 4.0, "Terrain addition/subtraction error."); - x[0, 0] = Math.PI; - double[,] doublesExport = x.GetDoubles(); - Assert.That(doublesExport[0, 0] == Math.PI, "Export to double[,] array not working correctly."); - x[0, 0] = 1.0; float[] floatsExport = x.GetFloatsSerialised(); Assert.That(floatsExport[0] == 1.0f, "Export to float[] not working correctly."); -- cgit v1.1