Merge branch 'master' of /home/opensim/var/repo/opensim

integration
BlueWall 2012-06-07 06:48:48 -04:00
commit 860cf767a1
10 changed files with 378 additions and 257 deletions

View File

@ -66,6 +66,7 @@ namespace OpenSim.Framework.Servers.HttpServer
ThreadPriority.Normal, ThreadPriority.Normal,
false, false,
true, true,
null,
int.MaxValue); int.MaxValue);
} }
@ -75,6 +76,7 @@ namespace OpenSim.Framework.Servers.HttpServer
ThreadPriority.Normal, ThreadPriority.Normal,
false, false,
true, true,
null,
1000 * 60 * 10); 1000 * 60 * 10);
} }

View File

@ -42,7 +42,7 @@ namespace OpenSim.Framework
const double WATCHDOG_INTERVAL_MS = 2500.0d; const double WATCHDOG_INTERVAL_MS = 2500.0d;
/// <summary>Maximum timeout in milliseconds before a thread is considered dead</summary> /// <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}")] [System.Diagnostics.DebuggerDisplay("{Thread.Name}")]
public class ThreadWatchdogInfo public class ThreadWatchdogInfo
@ -58,7 +58,7 @@ namespace OpenSim.Framework
public int FirstTick { get; private set; } public int FirstTick { get; private set; }
/// <summary> /// <summary>
/// First time this heartbeat update was invoked /// Last time this heartbeat update was invoked
/// </summary> /// </summary>
public int LastTick { get; set; } public int LastTick { get; set; }
@ -77,6 +77,11 @@ namespace OpenSim.Framework
/// </summary> /// </summary>
public bool AlarmIfTimeout { get; set; } 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) public ThreadWatchdogInfo(Thread thread, int timeout)
{ {
Thread = thread; Thread = thread;
@ -87,16 +92,10 @@ namespace OpenSim.Framework
} }
/// <summary> /// <summary>
/// This event is called whenever a tracked thread is stopped or /// This event is called whenever a tracked thread is
/// has not called UpdateThread() in time /// stopped or has not called UpdateThread() in time<
/// </summary> /// /summary>
/// <param name="thread">The thread that has been identified as dead</param> public static event Action<ThreadWatchdogInfo> OnWatchdogTimeout;
/// <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;
private static readonly ILog m_log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); private static readonly ILog m_log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
private static Dictionary<int, ThreadWatchdogInfo> m_threads; private static Dictionary<int, ThreadWatchdogInfo> m_threads;
@ -123,7 +122,7 @@ namespace OpenSim.Framework
public static Thread StartThread( public static Thread StartThread(
ThreadStart start, string name, ThreadPriority priority, bool isBackground, bool alarmIfTimeout) 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> /// <summary>
@ -135,17 +134,24 @@ namespace OpenSim.Framework
/// <param name="isBackground">True to run this thread as a background /// <param name="isBackground">True to run this thread as a background
/// thread, otherwise false</param> /// thread, otherwise false</param>
/// <param name="alarmIfTimeout">Trigger an alarm function is we have timed out</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> /// <param name="timeout">Number of milliseconds to wait until we issue a warning about timeout.</param>
/// <returns>The newly created Thread object</returns> /// <returns>The newly created Thread object</returns>
public static Thread StartThread( 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 thread = new Thread(start);
thread.Name = name; thread.Name = name;
thread.Priority = priority; thread.Priority = priority;
thread.IsBackground = isBackground; thread.IsBackground = isBackground;
ThreadWatchdogInfo twi = new ThreadWatchdogInfo(thread, timeout) { AlarmIfTimeout = alarmIfTimeout }; ThreadWatchdogInfo twi
= new ThreadWatchdogInfo(thread, timeout)
{ AlarmIfTimeout = alarmIfTimeout, AlarmMethod = alarmMethod };
m_log.DebugFormat( m_log.DebugFormat(
"[WATCHDOG]: Started tracking thread {0}, ID {1}", twi.Thread.Name, twi.Thread.ManagedThreadId); "[WATCHDOG]: Started tracking thread {0}, ID {1}", twi.Thread.Name, twi.Thread.ManagedThreadId);
@ -258,7 +264,7 @@ namespace OpenSim.Framework
/// <param name="e"></param> /// <param name="e"></param>
private static void WatchdogTimerElapsed(object sender, System.Timers.ElapsedEventArgs e) private static void WatchdogTimerElapsed(object sender, System.Timers.ElapsedEventArgs e)
{ {
WatchdogTimeout callback = OnWatchdogTimeout; Action<ThreadWatchdogInfo> callback = OnWatchdogTimeout;
if (callback != null) if (callback != null)
{ {
@ -296,7 +302,7 @@ namespace OpenSim.Framework
if (callbackInfos != null) if (callbackInfos != null)
foreach (ThreadWatchdogInfo callbackInfo in callbackInfos) foreach (ThreadWatchdogInfo callbackInfo in callbackInfos)
callback(callbackInfo.Thread, callbackInfo.LastTick); callback(callbackInfo);
} }
m_watchdogTimer.Start(); m_watchdogTimer.Start();

View File

@ -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; int now = Environment.TickCount & Int32.MaxValue;
m_log.ErrorFormat("[WATCHDOG]: Timeout detected for thread \"{0}\". ThreadState={1}. Last tick was {2}ms ago", m_log.ErrorFormat(
thread.Name, thread.ThreadState, now - lastTick); "[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 #region Console Commands

View File

@ -163,6 +163,16 @@ namespace OpenSim.Region.ClientStack.LindenUDP
private int m_malformedCount = 0; // Guard against a spamming attack 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) public LLUDPServer(IPAddress listenIP, ref uint port, int proxyPortOffsetParm, bool allow_alternate_port, IConfigSource configSource, AgentCircuitManager circuitManager)
: base(listenIP, (int)port) : base(listenIP, (int)port)
{ {
@ -244,19 +254,56 @@ namespace OpenSim.Region.ClientStack.LindenUDP
if (m_scene == null) if (m_scene == null)
throw new InvalidOperationException("[LLUDPSERVER]: Cannot LLUDPServer.Start() without an IScene reference"); 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); base.Start(m_recvBufferSize, m_asyncPacketHandling);
// Start the packet processing threads // Start the packet processing threads
Watchdog.StartThread( 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( 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; 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() public new void Stop()
{ {
m_log.Info("[LLUDPSERVER]: Shutting down the LLUDP server for " + m_scene.RegionInfo.RegionName); 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.IsLoggingOut = true;
client.Close(); 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() 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 // client. m_packetSent will be set to true if a packet is sent
m_scene.ForEachClient(clientPacketHandler); m_scene.ForEachClient(clientPacketHandler);
m_currentOutgoingClient = null;
// If nothing was sent, sleep for the minimum amount of time before a // If nothing was sent, sleep for the minimum amount of time before a
// token bucket could get more tokens // token bucket could get more tokens
if (!m_packetSent) if (!m_packetSent)
@ -1191,6 +1246,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP
private void ClientOutgoingPacketHandler(IClientAPI client) private void ClientOutgoingPacketHandler(IClientAPI client)
{ {
m_currentOutgoingClient = client;
try try
{ {
if (client is LLClientView) if (client is LLClientView)
@ -1216,8 +1273,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP
} }
catch (Exception ex) catch (Exception ex)
{ {
m_log.Error("[LLUDPSERVER]: OutgoingPacketHandler iteration for " + client.Name + m_log.Error(
" threw an exception: " + ex.Message, ex); string.Format("[LLUDPSERVER]: OutgoingPacketHandler iteration for {0} threw ", client.Name), ex);
} }
} }
@ -1243,6 +1300,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP
{ {
nticks++; nticks++;
watch1.Start(); watch1.Start();
m_currentOutgoingClient = client;
try try
{ {
if (client is LLClientView) if (client is LLClientView)
@ -1344,6 +1403,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP
// Make sure this client is still alive // Make sure this client is still alive
if (m_scene.TryGetClient(udpClient.AgentID, out client)) if (m_scene.TryGetClient(udpClient.AgentID, out client))
{ {
m_currentIncomingClient = client;
try try
{ {
// Process this packet // 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.ErrorFormat("[LLUDPSERVER]: Client packet handler for {0} for packet {1} threw an exception", udpClient.AgentID, packet.Type);
m_log.Error(e.Message, e); m_log.Error(e.Message, e);
} }
finally
{
m_currentIncomingClient = null;
}
} }
else else
{ {

View File

@ -73,7 +73,7 @@ namespace OpenSim.Region.CoreModules.World.Estate
"set terrain heights <corner> <min> <max> [<x>] [<y>]", "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 " + "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" + "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); consoleSetTerrainHeights);
m_module.Scene.AddCommand( m_module.Scene.AddCommand(
@ -143,6 +143,16 @@ namespace OpenSim.Region.CoreModules.World.Estate
switch (corner) 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: case 0:
m_module.Scene.RegionInfo.RegionSettings.Elevation1SW = lowValue; m_module.Scene.RegionInfo.RegionSettings.Elevation1SW = lowValue;
m_module.Scene.RegionInfo.RegionSettings.Elevation2SW = highValue; m_module.Scene.RegionInfo.RegionSettings.Elevation2SW = highValue;

View File

@ -178,8 +178,6 @@ namespace OpenSim.Region.CoreModules.World.Terrain.FileLoaders
// int sztmp = bs.ReadInt16() + 1; // int sztmp = bs.ReadInt16() + 1;
// fileWidth = sztmp; // fileWidth = sztmp;
// fileHeight = sztmp; // fileHeight = sztmp;
bs.ReadInt16();
bs.ReadInt16();
bs.ReadInt16(); bs.ReadInt16();
bs.ReadInt16(); bs.ReadInt16();
break; break;

View File

@ -84,218 +84,234 @@ namespace OpenSim.Region.CoreModules.World.Warp3DMap
Debug.Assert(heightRanges.Length == 4); Debug.Assert(heightRanges.Length == 4);
Bitmap[] detailTexture = new Bitmap[4]; Bitmap[] detailTexture = new Bitmap[4];
Bitmap output = null;
BitmapData outputData = null;
if (textureTerrain) try
{ {
// Swap empty terrain textureIDs with default IDs if (textureTerrain)
for (int i = 0; i < textureIDs.Length; i++)
{ {
if (textureIDs[i] == UUID.Zero) // Swap empty terrain textureIDs with default IDs
textureIDs[i] = DEFAULT_TERRAIN_DETAIL[i]; for (int i = 0; i < textureIDs.Length; i++)
}
#region Texture Fetching
if (assetService != null)
{
for (int i = 0; i < 4; i++)
{ {
AssetBase asset; if (textureIDs[i] == UUID.Zero)
UUID cacheID = UUID.Combine(TERRAIN_CACHE_MAGIC, textureIDs[i]); 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()); #region Texture Fetching
if (asset != null)
if (assetService != null)
{
for (int i = 0; i < 4; i++)
{ {
try AssetBase asset;
{ UUID cacheID = UUID.Combine(TERRAIN_CACHE_MAGIC, textureIDs[i]);
using (System.IO.MemoryStream stream = new System.IO.MemoryStream(asset.Data))
detailTexture[i] = (Bitmap)Image.FromStream(stream); // Try to fetch a cached copy of the decoded/resized version of this texture
} asset = assetService.GetCached(cacheID.ToString());
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());
if (asset != null) 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) 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]; // Try to fetch the original JPEG2000 texture, resize if needed, and cache as PNG
asset = assetService.Get(textureIDs[i].ToString());
// Make sure this texture is the correct size, otherwise resize if (asset != null)
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())
{ {
bitmap.Save(stream, ImageFormat.Png); try { detailTexture[i] = (Bitmap)CSJ2K.J2kImage.FromBytes(asset.Data); }
data = stream.ToArray(); catch (Exception ex)
{
m_log.Warn("Failed to decode terrain texture " + asset.ID + ": " + ex.Message);
}
} }
// Cache a PNG copy of this terrain texture if (detailTexture[i] != null)
AssetBase newAsset = new AssetBase
{ {
Data = data, Bitmap bitmap = detailTexture[i];
Description = "PNG",
Flags = AssetFlags.Collectable, // Make sure this texture is the correct size, otherwise resize
FullID = cacheID, if (bitmap.Width != 256 || bitmap.Height != 256)
ID = cacheID.ToString(), {
Local = true, using (Bitmap origBitmap = bitmap)
Name = String.Empty, {
Temporary = true, bitmap = ImageUtils.ResizeImage(origBitmap, 256, 256);
Type = (sbyte)AssetType.Unknown }
}; }
newAsset.Metadata.ContentType = "image/png";
assetService.Store(newAsset); // 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++)
// 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 if (detailTexture[i] == null)
detailTexture[i] = new Bitmap(256, 256, PixelFormat.Format24bppRgb);
using (Graphics gfx = Graphics.FromImage(detailTexture[i]))
{ {
using (SolidBrush brush = new SolidBrush(DEFAULT_TERRAIN_COLOR[i])) // Create a solid color texture for this layer
gfx.FillRectangle(brush, 0, 0, 256, 256); 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
#region Layer Map
float[] layermap = new float[256 * 256];
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
};
for (int y = 0; y < 256; y++) for (int y = 0; y < 256; y++)
{ {
for (int x = 0; x < 256; x++) for (int x = 0; x < 256; x++)
{ {
float layer = layermap[y * 256 + x]; float height = heightmap[y * 256 + x];
// Select two textures float pctX = (float)x / 255f;
int l0 = (int)Math.Floor(layer); float pctY = (float)y / 255f;
int l1 = Math.Min(l0 + 1, 3);
// Use bilinear interpolation between the four corners of start height and
byte* ptrA = (byte*)datas[l0].Scan0 + y * datas[l0].Stride + x * comps[l0]; // height range to select the current values at this position
byte* ptrB = (byte*)datas[l1].Scan0 + y * datas[l1].Stride + x * comps[l1]; float startHeight = ImageUtils.Bilinear(
byte* ptrO = (byte*)outputData.Scan0 + y * outputData.Stride + x * 3; startHeights[0],
startHeights[2],
float aB = *(ptrA + 0); startHeights[1],
float aG = *(ptrA + 1); startHeights[3],
float aR = *(ptrA + 2); pctX, pctY);
startHeight = Utils.Clamp(startHeight, 0f, 255f);
float bB = *(ptrB + 0);
float bG = *(ptrB + 1); float heightRange = ImageUtils.Bilinear(
float bR = *(ptrB + 2); heightRanges[0],
heightRanges[2],
float layerDiff = layer - l0; heightRanges[1],
heightRanges[3],
// Interpolate between the two selected textures pctX, pctY);
*(ptrO + 0) = (byte)Math.Floor(aB + layerDiff * (bB - aB)); heightRange = Utils.Clamp(heightRange, 0f, 255f);
*(ptrO + 1) = (byte)Math.Floor(aG + layerDiff * (bG - aG));
*(ptrO + 2) = (byte)Math.Floor(aR + layerDiff * (bR - aR)); // 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++) for (int i = 0; i < 4; i++)
detailTexture[i].UnlockBits(datas[i]); if (detailTexture[i] != null)
detailTexture[i].Dispose();
} }
output.UnlockBits(outputData); output.UnlockBits(outputData);

View File

@ -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 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 Color4 WATER_COLOR = new Color4(29, 72, 96, 216);
private static readonly ILog m_log = private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
private Scene m_scene; private Scene m_scene;
private IRendering m_primMesher; private IRendering m_primMesher;
@ -88,11 +87,11 @@ namespace OpenSim.Region.CoreModules.World.Warp3DMap
if (renderers.Count > 0) if (renderers.Count > 0)
{ {
m_primMesher = RenderingLoader.LoadRenderer(renderers[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 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); m_scene.RegisterModuleInterface<IMapImageGenerator>(this);
@ -150,7 +149,7 @@ namespace OpenSim.Region.CoreModules.World.Warp3DMap
} }
catch catch
{ {
m_log.Warn("[MAPTILE]: Failed to load StartupConfig"); m_log.Warn("[WARP 3D IMAGE MODULE]: Failed to load StartupConfig");
} }
m_colors.Clear(); m_colors.Clear();
@ -204,7 +203,10 @@ namespace OpenSim.Region.CoreModules.World.Warp3DMap
Bitmap bitmap = renderer.Scene.getImage(); Bitmap bitmap = renderer.Scene.getImage();
if (m_useAntiAliasing) 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; return bitmap;
} }
@ -219,7 +221,7 @@ namespace OpenSim.Region.CoreModules.World.Warp3DMap
catch (Exception e) catch (Exception e)
{ {
// JPEG2000 encoder failed // 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; return null;
@ -318,8 +320,17 @@ namespace OpenSim.Region.CoreModules.World.Warp3DMap
uint globalX, globalY; uint globalX, globalY;
Utils.LongToUInts(m_scene.RegionInfo.RegionHandle, out globalX, out 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;
warp_Texture texture = new warp_Texture(image);
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); warp_Material material = new warp_Material(texture);
material.setReflectivity(50); material.setReflectivity(50);
renderer.Scene.addMaterial("TerrainColor", material); renderer.Scene.addMaterial("TerrainColor", material);
@ -546,42 +557,46 @@ namespace OpenSim.Region.CoreModules.World.Warp3DMap
{ {
try try
{ {
Bitmap bitmap = (Bitmap)J2kImage.FromStream(stream); int pixelBytes;
width = bitmap.Width;
height = bitmap.Height;
BitmapData bitmapData = bitmap.LockBits(new Rectangle(0, 0, width, height), ImageLockMode.ReadOnly, bitmap.PixelFormat); using (Bitmap bitmap = (Bitmap)J2kImage.FromStream(stream))
int pixelBytes = (bitmap.PixelFormat == PixelFormat.Format24bppRgb) ? 3 : 4;
// Sum up the individual channels
unsafe
{ {
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 y = 0; y < height; y++)
for (int x = 0; x < width; x++)
{ {
b += row[x * pixelBytes + 0]; byte* row = (byte*)bitmapData.Scan0 + (y * bitmapData.Stride);
g += row[x * pixelBytes + 1];
r += row[x * pixelBytes + 2]; for (int x = 0; x < width; x++)
a += row[x * pixelBytes + 3]; {
b += row[x * pixelBytes + 0];
g += row[x * pixelBytes + 1];
r += row[x * pixelBytes + 2];
a += row[x * pixelBytes + 3];
}
} }
} }
} else
else
{
for (int y = 0; y < height; y++)
{ {
byte* row = (byte*)bitmapData.Scan0 + (y * bitmapData.Stride); for (int y = 0; y < height; y++)
for (int x = 0; x < width; x++)
{ {
b += row[x * pixelBytes + 0]; byte* row = (byte*)bitmapData.Scan0 + (y * bitmapData.Stride);
g += row[x * pixelBytes + 1];
r += row[x * pixelBytes + 2]; 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) 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; width = 0;
height = 0; height = 0;
return new Color4(0.5f, 0.5f, 0.5f, 1.0f); return new Color4(0.5f, 0.5f, 0.5f, 1.0f);

View File

@ -1343,14 +1343,14 @@ namespace OpenSim.Region.CoreModules.World.WorldMap
if (terrain == null) if (terrain == null)
return; return;
m_log.DebugFormat("[WORLDMAP]: Generating map image for {0}", m_scene.RegionInfo.RegionName);
byte[] data = terrain.WriteJpeg2000Image(); byte[] data = terrain.WriteJpeg2000Image();
if (data == null) if (data == null)
return; return;
byte[] overlay = GenerateOverlay(); byte[] overlay = GenerateOverlay();
m_log.Debug("[WORLDMAP]: STORING MAPTILE IMAGE");
UUID terrainImageID = UUID.Random(); UUID terrainImageID = UUID.Random();
UUID parcelImageID = UUID.Zero; UUID parcelImageID = UUID.Zero;
@ -1365,7 +1365,8 @@ namespace OpenSim.Region.CoreModules.World.WorldMap
asset.Flags = AssetFlags.Maptile; asset.Flags = AssetFlags.Maptile;
// Store the new one // 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); m_scene.AssetService.Store(asset);
if (overlay != null) if (overlay != null)

View File

@ -113,14 +113,15 @@ namespace OpenSim.Region.OptionalModules.World.WorldView
if (!m_Enabled) if (!m_Enabled)
return new Byte[0]; return new Byte[0];
Bitmap bmp = m_Generator.CreateViewImage(pos, rot, fov, width, using (Bitmap bmp = m_Generator.CreateViewImage(pos, rot, fov, width, height, usetex))
height, usetex); {
using (MemoryStream str = new MemoryStream())
{
bmp.Save(str, ImageFormat.Jpeg);
MemoryStream str = new MemoryStream(); return str.ToArray();
}
bmp.Save(str, ImageFormat.Jpeg); }
return str.ToArray();
} }
} }
} }