Merge branch 'master' of /home/opensim/var/repo/opensim
commit
860cf767a1
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -42,7 +42,7 @@ namespace OpenSim.Framework
|
|||
const double WATCHDOG_INTERVAL_MS = 2500.0d;
|
||||
|
||||
/// <summary>Maximum timeout in milliseconds before a thread is considered dead</summary>
|
||||
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; }
|
||||
|
||||
/// <summary>
|
||||
/// First time this heartbeat update was invoked
|
||||
/// Last time this heartbeat update was invoked
|
||||
/// </summary>
|
||||
public int LastTick { get; set; }
|
||||
|
||||
|
@ -77,6 +77,11 @@ namespace OpenSim.Framework
|
|||
/// </summary>
|
||||
public bool AlarmIfTimeout { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Method execute if alarm goes off. If null then no alarm method is fired.
|
||||
/// </summary>
|
||||
public Func<string> AlarmMethod { get; set; }
|
||||
|
||||
public ThreadWatchdogInfo(Thread thread, int timeout)
|
||||
{
|
||||
Thread = thread;
|
||||
|
@ -87,16 +92,10 @@ namespace OpenSim.Framework
|
|||
}
|
||||
|
||||
/// <summary>
|
||||
/// This event is called whenever a tracked thread is stopped or
|
||||
/// has not called UpdateThread() in time
|
||||
/// </summary>
|
||||
/// <param name="thread">The thread that has been identified as dead</param>
|
||||
/// <param name="lastTick">The last time this thread called UpdateThread()</param>
|
||||
public delegate void WatchdogTimeout(Thread thread, int lastTick);
|
||||
|
||||
/// <summary>This event is called whenever a tracked thread is
|
||||
/// stopped or has not called UpdateThread() in time</summary>
|
||||
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<ThreadWatchdogInfo> OnWatchdogTimeout;
|
||||
|
||||
private static readonly ILog m_log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
|
||||
private static Dictionary<int, ThreadWatchdogInfo> 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);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -135,17 +134,24 @@ namespace OpenSim.Framework
|
|||
/// <param name="isBackground">True to run this thread as a background
|
||||
/// thread, otherwise false</param>
|
||||
/// <param name="alarmIfTimeout">Trigger an alarm function is we have timed out</param>
|
||||
/// <param name="alarmMethod">
|
||||
/// Alarm method to call if alarmIfTimeout is true and there is a timeout.
|
||||
/// Normally, this will just return some useful debugging information.
|
||||
/// </param>
|
||||
/// <param name="timeout">Number of milliseconds to wait until we issue a warning about timeout.</param>
|
||||
/// <returns>The newly created Thread object</returns>
|
||||
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<string> 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
|
|||
/// <param name="e"></param>
|
||||
private static void WatchdogTimerElapsed(object sender, System.Timers.ElapsedEventArgs e)
|
||||
{
|
||||
WatchdogTimeout callback = OnWatchdogTimeout;
|
||||
Action<ThreadWatchdogInfo> 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();
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -163,6 +163,16 @@ namespace OpenSim.Region.ClientStack.LindenUDP
|
|||
|
||||
private int m_malformedCount = 0; // Guard against a spamming attack
|
||||
|
||||
/// <summary>
|
||||
/// Record current outgoing client for monitoring purposes.
|
||||
/// </summary>
|
||||
private IClientAPI m_currentOutgoingClient;
|
||||
|
||||
/// <summary>
|
||||
/// Recording current incoming client for monitoring purposes.
|
||||
/// </summary>
|
||||
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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// If the outgoing UDP thread times out, then return client that was being processed to help with debugging.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
private string GetWatchdogIncomingAlarmData()
|
||||
{
|
||||
return string.Format(
|
||||
"Client is {0}",
|
||||
m_currentIncomingClient != null ? m_currentIncomingClient.Name : "none");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// If the outgoing UDP thread times out, then return client that was being processed to help with debugging.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
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
|
||||
{
|
||||
|
|
|
@ -73,7 +73,7 @@ namespace OpenSim.Region.CoreModules.World.Estate
|
|||
"set terrain heights <corner> <min> <max> [<x>] [<y>]",
|
||||
"Sets the terrain texture heights on corner #<corner> to <min>/<max>, if <x> or <y> are specified, it will only " +
|
||||
"set it on regions with a matching coordinate. Specify -1 in <x> or <y> 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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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]);
|
||||
if (textureIDs[i] == UUID.Zero)
|
||||
textureIDs[i] = DEFAULT_TERRAIN_DETAIL[i];
|
||||
}
|
||||
|
||||
// Try to fetch a cached copy of the decoded/resized version of this texture
|
||||
asset = assetService.GetCached(cacheID.ToString());
|
||||
if (asset != null)
|
||||
{
|
||||
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);
|
||||
}
|
||||
}
|
||||
#region Texture Fetching
|
||||
|
||||
if (detailTexture[i] == null)
|
||||
if (assetService != null)
|
||||
{
|
||||
for (int i = 0; i < 4; i++)
|
||||
{
|
||||
// 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
|
||||
}
|
||||
|
||||
// Fill in any missing textures with a solid color
|
||||
for (int i = 0; i < 4; i++)
|
||||
{
|
||||
if (detailTexture[i] == null)
|
||||
{
|
||||
// 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endregion Texture Fetching
|
||||
}
|
||||
#region Layer Map
|
||||
|
||||
// Fill in any missing textures with a solid color
|
||||
for (int i = 0; i < 4; i++)
|
||||
{
|
||||
if (detailTexture[i] == null)
|
||||
{
|
||||
// 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
|
||||
};
|
||||
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];
|
||||
float height = heightmap[y * 256 + x];
|
||||
|
||||
// Select two textures
|
||||
int l0 = (int)Math.Floor(layer);
|
||||
int l1 = Math.Min(l0 + 1, 3);
|
||||
float pctX = (float)x / 255f;
|
||||
float pctY = (float)y / 255f;
|
||||
|
||||
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;
|
||||
// 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 aB = *(ptrA + 0);
|
||||
float aG = *(ptrA + 1);
|
||||
float aR = *(ptrA + 2);
|
||||
float heightRange = ImageUtils.Bilinear(
|
||||
heightRanges[0],
|
||||
heightRanges[2],
|
||||
heightRanges[1],
|
||||
heightRanges[3],
|
||||
pctX, pctY);
|
||||
heightRange = Utils.Clamp(heightRange, 0f, 255f);
|
||||
|
||||
float bB = *(ptrB + 0);
|
||||
float bG = *(ptrB + 1);
|
||||
float bR = *(ptrB + 2);
|
||||
// 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 layerDiff = layer - l0;
|
||||
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;
|
||||
|
||||
// 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));
|
||||
// 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);
|
||||
|
|
|
@ -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<IMapImageGenerator>(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)
|
||||
{
|
||||
for (int y = 0; y < height; y++)
|
||||
{
|
||||
byte* row = (byte*)bitmapData.Scan0 + (y * bitmapData.Stride);
|
||||
width = bitmap.Width;
|
||||
height = bitmap.Height;
|
||||
|
||||
for (int x = 0; x < width; x++)
|
||||
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
|
||||
{
|
||||
if (pixelBytes == 4)
|
||||
{
|
||||
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);
|
|
@ -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)
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue