2008-05-01 14:31:30 +00:00
/ *
* 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 .
2009-06-01 06:37:14 +00:00
* * Neither the name of the OpenSimulator Project nor the
2008-05-01 14:31:30 +00:00
* 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 .
* /
2009-02-12 18:59:45 +00:00
using System ;
using System.Collections.Generic ;
using System.IO ;
using System.Reflection ;
2009-12-04 18:26:58 +00:00
using System.Net ;
2009-02-12 18:59:45 +00:00
using log4net ;
using Nini.Config ;
using OpenMetaverse ;
2012-11-13 05:14:58 +00:00
using Mono.Addins ;
2009-02-12 18:59:45 +00:00
using OpenSim.Framework ;
using OpenSim.Region.CoreModules.Framework.InterfaceCommander ;
using OpenSim.Region.CoreModules.World.Terrain.FileLoaders ;
using OpenSim.Region.CoreModules.World.Terrain.FloodBrushes ;
using OpenSim.Region.CoreModules.World.Terrain.PaintBrushes ;
using OpenSim.Region.Framework.Interfaces ;
using OpenSim.Region.Framework.Scenes ;
2009-02-10 13:10:57 +00:00
namespace OpenSim.Region.CoreModules.World.Terrain
2008-05-01 14:31:30 +00:00
{
2012-11-13 05:14:58 +00:00
[Extension(Path = "/OpenSim/RegionModules", NodeName = "RegionModule", Id = "TerrainModule")]
2009-04-13 21:23:33 +00:00
public class TerrainModule : INonSharedRegionModule , ICommandableModule , ITerrainModule
2008-05-01 14:31:30 +00:00
{
#region StandardTerrainEffects enum
/// <summary>
/// A standard set of terrain brushes and effects recognised by viewers
/// </summary>
public enum StandardTerrainEffects : byte
{
Flatten = 0 ,
Raise = 1 ,
Lower = 2 ,
Smooth = 3 ,
Noise = 4 ,
Revert = 5 ,
// Extended brushes
Erode = 255 ,
Weather = 254 ,
Olsen = 253
}
# endregion
private static readonly ILog m_log = LogManager . GetLogger ( MethodBase . GetCurrentMethod ( ) . DeclaringType ) ;
2010-07-30 19:54:02 +00:00
2009-02-05 21:35:59 +00:00
private readonly Commander m_commander = new Commander ( "terrain" ) ;
2008-05-01 14:31:30 +00:00
private readonly Dictionary < StandardTerrainEffects , ITerrainFloodEffect > m_floodeffects =
new Dictionary < StandardTerrainEffects , ITerrainFloodEffect > ( ) ;
private readonly Dictionary < string , ITerrainLoader > m_loaders = new Dictionary < string , ITerrainLoader > ( ) ;
private readonly Dictionary < StandardTerrainEffects , ITerrainPaintableEffect > m_painteffects =
new Dictionary < StandardTerrainEffects , ITerrainPaintableEffect > ( ) ;
private ITerrainChannel m_channel ;
private Dictionary < string , ITerrainEffect > m_plugineffects ;
private ITerrainChannel m_revert ;
private Scene m_scene ;
2009-04-19 13:33:46 +00:00
private volatile bool m_tainted ;
2011-07-18 01:06:06 +00:00
private readonly Stack < LandUndoState > m_undo = new Stack < LandUndoState > ( 5 ) ;
2008-05-01 14:31:30 +00:00
2012-03-16 20:08:05 +00:00
private String m_InitialTerrain = "pinhead-island" ;
2011-11-17 22:13:32 +00:00
/// <summary>
/// Human readable list of terrain file extensions that are supported.
/// </summary>
private string m_supportedFileExtensions = "" ;
2012-04-03 14:56:51 +00:00
//For terrain save-tile file extensions
private string m_supportFileExtensionsForTileSave = "" ;
2008-05-01 14:31:30 +00:00
#region ICommandableModule Members
public ICommander CommandInterface
{
get { return m_commander ; }
}
# endregion
2009-04-13 21:23:33 +00:00
#region INonSharedRegionModule Members
2008-05-01 14:31:30 +00:00
/// <summary>
/// Creates and initialises a terrain module for a region
/// </summary>
/// <param name="scene">Region initialising</param>
/// <param name="config">Config for the region</param>
2009-04-13 21:23:33 +00:00
public void Initialise ( IConfigSource config )
{
2012-03-16 20:08:05 +00:00
IConfig terrainConfig = config . Configs [ "Terrain" ] ;
if ( terrainConfig ! = null )
m_InitialTerrain = terrainConfig . GetString ( "InitialTerrain" , m_InitialTerrain ) ;
2009-04-13 21:23:33 +00:00
}
public void AddRegion ( Scene scene )
2008-05-01 14:31:30 +00:00
{
m_scene = scene ;
// Install terrain module in the simulator
2009-04-13 21:23:33 +00:00
lock ( m_scene )
2008-05-01 14:31:30 +00:00
{
2009-04-13 21:23:33 +00:00
if ( m_scene . Heightmap = = null )
2008-05-01 14:31:30 +00:00
{
2012-03-16 20:08:05 +00:00
m_channel = new TerrainChannel ( m_InitialTerrain ) ;
2008-05-01 14:31:30 +00:00
m_scene . Heightmap = m_channel ;
m_revert = new TerrainChannel ( ) ;
UpdateRevertMap ( ) ;
}
2009-04-13 21:23:33 +00:00
else
{
m_channel = m_scene . Heightmap ;
m_revert = new TerrainChannel ( ) ;
UpdateRevertMap ( ) ;
}
m_scene . RegisterModuleInterface < ITerrainModule > ( this ) ;
m_scene . EventManager . OnNewClient + = EventManager_OnNewClient ;
m_scene . EventManager . OnPluginConsole + = EventManager_OnPluginConsole ;
m_scene . EventManager . OnTerrainTick + = EventManager_OnTerrainTick ;
2008-05-01 14:31:30 +00:00
}
2009-04-13 21:23:33 +00:00
InstallDefaultEffects ( ) ;
LoadPlugins ( ) ;
2011-11-17 22:13:32 +00:00
// Generate user-readable extensions list
string supportedFilesSeparator = "" ;
2012-04-03 14:56:51 +00:00
string supportedFilesSeparatorForTileSave = "" ;
2011-11-17 22:13:32 +00:00
2012-04-03 14:56:51 +00:00
m_supportFileExtensionsForTileSave = "" ;
2011-11-17 22:13:32 +00:00
foreach ( KeyValuePair < string , ITerrainLoader > loader in m_loaders )
{
m_supportedFileExtensions + = supportedFilesSeparator + loader . Key + " (" + loader . Value + ")" ;
supportedFilesSeparator = ", " ;
2012-04-03 14:56:51 +00:00
//For terrain save-tile file extensions
if ( loader . Value . SupportsTileSave ( ) = = true )
{
m_supportFileExtensionsForTileSave + = supportedFilesSeparatorForTileSave + loader . Key + " (" + loader . Value + ")" ;
supportedFilesSeparatorForTileSave = ", " ;
}
2011-11-17 22:13:32 +00:00
}
2008-05-01 14:31:30 +00:00
}
2009-04-14 03:22:02 +00:00
public void RegionLoaded ( Scene scene )
{
2011-01-28 03:57:08 +00:00
//Do this here to give file loaders time to initialize and
//register their supported file extensions and file formats.
InstallInterfaces ( ) ;
2009-04-14 03:22:02 +00:00
}
2009-04-13 21:23:33 +00:00
public void RemoveRegion ( Scene scene )
2008-05-01 14:31:30 +00:00
{
2009-04-13 21:23:33 +00:00
lock ( m_scene )
{
// remove the commands
m_scene . UnregisterModuleCommander ( m_commander . Name ) ;
// remove the event-handlers
m_scene . EventManager . OnTerrainTick - = EventManager_OnTerrainTick ;
m_scene . EventManager . OnPluginConsole - = EventManager_OnPluginConsole ;
m_scene . EventManager . OnNewClient - = EventManager_OnNewClient ;
// remove the interface
m_scene . UnregisterModuleInterface < ITerrainModule > ( this ) ;
}
2008-05-01 14:31:30 +00:00
}
public void Close ( )
{
}
2009-08-10 22:08:22 +00:00
public Type ReplaceableInterface
2009-07-10 20:17:13 +00:00
{
get { return null ; }
}
2008-05-01 14:31:30 +00:00
public string Name
{
get { return "TerrainModule" ; }
}
# endregion
#region ITerrainModule Members
2010-02-14 21:41:57 +00:00
public void UndoTerrain ( ITerrainChannel channel )
{
m_channel = channel ;
}
2008-05-01 14:31:30 +00:00
/// <summary>
/// Loads a terrain file from disk and installs it in the scene.
/// </summary>
/// <param name="filename">Filename to terrain file. Type is determined by extension.</param>
public void LoadFromFile ( string filename )
{
foreach ( KeyValuePair < string , ITerrainLoader > loader in m_loaders )
{
if ( filename . EndsWith ( loader . Key ) )
{
lock ( m_scene )
{
try
{
ITerrainChannel channel = loader . Value . LoadFile ( filename ) ;
2008-10-15 21:20:07 +00:00
if ( channel . Width ! = Constants . RegionSize | | channel . Height ! = Constants . RegionSize )
{
// 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_log . DebugFormat ( "[TERRAIN]: Loaded terrain, wd/ht: {0}/{1}" , channel . Width , channel . Height ) ;
2008-05-01 14:31:30 +00:00
m_scene . Heightmap = channel ;
m_channel = channel ;
UpdateRevertMap ( ) ;
}
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 )
{
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 ) ) ;
}
2008-10-15 21:20:07 +00:00
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 ) ) ;
}
2008-05-01 14:31:30 +00:00
}
CheckForTerrainUpdates ( ) ;
m_log . Info ( "[TERRAIN]: File (" + filename + ") loaded successfully" ) ;
return ;
}
}
2009-04-13 21:23:33 +00:00
2009-01-05 15:43:30 +00:00
m_log . Error ( "[TERRAIN]: Unable to load heightmap, no file loader available for that format." ) ;
2008-05-01 14:31:30 +00:00
throw new TerrainException ( String . Format ( "unable to load heightmap from file {0}: no loader available for that format" , filename ) ) ;
}
/// <summary>
/// Saves the current heightmap to a specified file.
/// </summary>
/// <param name="filename">The destination filename</param>
public void SaveToFile ( string filename )
{
try
{
foreach ( KeyValuePair < string , ITerrainLoader > loader in m_loaders )
{
if ( filename . EndsWith ( loader . Key ) )
{
loader . Value . SaveFile ( filename , m_channel ) ;
2011-11-17 22:13:32 +00:00
m_log . InfoFormat ( "[TERRAIN]: Saved terrain from {0} to {1}" , m_scene . RegionInfo . RegionName , filename ) ;
2008-05-01 14:31:30 +00:00
return ;
}
}
}
2009-04-04 22:26:38 +00:00
catch ( IOException ioe )
{
m_log . Error ( String . Format ( "[TERRAIN]: Unable to save to {0}, {1}" , filename , ioe . Message ) ) ;
}
2011-11-17 22:13:32 +00:00
m_log . ErrorFormat (
"[TERRAIN]: Could not save terrain from {0} to {1}. Valid file extensions are {2}" ,
m_scene . RegionInfo . RegionName , filename , m_supportedFileExtensions ) ;
2008-05-01 14:31:30 +00:00
}
2008-08-18 00:39:10 +00:00
2009-12-04 18:26:58 +00:00
/// <summary>
/// Loads a terrain file from the specified URI
/// </summary>
/// <param name="filename">The name of the terrain to load</param>
/// <param name="pathToTerrainHeightmap">The URI to the terrain height map</param>
public void LoadFromStream ( string filename , Uri pathToTerrainHeightmap )
{
LoadFromStream ( filename , URIFetch ( pathToTerrainHeightmap ) ) ;
}
2008-06-29 19:21:43 +00:00
/// <summary>
/// Loads a terrain file from a stream and installs it in the scene.
/// </summary>
/// <param name="filename">Filename to terrain file. Type is determined by extension.</param>
/// <param name="stream"></param>
public void LoadFromStream ( string filename , Stream stream )
{
foreach ( KeyValuePair < string , ITerrainLoader > loader in m_loaders )
{
2009-12-04 18:26:58 +00:00
if ( filename . EndsWith ( loader . Key ) )
2008-06-29 19:21:43 +00:00
{
lock ( m_scene )
{
try
{
ITerrainChannel channel = loader . Value . LoadStream ( stream ) ;
m_scene . Heightmap = channel ;
m_channel = channel ;
UpdateRevertMap ( ) ;
}
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 ) ) ;
}
}
2009-04-13 21:23:33 +00:00
2008-06-29 19:21:43 +00:00
CheckForTerrainUpdates ( ) ;
m_log . Info ( "[TERRAIN]: File (" + filename + ") loaded successfully" ) ;
return ;
}
}
2009-01-05 15:43:30 +00:00
m_log . Error ( "[TERRAIN]: Unable to load heightmap, no file loader available for that format." ) ;
2008-06-29 19:21:43 +00:00
throw new TerrainException ( String . Format ( "unable to load heightmap from file {0}: no loader available for that format" , filename ) ) ;
}
2009-12-04 18:26:58 +00:00
private static Stream URIFetch ( Uri uri )
{
HttpWebRequest request = ( HttpWebRequest ) WebRequest . Create ( uri ) ;
// request.Credentials = credentials;
request . ContentLength = 0 ;
request . KeepAlive = false ;
WebResponse response = request . GetResponse ( ) ;
Stream file = response . GetResponseStream ( ) ;
if ( response . ContentLength = = 0 )
throw new Exception ( String . Format ( "{0} returned an empty file" , uri . ToString ( ) ) ) ;
// return new BufferedStream(file, (int) response.ContentLength);
return new BufferedStream ( file , 1000000 ) ;
}
2008-10-07 14:49:12 +00:00
/// <summary>
/// Modify Land
/// </summary>
/// <param name="pos">Land-position (X,Y,0)</param>
/// <param name="size">The size of the brush (0=small, 1=medium, 2=large)</param>
/// <param name="action">0=LAND_LEVEL, 1=LAND_RAISE, 2=LAND_LOWER, 3=LAND_SMOOTH, 4=LAND_NOISE, 5=LAND_REVERT</param>
/// <param name="agentId">UUID of script-owner</param>
2008-11-11 01:47:40 +00:00
public void ModifyTerrain ( UUID user , Vector3 pos , byte size , byte action , UUID agentId )
2008-10-07 14:49:12 +00:00
{
2009-06-18 23:02:12 +00:00
float duration = 0.25f ;
if ( action = = 0 )
duration = 4.0f ;
2011-11-17 22:13:32 +00:00
2009-06-18 23:02:12 +00:00
client_OnModifyTerrain ( user , ( float ) pos . Z , duration , size , action , pos . Y , pos . X , pos . Y , pos . X , agentId ) ;
2009-04-13 21:23:33 +00:00
}
2008-10-07 14:49:12 +00:00
2008-06-29 19:21:43 +00:00
/// <summary>
/// Saves the current heightmap to a specified stream.
/// </summary>
/// <param name="filename">The destination filename. Used here only to identify the image type</param>
/// <param name="stream"></param>
public void SaveToStream ( string filename , Stream stream )
{
try
{
foreach ( KeyValuePair < string , ITerrainLoader > loader in m_loaders )
{
if ( filename . EndsWith ( loader . Key ) )
{
loader . Value . SaveStream ( stream , m_channel ) ;
return ;
}
}
}
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" ) ) ;
}
2008-08-18 00:39:10 +00:00
}
2008-05-01 14:31:30 +00:00
2009-04-19 13:33:46 +00:00
public void TaintTerrain ( )
{
CheckForTerrainUpdates ( ) ;
}
2008-05-01 14:31:30 +00:00
#region Plugin Loading Methods
private void LoadPlugins ( )
{
m_plugineffects = new Dictionary < string , ITerrainEffect > ( ) ;
2012-08-26 21:53:59 +00:00
LoadPlugins ( Assembly . GetCallingAssembly ( ) ) ;
2010-07-30 19:54:02 +00:00
string plugineffectsPath = "Terrain" ;
2008-05-01 14:31:30 +00:00
// Load the files in the Terrain/ dir
2010-07-30 19:54:02 +00:00
if ( ! Directory . Exists ( plugineffectsPath ) )
return ;
string [ ] files = Directory . GetFiles ( plugineffectsPath ) ;
2008-05-01 14:31:30 +00:00
foreach ( string file in files )
{
m_log . Info ( "Loading effects in " + file ) ;
try
{
Assembly library = Assembly . LoadFrom ( file ) ;
2012-08-26 21:53:59 +00:00
LoadPlugins ( library ) ;
}
catch ( BadImageFormatException )
{
}
}
}
private void LoadPlugins ( Assembly library )
{
2012-08-27 00:50:53 +00:00
foreach ( Type pluginType in library . GetTypes ( ) )
{
try
{
if ( pluginType . IsAbstract | | pluginType . IsNotPublic )
continue ;
2008-05-01 14:31:30 +00:00
2012-08-27 00:50:53 +00:00
string typeName = pluginType . Name ;
2008-05-27 21:06:48 +00:00
2012-08-27 00:50:53 +00:00
if ( pluginType . GetInterface ( "ITerrainEffect" , false ) ! = null )
{
ITerrainEffect terEffect = ( ITerrainEffect ) Activator . CreateInstance ( library . GetType ( pluginType . ToString ( ) ) ) ;
2008-05-27 21:06:48 +00:00
2012-08-27 00:50:53 +00:00
InstallPlugin ( typeName , terEffect ) ;
}
else if ( pluginType . GetInterface ( "ITerrainLoader" , false ) ! = null )
{
ITerrainLoader terLoader = ( ITerrainLoader ) Activator . CreateInstance ( library . GetType ( pluginType . ToString ( ) ) ) ;
m_loaders [ terLoader . FileExtension ] = terLoader ;
m_log . Info ( "L ... " + typeName ) ;
2008-05-01 14:31:30 +00:00
}
2012-08-27 00:50:53 +00:00
}
catch ( AmbiguousMatchException )
{
}
}
2008-05-01 14:31:30 +00:00
}
2008-05-27 21:06:48 +00:00
public void InstallPlugin ( string pluginName , ITerrainEffect effect )
{
lock ( m_plugineffects )
{
if ( ! m_plugineffects . ContainsKey ( pluginName ) )
{
m_plugineffects . Add ( pluginName , effect ) ;
m_log . Info ( "E ... " + pluginName ) ;
}
else
{
2008-05-28 23:52:24 +00:00
m_plugineffects [ pluginName ] = effect ;
2013-01-03 00:03:29 +00:00
m_log . Info ( "E ... " + pluginName + " (Replaced)" ) ;
2008-05-27 21:06:48 +00:00
}
}
}
2008-05-01 14:31:30 +00:00
# endregion
2008-05-01 18:04:42 +00:00
# endregion
2008-05-01 14:31:30 +00:00
/// <summary>
/// Installs into terrain module the standard suite of brushes
/// </summary>
private void InstallDefaultEffects ( )
{
// Draggable Paint Brush Effects
m_painteffects [ StandardTerrainEffects . Raise ] = new RaiseSphere ( ) ;
m_painteffects [ StandardTerrainEffects . Lower ] = new LowerSphere ( ) ;
m_painteffects [ StandardTerrainEffects . Smooth ] = new SmoothSphere ( ) ;
m_painteffects [ StandardTerrainEffects . Noise ] = new NoiseSphere ( ) ;
m_painteffects [ StandardTerrainEffects . Flatten ] = new FlattenSphere ( ) ;
m_painteffects [ StandardTerrainEffects . Revert ] = new RevertSphere ( m_revert ) ;
m_painteffects [ StandardTerrainEffects . Erode ] = new ErodeSphere ( ) ;
m_painteffects [ StandardTerrainEffects . Weather ] = new WeatherSphere ( ) ;
m_painteffects [ StandardTerrainEffects . Olsen ] = new OlsenSphere ( ) ;
// Area of effect selection effects
m_floodeffects [ StandardTerrainEffects . Raise ] = new RaiseArea ( ) ;
m_floodeffects [ StandardTerrainEffects . Lower ] = new LowerArea ( ) ;
m_floodeffects [ StandardTerrainEffects . Smooth ] = new SmoothArea ( ) ;
m_floodeffects [ StandardTerrainEffects . Noise ] = new NoiseArea ( ) ;
m_floodeffects [ StandardTerrainEffects . Flatten ] = new FlattenArea ( ) ;
m_floodeffects [ StandardTerrainEffects . Revert ] = new RevertArea ( m_revert ) ;
// Filesystem load/save loaders
m_loaders [ ".r32" ] = new RAW32 ( ) ;
m_loaders [ ".f32" ] = m_loaders [ ".r32" ] ;
m_loaders [ ".ter" ] = new Terragen ( ) ;
m_loaders [ ".raw" ] = new LLRAW ( ) ;
m_loaders [ ".jpg" ] = new JPEG ( ) ;
m_loaders [ ".jpeg" ] = m_loaders [ ".jpg" ] ;
m_loaders [ ".bmp" ] = new BMP ( ) ;
m_loaders [ ".png" ] = new PNG ( ) ;
m_loaders [ ".gif" ] = new GIF ( ) ;
m_loaders [ ".tif" ] = new TIFF ( ) ;
m_loaders [ ".tiff" ] = m_loaders [ ".tif" ] ;
}
/// <summary>
/// Saves the current state of the region into the revert map buffer.
/// </summary>
public void UpdateRevertMap ( )
{
int x ;
for ( x = 0 ; x < m_channel . Width ; x + + )
{
int y ;
for ( y = 0 ; y < m_channel . Height ; y + + )
{
m_revert [ x , y ] = m_channel [ x , y ] ;
}
}
}
/// <summary>
/// Loads a tile from a larger terrain file and installs it into the region.
/// </summary>
/// <param name="filename">The terrain file to load</param>
/// <param name="fileWidth">The width of the file in units</param>
/// <param name="fileHeight">The height of the file in units</param>
/// <param name="fileStartX">Where to begin our slice</param>
/// <param name="fileStartY">Where to begin our slice</param>
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 ;
if ( offsetX > = 0 & & offsetX < fileWidth & & offsetY > = 0 & & offsetY < fileHeight )
{
// this region is included in the tile request
foreach ( KeyValuePair < string , ITerrainLoader > loader in m_loaders )
{
if ( filename . EndsWith ( loader . Key ) )
{
lock ( m_scene )
{
ITerrainChannel channel = loader . Value . LoadFile ( filename , offsetX , offsetY ,
fileWidth , fileHeight ,
( int ) Constants . RegionSize ,
( int ) Constants . RegionSize ) ;
m_scene . Heightmap = channel ;
m_channel = channel ;
UpdateRevertMap ( ) ;
}
2011-11-17 22:13:32 +00:00
2008-05-01 14:31:30 +00:00
return ;
}
}
}
}
2011-04-13 02:46:27 +00:00
/// <summary>
2012-03-24 02:16:44 +00:00
/// Save a number of map tiles to a single big image file.
2011-04-13 02:46:27 +00:00
/// </summary>
2012-03-24 02:16:44 +00:00
/// <remarks>
/// If the image file already exists then the tiles saved will replace those already in the file - other tiles
/// will be untouched.
/// </remarks>
2011-04-13 02:46:27 +00:00
/// <param name="filename">The terrain file to save</param>
2012-03-24 02:16:44 +00:00
/// <param name="fileWidth">The number of tiles to save along the X axis.</param>
/// <param name="fileHeight">The number of tiles to save along the Y axis.</param>
/// <param name="fileStartX">The map x co-ordinate at which to begin the save.</param>
/// <param name="fileStartY">The may y co-ordinate at which to begin the save.</param>
2011-04-13 02:46:27 +00:00
public void SaveToFile ( 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 ;
2012-03-24 02:16:44 +00:00
if ( offsetX < 0 | | offsetX > = fileWidth | | offsetY < 0 | | offsetY > = fileHeight )
2011-04-13 02:46:27 +00:00
{
2012-03-24 02:16:44 +00:00
MainConsole . Instance . OutputFormat (
"ERROR: file width + minimum X tile and file height + minimum Y tile must incorporate the current region at ({0},{1}). File width {2} from {3} and file height {4} from {5} does not." ,
m_scene . RegionInfo . RegionLocX , m_scene . RegionInfo . RegionLocY , fileWidth , fileStartX , fileHeight , fileStartY ) ;
return ;
}
// this region is included in the tile request
foreach ( KeyValuePair < string , ITerrainLoader > loader in m_loaders )
{
2012-04-03 14:56:51 +00:00
if ( filename . EndsWith ( loader . Key ) & & loader . Value . SupportsTileSave ( ) )
2011-04-13 02:46:27 +00:00
{
2012-03-24 02:16:44 +00:00
lock ( m_scene )
2011-04-13 02:46:27 +00:00
{
2012-03-24 02:16:44 +00:00
loader . Value . SaveFile ( m_channel , filename , offsetX , offsetY ,
fileWidth , fileHeight ,
( int ) Constants . RegionSize ,
( int ) Constants . RegionSize ) ;
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 ) ;
2011-04-13 02:46:27 +00:00
}
2012-03-24 02:16:44 +00:00
return ;
2011-04-13 02:46:27 +00:00
}
}
2012-03-24 02:16:44 +00:00
MainConsole . Instance . OutputFormat (
"ERROR: Could not save terrain from {0} to {1}. Valid file extensions are {2}" ,
2012-04-03 14:56:51 +00:00
m_scene . RegionInfo . RegionName , filename , m_supportFileExtensionsForTileSave ) ;
2011-04-13 02:46:27 +00:00
}
2008-05-01 14:31:30 +00:00
/// <summary>
/// Performs updates to the region periodically, synchronising physics and other heightmap aware sections
/// </summary>
private void EventManager_OnTerrainTick ( )
{
if ( m_tainted )
{
m_tainted = false ;
m_scene . PhysicsScene . SetTerrain ( m_channel . GetFloatsSerialised ( ) ) ;
m_scene . SaveTerrain ( ) ;
2008-08-18 00:39:10 +00:00
2008-06-14 02:39:27 +00:00
// Clients who look at the map will never see changes after they looked at the map, so i've commented this out.
//m_scene.CreateTerrainTexture(true);
2008-05-01 14:31:30 +00:00
}
}
/// <summary>
/// Processes commandline input. Do not call directly.
/// </summary>
/// <param name="args">Commandline arguments</param>
private void EventManager_OnPluginConsole ( string [ ] args )
{
if ( args [ 0 ] = = "terrain" )
{
2008-12-26 11:59:37 +00:00
if ( args . Length = = 1 )
{
m_commander . ProcessConsoleCommand ( "help" , new string [ 0 ] ) ;
return ;
}
2009-04-13 21:23:33 +00:00
2008-05-01 14:31:30 +00:00
string [ ] tmpArgs = new string [ args . Length - 2 ] ;
int i ;
for ( i = 2 ; i < args . Length ; i + + )
tmpArgs [ i - 2 ] = args [ i ] ;
m_commander . ProcessConsoleCommand ( args [ 1 ] , tmpArgs ) ;
}
}
/// <summary>
/// Installs terrain brush hook to IClientAPI
/// </summary>
/// <param name="client"></param>
private void EventManager_OnNewClient ( IClientAPI client )
{
client . OnModifyTerrain + = client_OnModifyTerrain ;
2008-06-19 20:57:00 +00:00
client . OnBakeTerrain + = client_OnBakeTerrain ;
2010-02-14 21:41:57 +00:00
client . OnLandUndo + = client_OnLandUndo ;
2010-08-13 20:18:26 +00:00
client . OnUnackedTerrain + = client_OnUnackedTerrain ;
2008-05-01 14:31:30 +00:00
}
2010-08-13 20:18:26 +00:00
2008-05-01 14:31:30 +00:00
/// <summary>
/// Checks to see if the terrain has been modified since last check
2008-06-19 20:57:00 +00:00
/// 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
2008-05-01 14:31:30 +00:00
/// </summary>
private void CheckForTerrainUpdates ( )
2008-06-19 20:57:00 +00:00
{
CheckForTerrainUpdates ( false ) ;
}
/// <summary>
2008-07-03 23:04:12 +00:00
/// 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
2008-06-19 20:57:00 +00:00
/// terrain_lower_limit, it will clamp terrain updates between these values
/// currently invoked by client_OnModifyTerrain only and not the Commander interfaces
/// <param name="respectEstateSettings">should height map deltas be limited to the estate settings limits</param>
/// </summary>
private void CheckForTerrainUpdates ( bool respectEstateSettings )
2008-05-01 14:31:30 +00:00
{
bool shouldTaint = false ;
float [ ] serialised = m_channel . GetFloatsSerialised ( ) ;
int x ;
for ( x = 0 ; x < m_channel . Width ; x + = Constants . TerrainPatchSize )
{
int y ;
for ( y = 0 ; y < m_channel . Height ; y + = Constants . TerrainPatchSize )
{
if ( m_channel . Tainted ( x , y ) )
{
2008-06-19 20:57:00 +00:00
// if we should respect the estate settings then
// fixup and height deltas that don't respect them
if ( respectEstateSettings & & LimitChannelChanges ( x , y ) )
{
2008-08-18 00:39:10 +00:00
// this has been vetoed, so update
2008-06-19 20:57:00 +00:00
// what we are going to send to the client
serialised = m_channel . GetFloatsSerialised ( ) ;
}
2008-08-18 00:39:10 +00:00
2008-05-01 14:31:30 +00:00
SendToClients ( serialised , x , y ) ;
shouldTaint = true ;
}
}
}
if ( shouldTaint )
{
2012-05-10 21:42:46 +00:00
m_scene . EventManager . TriggerTerrainTainted ( ) ;
2008-05-01 14:31:30 +00:00
m_tainted = true ;
}
}
2008-06-19 20:57:00 +00:00
/// <summary>
/// Checks to see height deltas in the tainted terrain patch at xStart ,yStart
/// are all within the current estate limits
/// <returns>true if changes were limited, false otherwise</returns>
/// </summary>
private bool LimitChannelChanges ( int xStart , int yStart )
{
bool changesLimited = false ;
2008-07-14 01:27:47 +00:00
double minDelta = m_scene . RegionInfo . RegionSettings . TerrainLowerLimit ;
double maxDelta = m_scene . RegionInfo . RegionSettings . TerrainRaiseLimit ;
2008-06-19 20:57:00 +00:00
// 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 y = yStart ; y < yStart + Constants . TerrainPatchSize ; y + + )
{
double requestedHeight = m_channel [ x , y ] ;
double bakedHeight = m_revert [ x , y ] ;
double requestedDelta = requestedHeight - bakedHeight ;
2008-06-25 14:30:28 +00:00
if ( requestedDelta > maxDelta )
2008-06-19 20:57:00 +00:00
{
m_channel [ x , y ] = bakedHeight + maxDelta ;
changesLimited = true ;
}
else if ( requestedDelta < minDelta )
{
m_channel [ x , y ] = bakedHeight + minDelta ; //as lower is a -ve delta
changesLimited = true ;
}
}
}
return changesLimited ;
}
2010-02-14 21:41:57 +00:00
private void client_OnLandUndo ( IClientAPI client )
{
lock ( m_undo )
{
if ( m_undo . Count > 0 )
{
LandUndoState goback = m_undo . Pop ( ) ;
if ( goback ! = null )
goback . PlaybackState ( ) ;
}
}
}
2008-05-01 14:31:30 +00:00
/// <summary>
/// Sends a copy of the current terrain to the scenes clients
/// </summary>
/// <param name="serialised">A copy of the terrain as a 1D float array of size w*h</param>
/// <param name="x">The patch corner to send</param>
/// <param name="y">The patch corner to send</param>
private void SendToClients ( float [ ] serialised , int x , int y )
{
m_scene . ForEachClient (
2008-08-18 00:39:10 +00:00
delegate ( IClientAPI controller )
2008-07-03 23:04:12 +00:00
{ controller . SendLayerData (
2008-08-18 00:39:10 +00:00
x / Constants . TerrainPatchSize , y / Constants . TerrainPatchSize , serialised ) ;
2008-07-03 23:04:12 +00:00
}
) ;
2008-05-01 14:31:30 +00:00
}
2008-11-11 01:47:40 +00:00
private void client_OnModifyTerrain ( UUID user , float height , float seconds , byte size , byte action ,
2008-10-07 14:49:12 +00:00
float north , float west , float south , float east , UUID agentId )
2008-05-01 14:31:30 +00:00
{
2008-11-21 22:14:57 +00:00
bool god = m_scene . Permissions . IsGod ( user ) ;
2008-10-07 14:49:12 +00:00
bool allowed = false ;
if ( north = = south & & east = = west )
2008-05-01 14:31:30 +00:00
{
2008-10-07 14:49:12 +00:00
if ( m_painteffects . ContainsKey ( ( StandardTerrainEffects ) action ) )
2008-05-01 14:31:30 +00:00
{
2008-10-07 14:49:12 +00:00
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 dx ;
for ( dx = - n ; dx < = n ; dx + + )
{
int dy ;
for ( dy = - n ; dy < = n ; dy + + )
{
int x = zx + dx ;
int y = zy + dy ;
if ( x > = 0 & & y > = 0 & & x < m_channel . Width & & y < m_channel . Height )
{
2008-11-21 22:14:57 +00:00
if ( m_scene . Permissions . CanTerraformLand ( agentId , new Vector3 ( x , y , 0 ) ) )
2008-10-07 14:49:12 +00:00
{
allowMask [ x , y ] = true ;
allowed = true ;
}
}
}
}
if ( allowed )
2008-05-01 14:31:30 +00:00
{
2010-02-14 21:41:57 +00:00
StoreUndoState ( ) ;
2008-05-01 14:31:30 +00:00
m_painteffects [ ( StandardTerrainEffects ) action ] . PaintEffect (
2008-10-07 14:49:12 +00:00
m_channel , allowMask , west , south , height , size , seconds ) ;
2008-05-01 14:31:30 +00:00
2008-11-11 01:47:40 +00:00
CheckForTerrainUpdates ( ! god ) ; //revert changes outside estate limits
2008-05-01 14:31:30 +00:00
}
}
else
{
2008-10-07 14:49:12 +00:00
m_log . Debug ( "Unknown terrain brush type " + action ) ;
}
}
else
{
if ( m_floodeffects . ContainsKey ( ( StandardTerrainEffects ) action ) )
{
bool [ , ] fillArea = new bool [ m_channel . Width , m_channel . Height ] ;
fillArea . Initialize ( ) ;
2008-10-06 00:58:43 +00:00
2008-10-07 14:49:12 +00:00
int x ;
for ( x = 0 ; x < m_channel . Width ; x + + )
{
int y ;
for ( y = 0 ; y < m_channel . Height ; y + + )
2008-05-01 14:31:30 +00:00
{
2008-10-07 14:49:12 +00:00
if ( x < east & & x > west )
2008-05-01 14:31:30 +00:00
{
2008-10-07 14:49:12 +00:00
if ( y < north & & y > south )
2008-05-01 14:31:30 +00:00
{
2008-11-21 22:14:57 +00:00
if ( m_scene . Permissions . CanTerraformLand ( agentId , new Vector3 ( x , y , 0 ) ) )
2008-05-01 14:31:30 +00:00
{
fillArea [ x , y ] = true ;
2008-10-07 14:49:12 +00:00
allowed = true ;
2008-05-01 14:31:30 +00:00
}
}
}
}
2008-10-07 14:49:12 +00:00
}
2008-05-01 14:31:30 +00:00
2008-10-07 14:49:12 +00:00
if ( allowed )
{
2010-02-14 21:41:57 +00:00
StoreUndoState ( ) ;
2008-05-01 14:31:30 +00:00
m_floodeffects [ ( StandardTerrainEffects ) action ] . FloodEffect (
m_channel , fillArea , size ) ;
2008-11-11 01:47:40 +00:00
CheckForTerrainUpdates ( ! god ) ; //revert changes outside estate limits
2008-05-01 14:31:30 +00:00
}
2008-10-07 14:49:12 +00:00
}
else
{
m_log . Debug ( "Unknown terrain flood type " + action ) ;
2008-05-01 14:31:30 +00:00
}
}
}
2008-06-19 20:57:00 +00:00
private void client_OnBakeTerrain ( IClientAPI remoteClient )
{
// Not a good permissions check (see client_OnModifyTerrain above), need to check the entire area.
// for now check a point in the centre of the region
2008-08-18 00:39:10 +00:00
2008-11-21 22:14:57 +00:00
if ( m_scene . Permissions . CanIssueEstateCommand ( remoteClient . AgentId , true ) )
2008-06-19 20:57:00 +00:00
{
InterfaceBakeTerrain ( null ) ; //bake terrain does not use the passed in parameter
}
}
2010-08-13 20:18:26 +00:00
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 ( ) ) ;
2010-09-12 17:43:49 +00:00
}
2008-06-19 20:57:00 +00:00
2010-02-14 21:41:57 +00:00
private void StoreUndoState ( )
{
lock ( m_undo )
{
if ( m_undo . Count > 0 )
{
LandUndoState last = m_undo . Peek ( ) ;
if ( last ! = null )
{
if ( last . Compare ( m_channel ) )
return ;
}
}
LandUndoState nUndo = new LandUndoState ( this , m_channel ) ;
m_undo . Push ( nUndo ) ;
}
}
2008-05-01 14:31:30 +00:00
#region Console Commands
private void InterfaceLoadFile ( Object [ ] args )
{
LoadFromFile ( ( string ) args [ 0 ] ) ;
CheckForTerrainUpdates ( ) ;
}
private void InterfaceLoadTileFile ( Object [ ] args )
{
LoadFromFile ( ( string ) args [ 0 ] ,
( int ) args [ 1 ] ,
( int ) args [ 2 ] ,
( int ) args [ 3 ] ,
( int ) args [ 4 ] ) ;
CheckForTerrainUpdates ( ) ;
}
private void InterfaceSaveFile ( Object [ ] args )
{
SaveToFile ( ( string ) args [ 0 ] ) ;
}
2011-04-13 02:46:27 +00:00
private void InterfaceSaveTileFile ( Object [ ] args )
{
SaveToFile ( ( string ) args [ 0 ] ,
( int ) args [ 1 ] ,
( int ) args [ 2 ] ,
( int ) args [ 3 ] ,
( int ) args [ 4 ] ) ;
}
2008-05-01 14:31:30 +00:00
private void InterfaceBakeTerrain ( Object [ ] args )
{
UpdateRevertMap ( ) ;
}
private void InterfaceRevertTerrain ( 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 ] = m_revert [ x , y ] ;
CheckForTerrainUpdates ( ) ;
}
2008-12-09 17:00:42 +00:00
private void InterfaceFlipTerrain ( Object [ ] args )
{
String direction = ( String ) args [ 0 ] ;
2008-12-11 02:35:51 +00:00
if ( direction . ToLower ( ) . StartsWith ( "y" ) )
2008-12-09 17:00:42 +00:00
{
for ( int x = 0 ; x < Constants . RegionSize ; x + + )
{
for ( int y = 0 ; y < Constants . RegionSize / 2 ; y + + )
{
double height = m_channel [ x , y ] ;
double flippedHeight = m_channel [ x , ( int ) Constants . RegionSize - 1 - y ] ;
m_channel [ x , y ] = flippedHeight ;
m_channel [ x , ( int ) Constants . RegionSize - 1 - y ] = height ;
}
}
}
else if ( direction . ToLower ( ) . StartsWith ( "x" ) )
{
for ( int y = 0 ; y < Constants . RegionSize ; y + + )
{
for ( int x = 0 ; x < Constants . RegionSize / 2 ; x + + )
{
double height = m_channel [ x , y ] ;
double flippedHeight = m_channel [ ( int ) Constants . RegionSize - 1 - x , y ] ;
m_channel [ x , y ] = flippedHeight ;
m_channel [ ( int ) Constants . RegionSize - 1 - x , y ] = height ;
}
}
}
else
{
m_log . Error ( "Unrecognised direction - need x or y" ) ;
}
CheckForTerrainUpdates ( ) ;
}
2009-03-26 17:42:02 +00:00
private void InterfaceRescaleTerrain ( Object [ ] args )
{
double desiredMin = ( double ) args [ 0 ] ;
double desiredMax = ( double ) args [ 1 ] ;
// determine desired scaling factor
double desiredRange = desiredMax - desiredMin ;
//m_log.InfoFormat("Desired {0}, {1} = {2}", new Object[] { desiredMin, desiredMax, desiredRange });
if ( desiredRange = = 0d )
{
// delta is zero so flatten at requested height
InterfaceFillTerrain ( new Object [ ] { args [ 1 ] } ) ;
}
else
{
//work out current heightmap range
double currMin = double . MaxValue ;
double currMax = double . MinValue ;
int width = m_channel . Width ;
int height = m_channel . Height ;
for ( int x = 0 ; x < width ; x + + )
{
for ( int y = 0 ; y < height ; y + + )
{
double currHeight = m_channel [ x , y ] ;
if ( currHeight < currMin )
{
currMin = currHeight ;
}
else if ( currHeight > currMax )
{
currMax = currHeight ;
}
}
}
double currRange = currMax - currMin ;
double scale = desiredRange / currRange ;
//m_log.InfoFormat("Current {0}, {1} = {2}", new Object[] { currMin, currMax, currRange });
//m_log.InfoFormat("Scale = {0}", scale);
// scale the heightmap accordingly
for ( int x = 0 ; x < width ; x + + )
{
for ( int y = 0 ; y < height ; y + + )
{
double currHeight = m_channel [ x , y ] - currMin ;
m_channel [ x , y ] = desiredMin + ( currHeight * scale ) ;
}
}
CheckForTerrainUpdates ( ) ;
}
}
2008-05-01 14:31:30 +00:00
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 ( ) ;
}
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 ( ) ;
}
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 ( ) ;
}
private 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 ( ) ;
}
2012-07-03 23:53:16 +00:00
private void InterfaceMinTerrain ( 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 ] = 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 ( y = 0 ; y < m_channel . Height ; y + + )
{
m_channel [ x , y ] = Math . Min ( ( double ) args [ 0 ] , m_channel [ x , y ] ) ;
}
}
CheckForTerrainUpdates ( ) ;
}
2008-05-01 14:31:30 +00:00
private void InterfaceShowDebugStats ( Object [ ] args )
{
double max = Double . MinValue ;
double min = double . MaxValue ;
double sum = 0 ;
int x ;
for ( x = 0 ; x < m_channel . Width ; x + + )
{
int y ;
for ( y = 0 ; y < m_channel . Height ; y + + )
{
sum + = m_channel [ x , y ] ;
if ( max < m_channel [ x , y ] )
max = m_channel [ x , y ] ;
if ( min > m_channel [ x , y ] )
min = m_channel [ x , y ] ;
}
}
2008-05-03 20:00:35 +00:00
double avg = sum / ( m_channel . Height * m_channel . Width ) ;
2008-05-01 14:31:30 +00:00
m_log . Info ( "Channel " + m_channel . Width + "x" + m_channel . Height ) ;
m_log . Info ( "max/min/avg/sum: " + max + "/" + min + "/" + avg + "/" + sum ) ;
}
private void InterfaceEnableExperimentalBrushes ( Object [ ] args )
{
if ( ( bool ) args [ 0 ] )
{
m_painteffects [ StandardTerrainEffects . Revert ] = new WeatherSphere ( ) ;
m_painteffects [ StandardTerrainEffects . Flatten ] = new OlsenSphere ( ) ;
m_painteffects [ StandardTerrainEffects . Smooth ] = new ErodeSphere ( ) ;
}
else
{
InstallDefaultEffects ( ) ;
}
}
private void InterfaceRunPluginEffect ( Object [ ] args )
{
2012-08-26 21:52:33 +00:00
string firstArg = ( string ) args [ 0 ] ;
if ( firstArg = = "list" )
2008-05-01 14:31:30 +00:00
{
m_log . Info ( "List of loaded plugins" ) ;
foreach ( KeyValuePair < string , ITerrainEffect > kvp in m_plugineffects )
{
m_log . Info ( kvp . Key ) ;
}
return ;
}
2012-08-26 21:52:33 +00:00
if ( firstArg = = "reload" )
2008-05-01 14:31:30 +00:00
{
LoadPlugins ( ) ;
return ;
}
2012-08-26 21:52:33 +00:00
if ( m_plugineffects . ContainsKey ( firstArg ) )
2008-05-01 14:31:30 +00:00
{
2012-08-26 21:52:33 +00:00
m_plugineffects [ firstArg ] . RunEffect ( m_channel ) ;
2008-05-01 14:31:30 +00:00
CheckForTerrainUpdates ( ) ;
}
else
{
m_log . Warn ( "No such plugin effect loaded." ) ;
}
}
private void InstallInterfaces ( )
{
Command loadFromFileCommand =
2008-07-25 02:30:07 +00:00
new Command ( "load" , CommandIntentions . COMMAND_HAZARDOUS , InterfaceLoadFile , "Loads a terrain from a specified file." ) ;
2008-05-01 14:31:30 +00:00
loadFromFileCommand . AddArgument ( "filename" ,
"The file you wish to load from, the file extension determines the loader to be used. Supported extensions include: " +
2011-11-17 22:13:32 +00:00
m_supportedFileExtensions , "String" ) ;
2008-05-01 14:31:30 +00:00
Command saveToFileCommand =
2008-07-25 02:30:07 +00:00
new Command ( "save" , CommandIntentions . COMMAND_NON_HAZARDOUS , InterfaceSaveFile , "Saves the current heightmap to a specified file." ) ;
2008-05-01 14:31:30 +00:00
saveToFileCommand . AddArgument ( "filename" ,
"The destination filename for your heightmap, the file extension determines the format to save in. Supported extensions include: " +
2011-11-17 22:13:32 +00:00
m_supportedFileExtensions , "String" ) ;
2008-05-01 14:31:30 +00:00
Command loadFromTileCommand =
2008-07-25 02:30:07 +00:00
new Command ( "load-tile" , CommandIntentions . COMMAND_HAZARDOUS , InterfaceLoadTileFile , "Loads a terrain from a section of a larger file." ) ;
2008-05-01 14:31:30 +00:00
loadFromTileCommand . AddArgument ( "filename" ,
"The file you wish to load from, the file extension determines the loader to be used. Supported extensions include: " +
2011-11-17 22:13:32 +00:00
m_supportedFileExtensions , "String" ) ;
2008-05-01 14:31:30 +00:00
loadFromTileCommand . AddArgument ( "file width" , "The width of the file in tiles" , "Integer" ) ;
loadFromTileCommand . AddArgument ( "file height" , "The height of the file in tiles" , "Integer" ) ;
loadFromTileCommand . AddArgument ( "minimum X tile" , "The X region coordinate of the first section on the file" ,
"Integer" ) ;
loadFromTileCommand . AddArgument ( "minimum Y tile" , "The Y region coordinate of the first section on the file" ,
"Integer" ) ;
2011-04-13 02:46:27 +00:00
Command saveToTileCommand =
new Command ( "save-tile" , CommandIntentions . COMMAND_HAZARDOUS , InterfaceSaveTileFile , "Saves the current heightmap to the larger file." ) ;
saveToTileCommand . AddArgument ( "filename" ,
"The file you wish to save to, the file extension determines the loader to be used. Supported extensions include: " +
2012-04-03 14:56:51 +00:00
m_supportFileExtensionsForTileSave , "String" ) ;
2011-04-13 02:46:27 +00:00
saveToTileCommand . AddArgument ( "file width" , "The width of the file in tiles" , "Integer" ) ;
saveToTileCommand . AddArgument ( "file height" , "The height of the file in tiles" , "Integer" ) ;
saveToTileCommand . AddArgument ( "minimum X tile" , "The X region coordinate of the first section on the file" ,
"Integer" ) ;
2012-03-24 02:41:45 +00:00
saveToTileCommand . AddArgument ( "minimum Y tile" , "The Y region coordinate of the first tile on the file\n"
+ "= Example =\n"
+ "To save a PNG file for a set of map tiles 2 regions wide and 3 regions high from map co-ordinate (9910,10234)\n"
+ " # terrain save-tile ST06.png 2 3 9910 10234\n" ,
"Integer" ) ;
2008-05-01 14:31:30 +00:00
// Terrain adjustments
Command fillRegionCommand =
2008-07-25 02:30:07 +00:00
new Command ( "fill" , CommandIntentions . COMMAND_HAZARDOUS , InterfaceFillTerrain , "Fills the current heightmap with a specified value." ) ;
2008-05-01 14:31:30 +00:00
fillRegionCommand . AddArgument ( "value" , "The numeric value of the height you wish to set your region to." ,
"Double" ) ;
Command elevateCommand =
2008-07-25 02:30:07 +00:00
new Command ( "elevate" , CommandIntentions . COMMAND_HAZARDOUS , InterfaceElevateTerrain , "Raises the current heightmap by the specified amount." ) ;
2008-05-01 14:31:30 +00:00
elevateCommand . AddArgument ( "amount" , "The amount of height to add to the terrain in meters." , "Double" ) ;
Command lowerCommand =
2008-07-25 02:30:07 +00:00
new Command ( "lower" , CommandIntentions . COMMAND_HAZARDOUS , InterfaceLowerTerrain , "Lowers the current heightmap by the specified amount." ) ;
2008-05-01 14:31:30 +00:00
lowerCommand . AddArgument ( "amount" , "The amount of height to remove from the terrain in meters." , "Double" ) ;
Command multiplyCommand =
2008-07-25 02:30:07 +00:00
new Command ( "multiply" , CommandIntentions . COMMAND_HAZARDOUS , InterfaceMultiplyTerrain , "Multiplies the heightmap by the value specified." ) ;
2008-05-01 14:31:30 +00:00
multiplyCommand . AddArgument ( "value" , "The value to multiply the heightmap by." , "Double" ) ;
Command bakeRegionCommand =
2008-07-25 02:30:07 +00:00
new Command ( "bake" , CommandIntentions . COMMAND_HAZARDOUS , InterfaceBakeTerrain , "Saves the current terrain into the regions revert map." ) ;
2008-05-01 14:31:30 +00:00
Command revertRegionCommand =
2008-07-25 02:30:07 +00:00
new Command ( "revert" , CommandIntentions . COMMAND_HAZARDOUS , InterfaceRevertTerrain , "Loads the revert map terrain into the regions heightmap." ) ;
2008-05-01 14:31:30 +00:00
2008-12-09 17:00:42 +00:00
Command flipCommand =
new Command ( "flip" , CommandIntentions . COMMAND_HAZARDOUS , InterfaceFlipTerrain , "Flips the current terrain about the X or Y axis" ) ;
flipCommand . AddArgument ( "direction" , "[x|y] the direction to flip the terrain in" , "String" ) ;
2009-03-26 17:42:02 +00:00
Command rescaleCommand =
new Command ( "rescale" , CommandIntentions . COMMAND_HAZARDOUS , InterfaceRescaleTerrain , "Rescales the current terrain to fit between the given min and max heights" ) ;
rescaleCommand . AddArgument ( "min" , "min terrain height after rescaling" , "Double" ) ;
rescaleCommand . AddArgument ( "max" , "max terrain height after rescaling" , "Double" ) ;
2012-07-03 23:53:16 +00:00
Command minCommand = new Command ( "min" , CommandIntentions . COMMAND_HAZARDOUS , InterfaceMinTerrain , "Sets the minimum terrain height to the specified value." ) ;
minCommand . AddArgument ( "min" , "terrain height to use as minimum" , "Double" ) ;
Command maxCommand = new Command ( "max" , CommandIntentions . COMMAND_HAZARDOUS , InterfaceMaxTerrain , "Sets the maximum terrain height to the specified value." ) ;
maxCommand . AddArgument ( "min" , "terrain height to use as maximum" , "Double" ) ;
2009-03-26 17:42:02 +00:00
2008-05-01 14:31:30 +00:00
// Debug
Command showDebugStatsCommand =
2008-07-25 02:30:07 +00:00
new Command ( "stats" , CommandIntentions . COMMAND_STATISTICAL , InterfaceShowDebugStats ,
2008-05-01 14:31:30 +00:00
"Shows some information about the regions heightmap for debugging purposes." ) ;
Command experimentalBrushesCommand =
2008-07-25 02:30:07 +00:00
new Command ( "newbrushes" , CommandIntentions . COMMAND_HAZARDOUS , InterfaceEnableExperimentalBrushes ,
2008-05-01 14:31:30 +00:00
"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
Command pluginRunCommand =
2008-07-25 02:30:07 +00:00
new Command ( "effect" , CommandIntentions . COMMAND_HAZARDOUS , InterfaceRunPluginEffect , "Runs a specified plugin effect" ) ;
2008-05-01 14:31:30 +00:00
pluginRunCommand . AddArgument ( "name" , "The plugin effect you wish to run, or 'list' to see all plugins" , "String" ) ;
m_commander . RegisterCommand ( "load" , loadFromFileCommand ) ;
m_commander . RegisterCommand ( "load-tile" , loadFromTileCommand ) ;
m_commander . RegisterCommand ( "save" , saveToFileCommand ) ;
2011-04-13 02:46:27 +00:00
m_commander . RegisterCommand ( "save-tile" , saveToTileCommand ) ;
2008-05-01 14:31:30 +00:00
m_commander . RegisterCommand ( "fill" , fillRegionCommand ) ;
m_commander . RegisterCommand ( "elevate" , elevateCommand ) ;
m_commander . RegisterCommand ( "lower" , lowerCommand ) ;
m_commander . RegisterCommand ( "multiply" , multiplyCommand ) ;
m_commander . RegisterCommand ( "bake" , bakeRegionCommand ) ;
m_commander . RegisterCommand ( "revert" , revertRegionCommand ) ;
m_commander . RegisterCommand ( "newbrushes" , experimentalBrushesCommand ) ;
m_commander . RegisterCommand ( "stats" , showDebugStatsCommand ) ;
m_commander . RegisterCommand ( "effect" , pluginRunCommand ) ;
2008-12-09 17:00:42 +00:00
m_commander . RegisterCommand ( "flip" , flipCommand ) ;
2009-03-26 17:42:02 +00:00
m_commander . RegisterCommand ( "rescale" , rescaleCommand ) ;
2012-07-03 23:53:16 +00:00
m_commander . RegisterCommand ( "min" , minCommand ) ;
m_commander . RegisterCommand ( "max" , maxCommand ) ;
2008-05-01 14:31:30 +00:00
// Add this to our scene so scripts can call these functions
2009-02-05 19:54:22 +00:00
m_scene . RegisterModuleCommander ( m_commander ) ;
2008-05-01 14:31:30 +00:00
}
2009-04-19 13:33:46 +00:00
2008-05-01 14:31:30 +00:00
# endregion
2009-04-19 13:33:46 +00:00
2008-05-01 14:31:30 +00:00
}
2008-06-19 20:57:00 +00:00
}