From 742f5054408bbd928ea2422dd9abc04ee57c80dc Mon Sep 17 00:00:00 2001 From: Robert Adams Date: Mon, 10 Mar 2014 22:01:55 -0700 Subject: Change terrain update sending to be triggered by frame tick rather than everytime terrain is changed. The TerrainModule now hooks the frame event and, if terrain has changed, sends terrain updates to the clients. This polling pattern replaces the previous push on change pattern and will make it easier to do per client throttling and per scene presence terrain update ordering. --- OpenSim/Framework/TerrainData.cs | 51 +++++++- .../CoreModules/World/Terrain/TerrainModule.cs | 142 +++++++++++++-------- OpenSim/Region/Framework/Scenes/TerrainChannel.cs | 12 +- 3 files changed, 139 insertions(+), 66 deletions(-) diff --git a/OpenSim/Framework/TerrainData.cs b/OpenSim/Framework/TerrainData.cs index 9325df2..25f9ca6 100644 --- a/OpenSim/Framework/TerrainData.cs +++ b/OpenSim/Framework/TerrainData.cs @@ -50,8 +50,9 @@ namespace OpenSim.Framework // Someday terrain will have caves public abstract float this[int x, int y, int z] { get; set; } - public bool IsTainted { get; protected set; } public abstract bool IsTaintedAt(int xx, int yy); + public abstract bool IsTaintedAt(int xx, int yy, bool clearOnTest); + public abstract void TaintAllTerrain(); public abstract void ClearTaint(); public abstract void ClearLand(); @@ -75,6 +76,7 @@ namespace OpenSim.Framework public abstract short[] GetCompressedMap(); public abstract float CompressionFactor { get; } + public abstract float[] GetFloatsSerialized(); public abstract double[,] GetDoubles(); public abstract TerrainData Clone(); } @@ -138,10 +140,20 @@ namespace OpenSim.Framework // TerrainData.ClearTaint public override void ClearTaint() { - IsTainted = false; + SetAllTaint(false); + } + + // TerrainData.TaintAllTerrain + public override void TaintAllTerrain() + { + SetAllTaint(true); + } + + private void SetAllTaint(bool setting) + { for (int ii = 0; ii < m_taint.GetLength(0); ii++) for (int jj = 0; jj < m_taint.GetLength(1); jj++) - m_taint[ii, jj] = false; + m_taint[ii, jj] = setting; } // TerrainData.ClearLand @@ -158,15 +170,25 @@ namespace OpenSim.Framework m_heightmap[xx, yy] = flatHeight; } - public override bool IsTaintedAt(int xx, int yy) + // Return 'true' of the patch that contains these region coordinates has been modified. + // Note that checking the taint clears it. + // There is existing code that relies on this feature. + public override bool IsTaintedAt(int xx, int yy, bool clearOnTest) { int tx = xx / Constants.TerrainPatchSize; int ty = yy / Constants.TerrainPatchSize; bool ret = m_taint[tx, ty]; - m_taint[tx, ty] = false; + if (ret && clearOnTest) + m_taint[tx, ty] = false; return ret; } + // Old form that clears the taint flag when we check it. + public override bool IsTaintedAt(int xx, int yy) + { + return IsTaintedAt(xx, yy, true /* clearOnTest */); + } + // TerrainData.GetDatabaseBlob // The user wants something to store in the database. public override bool GetDatabaseBlob(out int DBRevisionCode, out Array blob) @@ -212,6 +234,25 @@ namespace OpenSim.Framework return ret; } + // TerrainData.GetFloatsSerialized + // This one dimensional version is ordered so height = map[y*sizeX+x]; + // DEPRECATED: don't use this function as it does not retain the dimensions of the terrain + // and the caller will probably do the wrong thing if the terrain is not the legacy 256x256. + public override float[] GetFloatsSerialized() + { + int points = SizeX * SizeY; + float[] heights = new float[points]; + + int idx = 0; + for (int jj = 0; jj < SizeY; jj++) + for (int ii = 0; ii < SizeX; ii++) + { + heights[idx++] = FromCompressedHeight(m_heightmap[ii, jj]); + } + + return heights; + } + // TerrainData.GetDoubles public override double[,] GetDoubles() { diff --git a/OpenSim/Region/CoreModules/World/Terrain/TerrainModule.cs b/OpenSim/Region/CoreModules/World/Terrain/TerrainModule.cs index 08891d9..daf9901 100644 --- a/OpenSim/Region/CoreModules/World/Terrain/TerrainModule.cs +++ b/OpenSim/Region/CoreModules/World/Terrain/TerrainModule.cs @@ -149,8 +149,10 @@ namespace OpenSim.Region.CoreModules.World.Terrain 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(); @@ -189,8 +191,10 @@ namespace OpenSim.Region.CoreModules.World.Terrain // 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); @@ -266,7 +270,6 @@ namespace OpenSim.Region.CoreModules.World.Terrain String.Format("Unable to load heightmap: {0}", e.Message)); } } - CheckForTerrainUpdates(); m_log.Info("[TERRAIN]: File (" + filename + ") loaded successfully"); return; } @@ -347,7 +350,6 @@ namespace OpenSim.Region.CoreModules.World.Terrain } } - CheckForTerrainUpdates(); m_log.Info("[TERRAIN]: File (" + filename + ") loaded successfully"); return; } @@ -418,7 +420,7 @@ namespace OpenSim.Region.CoreModules.World.Terrain public void TaintTerrain () { - CheckForTerrainUpdates(); + m_channel.GetTerrainData().TaintAllTerrain(); } #region Plugin Loading Methods @@ -647,7 +649,39 @@ 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; + } + } + } + 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() { @@ -698,6 +732,22 @@ namespace OpenSim.Region.CoreModules.World.Terrain } /// + /// Installs terrain brush hook to IClientAPI + /// + /// + private void EventManager_OnClientClosed(UUID client, Scene scene) + { + 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; + } + } + + /// /// 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 @@ -717,36 +767,32 @@ namespace OpenSim.Region.CoreModules.World.Terrain /// private void CheckForTerrainUpdates(bool respectEstateSettings) { - bool shouldTaint = false; - float[] terrHeights = m_channel.GetFloatsSerialised(); - int x; - for (x = 0; x < m_channel.Width; x += Constants.TerrainPatchSize) + } + + /// + /// 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 bool EnforceEstateLimits() + { + 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. // Note that LimitChannelChanges() modifies the TerrainChannel with the limited height values. - if (respectEstateSettings && LimitChannelChanges(x, y)) - { - // Terrain heights were modified. Refetch the terrain info. - terrHeights = m_channel.GetFloatsSerialised(); - } - - // m_log.DebugFormat("{0} Patch modified. Sending (x,y) = ({1},{2})", LogHeader, x, y); - SendToClients(terrHeights, x, y); - shouldTaint = true; + wasLimited |= LimitChannelChanges(terrData, x, y); } } } - if (shouldTaint) - { - m_scene.EventManager.TriggerTerrainTainted(); - m_tainted = true; - } + return wasLimited; } /// @@ -754,11 +800,11 @@ 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 @@ -766,19 +812,18 @@ namespace OpenSim.Region.CoreModules.World.Terrain { 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; } } @@ -806,11 +851,16 @@ 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[] heightMap, int x, int y) + private void SendToClients(TerrainData terrData, int x, int y) { + float[] heightMap = terrData.GetFloatsSerialized(); m_scene.ForEachClient( delegate(IClientAPI controller) - { controller.SendLayerData( x / Constants.TerrainPatchSize, y / Constants.TerrainPatchSize, heightMap); } + { + controller.SendLayerData( x / Constants.TerrainPatchSize, + y / Constants.TerrainPatchSize, + heightMap); + } ); } @@ -856,7 +906,9 @@ namespace OpenSim.Region.CoreModules.World.Terrain 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 @@ -897,7 +949,9 @@ namespace OpenSim.Region.CoreModules.World.Terrain 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 @@ -948,7 +1002,6 @@ namespace OpenSim.Region.CoreModules.World.Terrain private void InterfaceLoadFile(Object[] args) { LoadFromFile((string) args[0]); - CheckForTerrainUpdates(); } private void InterfaceLoadTileFile(Object[] args) @@ -958,7 +1011,6 @@ namespace OpenSim.Region.CoreModules.World.Terrain (int) args[2], (int) args[3], (int) args[4]); - CheckForTerrainUpdates(); } private void InterfaceSaveFile(Object[] args) @@ -987,7 +1039,6 @@ namespace OpenSim.Region.CoreModules.World.Terrain for (y = 0; y < m_channel.Height; y++) m_channel[x, y] = m_revert[x, y]; - CheckForTerrainUpdates(); } private void InterfaceFlipTerrain(Object[] args) @@ -1028,7 +1079,6 @@ namespace OpenSim.Region.CoreModules.World.Terrain } - CheckForTerrainUpdates(); } private void InterfaceRescaleTerrain(Object[] args) @@ -1086,7 +1136,6 @@ namespace OpenSim.Region.CoreModules.World.Terrain } } - CheckForTerrainUpdates(); } } @@ -1097,7 +1146,6 @@ namespace OpenSim.Region.CoreModules.World.Terrain 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) @@ -1106,7 +1154,6 @@ namespace OpenSim.Region.CoreModules.World.Terrain 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) @@ -1115,7 +1162,6 @@ namespace OpenSim.Region.CoreModules.World.Terrain 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) @@ -1125,7 +1171,6 @@ namespace OpenSim.Region.CoreModules.World.Terrain 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 InterfaceMinTerrain(Object[] args) @@ -1138,7 +1183,6 @@ namespace OpenSim.Region.CoreModules.World.Terrain m_channel[x, y] = Math.Max((double)args[0], m_channel[x, y]); } } - CheckForTerrainUpdates(); } private void InterfaceMaxTerrain(Object[] args) @@ -1151,7 +1195,6 @@ namespace OpenSim.Region.CoreModules.World.Terrain m_channel[x, y] = Math.Min((double)args[0], m_channel[x, y]); } } - CheckForTerrainUpdates(); } private void InterfaceShowDebugStats(Object[] args) @@ -1214,7 +1257,6 @@ namespace OpenSim.Region.CoreModules.World.Terrain if (m_plugineffects.ContainsKey(firstArg)) { m_plugineffects[firstArg].RunEffect(m_channel); - CheckForTerrainUpdates(); } else { diff --git a/OpenSim/Region/Framework/Scenes/TerrainChannel.cs b/OpenSim/Region/Framework/Scenes/TerrainChannel.cs index 60dc6c9..cc040a6 100644 --- a/OpenSim/Region/Framework/Scenes/TerrainChannel.cs +++ b/OpenSim/Region/Framework/Scenes/TerrainChannel.cs @@ -124,17 +124,7 @@ namespace OpenSim.Region.Framework.Scenes // and the caller will probably do the wrong thing if the terrain is not the legacy 256x256. public float[] GetFloatsSerialised() { - int points = Width * Height; - float[] heights = new float[points]; - - int idx = 0; - for (int jj = 0; jj < Height; jj++) - for (int ii = 0; ii < Width; ii++) - { - heights[idx++] = m_terrainData[ii, jj]; - } - - return heights; + return m_terrainData.GetFloatsSerialized(); } // ITerrainChannel.GetDoubles() -- cgit v1.1 From 3e8f593bf2ed525dbd53ef9807382e9dc2a64c6d Mon Sep 17 00:00:00 2001 From: Lani Global Date: Fri, 21 Feb 2014 19:53:25 +0000 Subject: PhysicalPrimMax to 64m for ini Default files to enable standard size prims and mesh to be used with vehicles. Signed-off-by: Robert Adams --- bin/OpenSimDefaults.ini | 2 +- bin/Regions/Regions.ini.example | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/bin/OpenSimDefaults.ini b/bin/OpenSimDefaults.ini index 026f285..f59dbf2 100644 --- a/bin/OpenSimDefaults.ini +++ b/bin/OpenSimDefaults.ini @@ -97,7 +97,7 @@ NonPhysicalPrimMax = 256 ; Maximum size of physical prims. Affects resizing of existing prims. This can be overriden in the region config file. - PhysicalPrimMax = 10 + PhysicalPrimMax = 64 ; If a viewer attempts to rez a prim larger than the non-physical or physical prim max, clamp the dimensions to the appropriate maximum ; This can be overriden in the region config file. diff --git a/bin/Regions/Regions.ini.example b/bin/Regions/Regions.ini.example index ab3a62a..aabc4f8 100644 --- a/bin/Regions/Regions.ini.example +++ b/bin/Regions/Regions.ini.example @@ -29,7 +29,7 @@ ExternalHostName = "SYSTEMIP" ; * ; NonphysicalPrimMax = 256 -; PhysicalPrimMax = 10 +; PhysicalPrimMax = 64 ; ClampPrimSize = False ; MaxPrims = 15000 ; MaxAgents = 100 -- cgit v1.1 From 71c808cd32f6049d99b8d802d24ab1d07011fb48 Mon Sep 17 00:00:00 2001 From: Lani Global Date: Sat, 22 Feb 2014 18:28:48 +0000 Subject: PhysicalPrimMax 64m for OpenSim_ini_example standard size prim Signed-off-by: Robert Adams --- bin/OpenSim.ini.example | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/bin/OpenSim.ini.example b/bin/OpenSim.ini.example index e3b91ae..c4697a1 100644 --- a/bin/OpenSim.ini.example +++ b/bin/OpenSim.ini.example @@ -113,15 +113,15 @@ ;; NonPhysicalPrimMax!). ; NonPhysicalPrimMax = 256 - ;# {PhysicalPrimMin} {} {Minimum size of physical prims?} {} 10 + ;# {PhysicalPrimMin} {} {Minimum size of physical prims?} {} 0.01 ;; Maximum size where a prim can be physical. Affects resizing of ;; existing prims. This can be overriden in the region config file. ; PhysicalPrimMin = 0.01 - ;# {PhysicalPrimMax} {} {Maximum size of physical prims?} {} 10 + ;# {PhysicalPrimMax} {} {Maximum size of physical prims?} {} 64 ;; Maximum size where a prim can be physical. Affects resizing of ;; existing prims. This can be overriden in the region config file. - ; PhysicalPrimMax = 10 + ; PhysicalPrimMax = 64 ;# {ClampPrimSize} {} {Clamp viewer rezzed prims to max sizes?} {true false} false ;; If a viewer attempts to rez a prim larger than the non-physical or -- cgit v1.1 From 6b17c9bd98751aae47e0f5e521ffba1f70e9980a Mon Sep 17 00:00:00 2001 From: Robert Adams Date: Mon, 10 Mar 2014 22:12:56 -0700 Subject: Add Lani Global to CONTRIBUTORS.txt --- CONTRIBUTORS.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/CONTRIBUTORS.txt b/CONTRIBUTORS.txt index d516fa8..b17ab8b 100644 --- a/CONTRIBUTORS.txt +++ b/CONTRIBUTORS.txt @@ -114,6 +114,7 @@ what it is today. * Kitto Flora * KittyLiu * Kurt Taylor (IBM) +* Lani Global * lkalif * lulurun * M.Igarashi -- cgit v1.1 From 8edf4225f32ab5fe1e9a1796e1c51b99c391de69 Mon Sep 17 00:00:00 2001 From: Robert Adams Date: Tue, 11 Mar 2014 07:12:47 -0700 Subject: varregion: remove serialization of region terrain to floats when sending patches. This should eliminate much memory thrashing and CPU usage while sending initial terrain. The old way of passing terrain was to convert it to an array of floats. This is really bad for large terrain (think 4096x4096 floats). This change passes a dummy float array since the real region info is used anyway and the floats are ignored. (The ignoring the terrain floats is a kludge so as to not change IClientAPI.) --- OpenSim/Region/CoreModules/World/Terrain/TerrainModule.cs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/OpenSim/Region/CoreModules/World/Terrain/TerrainModule.cs b/OpenSim/Region/CoreModules/World/Terrain/TerrainModule.cs index daf9901..3fda67f 100644 --- a/OpenSim/Region/CoreModules/World/Terrain/TerrainModule.cs +++ b/OpenSim/Region/CoreModules/World/Terrain/TerrainModule.cs @@ -853,7 +853,9 @@ namespace OpenSim.Region.CoreModules.World.Terrain /// The patch corner to send private void SendToClients(TerrainData terrData, int x, int y) { - float[] heightMap = terrData.GetFloatsSerialized(); + // We know the actual terrain data passed is ignored. This kludge saves changing IClientAPI. + //float[] heightMap = terrData.GetFloatsSerialized(); + float[] heightMap = new float[10]; m_scene.ForEachClient( delegate(IClientAPI controller) { @@ -975,7 +977,9 @@ namespace OpenSim.Region.CoreModules.World.Terrain 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() -- cgit v1.1