diff --git a/OpenSim/Framework/Servers/HttpServer/PollServiceRequestManager.cs b/OpenSim/Framework/Servers/HttpServer/PollServiceRequestManager.cs
index 0062d4ef15..f96fd1fa20 100644
--- a/OpenSim/Framework/Servers/HttpServer/PollServiceRequestManager.cs
+++ b/OpenSim/Framework/Servers/HttpServer/PollServiceRequestManager.cs
@@ -66,6 +66,7 @@ namespace OpenSim.Framework.Servers.HttpServer
ThreadPriority.Normal,
false,
true,
+ null,
int.MaxValue);
}
@@ -75,6 +76,7 @@ namespace OpenSim.Framework.Servers.HttpServer
ThreadPriority.Normal,
false,
true,
+ null,
1000 * 60 * 10);
}
diff --git a/OpenSim/Framework/Watchdog.cs b/OpenSim/Framework/Watchdog.cs
index e93e50e2ca..7552cd15b6 100644
--- a/OpenSim/Framework/Watchdog.cs
+++ b/OpenSim/Framework/Watchdog.cs
@@ -42,7 +42,7 @@ namespace OpenSim.Framework
const double WATCHDOG_INTERVAL_MS = 2500.0d;
/// Maximum timeout in milliseconds before a thread is considered dead
- const int WATCHDOG_TIMEOUT_MS = 5000;
+ public const int WATCHDOG_TIMEOUT_MS = 5000;
[System.Diagnostics.DebuggerDisplay("{Thread.Name}")]
public class ThreadWatchdogInfo
@@ -58,7 +58,7 @@ namespace OpenSim.Framework
public int FirstTick { get; private set; }
///
- /// First time this heartbeat update was invoked
+ /// Last time this heartbeat update was invoked
///
public int LastTick { get; set; }
@@ -77,6 +77,11 @@ namespace OpenSim.Framework
///
public bool AlarmIfTimeout { get; set; }
+ ///
+ /// Method execute if alarm goes off. If null then no alarm method is fired.
+ ///
+ public Func AlarmMethod { get; set; }
+
public ThreadWatchdogInfo(Thread thread, int timeout)
{
Thread = thread;
@@ -87,16 +92,10 @@ namespace OpenSim.Framework
}
///
- /// This event is called whenever a tracked thread is stopped or
- /// has not called UpdateThread() in time
- ///
- /// The thread that has been identified as dead
- /// The last time this thread called UpdateThread()
- public delegate void WatchdogTimeout(Thread thread, int lastTick);
-
- /// This event is called whenever a tracked thread is
- /// stopped or has not called UpdateThread() in time
- public static event WatchdogTimeout OnWatchdogTimeout;
+ /// This event is called whenever a tracked thread is
+ /// stopped or has not called UpdateThread() in time<
+ /// /summary>
+ public static event Action OnWatchdogTimeout;
private static readonly ILog m_log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
private static Dictionary m_threads;
@@ -123,7 +122,7 @@ namespace OpenSim.Framework
public static Thread StartThread(
ThreadStart start, string name, ThreadPriority priority, bool isBackground, bool alarmIfTimeout)
{
- return StartThread(start, name, priority, isBackground, alarmIfTimeout, WATCHDOG_TIMEOUT_MS);
+ return StartThread(start, name, priority, isBackground, alarmIfTimeout, null, WATCHDOG_TIMEOUT_MS);
}
///
@@ -135,17 +134,24 @@ namespace OpenSim.Framework
/// True to run this thread as a background
/// thread, otherwise false
/// Trigger an alarm function is we have timed out
+ ///
+ /// Alarm method to call if alarmIfTimeout is true and there is a timeout.
+ /// Normally, this will just return some useful debugging information.
+ ///
/// Number of milliseconds to wait until we issue a warning about timeout.
/// The newly created Thread object
public static Thread StartThread(
- ThreadStart start, string name, ThreadPriority priority, bool isBackground, bool alarmIfTimeout, int timeout)
+ ThreadStart start, string name, ThreadPriority priority, bool isBackground,
+ bool alarmIfTimeout, Func alarmMethod, int timeout)
{
Thread thread = new Thread(start);
thread.Name = name;
thread.Priority = priority;
thread.IsBackground = isBackground;
- ThreadWatchdogInfo twi = new ThreadWatchdogInfo(thread, timeout) { AlarmIfTimeout = alarmIfTimeout };
+ ThreadWatchdogInfo twi
+ = new ThreadWatchdogInfo(thread, timeout)
+ { AlarmIfTimeout = alarmIfTimeout, AlarmMethod = alarmMethod };
m_log.DebugFormat(
"[WATCHDOG]: Started tracking thread {0}, ID {1}", twi.Thread.Name, twi.Thread.ManagedThreadId);
@@ -258,7 +264,7 @@ namespace OpenSim.Framework
///
private static void WatchdogTimerElapsed(object sender, System.Timers.ElapsedEventArgs e)
{
- WatchdogTimeout callback = OnWatchdogTimeout;
+ Action callback = OnWatchdogTimeout;
if (callback != null)
{
@@ -296,7 +302,7 @@ namespace OpenSim.Framework
if (callbackInfos != null)
foreach (ThreadWatchdogInfo callbackInfo in callbackInfos)
- callback(callbackInfo.Thread, callbackInfo.LastTick);
+ callback(callbackInfo);
}
m_watchdogTimer.Start();
diff --git a/OpenSim/Region/Application/OpenSim.cs b/OpenSim/Region/Application/OpenSim.cs
index 76ac82797b..caba236a9c 100644
--- a/OpenSim/Region/Application/OpenSim.cs
+++ b/OpenSim/Region/Application/OpenSim.cs
@@ -438,12 +438,16 @@ namespace OpenSim
}
}
- private void WatchdogTimeoutHandler(System.Threading.Thread thread, int lastTick)
+ private void WatchdogTimeoutHandler(Watchdog.ThreadWatchdogInfo twi)
{
int now = Environment.TickCount & Int32.MaxValue;
- m_log.ErrorFormat("[WATCHDOG]: Timeout detected for thread \"{0}\". ThreadState={1}. Last tick was {2}ms ago",
- thread.Name, thread.ThreadState, now - lastTick);
+ m_log.ErrorFormat(
+ "[WATCHDOG]: Timeout detected for thread \"{0}\". ThreadState={1}. Last tick was {2}ms ago. {3}",
+ twi.Thread.Name,
+ twi.Thread.ThreadState,
+ now - twi.LastTick,
+ twi.AlarmMethod != null ? string.Format("Data: {0}", twi.AlarmMethod()) : "");
}
#region Console Commands
diff --git a/OpenSim/Region/ClientStack/Linden/UDP/LLUDPServer.cs b/OpenSim/Region/ClientStack/Linden/UDP/LLUDPServer.cs
index 32ba590718..09bb52c046 100644
--- a/OpenSim/Region/ClientStack/Linden/UDP/LLUDPServer.cs
+++ b/OpenSim/Region/ClientStack/Linden/UDP/LLUDPServer.cs
@@ -163,6 +163,16 @@ namespace OpenSim.Region.ClientStack.LindenUDP
private int m_malformedCount = 0; // Guard against a spamming attack
+ ///
+ /// Record current outgoing client for monitoring purposes.
+ ///
+ private IClientAPI m_currentOutgoingClient;
+
+ ///
+ /// Recording current incoming client for monitoring purposes.
+ ///
+ private IClientAPI m_currentIncomingClient;
+
public LLUDPServer(IPAddress listenIP, ref uint port, int proxyPortOffsetParm, bool allow_alternate_port, IConfigSource configSource, AgentCircuitManager circuitManager)
: base(listenIP, (int)port)
{
@@ -244,19 +254,56 @@ namespace OpenSim.Region.ClientStack.LindenUDP
if (m_scene == null)
throw new InvalidOperationException("[LLUDPSERVER]: Cannot LLUDPServer.Start() without an IScene reference");
- m_log.Info("[LLUDPSERVER]: Starting the LLUDP server in " + (m_asyncPacketHandling ? "asynchronous" : "synchronous") + " mode");
+ m_log.InfoFormat(
+ "[LLUDPSERVER]: Starting the LLUDP server in {0} mode",
+ m_asyncPacketHandling ? "asynchronous" : "synchronous");
base.Start(m_recvBufferSize, m_asyncPacketHandling);
// Start the packet processing threads
Watchdog.StartThread(
- IncomingPacketHandler, "Incoming Packets (" + m_scene.RegionInfo.RegionName + ")", ThreadPriority.Normal, false, true);
+ IncomingPacketHandler,
+ string.Format("Incoming Packets ({0})", m_scene.RegionInfo.RegionName),
+ ThreadPriority.Normal,
+ false,
+ true,
+ GetWatchdogIncomingAlarmData,
+ Watchdog.WATCHDOG_TIMEOUT_MS);
+
Watchdog.StartThread(
- OutgoingPacketHandler, "Outgoing Packets (" + m_scene.RegionInfo.RegionName + ")", ThreadPriority.Normal, false, true);
+ OutgoingPacketHandler,
+ string.Format("Outgoing Packets ({0})", m_scene.RegionInfo.RegionName),
+ ThreadPriority.Normal,
+ false,
+ true,
+ GetWatchdogOutgoingAlarmData,
+ Watchdog.WATCHDOG_TIMEOUT_MS);
m_elapsedMSSinceLastStatReport = Environment.TickCount;
}
+ ///
+ /// If the outgoing UDP thread times out, then return client that was being processed to help with debugging.
+ ///
+ ///
+ private string GetWatchdogIncomingAlarmData()
+ {
+ return string.Format(
+ "Client is {0}",
+ m_currentIncomingClient != null ? m_currentIncomingClient.Name : "none");
+ }
+
+ ///
+ /// If the outgoing UDP thread times out, then return client that was being processed to help with debugging.
+ ///
+ ///
+ private string GetWatchdogOutgoingAlarmData()
+ {
+ return string.Format(
+ "Client is {0}",
+ m_currentOutgoingClient != null ? m_currentOutgoingClient.Name : "none");
+ }
+
public new void Stop()
{
m_log.Info("[LLUDPSERVER]: Shutting down the LLUDP server for " + m_scene.RegionInfo.RegionName);
@@ -1067,6 +1114,12 @@ namespace OpenSim.Region.ClientStack.LindenUDP
client.IsLoggingOut = true;
client.Close();
}
+ else
+ {
+ m_log.WarnFormat(
+ "[LLUDPSERVER]: Tried to remove client with id {0} but not such client in {1}",
+ udpClient.AgentID, m_scene.RegionInfo.RegionName);
+ }
}
private void IncomingPacketHandler()
@@ -1173,6 +1226,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP
// client. m_packetSent will be set to true if a packet is sent
m_scene.ForEachClient(clientPacketHandler);
+ m_currentOutgoingClient = null;
+
// If nothing was sent, sleep for the minimum amount of time before a
// token bucket could get more tokens
if (!m_packetSent)
@@ -1191,6 +1246,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP
private void ClientOutgoingPacketHandler(IClientAPI client)
{
+ m_currentOutgoingClient = client;
+
try
{
if (client is LLClientView)
@@ -1216,8 +1273,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP
}
catch (Exception ex)
{
- m_log.Error("[LLUDPSERVER]: OutgoingPacketHandler iteration for " + client.Name +
- " threw an exception: " + ex.Message, ex);
+ m_log.Error(
+ string.Format("[LLUDPSERVER]: OutgoingPacketHandler iteration for {0} threw ", client.Name), ex);
}
}
@@ -1243,6 +1300,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP
{
nticks++;
watch1.Start();
+ m_currentOutgoingClient = client;
+
try
{
if (client is LLClientView)
@@ -1344,6 +1403,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP
// Make sure this client is still alive
if (m_scene.TryGetClient(udpClient.AgentID, out client))
{
+ m_currentIncomingClient = client;
+
try
{
// Process this packet
@@ -1361,6 +1422,10 @@ namespace OpenSim.Region.ClientStack.LindenUDP
m_log.ErrorFormat("[LLUDPSERVER]: Client packet handler for {0} for packet {1} threw an exception", udpClient.AgentID, packet.Type);
m_log.Error(e.Message, e);
}
+ finally
+ {
+ m_currentIncomingClient = null;
+ }
}
else
{
diff --git a/OpenSim/Region/CoreModules/World/Estate/EstateManagementCommands.cs b/OpenSim/Region/CoreModules/World/Estate/EstateManagementCommands.cs
index d2bbea34b8..3b84d5728f 100644
--- a/OpenSim/Region/CoreModules/World/Estate/EstateManagementCommands.cs
+++ b/OpenSim/Region/CoreModules/World/Estate/EstateManagementCommands.cs
@@ -73,7 +73,7 @@ namespace OpenSim.Region.CoreModules.World.Estate
"set terrain heights [] []",
"Sets the terrain texture heights on corner # to /, if or are specified, it will only " +
"set it on regions with a matching coordinate. Specify -1 in or to wildcard" +
- " that coordinate. Corner # SW = 0, NW = 1, SE = 2, NE = 3.",
+ " that coordinate. Corner # SW = 0, NW = 1, SE = 2, NE = 3, all corners = -1.",
consoleSetTerrainHeights);
m_module.Scene.AddCommand(
@@ -143,6 +143,16 @@ namespace OpenSim.Region.CoreModules.World.Estate
switch (corner)
{
+ case -1:
+ m_module.Scene.RegionInfo.RegionSettings.Elevation1SW = lowValue;
+ m_module.Scene.RegionInfo.RegionSettings.Elevation2SW = highValue;
+ m_module.Scene.RegionInfo.RegionSettings.Elevation1NW = lowValue;
+ m_module.Scene.RegionInfo.RegionSettings.Elevation2NW = highValue;
+ m_module.Scene.RegionInfo.RegionSettings.Elevation1SE = lowValue;
+ m_module.Scene.RegionInfo.RegionSettings.Elevation2SE = highValue;
+ m_module.Scene.RegionInfo.RegionSettings.Elevation1NE = lowValue;
+ m_module.Scene.RegionInfo.RegionSettings.Elevation2NE = highValue;
+ break;
case 0:
m_module.Scene.RegionInfo.RegionSettings.Elevation1SW = lowValue;
m_module.Scene.RegionInfo.RegionSettings.Elevation2SW = highValue;
diff --git a/OpenSim/Region/CoreModules/World/Terrain/FileLoaders/Terragen.cs b/OpenSim/Region/CoreModules/World/Terrain/FileLoaders/Terragen.cs
index 71c71e6cd5..7a0db2664c 100644
--- a/OpenSim/Region/CoreModules/World/Terrain/FileLoaders/Terragen.cs
+++ b/OpenSim/Region/CoreModules/World/Terrain/FileLoaders/Terragen.cs
@@ -178,8 +178,6 @@ namespace OpenSim.Region.CoreModules.World.Terrain.FileLoaders
// int sztmp = bs.ReadInt16() + 1;
// fileWidth = sztmp;
// fileHeight = sztmp;
- bs.ReadInt16();
- bs.ReadInt16();
bs.ReadInt16();
bs.ReadInt16();
break;
diff --git a/OpenSim/Region/CoreModules/World/Warp3DMap/TerrainSplat.cs b/OpenSim/Region/CoreModules/World/Warp3DMap/TerrainSplat.cs
index 7bf675daff..91252f7396 100644
--- a/OpenSim/Region/CoreModules/World/Warp3DMap/TerrainSplat.cs
+++ b/OpenSim/Region/CoreModules/World/Warp3DMap/TerrainSplat.cs
@@ -84,218 +84,234 @@ namespace OpenSim.Region.CoreModules.World.Warp3DMap
Debug.Assert(heightRanges.Length == 4);
Bitmap[] detailTexture = new Bitmap[4];
+ Bitmap output = null;
+ BitmapData outputData = null;
- if (textureTerrain)
+ try
{
- // Swap empty terrain textureIDs with default IDs
- for (int i = 0; i < textureIDs.Length; i++)
+ if (textureTerrain)
{
- if (textureIDs[i] == UUID.Zero)
- textureIDs[i] = DEFAULT_TERRAIN_DETAIL[i];
- }
-
- #region Texture Fetching
-
- if (assetService != null)
- {
- for (int i = 0; i < 4; i++)
+ // Swap empty terrain textureIDs with default IDs
+ for (int i = 0; i < textureIDs.Length; i++)
{
- AssetBase asset;
- UUID cacheID = UUID.Combine(TERRAIN_CACHE_MAGIC, textureIDs[i]);
-
- // Try to fetch a cached copy of the decoded/resized version of this texture
- asset = assetService.GetCached(cacheID.ToString());
- if (asset != null)
+ if (textureIDs[i] == UUID.Zero)
+ textureIDs[i] = DEFAULT_TERRAIN_DETAIL[i];
+ }
+
+ #region Texture Fetching
+
+ if (assetService != null)
+ {
+ for (int i = 0; i < 4; i++)
{
- try
- {
- using (System.IO.MemoryStream stream = new System.IO.MemoryStream(asset.Data))
- detailTexture[i] = (Bitmap)Image.FromStream(stream);
- }
- catch (Exception ex)
- {
- m_log.Warn("Failed to decode cached terrain texture " + cacheID +
- " (textureID: " + textureIDs[i] + "): " + ex.Message);
- }
- }
-
- if (detailTexture[i] == null)
- {
- // Try to fetch the original JPEG2000 texture, resize if needed, and cache as PNG
- asset = assetService.Get(textureIDs[i].ToString());
+ AssetBase asset;
+ UUID cacheID = UUID.Combine(TERRAIN_CACHE_MAGIC, textureIDs[i]);
+
+ // Try to fetch a cached copy of the decoded/resized version of this texture
+ asset = assetService.GetCached(cacheID.ToString());
if (asset != null)
{
- try { detailTexture[i] = (Bitmap)CSJ2K.J2kImage.FromBytes(asset.Data); }
+ try
+ {
+ using (System.IO.MemoryStream stream = new System.IO.MemoryStream(asset.Data))
+ detailTexture[i] = (Bitmap)Image.FromStream(stream);
+ }
catch (Exception ex)
{
- m_log.Warn("Failed to decode terrain texture " + asset.ID + ": " + ex.Message);
+ m_log.Warn("Failed to decode cached terrain texture " + cacheID +
+ " (textureID: " + textureIDs[i] + "): " + ex.Message);
}
}
-
- if (detailTexture[i] != null)
+
+ if (detailTexture[i] == null)
{
- Bitmap bitmap = detailTexture[i];
-
- // Make sure this texture is the correct size, otherwise resize
- if (bitmap.Width != 256 || bitmap.Height != 256)
- bitmap = ImageUtils.ResizeImage(bitmap, 256, 256);
-
- // Save the decoded and resized texture to the cache
- byte[] data;
- using (System.IO.MemoryStream stream = new System.IO.MemoryStream())
+ // Try to fetch the original JPEG2000 texture, resize if needed, and cache as PNG
+ asset = assetService.Get(textureIDs[i].ToString());
+ if (asset != null)
{
- bitmap.Save(stream, ImageFormat.Png);
- data = stream.ToArray();
+ try { detailTexture[i] = (Bitmap)CSJ2K.J2kImage.FromBytes(asset.Data); }
+ catch (Exception ex)
+ {
+ m_log.Warn("Failed to decode terrain texture " + asset.ID + ": " + ex.Message);
+ }
}
-
- // Cache a PNG copy of this terrain texture
- AssetBase newAsset = new AssetBase
+
+ if (detailTexture[i] != null)
{
- Data = data,
- Description = "PNG",
- Flags = AssetFlags.Collectable,
- FullID = cacheID,
- ID = cacheID.ToString(),
- Local = true,
- Name = String.Empty,
- Temporary = true,
- Type = (sbyte)AssetType.Unknown
- };
- newAsset.Metadata.ContentType = "image/png";
- assetService.Store(newAsset);
+ Bitmap bitmap = detailTexture[i];
+
+ // Make sure this texture is the correct size, otherwise resize
+ if (bitmap.Width != 256 || bitmap.Height != 256)
+ {
+ using (Bitmap origBitmap = bitmap)
+ {
+ bitmap = ImageUtils.ResizeImage(origBitmap, 256, 256);
+ }
+ }
+
+ // Save the decoded and resized texture to the cache
+ byte[] data;
+ using (System.IO.MemoryStream stream = new System.IO.MemoryStream())
+ {
+ bitmap.Save(stream, ImageFormat.Png);
+ data = stream.ToArray();
+ }
+
+ // Cache a PNG copy of this terrain texture
+ AssetBase newAsset = new AssetBase
+ {
+ Data = data,
+ Description = "PNG",
+ Flags = AssetFlags.Collectable,
+ FullID = cacheID,
+ ID = cacheID.ToString(),
+ Local = true,
+ Name = String.Empty,
+ Temporary = true,
+ Type = (sbyte)AssetType.Unknown
+ };
+ newAsset.Metadata.ContentType = "image/png";
+ assetService.Store(newAsset);
+ }
}
}
}
+
+ #endregion Texture Fetching
}
-
- #endregion Texture Fetching
- }
-
- // Fill in any missing textures with a solid color
- for (int i = 0; i < 4; i++)
- {
- if (detailTexture[i] == null)
+
+ // Fill in any missing textures with a solid color
+ for (int i = 0; i < 4; i++)
{
- // Create a solid color texture for this layer
- detailTexture[i] = new Bitmap(256, 256, PixelFormat.Format24bppRgb);
- using (Graphics gfx = Graphics.FromImage(detailTexture[i]))
+ if (detailTexture[i] == null)
{
- using (SolidBrush brush = new SolidBrush(DEFAULT_TERRAIN_COLOR[i]))
- gfx.FillRectangle(brush, 0, 0, 256, 256);
+ // Create a solid color texture for this layer
+ detailTexture[i] = new Bitmap(256, 256, PixelFormat.Format24bppRgb);
+ using (Graphics gfx = Graphics.FromImage(detailTexture[i]))
+ {
+ using (SolidBrush brush = new SolidBrush(DEFAULT_TERRAIN_COLOR[i]))
+ gfx.FillRectangle(brush, 0, 0, 256, 256);
+ }
}
}
- }
-
- #region Layer Map
-
- float[] layermap = new float[256 * 256];
-
- for (int y = 0; y < 256; y++)
- {
- for (int x = 0; x < 256; x++)
- {
- float height = heightmap[y * 256 + x];
-
- float pctX = (float)x / 255f;
- float pctY = (float)y / 255f;
-
- // Use bilinear interpolation between the four corners of start height and
- // height range to select the current values at this position
- float startHeight = ImageUtils.Bilinear(
- startHeights[0],
- startHeights[2],
- startHeights[1],
- startHeights[3],
- pctX, pctY);
- startHeight = Utils.Clamp(startHeight, 0f, 255f);
-
- float heightRange = ImageUtils.Bilinear(
- heightRanges[0],
- heightRanges[2],
- heightRanges[1],
- heightRanges[3],
- pctX, pctY);
- heightRange = Utils.Clamp(heightRange, 0f, 255f);
-
- // Generate two frequencies of perlin noise based on our global position
- // The magic values were taken from http://opensimulator.org/wiki/Terrain_Splatting
- Vector3 vec = new Vector3
- (
- ((float)regionPosition.X + x) * 0.20319f,
- ((float)regionPosition.Y + y) * 0.20319f,
- height * 0.25f
- );
-
- float lowFreq = Perlin.noise2(vec.X * 0.222222f, vec.Y * 0.222222f) * 6.5f;
- float highFreq = Perlin.turbulence2(vec.X, vec.Y, 2f) * 2.25f;
- float noise = (lowFreq + highFreq) * 2f;
-
- // Combine the current height, generated noise, start height, and height range parameters, then scale all of it
- float layer = ((height + noise - startHeight) / heightRange) * 4f;
- if (Single.IsNaN(layer)) layer = 0f;
- layermap[y * 256 + x] = Utils.Clamp(layer, 0f, 3f);
- }
- }
-
- #endregion Layer Map
-
- #region Texture Compositing
-
- Bitmap output = new Bitmap(256, 256, PixelFormat.Format24bppRgb);
- BitmapData outputData = output.LockBits(new Rectangle(0, 0, 256, 256), ImageLockMode.WriteOnly, PixelFormat.Format24bppRgb);
-
- unsafe
- {
- // Get handles to all of the texture data arrays
- BitmapData[] datas = new BitmapData[]
- {
- detailTexture[0].LockBits(new Rectangle(0, 0, 256, 256), ImageLockMode.ReadOnly, detailTexture[0].PixelFormat),
- detailTexture[1].LockBits(new Rectangle(0, 0, 256, 256), ImageLockMode.ReadOnly, detailTexture[1].PixelFormat),
- detailTexture[2].LockBits(new Rectangle(0, 0, 256, 256), ImageLockMode.ReadOnly, detailTexture[2].PixelFormat),
- detailTexture[3].LockBits(new Rectangle(0, 0, 256, 256), ImageLockMode.ReadOnly, detailTexture[3].PixelFormat)
- };
-
- int[] comps = new int[]
- {
- (datas[0].PixelFormat == PixelFormat.Format32bppArgb) ? 4 : 3,
- (datas[1].PixelFormat == PixelFormat.Format32bppArgb) ? 4 : 3,
- (datas[2].PixelFormat == PixelFormat.Format32bppArgb) ? 4 : 3,
- (datas[3].PixelFormat == PixelFormat.Format32bppArgb) ? 4 : 3
- };
-
+
+ #region Layer Map
+
+ float[] layermap = new float[256 * 256];
+
for (int y = 0; y < 256; y++)
{
for (int x = 0; x < 256; x++)
{
- float layer = layermap[y * 256 + x];
-
- // Select two textures
- int l0 = (int)Math.Floor(layer);
- int l1 = Math.Min(l0 + 1, 3);
-
- byte* ptrA = (byte*)datas[l0].Scan0 + y * datas[l0].Stride + x * comps[l0];
- byte* ptrB = (byte*)datas[l1].Scan0 + y * datas[l1].Stride + x * comps[l1];
- byte* ptrO = (byte*)outputData.Scan0 + y * outputData.Stride + x * 3;
-
- float aB = *(ptrA + 0);
- float aG = *(ptrA + 1);
- float aR = *(ptrA + 2);
-
- float bB = *(ptrB + 0);
- float bG = *(ptrB + 1);
- float bR = *(ptrB + 2);
-
- float layerDiff = layer - l0;
-
- // Interpolate between the two selected textures
- *(ptrO + 0) = (byte)Math.Floor(aB + layerDiff * (bB - aB));
- *(ptrO + 1) = (byte)Math.Floor(aG + layerDiff * (bG - aG));
- *(ptrO + 2) = (byte)Math.Floor(aR + layerDiff * (bR - aR));
+ float height = heightmap[y * 256 + x];
+
+ float pctX = (float)x / 255f;
+ float pctY = (float)y / 255f;
+
+ // Use bilinear interpolation between the four corners of start height and
+ // height range to select the current values at this position
+ float startHeight = ImageUtils.Bilinear(
+ startHeights[0],
+ startHeights[2],
+ startHeights[1],
+ startHeights[3],
+ pctX, pctY);
+ startHeight = Utils.Clamp(startHeight, 0f, 255f);
+
+ float heightRange = ImageUtils.Bilinear(
+ heightRanges[0],
+ heightRanges[2],
+ heightRanges[1],
+ heightRanges[3],
+ pctX, pctY);
+ heightRange = Utils.Clamp(heightRange, 0f, 255f);
+
+ // Generate two frequencies of perlin noise based on our global position
+ // The magic values were taken from http://opensimulator.org/wiki/Terrain_Splatting
+ Vector3 vec = new Vector3
+ (
+ ((float)regionPosition.X + x) * 0.20319f,
+ ((float)regionPosition.Y + y) * 0.20319f,
+ height * 0.25f
+ );
+
+ float lowFreq = Perlin.noise2(vec.X * 0.222222f, vec.Y * 0.222222f) * 6.5f;
+ float highFreq = Perlin.turbulence2(vec.X, vec.Y, 2f) * 2.25f;
+ float noise = (lowFreq + highFreq) * 2f;
+
+ // Combine the current height, generated noise, start height, and height range parameters, then scale all of it
+ float layer = ((height + noise - startHeight) / heightRange) * 4f;
+ if (Single.IsNaN(layer)) layer = 0f;
+ layermap[y * 256 + x] = Utils.Clamp(layer, 0f, 3f);
}
}
-
+
+ #endregion Layer Map
+
+ #region Texture Compositing
+
+ output = new Bitmap(256, 256, PixelFormat.Format24bppRgb);
+ outputData = output.LockBits(new Rectangle(0, 0, 256, 256), ImageLockMode.WriteOnly, PixelFormat.Format24bppRgb);
+
+ unsafe
+ {
+ // Get handles to all of the texture data arrays
+ BitmapData[] datas = new BitmapData[]
+ {
+ detailTexture[0].LockBits(new Rectangle(0, 0, 256, 256), ImageLockMode.ReadOnly, detailTexture[0].PixelFormat),
+ detailTexture[1].LockBits(new Rectangle(0, 0, 256, 256), ImageLockMode.ReadOnly, detailTexture[1].PixelFormat),
+ detailTexture[2].LockBits(new Rectangle(0, 0, 256, 256), ImageLockMode.ReadOnly, detailTexture[2].PixelFormat),
+ detailTexture[3].LockBits(new Rectangle(0, 0, 256, 256), ImageLockMode.ReadOnly, detailTexture[3].PixelFormat)
+ };
+
+ int[] comps = new int[]
+ {
+ (datas[0].PixelFormat == PixelFormat.Format32bppArgb) ? 4 : 3,
+ (datas[1].PixelFormat == PixelFormat.Format32bppArgb) ? 4 : 3,
+ (datas[2].PixelFormat == PixelFormat.Format32bppArgb) ? 4 : 3,
+ (datas[3].PixelFormat == PixelFormat.Format32bppArgb) ? 4 : 3
+ };
+
+ for (int y = 0; y < 256; y++)
+ {
+ for (int x = 0; x < 256; x++)
+ {
+ float layer = layermap[y * 256 + x];
+
+ // Select two textures
+ int l0 = (int)Math.Floor(layer);
+ int l1 = Math.Min(l0 + 1, 3);
+
+ byte* ptrA = (byte*)datas[l0].Scan0 + y * datas[l0].Stride + x * comps[l0];
+ byte* ptrB = (byte*)datas[l1].Scan0 + y * datas[l1].Stride + x * comps[l1];
+ byte* ptrO = (byte*)outputData.Scan0 + y * outputData.Stride + x * 3;
+
+ float aB = *(ptrA + 0);
+ float aG = *(ptrA + 1);
+ float aR = *(ptrA + 2);
+
+ float bB = *(ptrB + 0);
+ float bG = *(ptrB + 1);
+ float bR = *(ptrB + 2);
+
+ float layerDiff = layer - l0;
+
+ // Interpolate between the two selected textures
+ *(ptrO + 0) = (byte)Math.Floor(aB + layerDiff * (bB - aB));
+ *(ptrO + 1) = (byte)Math.Floor(aG + layerDiff * (bG - aG));
+ *(ptrO + 2) = (byte)Math.Floor(aR + layerDiff * (bR - aR));
+ }
+ }
+
+ for (int i = 0; i < 4; i++)
+ detailTexture[i].UnlockBits(datas[i]);
+ }
+ }
+ finally
+ {
for (int i = 0; i < 4; i++)
- detailTexture[i].UnlockBits(datas[i]);
+ if (detailTexture[i] != null)
+ detailTexture[i].Dispose();
}
output.UnlockBits(outputData);
diff --git a/OpenSim/Region/CoreModules/World/Warp3DMap/MapImageModule.cs b/OpenSim/Region/CoreModules/World/Warp3DMap/Warp3DImageModule.cs
similarity index 88%
rename from OpenSim/Region/CoreModules/World/Warp3DMap/MapImageModule.cs
rename to OpenSim/Region/CoreModules/World/Warp3DMap/Warp3DImageModule.cs
index 4f4e296b27..9002a9f330 100644
--- a/OpenSim/Region/CoreModules/World/Warp3DMap/MapImageModule.cs
+++ b/OpenSim/Region/CoreModules/World/Warp3DMap/Warp3DImageModule.cs
@@ -54,8 +54,7 @@ namespace OpenSim.Region.CoreModules.World.Warp3DMap
private static readonly UUID TEXTURE_METADATA_MAGIC = new UUID("802dc0e0-f080-4931-8b57-d1be8611c4f3");
private static readonly Color4 WATER_COLOR = new Color4(29, 72, 96, 216);
- private static readonly ILog m_log =
- LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
+ private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
private Scene m_scene;
private IRendering m_primMesher;
@@ -88,11 +87,11 @@ namespace OpenSim.Region.CoreModules.World.Warp3DMap
if (renderers.Count > 0)
{
m_primMesher = RenderingLoader.LoadRenderer(renderers[0]);
- m_log.Debug("[MAPTILE]: Loaded prim mesher " + m_primMesher.ToString());
+ m_log.DebugFormat("[WARP 3D IMAGE MODULE]: Loaded prim mesher {0}", m_primMesher);
}
else
{
- m_log.Debug("[MAPTILE]: No prim mesher loaded, prim rendering will be disabled");
+ m_log.Debug("[WARP 3D IMAGE MODULE]: No prim mesher loaded, prim rendering will be disabled");
}
m_scene.RegisterModuleInterface(this);
@@ -150,7 +149,7 @@ namespace OpenSim.Region.CoreModules.World.Warp3DMap
}
catch
{
- m_log.Warn("[MAPTILE]: Failed to load StartupConfig");
+ m_log.Warn("[WARP 3D IMAGE MODULE]: Failed to load StartupConfig");
}
m_colors.Clear();
@@ -204,7 +203,10 @@ namespace OpenSim.Region.CoreModules.World.Warp3DMap
Bitmap bitmap = renderer.Scene.getImage();
if (m_useAntiAliasing)
- bitmap = ImageUtils.ResizeImage(bitmap, viewport.Width, viewport.Height);
+ {
+ using (Bitmap origBitmap = bitmap)
+ bitmap = ImageUtils.ResizeImage(origBitmap, viewport.Width, viewport.Height);
+ }
return bitmap;
}
@@ -219,7 +221,7 @@ namespace OpenSim.Region.CoreModules.World.Warp3DMap
catch (Exception e)
{
// JPEG2000 encoder failed
- m_log.Error("[MAPTILE]: Failed generating terrain map: " + e);
+ m_log.Error("[WARP 3D IMAGE MODULE]: Failed generating terrain map: ", e);
}
return null;
@@ -318,8 +320,17 @@ namespace OpenSim.Region.CoreModules.World.Warp3DMap
uint globalX, globalY;
Utils.LongToUInts(m_scene.RegionInfo.RegionHandle, out globalX, out globalY);
- Bitmap image = TerrainSplat.Splat(heightmap, textureIDs, startHeights, heightRanges, new Vector3d(globalX, globalY, 0.0), m_scene.AssetService, textureTerrain);
- warp_Texture texture = new warp_Texture(image);
+ warp_Texture texture;
+
+ using (
+ Bitmap image
+ = TerrainSplat.Splat(
+ heightmap, textureIDs, startHeights, heightRanges,
+ new Vector3d(globalX, globalY, 0.0), m_scene.AssetService, textureTerrain))
+ {
+ texture = new warp_Texture(image);
+ }
+
warp_Material material = new warp_Material(texture);
material.setReflectivity(50);
renderer.Scene.addMaterial("TerrainColor", material);
@@ -546,42 +557,46 @@ namespace OpenSim.Region.CoreModules.World.Warp3DMap
{
try
{
- Bitmap bitmap = (Bitmap)J2kImage.FromStream(stream);
- width = bitmap.Width;
- height = bitmap.Height;
+ int pixelBytes;
- BitmapData bitmapData = bitmap.LockBits(new Rectangle(0, 0, width, height), ImageLockMode.ReadOnly, bitmap.PixelFormat);
- int pixelBytes = (bitmap.PixelFormat == PixelFormat.Format24bppRgb) ? 3 : 4;
-
- // Sum up the individual channels
- unsafe
+ using (Bitmap bitmap = (Bitmap)J2kImage.FromStream(stream))
{
- if (pixelBytes == 4)
+ width = bitmap.Width;
+ height = bitmap.Height;
+
+ BitmapData bitmapData = bitmap.LockBits(new Rectangle(0, 0, width, height), ImageLockMode.ReadOnly, bitmap.PixelFormat);
+ pixelBytes = (bitmap.PixelFormat == PixelFormat.Format24bppRgb) ? 3 : 4;
+
+ // Sum up the individual channels
+ unsafe
{
- for (int y = 0; y < height; y++)
+ if (pixelBytes == 4)
{
- byte* row = (byte*)bitmapData.Scan0 + (y * bitmapData.Stride);
-
- for (int x = 0; x < width; x++)
+ for (int y = 0; y < height; y++)
{
- b += row[x * pixelBytes + 0];
- g += row[x * pixelBytes + 1];
- r += row[x * pixelBytes + 2];
- a += row[x * pixelBytes + 3];
+ byte* row = (byte*)bitmapData.Scan0 + (y * bitmapData.Stride);
+
+ for (int x = 0; x < width; x++)
+ {
+ b += row[x * pixelBytes + 0];
+ g += row[x * pixelBytes + 1];
+ r += row[x * pixelBytes + 2];
+ a += row[x * pixelBytes + 3];
+ }
}
}
- }
- else
- {
- for (int y = 0; y < height; y++)
+ else
{
- byte* row = (byte*)bitmapData.Scan0 + (y * bitmapData.Stride);
-
- for (int x = 0; x < width; x++)
+ for (int y = 0; y < height; y++)
{
- b += row[x * pixelBytes + 0];
- g += row[x * pixelBytes + 1];
- r += row[x * pixelBytes + 2];
+ byte* row = (byte*)bitmapData.Scan0 + (y * bitmapData.Stride);
+
+ for (int x = 0; x < width; x++)
+ {
+ b += row[x * pixelBytes + 0];
+ g += row[x * pixelBytes + 1];
+ r += row[x * pixelBytes + 2];
+ }
}
}
}
@@ -603,7 +618,10 @@ namespace OpenSim.Region.CoreModules.World.Warp3DMap
}
catch (Exception ex)
{
- m_log.WarnFormat("[MAPTILE]: Error decoding JPEG2000 texture {0} ({1} bytes): {2}", textureID, j2kData.Length, ex.Message);
+ m_log.WarnFormat(
+ "[WARP 3D IMAGE MODULE]: Error decoding JPEG2000 texture {0} ({1} bytes): {2}",
+ textureID, j2kData.Length, ex.Message);
+
width = 0;
height = 0;
return new Color4(0.5f, 0.5f, 0.5f, 1.0f);
diff --git a/OpenSim/Region/CoreModules/World/WorldMap/WorldMapModule.cs b/OpenSim/Region/CoreModules/World/WorldMap/WorldMapModule.cs
index 2335bea2a4..c1c6b49705 100644
--- a/OpenSim/Region/CoreModules/World/WorldMap/WorldMapModule.cs
+++ b/OpenSim/Region/CoreModules/World/WorldMap/WorldMapModule.cs
@@ -1343,14 +1343,14 @@ namespace OpenSim.Region.CoreModules.World.WorldMap
if (terrain == null)
return;
+ m_log.DebugFormat("[WORLDMAP]: Generating map image for {0}", m_scene.RegionInfo.RegionName);
+
byte[] data = terrain.WriteJpeg2000Image();
if (data == null)
return;
byte[] overlay = GenerateOverlay();
- m_log.Debug("[WORLDMAP]: STORING MAPTILE IMAGE");
-
UUID terrainImageID = UUID.Random();
UUID parcelImageID = UUID.Zero;
@@ -1365,7 +1365,8 @@ namespace OpenSim.Region.CoreModules.World.WorldMap
asset.Flags = AssetFlags.Maptile;
// Store the new one
- m_log.DebugFormat("[WORLDMAP]: Storing map tile {0}", asset.ID);
+ m_log.DebugFormat("[WORLDMAP]: Storing map tile {0} for {1}", asset.ID, m_scene.RegionInfo.RegionName);
+
m_scene.AssetService.Store(asset);
if (overlay != null)
diff --git a/OpenSim/Region/OptionalModules/World/WorldView/WorldViewModule.cs b/OpenSim/Region/OptionalModules/World/WorldView/WorldViewModule.cs
index 48c242db51..1aee39a5b9 100644
--- a/OpenSim/Region/OptionalModules/World/WorldView/WorldViewModule.cs
+++ b/OpenSim/Region/OptionalModules/World/WorldView/WorldViewModule.cs
@@ -113,14 +113,15 @@ namespace OpenSim.Region.OptionalModules.World.WorldView
if (!m_Enabled)
return new Byte[0];
- Bitmap bmp = m_Generator.CreateViewImage(pos, rot, fov, width,
- height, usetex);
+ using (Bitmap bmp = m_Generator.CreateViewImage(pos, rot, fov, width, height, usetex))
+ {
+ using (MemoryStream str = new MemoryStream())
+ {
+ bmp.Save(str, ImageFormat.Jpeg);
- MemoryStream str = new MemoryStream();
-
- bmp.Save(str, ImageFormat.Jpeg);
-
- return str.ToArray();
+ return str.ToArray();
+ }
+ }
}
}
}