From 5c646e26031400961dcfe3d7c4bd84512a1bc7cb Mon Sep 17 00:00:00 2001 From: Melanie Date: Mon, 4 Jun 2012 18:22:09 +0100 Subject: Remove the "Profile" config as it's covered by the replaceable interface --- OpenSim/Region/CoreModules/Avatar/Profile/BasicProfileModule.cs | 8 -------- 1 file changed, 8 deletions(-) (limited to 'OpenSim/Region') diff --git a/OpenSim/Region/CoreModules/Avatar/Profile/BasicProfileModule.cs b/OpenSim/Region/CoreModules/Avatar/Profile/BasicProfileModule.cs index 8101ca2..87ca327 100644 --- a/OpenSim/Region/CoreModules/Avatar/Profile/BasicProfileModule.cs +++ b/OpenSim/Region/CoreModules/Avatar/Profile/BasicProfileModule.cs @@ -57,14 +57,6 @@ namespace OpenSim.Region.CoreModules.Avatar.Profile public void Initialise(IConfigSource config) { - // This can be reduced later as the loader will determine - // whether we are needed - if (config.Configs["Profile"] != null) - { - if (config.Configs["Profile"].GetString("Module", string.Empty) != "BasicProfileModule") - return; - } - m_log.DebugFormat("[PROFILE MODULE]: Basic Profile Module enabled"); m_Enabled = true; } -- cgit v1.1 From f94ef37b46c680e3d74b21cdb2e2a89f482bcc62 Mon Sep 17 00:00:00 2001 From: Robert Adams Date: Mon, 4 Jun 2012 10:26:39 -0700 Subject: Correct the delegate specification in EventManager.TriggerTerrainTainted. Looks like the wrong one was cut and pasted. --- OpenSim/Region/Framework/Scenes/EventManager.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'OpenSim/Region') diff --git a/OpenSim/Region/Framework/Scenes/EventManager.cs b/OpenSim/Region/Framework/Scenes/EventManager.cs index f97b0a9..f92ed8e 100644 --- a/OpenSim/Region/Framework/Scenes/EventManager.cs +++ b/OpenSim/Region/Framework/Scenes/EventManager.cs @@ -923,7 +923,7 @@ namespace OpenSim.Region.Framework.Scenes OnTerrainTaintedDelegate handlerTerrainTainted = OnTerrainTainted; if (handlerTerrainTainted != null) { - foreach (OnTerrainTickDelegate d in handlerTerrainTainted.GetInvocationList()) + foreach (OnTerrainTaintedDelegate d in handlerTerrainTainted.GetInvocationList()) { try { -- cgit v1.1 From 3229e32b4e818c6a0897800d8770c95f90ee3a94 Mon Sep 17 00:00:00 2001 From: BlueWall Date: Mon, 4 Jun 2012 17:22:46 -0400 Subject: Add replaceable region modules to the "show modules" command --- OpenSim/Region/Application/OpenSim.cs | 78 ++++++++++++++++++++++------------- 1 file changed, 49 insertions(+), 29 deletions(-) (limited to 'OpenSim/Region') diff --git a/OpenSim/Region/Application/OpenSim.cs b/OpenSim/Region/Application/OpenSim.cs index c0913c5..76ac827 100644 --- a/OpenSim/Region/Application/OpenSim.cs +++ b/OpenSim/Region/Application/OpenSim.cs @@ -971,8 +971,7 @@ namespace OpenSim if (showParams.Length > 1 && showParams[1] == "full") { agents = m_sceneManager.GetCurrentScenePresences(); - } - else + } else { agents = m_sceneManager.GetCurrentSceneAvatars(); } @@ -981,7 +980,8 @@ namespace OpenSim MainConsole.Instance.Output( String.Format("{0,-16} {1,-16} {2,-37} {3,-11} {4,-16} {5,-30}", "Firstname", "Lastname", - "Agent ID", "Root/Child", "Region", "Position")); + "Agent ID", "Root/Child", "Region", "Position") + ); foreach (ScenePresence presence in agents) { @@ -991,8 +991,7 @@ namespace OpenSim if (regionInfo == null) { regionName = "Unresolvable"; - } - else + } else { regionName = regionInfo.RegionName; } @@ -1005,7 +1004,8 @@ namespace OpenSim presence.UUID, presence.IsChildAgent ? "Child" : "Root", regionName, - presence.AbsolutePosition.ToString())); + presence.AbsolutePosition.ToString()) + ); } MainConsole.Instance.Output(String.Empty); @@ -1014,16 +1014,20 @@ namespace OpenSim case "connections": System.Text.StringBuilder connections = new System.Text.StringBuilder("Connections:\n"); m_sceneManager.ForEachScene( - delegate(Scene scene) - { - scene.ForEachClient( - delegate(IClientAPI client) - { - connections.AppendFormat("{0}: {1} ({2}) from {3} on circuit {4}\n", - scene.RegionInfo.RegionName, client.Name, client.AgentId, client.RemoteEndPoint, client.CircuitCode); - } + delegate(Scene scene) { + scene.ForEachClient( + delegate(IClientAPI client) { + connections.AppendFormat( + "{0}: {1} ({2}) from {3} on circuit {4}\n", + scene.RegionInfo.RegionName, + client.Name, + client.AgentId, + client.RemoteEndPoint, + client.CircuitCode ); } + ); + } ); MainConsole.Instance.Output(connections.ToString()); @@ -1032,13 +1036,17 @@ namespace OpenSim case "circuits": System.Text.StringBuilder acd = new System.Text.StringBuilder("Agent Circuits:\n"); m_sceneManager.ForEachScene( - delegate(Scene scene) - { - //this.HttpServer. - acd.AppendFormat("{0}:\n", scene.RegionInfo.RegionName); - foreach (AgentCircuitData aCircuit in scene.AuthenticateHandler.GetAgentCircuits().Values) - acd.AppendFormat("\t{0} {1} ({2})\n", aCircuit.firstname, aCircuit.lastname, (aCircuit.child ? "Child" : "Root")); - } + delegate(Scene scene) { + //this.HttpServer. + acd.AppendFormat("{0}:\n", scene.RegionInfo.RegionName); + foreach (AgentCircuitData aCircuit in scene.AuthenticateHandler.GetAgentCircuits().Values) + acd.AppendFormat( + "\t{0} {1} ({2})\n", + aCircuit.firstname, + aCircuit.lastname, + (aCircuit.child ? "Child" : "Root") + ); + } ); MainConsole.Instance.Output(acd.ToString()); @@ -1079,17 +1087,29 @@ namespace OpenSim } m_sceneManager.ForEachScene( - delegate(Scene scene) + delegate(Scene scene) { + m_log.Error("The currently loaded modules in " + scene.RegionInfo.RegionName + " are:"); + foreach (IRegionModule module in scene.Modules.Values) { - m_log.Error("The currently loaded modules in " + scene.RegionInfo.RegionName + " are:"); - foreach (IRegionModule module in scene.Modules.Values) + if (!module.IsSharedModule) { - if (!module.IsSharedModule) - { - m_log.Error("Region Module: " + module.Name); - } + m_log.Error("Region Module: " + module.Name); } - }); + } + } + ); + + m_sceneManager.ForEachScene( + delegate(Scene scene) { + MainConsole.Instance.Output("Loaded new region modules in" + scene.RegionInfo.RegionName + " are:"); + foreach (IRegionModuleBase module in scene.RegionModules.Values) + { + Type type = module.GetType().GetInterface("ISharedRegionModule"); + string module_type = type != null ? "Shared" : "Non-Shared"; + MainConsole.Instance.OutputFormat("New Region Module ({0}): {1}", module_type, module.Name); + } + } + ); MainConsole.Instance.Output(""); break; -- cgit v1.1 From a7f4804f53f391d5d67bf3484733ab5e41bc34be Mon Sep 17 00:00:00 2001 From: Justin Clark-Casey (justincc) Date: Mon, 4 Jun 2012 23:07:53 +0100 Subject: Properly show per frame millisecond statistics per frame, not as amount of time taken per second. This is to make these statistics actually match their names (and also be more accurate as number of frames can vary under heavy load) Currently using scene frames (11.23 every second) instead of physics frames (56.18 per second) --- .../Region/Framework/Scenes/SimStatsReporter.cs | 63 ++++++++++++++-------- 1 file changed, 41 insertions(+), 22 deletions(-) (limited to 'OpenSim/Region') diff --git a/OpenSim/Region/Framework/Scenes/SimStatsReporter.cs b/OpenSim/Region/Framework/Scenes/SimStatsReporter.cs index 87af311..11c321b 100644 --- a/OpenSim/Region/Framework/Scenes/SimStatsReporter.cs +++ b/OpenSim/Region/Framework/Scenes/SimStatsReporter.cs @@ -117,12 +117,17 @@ namespace OpenSim.Region.Framework.Scenes private Dictionary m_lastReportedExtraSimStats = new Dictionary(); // Sending a stats update every 3 seconds- - private int statsUpdatesEveryMS = 3000; - private float statsUpdateFactor = 0; + private int m_statsUpdatesEveryMS = 3000; + private float m_statsUpdateFactor = 0; private float m_timeDilation = 0; private int m_fps = 0; /// + /// Number of the last frame on which we processed a stats udpate. + /// + private uint m_lastUpdateFrame; + + /// /// Our nominal fps target, as expected in fps stats when a sim is running normally. /// private float m_nominalReportedFps = 55; @@ -188,12 +193,12 @@ namespace OpenSim.Region.Framework.Scenes { m_scene = scene; m_reportedFpsCorrectionFactor = scene.MinFrameTime * m_nominalReportedFps; - statsUpdateFactor = (float)(statsUpdatesEveryMS / 1000); + m_statsUpdateFactor = (float)(m_statsUpdatesEveryMS / 1000); ReportingRegion = scene.RegionInfo; m_objectCapacity = scene.RegionInfo.ObjectCapacity; m_report.AutoReset = true; - m_report.Interval = statsUpdatesEveryMS; + m_report.Interval = m_statsUpdatesEveryMS; m_report.Elapsed += statsHeartBeat; m_report.Enabled = true; @@ -213,9 +218,9 @@ namespace OpenSim.Region.Framework.Scenes /// public void SetUpdateMS(int ms) { - statsUpdatesEveryMS = ms; - statsUpdateFactor = (float)(statsUpdatesEveryMS / 1000); - m_report.Interval = statsUpdatesEveryMS; + m_statsUpdatesEveryMS = ms; + m_statsUpdateFactor = (float)(m_statsUpdatesEveryMS / 1000); + m_report.Interval = m_statsUpdatesEveryMS; } private void statsHeartBeat(object sender, EventArgs e) @@ -247,7 +252,7 @@ namespace OpenSim.Region.Framework.Scenes int reportedFPS = (int)(m_fps * m_reportedFpsCorrectionFactor); // save the reported value so there is something available for llGetRegionFPS - lastReportedSimFPS = reportedFPS / statsUpdateFactor; + lastReportedSimFPS = reportedFPS / m_statsUpdateFactor; float physfps = ((m_pfps / 1000)); @@ -279,6 +284,14 @@ namespace OpenSim.Region.Framework.Scenes // 'statsUpdateFactor' is how often stats packets are sent in seconds. Used below to change // values to X-per-second values. + uint thisFrame = m_scene.Frame; + float framesUpdated = (float)(thisFrame - m_lastUpdateFrame); + m_lastUpdateFrame = thisFrame; + + // Avoid div-by-zero if somehow we've not updated any frames. + if (framesUpdated == 0) + framesUpdated = 1; + for (int i = 0; i < 21; i++) { sb[i] = new SimStatsPacket.StatBlock(); @@ -288,13 +301,13 @@ namespace OpenSim.Region.Framework.Scenes sb[0].StatValue = (Single.IsNaN(m_timeDilation)) ? 0.1f : m_timeDilation ; //((((m_timeDilation + (0.10f * statsUpdateFactor)) /10) / statsUpdateFactor)); sb[1].StatID = (uint) Stats.SimFPS; - sb[1].StatValue = reportedFPS / statsUpdateFactor; + sb[1].StatValue = reportedFPS / m_statsUpdateFactor; sb[2].StatID = (uint) Stats.PhysicsFPS; - sb[2].StatValue = physfps / statsUpdateFactor; + sb[2].StatValue = physfps / m_statsUpdateFactor; sb[3].StatID = (uint) Stats.AgentUpdates; - sb[3].StatValue = (m_agentUpdates / statsUpdateFactor); + sb[3].StatValue = (m_agentUpdates / m_statsUpdateFactor); sb[4].StatID = (uint) Stats.Agents; sb[4].StatValue = m_rootAgents; @@ -309,31 +322,31 @@ namespace OpenSim.Region.Framework.Scenes sb[7].StatValue = m_activePrim; sb[8].StatID = (uint)Stats.FrameMS; - sb[8].StatValue = m_frameMS / statsUpdateFactor; + sb[8].StatValue = m_frameMS / framesUpdated; sb[9].StatID = (uint)Stats.NetMS; - sb[9].StatValue = m_netMS / statsUpdateFactor; + sb[9].StatValue = m_netMS / framesUpdated; sb[10].StatID = (uint)Stats.PhysicsMS; - sb[10].StatValue = m_physicsMS / statsUpdateFactor; + sb[10].StatValue = m_physicsMS / framesUpdated; sb[11].StatID = (uint)Stats.ImageMS ; - sb[11].StatValue = m_imageMS / statsUpdateFactor; + sb[11].StatValue = m_imageMS / framesUpdated; sb[12].StatID = (uint)Stats.OtherMS; - sb[12].StatValue = m_otherMS / statsUpdateFactor; + sb[12].StatValue = m_otherMS / framesUpdated; sb[13].StatID = (uint)Stats.InPacketsPerSecond; - sb[13].StatValue = (m_inPacketsPerSecond / statsUpdateFactor); + sb[13].StatValue = (m_inPacketsPerSecond / m_statsUpdateFactor); sb[14].StatID = (uint)Stats.OutPacketsPerSecond; - sb[14].StatValue = (m_outPacketsPerSecond / statsUpdateFactor); + sb[14].StatValue = (m_outPacketsPerSecond / m_statsUpdateFactor); sb[15].StatID = (uint)Stats.UnAckedBytes; sb[15].StatValue = m_unAckedBytes; sb[16].StatID = (uint)Stats.AgentMS; - sb[16].StatValue = m_agentMS / statsUpdateFactor; + sb[16].StatValue = m_agentMS / framesUpdated; sb[17].StatID = (uint)Stats.PendingDownloads; sb[17].StatValue = m_pendingDownloads; @@ -345,7 +358,7 @@ namespace OpenSim.Region.Framework.Scenes sb[19].StatValue = m_activeScripts; sb[20].StatID = (uint)Stats.ScriptLinesPerSecond; - sb[20].StatValue = m_scriptLinesPerSecond / statsUpdateFactor; + sb[20].StatValue = m_scriptLinesPerSecond / m_statsUpdateFactor; for (int i = 0; i < 21; i++) { @@ -366,7 +379,7 @@ namespace OpenSim.Region.Framework.Scenes // Extra statistics that aren't currently sent to clients lock (m_lastReportedExtraSimStats) { - m_lastReportedExtraSimStats[LastReportedObjectUpdateStatName] = m_objectUpdates / statsUpdateFactor; + m_lastReportedExtraSimStats[LastReportedObjectUpdateStatName] = m_objectUpdates / m_statsUpdateFactor; Dictionary physicsStats = m_scene.PhysicsScene.GetStats(); @@ -374,7 +387,13 @@ namespace OpenSim.Region.Framework.Scenes { foreach (KeyValuePair tuple in physicsStats) { - m_lastReportedExtraSimStats[tuple.Key] = tuple.Value / statsUpdateFactor; + // FIXME: An extremely dirty hack to divide MS stats per frame rather than per second + // Need to change things so that stats source can indicate whether they are per second or + // per frame. + if (tuple.Key.EndsWith("MS")) + m_lastReportedExtraSimStats[tuple.Key] = tuple.Value / framesUpdated; + else + m_lastReportedExtraSimStats[tuple.Key] = tuple.Value / m_statsUpdateFactor; } } } -- cgit v1.1 From 655625ab872659cd8aff1fe5b11dc568c91aee6d Mon Sep 17 00:00:00 2001 From: Justin Clark-Casey (justincc) Date: Tue, 5 Jun 2012 00:17:55 +0100 Subject: Start sending spare frame time MS viewer stat. Make frame time correctly display total frame time, not just non-spare time. This makes it easier to see when components of frame time exceed normal permitted frame time. Currently reflect scene frame times. --- OpenSim/Region/Framework/Scenes/Scene.cs | 90 +++++++++-------- .../Region/Framework/Scenes/SimStatsReporter.cs | 112 ++++++++++++++------- 2 files changed, 124 insertions(+), 78 deletions(-) (limited to 'OpenSim/Region') diff --git a/OpenSim/Region/Framework/Scenes/Scene.cs b/OpenSim/Region/Framework/Scenes/Scene.cs index 702e322..ae35cb9 100644 --- a/OpenSim/Region/Framework/Scenes/Scene.cs +++ b/OpenSim/Region/Framework/Scenes/Scene.cs @@ -219,6 +219,7 @@ namespace OpenSim.Region.Framework.Scenes private int backupMS; private int terrainMS; private int landMS; + private int spareMS; /// /// Tick at which the last frame was processed. @@ -1360,43 +1361,41 @@ namespace OpenSim.Region.Framework.Scenes endFrame = Frame + frames; float physicsFPS = 0f; - int tmpPhysicsMS, tmpPhysicsMS2, tmpAgentMS, tmpTempOnRezMS, evMS, backMS, terMS; - int previousFrameTick; - int maintc; + int previousFrameTick, tmpMS; + int maintc = Util.EnvironmentTickCount(); while (!m_shuttingDown && (endFrame == null || Frame < endFrame)) { - maintc = Util.EnvironmentTickCount(); ++Frame; // m_log.DebugFormat("[SCENE]: Processing frame {0} in {1}", Frame, RegionInfo.RegionName); - agentMS = tempOnRezMS = eventMS = backupMS = terrainMS = landMS = 0; + agentMS = tempOnRezMS = eventMS = backupMS = terrainMS = landMS = spareMS = 0; try { // Apply taints in terrain module to terrain in physics scene if (Frame % m_update_terrain == 0) { - terMS = Util.EnvironmentTickCount(); + tmpMS = Util.EnvironmentTickCount(); UpdateTerrain(); - terrainMS = Util.EnvironmentTickCountSubtract(terMS); + terrainMS = Util.EnvironmentTickCountSubtract(tmpMS); } - tmpPhysicsMS2 = Util.EnvironmentTickCount(); + tmpMS = Util.EnvironmentTickCount(); if ((Frame % m_update_physics == 0) && m_physics_enabled) m_sceneGraph.UpdatePreparePhysics(); - physicsMS2 = Util.EnvironmentTickCountSubtract(tmpPhysicsMS2); + physicsMS2 = Util.EnvironmentTickCountSubtract(tmpMS); // Apply any pending avatar force input to the avatar's velocity - tmpAgentMS = Util.EnvironmentTickCount(); + tmpMS = Util.EnvironmentTickCount(); if (Frame % m_update_entitymovement == 0) m_sceneGraph.UpdateScenePresenceMovement(); - agentMS = Util.EnvironmentTickCountSubtract(tmpAgentMS); + agentMS = Util.EnvironmentTickCountSubtract(tmpMS); // Perform the main physics update. This will do the actual work of moving objects and avatars according to their // velocity - tmpPhysicsMS = Util.EnvironmentTickCount(); + tmpMS = Util.EnvironmentTickCount(); if (Frame % m_update_physics == 0) { if (m_physics_enabled) @@ -1405,9 +1404,9 @@ namespace OpenSim.Region.Framework.Scenes if (SynchronizeScene != null) SynchronizeScene(this); } - physicsMS = Util.EnvironmentTickCountSubtract(tmpPhysicsMS); + physicsMS = Util.EnvironmentTickCountSubtract(tmpMS); - tmpAgentMS = Util.EnvironmentTickCount(); + tmpMS = Util.EnvironmentTickCount(); // Check if any objects have reached their targets CheckAtTargets(); @@ -1422,29 +1421,29 @@ namespace OpenSim.Region.Framework.Scenes if (Frame % m_update_presences == 0) m_sceneGraph.UpdatePresences(); - agentMS += Util.EnvironmentTickCountSubtract(tmpAgentMS); + agentMS += Util.EnvironmentTickCountSubtract(tmpMS); // Delete temp-on-rez stuff if (Frame % m_update_temp_cleaning == 0 && !m_cleaningTemps) { - tmpTempOnRezMS = Util.EnvironmentTickCount(); + tmpMS = Util.EnvironmentTickCount(); m_cleaningTemps = true; Util.FireAndForget(delegate { CleanTempObjects(); m_cleaningTemps = false; }); - tempOnRezMS = Util.EnvironmentTickCountSubtract(tmpTempOnRezMS); + tempOnRezMS = Util.EnvironmentTickCountSubtract(tmpMS); } if (Frame % m_update_events == 0) { - evMS = Util.EnvironmentTickCount(); + tmpMS = Util.EnvironmentTickCount(); UpdateEvents(); - eventMS = Util.EnvironmentTickCountSubtract(evMS); + eventMS = Util.EnvironmentTickCountSubtract(tmpMS); } if (Frame % m_update_backup == 0) { - backMS = Util.EnvironmentTickCount(); + tmpMS = Util.EnvironmentTickCount(); UpdateStorageBackup(); - backupMS = Util.EnvironmentTickCountSubtract(backMS); + backupMS = Util.EnvironmentTickCountSubtract(tmpMS); } //if (Frame % m_update_land == 0) @@ -1453,24 +1452,6 @@ namespace OpenSim.Region.Framework.Scenes // UpdateLand(); // landMS = Util.EnvironmentTickCountSubtract(ldMS); //} - - frameMS = Util.EnvironmentTickCountSubtract(maintc); - otherMS = tempOnRezMS + eventMS + backupMS + terrainMS + landMS; - - // if (Frame%m_update_avatars == 0) - // UpdateInWorldTime(); - StatsReporter.AddPhysicsFPS(physicsFPS); - StatsReporter.AddTimeDilation(TimeDilation); - StatsReporter.AddFPS(1); - - // frameMS currently records work frame times, not total frame times (work + any required sleep to - // reach min frame time. - StatsReporter.addFrameMS(frameMS); - - StatsReporter.addAgentMS(agentMS); - StatsReporter.addPhysicsMS(physicsMS + physicsMS2); - StatsReporter.addOtherMS(otherMS); - StatsReporter.addScriptLines(m_sceneGraph.GetScriptLPS()); if (LoginsDisabled && Frame == 20) { @@ -1521,13 +1502,34 @@ namespace OpenSim.Region.Framework.Scenes previousFrameTick = m_lastFrameTick; m_lastFrameTick = Util.EnvironmentTickCount(); - maintc = Util.EnvironmentTickCountSubtract(m_lastFrameTick, maintc); - maintc = (int)(MinFrameTime * 1000) - maintc; + tmpMS = Util.EnvironmentTickCountSubtract(m_lastFrameTick, maintc); + tmpMS = (int)(MinFrameTime * 1000) - tmpMS; - if (maintc > 0) - Thread.Sleep(maintc); + if (tmpMS > 0) + { + Thread.Sleep(tmpMS); + spareMS += tmpMS; + } - // Optionally warn if a frame takes double the amount of time that it should. + frameMS = Util.EnvironmentTickCountSubtract(maintc); + maintc = Util.EnvironmentTickCount(); + + otherMS = tempOnRezMS + eventMS + backupMS + terrainMS + landMS; + + // if (Frame%m_update_avatars == 0) + // UpdateInWorldTime(); + StatsReporter.AddPhysicsFPS(physicsFPS); + StatsReporter.AddTimeDilation(TimeDilation); + StatsReporter.AddFPS(1); + + StatsReporter.addFrameMS(frameMS); + StatsReporter.addAgentMS(agentMS); + StatsReporter.addPhysicsMS(physicsMS + physicsMS2); + StatsReporter.addOtherMS(otherMS); + StatsReporter.AddSpareMS(spareMS); + StatsReporter.addScriptLines(m_sceneGraph.GetScriptLPS()); + + // Optionally warn if a frame takes double the amount of time that it should. if (DebugUpdates && Util.EnvironmentTickCountSubtract( m_lastFrameTick, previousFrameTick) > (int)(MinFrameTime * 1000 * 2)) diff --git a/OpenSim/Region/Framework/Scenes/SimStatsReporter.cs b/OpenSim/Region/Framework/Scenes/SimStatsReporter.cs index 11c321b..88e0b05 100644 --- a/OpenSim/Region/Framework/Scenes/SimStatsReporter.cs +++ b/OpenSim/Region/Framework/Scenes/SimStatsReporter.cs @@ -56,10 +56,17 @@ namespace OpenSim.Region.Framework.Scenes public event YourStatsAreWrong OnStatsIncorrect; - private SendStatResult handlerSendStatResult = null; + private SendStatResult handlerSendStatResult; - private YourStatsAreWrong handlerStatsIncorrect = null; + private YourStatsAreWrong handlerStatsIncorrect; + /// + /// These are the IDs of stats sent in the StatsPacket to the viewer. + /// + /// + /// Some of these are not relevant to OpenSimulator since it is architected differently to other simulators + /// (e.g. script instructions aren't executed as part of the frame loop so 'script time' is tricky). + /// public enum Stats : uint { TimeDilation = 0, @@ -83,7 +90,20 @@ namespace OpenSim.Region.Framework.Scenes OutPacketsPerSecond = 18, PendingDownloads = 19, PendingUploads = 20, + VirtualSizeKb = 21, + ResidentSizeKb = 22, + PendingLocalUploads = 23, UnAckedBytes = 24, + PhysicsPinnedTasks = 25, + PhysicsLodTasks = 26, + SimPhysicsStepMs = 27, + SimPhysicsShapeMs = 28, + SimPhysicsOtherMs = 29, + SimPhysicsMemory = 30, + ScriptEps = 31, + SimSpareMs = 32, + SimSleepMs = 33, + SimIoPumpTime = 34 } /// @@ -118,9 +138,9 @@ namespace OpenSim.Region.Framework.Scenes // Sending a stats update every 3 seconds- private int m_statsUpdatesEveryMS = 3000; - private float m_statsUpdateFactor = 0; - private float m_timeDilation = 0; - private int m_fps = 0; + private float m_statsUpdateFactor; + private float m_timeDilation; + private int m_fps; /// /// Number of the last frame on which we processed a stats udpate. @@ -143,41 +163,42 @@ namespace OpenSim.Region.Framework.Scenes private float m_reportedFpsCorrectionFactor = 5; // saved last reported value so there is something available for llGetRegionFPS - private float lastReportedSimFPS = 0; - private float[] lastReportedSimStats = new float[21]; - private float m_pfps = 0; + private float lastReportedSimFPS; + private float[] lastReportedSimStats = new float[22]; + private float m_pfps; /// /// Number of agent updates requested in this stats cycle /// - private int m_agentUpdates = 0; + private int m_agentUpdates; /// /// Number of object updates requested in this stats cycle /// private int m_objectUpdates; - private int m_frameMS = 0; - private int m_netMS = 0; - private int m_agentMS = 0; - private int m_physicsMS = 0; - private int m_imageMS = 0; - private int m_otherMS = 0; + private int m_frameMS; + private int m_spareMS; + private int m_netMS; + private int m_agentMS; + private int m_physicsMS; + private int m_imageMS; + private int m_otherMS; //Ckrinke: (3-21-08) Comment out to remove a compiler warning. Bring back into play when needed. //Ckrinke private int m_scriptMS = 0; - private int m_rootAgents = 0; - private int m_childAgents = 0; - private int m_numPrim = 0; - private int m_inPacketsPerSecond = 0; - private int m_outPacketsPerSecond = 0; - private int m_activePrim = 0; - private int m_unAckedBytes = 0; - private int m_pendingDownloads = 0; - private int m_pendingUploads = 0; - private int m_activeScripts = 0; - private int m_scriptLinesPerSecond = 0; + private int m_rootAgents; + private int m_childAgents; + private int m_numPrim; + private int m_inPacketsPerSecond; + private int m_outPacketsPerSecond; + private int m_activePrim; + private int m_unAckedBytes; + private int m_pendingDownloads; + private int m_pendingUploads = 0; // FIXME: Not currently filled in + private int m_activeScripts; + private int m_scriptLinesPerSecond; private int m_objectCapacity = 45000; @@ -199,7 +220,7 @@ namespace OpenSim.Region.Framework.Scenes m_objectCapacity = scene.RegionInfo.ObjectCapacity; m_report.AutoReset = true; m_report.Interval = m_statsUpdatesEveryMS; - m_report.Elapsed += statsHeartBeat; + m_report.Elapsed += TriggerStatsHeartbeat; m_report.Enabled = true; if (StatsManager.SimExtraStats != null) @@ -208,7 +229,7 @@ namespace OpenSim.Region.Framework.Scenes public void Close() { - m_report.Elapsed -= statsHeartBeat; + m_report.Elapsed -= TriggerStatsHeartbeat; m_report.Close(); } @@ -223,9 +244,23 @@ namespace OpenSim.Region.Framework.Scenes m_report.Interval = m_statsUpdatesEveryMS; } + private void TriggerStatsHeartbeat(object sender, EventArgs args) + { + try + { + statsHeartBeat(sender, args); + } + catch (Exception e) + { + m_log.Warn(string.Format( + "[SIM STATS REPORTER] Update for {0} failed with exception ", + m_scene.RegionInfo.RegionName), e); + } + } + private void statsHeartBeat(object sender, EventArgs e) { - SimStatsPacket.StatBlock[] sb = new SimStatsPacket.StatBlock[21]; + SimStatsPacket.StatBlock[] sb = new SimStatsPacket.StatBlock[22]; SimStatsPacket.RegionBlock rb = new SimStatsPacket.RegionBlock(); // Know what's not thread safe in Mono... modifying timers. @@ -292,7 +327,7 @@ namespace OpenSim.Region.Framework.Scenes if (framesUpdated == 0) framesUpdated = 1; - for (int i = 0; i < 21; i++) + for (int i = 0; i < 22; i++) { sb[i] = new SimStatsPacket.StatBlock(); } @@ -359,8 +394,11 @@ namespace OpenSim.Region.Framework.Scenes sb[20].StatID = (uint)Stats.ScriptLinesPerSecond; sb[20].StatValue = m_scriptLinesPerSecond / m_statsUpdateFactor; - - for (int i = 0; i < 21; i++) + + sb[21].StatID = (uint)Stats.SimSpareMs; + sb[21].StatValue = m_spareMS / framesUpdated; + + for (int i = 0; i < 22; i++) { lastReportedSimStats[i] = sb[i].StatValue; } @@ -398,11 +436,11 @@ namespace OpenSim.Region.Framework.Scenes } } - resetvalues(); + ResetValues(); } } - private void resetvalues() + private void ResetValues() { m_timeDilation = 0; m_fps = 0; @@ -420,6 +458,7 @@ namespace OpenSim.Region.Framework.Scenes m_physicsMS = 0; m_imageMS = 0; m_otherMS = 0; + m_spareMS = 0; //Ckrinke This variable is not used, so comment to remove compiler warning until it is used. //Ckrinke m_scriptMS = 0; @@ -498,6 +537,11 @@ namespace OpenSim.Region.Framework.Scenes m_frameMS += ms; } + public void AddSpareMS(int ms) + { + m_spareMS += ms; + } + public void addNetMS(int ms) { m_netMS += ms; -- cgit v1.1 From 96d1ba90d7f801c779924ea04656d0fb53ea9d32 Mon Sep 17 00:00:00 2001 From: Justin Clark-Casey (justincc) Date: Tue, 5 Jun 2012 00:27:51 +0100 Subject: Scale down per frame MS stats to match scaled simulator FPS stat. This makes frame time stats properly tally with fps, which saves confusion and makes it easier to interpret numbers. In some ways this is not so artifical - physics FPS runs at the higher rate. --- OpenSim/Region/Framework/Scenes/SimStatsReporter.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'OpenSim/Region') diff --git a/OpenSim/Region/Framework/Scenes/SimStatsReporter.cs b/OpenSim/Region/Framework/Scenes/SimStatsReporter.cs index 88e0b05..742d42a 100644 --- a/OpenSim/Region/Framework/Scenes/SimStatsReporter.cs +++ b/OpenSim/Region/Framework/Scenes/SimStatsReporter.cs @@ -320,7 +320,7 @@ namespace OpenSim.Region.Framework.Scenes // values to X-per-second values. uint thisFrame = m_scene.Frame; - float framesUpdated = (float)(thisFrame - m_lastUpdateFrame); + float framesUpdated = (float)(thisFrame - m_lastUpdateFrame) * m_reportedFpsCorrectionFactor; m_lastUpdateFrame = thisFrame; // Avoid div-by-zero if somehow we've not updated any frames. -- cgit v1.1 From 01280e9b9447f41347f7e22302c9d94503398882 Mon Sep 17 00:00:00 2001 From: Justin Clark-Casey (justincc) Date: Tue, 5 Jun 2012 00:34:18 +0100 Subject: Remove unused ScenePresence list structure in llGetAgentList() --- OpenSim/Region/ScriptEngine/Shared/Api/Implementation/LSL_Api.cs | 1 - 1 file changed, 1 deletion(-) (limited to 'OpenSim/Region') diff --git a/OpenSim/Region/ScriptEngine/Shared/Api/Implementation/LSL_Api.cs b/OpenSim/Region/ScriptEngine/Shared/Api/Implementation/LSL_Api.cs index deb68b8..fb0fdc9 100644 --- a/OpenSim/Region/ScriptEngine/Shared/Api/Implementation/LSL_Api.cs +++ b/OpenSim/Region/ScriptEngine/Shared/Api/Implementation/LSL_Api.cs @@ -5583,7 +5583,6 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api } } } - List presenceIds = new List(); World.ForEachRootScenePresence( delegate (ScenePresence ssp) -- cgit v1.1 From 008c6a4610fa7b710a9e2546cc09d9fee57e5795 Mon Sep 17 00:00:00 2001 From: Talun Date: Fri, 1 Jun 2012 00:39:26 +0100 Subject: Mantis 4597 AgentPaused packet is ignored. The packet was actually being handled but not acted on. This change extends the default timeout for paused clients to 5 minutes and makes both the paused and non-paused timeout periods configurable. --- OpenSim/Region/ClientStack/Linden/UDP/LLUDPServer.cs | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) (limited to 'OpenSim/Region') diff --git a/OpenSim/Region/ClientStack/Linden/UDP/LLUDPServer.cs b/OpenSim/Region/ClientStack/Linden/UDP/LLUDPServer.cs index edf91cb..32ba590 100644 --- a/OpenSim/Region/ClientStack/Linden/UDP/LLUDPServer.cs +++ b/OpenSim/Region/ClientStack/Linden/UDP/LLUDPServer.cs @@ -155,7 +155,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP private int m_defaultRTO = 0; private int m_maxRTO = 0; - + private int m_ackTimeout = 0; + private int m_pausedAckTimeout = 0; private bool m_disableFacelights = false; public Socket Server { get { return null; } } @@ -198,11 +199,15 @@ namespace OpenSim.Region.ClientStack.LindenUDP m_defaultRTO = config.GetInt("DefaultRTO", 0); m_maxRTO = config.GetInt("MaxRTO", 0); m_disableFacelights = config.GetBoolean("DisableFacelights", false); + m_ackTimeout = 1000 * config.GetInt("AckTimeout", 60); + m_pausedAckTimeout = 1000 * config.GetInt("PausedAckTimeout", 300); } else { PrimUpdatesPerCallback = 100; TextureSendLimit = 20; + m_ackTimeout = 1000 * 60; // 1 minute + m_pausedAckTimeout = 1000 * 300; // 5 minutes } #region BinaryStats @@ -491,8 +496,15 @@ namespace OpenSim.Region.ClientStack.LindenUDP return; // Disconnect an agent if no packets are received for some time - //FIXME: Make 60 an .ini setting - if ((Environment.TickCount & Int32.MaxValue) - udpClient.TickLastPacketReceived > 1000 * 60) + int timeoutTicks = m_ackTimeout; + + // Allow more slack if the client is "paused" eg file upload dialogue is open + // Some sort of limit is needed in case the client crashes, loses its network connection + // or some other disaster prevents it from sendung the AgentResume + if (udpClient.IsPaused) + timeoutTicks = m_pausedAckTimeout; + + if ((Environment.TickCount & Int32.MaxValue) - udpClient.TickLastPacketReceived > timeoutTicks) { m_log.Warn("[LLUDPSERVER]: Ack timeout, disconnecting " + udpClient.AgentID); StatsManager.SimExtraStats.AddAbnormalClientThreadTermination(); -- cgit v1.1 From 42179578fc5292d9bd12aeccc45948d908c42d1a Mon Sep 17 00:00:00 2001 From: Justin Clark-Casey (justincc) Date: Tue, 5 Jun 2012 01:33:58 +0100 Subject: Allow fix-phantoms command to appear even if CombineContiguousRegions = false, since this allows one to go back from a megaregion to normal regions. Adapted from a patch by Garmin Kawaguichi in http://opensimulator.org/mantis/view.php?id=6027 Garmin says that fix-phantoms allows one to reset objects when going back from megaregion to normal regions as well as the othe rway around. Thanks! --- .../RegionCombinerModule/RegionCombinerModule.cs | 24 ++++++++++------------ 1 file changed, 11 insertions(+), 13 deletions(-) (limited to 'OpenSim/Region') diff --git a/OpenSim/Region/RegionCombinerModule/RegionCombinerModule.cs b/OpenSim/Region/RegionCombinerModule/RegionCombinerModule.cs index 40daf13..3555028 100644 --- a/OpenSim/Region/RegionCombinerModule/RegionCombinerModule.cs +++ b/OpenSim/Region/RegionCombinerModule/RegionCombinerModule.cs @@ -61,7 +61,7 @@ namespace OpenSim.Region.RegionCombinerModule /// /// Is this module enabled? /// - private bool enabledYN = false; + private bool m_combineContiguousRegions = false; /// /// This holds the root regions for the megaregions. @@ -79,14 +79,12 @@ namespace OpenSim.Region.RegionCombinerModule public void Initialise(IConfigSource source) { IConfig myConfig = source.Configs["Startup"]; - enabledYN = myConfig.GetBoolean("CombineContiguousRegions", false); + m_combineContiguousRegions = myConfig.GetBoolean("CombineContiguousRegions", false); - if (enabledYN) - { - MainConsole.Instance.Commands.AddCommand( - "RegionCombinerModule", false, "fix-phantoms", "fix-phantoms", - "Fixes phantom objects after an import to megaregions", FixPhantoms); - } + MainConsole.Instance.Commands.AddCommand( + "RegionCombinerModule", false, "fix-phantoms", "fix-phantoms", + "Fixes phantom objects after an import to a megaregion or a change from a megaregion back to normal regions", + FixPhantoms); } public void Close() @@ -95,7 +93,7 @@ namespace OpenSim.Region.RegionCombinerModule public void AddRegion(Scene scene) { - if (enabledYN) + if (m_combineContiguousRegions) scene.RegisterModuleInterface(this); } @@ -105,7 +103,10 @@ namespace OpenSim.Region.RegionCombinerModule public void RegionLoaded(Scene scene) { - if (enabledYN) + lock (m_startingScenes) + m_startingScenes.Add(scene.RegionInfo.originRegionID, scene); + + if (m_combineContiguousRegions) { RegionLoadedDoWork(scene); @@ -208,7 +209,6 @@ namespace OpenSim.Region.RegionCombinerModule { return; } - } } @@ -220,8 +220,6 @@ namespace OpenSim.Region.RegionCombinerModule return; // */ - lock (m_startingScenes) - m_startingScenes.Add(scene.RegionInfo.originRegionID, scene); // Give each region a standard set of non-infinite borders Border northBorder = new Border(); -- cgit v1.1 From a5410c2c19bfef9242d9cfcfc1b4fea67f470fd5 Mon Sep 17 00:00:00 2001 From: Justin Clark-Casey (justincc) Date: Tue, 5 Jun 2012 01:37:40 +0100 Subject: minor: Add user feedback when executing fix-phantoms --- OpenSim/Region/RegionCombinerModule/RegionCombinerModule.cs | 2 ++ 1 file changed, 2 insertions(+) (limited to 'OpenSim/Region') diff --git a/OpenSim/Region/RegionCombinerModule/RegionCombinerModule.cs b/OpenSim/Region/RegionCombinerModule/RegionCombinerModule.cs index 3555028..204c4ff 100644 --- a/OpenSim/Region/RegionCombinerModule/RegionCombinerModule.cs +++ b/OpenSim/Region/RegionCombinerModule/RegionCombinerModule.cs @@ -1066,6 +1066,8 @@ namespace OpenSim.Region.RegionCombinerModule foreach (Scene s in scenes) { + MainConsole.Instance.OutputFormat("Fixing phantoms for {0}", s.RegionInfo.RegionName); + s.ForEachSOG(so => so.AbsolutePosition = so.AbsolutePosition); } } -- cgit v1.1 From 6adc810eaaf067e25ac88b023a20543577a2d5fd Mon Sep 17 00:00:00 2001 From: Justin Clark-Casey (justincc) Date: Tue, 5 Jun 2012 19:40:16 +0100 Subject: Stop accidentally reading 4 Int16s instead of 2 in SIZE section of Terragen file when loaded from a stream. Fixes a bug introduced 2 weeks ago in 67ebe80 Thanks to Plugh for pointing this out. --- OpenSim/Region/CoreModules/World/Terrain/FileLoaders/Terragen.cs | 2 -- 1 file changed, 2 deletions(-) (limited to 'OpenSim/Region') diff --git a/OpenSim/Region/CoreModules/World/Terrain/FileLoaders/Terragen.cs b/OpenSim/Region/CoreModules/World/Terrain/FileLoaders/Terragen.cs index 71c71e6..7a0db26 100644 --- a/OpenSim/Region/CoreModules/World/Terrain/FileLoaders/Terragen.cs +++ b/OpenSim/Region/CoreModules/World/Terrain/FileLoaders/Terragen.cs @@ -180,8 +180,6 @@ namespace OpenSim.Region.CoreModules.World.Terrain.FileLoaders // fileHeight = sztmp; bs.ReadInt16(); bs.ReadInt16(); - bs.ReadInt16(); - bs.ReadInt16(); break; case "XPTS": // fileWidth = bs.ReadInt16(); -- cgit v1.1 From abf94627f6752c81eec6ebe0412bdcb725a66037 Mon Sep 17 00:00:00 2001 From: Justin Clark-Casey (justincc) Date: Wed, 6 Jun 2012 02:45:36 +0100 Subject: Ensure closure of bitmap and memory stream with using() statements in WorldViewModule. If this has any effect then it will only be to the map images returned via requests to the /worldview simulator HTTP path (not enabled by default) --- .../OptionalModules/World/WorldView/WorldViewModule.cs | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) (limited to 'OpenSim/Region') diff --git a/OpenSim/Region/OptionalModules/World/WorldView/WorldViewModule.cs b/OpenSim/Region/OptionalModules/World/WorldView/WorldViewModule.cs index 48c242d..1aee39a 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); - - MemoryStream str = new MemoryStream(); - - bmp.Save(str, ImageFormat.Jpeg); + using (Bitmap bmp = m_Generator.CreateViewImage(pos, rot, fov, width, height, usetex)) + { + using (MemoryStream str = new MemoryStream()) + { + bmp.Save(str, ImageFormat.Jpeg); - return str.ToArray(); + return str.ToArray(); + } + } } } } -- cgit v1.1 From 3b250211803ee5a39888c6c5bd05b60d253a22b6 Mon Sep 17 00:00:00 2001 From: SignpostMarv Date: Tue, 5 Jun 2012 15:11:45 +0100 Subject: enabling all corners of a sim to be set in one call --- .../CoreModules/World/Estate/EstateManagementCommands.cs | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) (limited to 'OpenSim/Region') diff --git a/OpenSim/Region/CoreModules/World/Estate/EstateManagementCommands.cs b/OpenSim/Region/CoreModules/World/Estate/EstateManagementCommands.cs index d2bbea3..3b84d57 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; -- cgit v1.1 From 2b0de66216ca57cf2eac52e777bb362023f8f30a Mon Sep 17 00:00:00 2001 From: Justin Clark-Casey (justincc) Date: Wed, 6 Jun 2012 04:11:16 +0100 Subject: Actively dispose of Bitmaps in Warp3D image module and world map module once we've finished with them. This might help with memory leakage issues though I suspect it won't. --- .../CoreModules/World/Warp3DMap/MapImageModule.cs | 76 +++-- .../CoreModules/World/Warp3DMap/TerrainSplat.cs | 380 +++++++++++---------- .../CoreModules/World/WorldMap/WorldMapModule.cs | 7 +- 3 files changed, 248 insertions(+), 215 deletions(-) (limited to 'OpenSim/Region') diff --git a/OpenSim/Region/CoreModules/World/Warp3DMap/MapImageModule.cs b/OpenSim/Region/CoreModules/World/Warp3DMap/MapImageModule.cs index 4f4e296..3538b46 100644 --- a/OpenSim/Region/CoreModules/World/Warp3DMap/MapImageModule.cs +++ b/OpenSim/Region/CoreModules/World/Warp3DMap/MapImageModule.cs @@ -204,7 +204,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; } @@ -318,8 +321,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 +558,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]; + } } } } diff --git a/OpenSim/Region/CoreModules/World/Warp3DMap/TerrainSplat.cs b/OpenSim/Region/CoreModules/World/Warp3DMap/TerrainSplat.cs index 7bf675d..91252f7 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) - { - 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) + 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 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/WorldMap/WorldMapModule.cs b/OpenSim/Region/CoreModules/World/WorldMap/WorldMapModule.cs index 2335bea..c1c6b49 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) -- cgit v1.1 From 53c25a47780b5b1167f8e574dc8e9b935a0d5ddd Mon Sep 17 00:00:00 2001 From: Justin Clark-Casey (justincc) Date: Wed, 6 Jun 2012 04:15:00 +0100 Subject: Rename MapImageModule for Warp3D to Warp3DImageModule to match its class name and make it easier to distinguish between map image modules. --- .../CoreModules/World/Warp3DMap/MapImageModule.cs | 674 --------------------- .../World/Warp3DMap/Warp3DImageModule.cs | 674 +++++++++++++++++++++ 2 files changed, 674 insertions(+), 674 deletions(-) delete mode 100644 OpenSim/Region/CoreModules/World/Warp3DMap/MapImageModule.cs create mode 100644 OpenSim/Region/CoreModules/World/Warp3DMap/Warp3DImageModule.cs (limited to 'OpenSim/Region') diff --git a/OpenSim/Region/CoreModules/World/Warp3DMap/MapImageModule.cs b/OpenSim/Region/CoreModules/World/Warp3DMap/MapImageModule.cs deleted file mode 100644 index 3538b46..0000000 --- a/OpenSim/Region/CoreModules/World/Warp3DMap/MapImageModule.cs +++ /dev/null @@ -1,674 +0,0 @@ -/* - * Copyright (c) Contributors, http://opensimulator.org/ - * See CONTRIBUTORS.TXT for a full list of copyright holders. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * * Neither the name of the OpenSimulator Project nor the - * names of its contributors may be used to endorse or promote products - * derived from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -using System; -using System.Collections.Generic; -using System.Drawing; -using System.Drawing.Imaging; -using System.IO; -using System.Reflection; -using CSJ2K; -using Nini.Config; -using log4net; -using Rednettle.Warp3D; -using OpenMetaverse; -using OpenMetaverse.Imaging; -using OpenMetaverse.Rendering; -using OpenMetaverse.StructuredData; -using OpenSim.Framework; -using OpenSim.Region.Framework.Interfaces; -using OpenSim.Region.Framework.Scenes; -using OpenSim.Region.Physics.Manager; -using OpenSim.Services.Interfaces; - -using WarpRenderer = global::Warp3D.Warp3D; - -namespace OpenSim.Region.CoreModules.World.Warp3DMap -{ - public class Warp3DImageModule : IMapImageGenerator, INonSharedRegionModule - { - 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 Scene m_scene; - private IRendering m_primMesher; - private IConfigSource m_config; - private Dictionary m_colors = new Dictionary(); - private bool m_useAntiAliasing = false; // TODO: Make this a config option - private bool m_Enabled = false; - - #region IRegionModule Members - - public void Initialise(IConfigSource source) - { - m_config = source; - - IConfig startupConfig = m_config.Configs["Startup"]; - if (startupConfig.GetString("MapImageModule", "MapImageModule") != "Warp3DImageModule") - return; - - m_Enabled = true; - } - - public void AddRegion(Scene scene) - { - if (!m_Enabled) - return; - - m_scene = scene; - - List renderers = RenderingLoader.ListRenderers(Util.ExecutingDirectory()); - if (renderers.Count > 0) - { - m_primMesher = RenderingLoader.LoadRenderer(renderers[0]); - m_log.Debug("[MAPTILE]: Loaded prim mesher " + m_primMesher.ToString()); - } - else - { - m_log.Debug("[MAPTILE]: No prim mesher loaded, prim rendering will be disabled"); - } - - m_scene.RegisterModuleInterface(this); - } - - public void RegionLoaded(Scene scene) - { - } - - public void RemoveRegion(Scene scene) - { - } - - public void Close() - { - } - - public string Name - { - get { return "Warp3DImageModule"; } - } - - public Type ReplaceableInterface - { - get { return null; } - } - - #endregion - - #region IMapImageGenerator Members - - public Bitmap CreateMapTile() - { - Vector3 camPos = new Vector3(127.5f, 127.5f, 221.7025033688163f); - Viewport viewport = new Viewport(camPos, -Vector3.UnitZ, 1024f, 0.1f, (int)Constants.RegionSize, (int)Constants.RegionSize, (float)Constants.RegionSize, (float)Constants.RegionSize); - return CreateMapTile(viewport, false); - } - - public Bitmap CreateViewImage(Vector3 camPos, Vector3 camDir, float fov, int width, int height, bool useTextures) - { - Viewport viewport = new Viewport(camPos, camDir, fov, (float)Constants.RegionSize, 0.1f, width, height); - return CreateMapTile(viewport, useTextures); - } - - public Bitmap CreateMapTile(Viewport viewport, bool useTextures) - { - bool drawPrimVolume = true; - bool textureTerrain = true; - - try - { - IConfig startupConfig = m_config.Configs["Startup"]; - drawPrimVolume = startupConfig.GetBoolean("DrawPrimOnMapTile", drawPrimVolume); - textureTerrain = startupConfig.GetBoolean("TextureOnMapTile", textureTerrain); - } - catch - { - m_log.Warn("[MAPTILE]: Failed to load StartupConfig"); - } - - m_colors.Clear(); - - int width = viewport.Width; - int height = viewport.Height; - - if (m_useAntiAliasing) - { - width *= 2; - height *= 2; - } - - WarpRenderer renderer = new WarpRenderer(); - renderer.CreateScene(width, height); - renderer.Scene.autoCalcNormals = false; - - #region Camera - - warp_Vector pos = ConvertVector(viewport.Position); - pos.z -= 0.001f; // Works around an issue with the Warp3D camera - warp_Vector lookat = warp_Vector.add(ConvertVector(viewport.Position), ConvertVector(viewport.LookDirection)); - - renderer.Scene.defaultCamera.setPos(pos); - renderer.Scene.defaultCamera.lookAt(lookat); - - if (viewport.Orthographic) - { - renderer.Scene.defaultCamera.isOrthographic = true; - renderer.Scene.defaultCamera.orthoViewWidth = viewport.OrthoWindowWidth; - renderer.Scene.defaultCamera.orthoViewHeight = viewport.OrthoWindowHeight; - } - else - { - float fov = viewport.FieldOfView; - fov *= 1.75f; // FIXME: ??? - renderer.Scene.defaultCamera.setFov(fov); - } - - #endregion Camera - - renderer.Scene.addLight("Light1", new warp_Light(new warp_Vector(1.0f, 0.5f, 1f), 0xffffff, 0, 320, 40)); - renderer.Scene.addLight("Light2", new warp_Light(new warp_Vector(-1f, -1f, 1f), 0xffffff, 0, 100, 40)); - - CreateWater(renderer); - CreateTerrain(renderer, textureTerrain); - if (drawPrimVolume) - CreateAllPrims(renderer, useTextures); - - renderer.Render(); - Bitmap bitmap = renderer.Scene.getImage(); - - if (m_useAntiAliasing) - { - using (Bitmap origBitmap = bitmap) - bitmap = ImageUtils.ResizeImage(origBitmap, viewport.Width, viewport.Height); - } - - return bitmap; - } - - public byte[] WriteJpeg2000Image() - { - try - { - using (Bitmap mapbmp = CreateMapTile()) - return OpenJPEG.EncodeFromImage(mapbmp, true); - } - catch (Exception e) - { - // JPEG2000 encoder failed - m_log.Error("[MAPTILE]: Failed generating terrain map: " + e); - } - - return null; - } - - #endregion - - #region Rendering Methods - - private void CreateWater(WarpRenderer renderer) - { - float waterHeight = (float)m_scene.RegionInfo.RegionSettings.WaterHeight; - - renderer.AddPlane("Water", 256f * 0.5f); - renderer.Scene.sceneobject("Water").setPos(127.5f, waterHeight, 127.5f); - - renderer.AddMaterial("WaterColor", ConvertColor(WATER_COLOR)); - renderer.Scene.material("WaterColor").setReflectivity(0); // match water color with standard map module thanks lkalif - renderer.Scene.material("WaterColor").setTransparency((byte)((1f - WATER_COLOR.A) * 255f)); - renderer.SetObjectMaterial("Water", "WaterColor"); - } - - private void CreateTerrain(WarpRenderer renderer, bool textureTerrain) - { - ITerrainChannel terrain = m_scene.Heightmap; - float[] heightmap = terrain.GetFloatsSerialised(); - - warp_Object obj = new warp_Object(256 * 256, 255 * 255 * 2); - - for (int y = 0; y < 256; y++) - { - for (int x = 0; x < 256; x++) - { - int v = y * 256 + x; - float height = heightmap[v]; - - warp_Vector pos = ConvertVector(new Vector3(x, y, height)); - obj.addVertex(new warp_Vertex(pos, (float)x / 255f, (float)(255 - y) / 255f)); - } - } - - for (int y = 0; y < 256; y++) - { - for (int x = 0; x < 256; x++) - { - if (x < 255 && y < 255) - { - int v = y * 256 + x; - - // Normal - Vector3 v1 = new Vector3(x, y, heightmap[y * 256 + x]); - Vector3 v2 = new Vector3(x + 1, y, heightmap[y * 256 + x + 1]); - Vector3 v3 = new Vector3(x, y + 1, heightmap[(y + 1) * 256 + x]); - warp_Vector norm = ConvertVector(SurfaceNormal(v1, v2, v3)); - norm = norm.reverse(); - obj.vertex(v).n = norm; - - // Triangle 1 - obj.addTriangle( - v, - v + 1, - v + 256); - - // Triangle 2 - obj.addTriangle( - v + 256 + 1, - v + 256, - v + 1); - } - } - } - - renderer.Scene.addObject("Terrain", obj); - - UUID[] textureIDs = new UUID[4]; - float[] startHeights = new float[4]; - float[] heightRanges = new float[4]; - - RegionSettings regionInfo = m_scene.RegionInfo.RegionSettings; - - textureIDs[0] = regionInfo.TerrainTexture1; - textureIDs[1] = regionInfo.TerrainTexture2; - textureIDs[2] = regionInfo.TerrainTexture3; - textureIDs[3] = regionInfo.TerrainTexture4; - - startHeights[0] = (float)regionInfo.Elevation1SW; - startHeights[1] = (float)regionInfo.Elevation1NW; - startHeights[2] = (float)regionInfo.Elevation1SE; - startHeights[3] = (float)regionInfo.Elevation1NE; - - heightRanges[0] = (float)regionInfo.Elevation2SW; - heightRanges[1] = (float)regionInfo.Elevation2NW; - heightRanges[2] = (float)regionInfo.Elevation2SE; - heightRanges[3] = (float)regionInfo.Elevation2NE; - - uint globalX, globalY; - Utils.LongToUInts(m_scene.RegionInfo.RegionHandle, out globalX, out globalY); - - 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); - renderer.Scene.material("TerrainColor").setReflectivity(0); // reduces tile seams a bit thanks lkalif - renderer.SetObjectMaterial("Terrain", "TerrainColor"); - } - - private void CreateAllPrims(WarpRenderer renderer, bool useTextures) - { - if (m_primMesher == null) - return; - - m_scene.ForEachSOG( - delegate(SceneObjectGroup group) - { - CreatePrim(renderer, group.RootPart, useTextures); - foreach (SceneObjectPart child in group.Parts) - CreatePrim(renderer, child, useTextures); - } - ); - } - - private void CreatePrim(WarpRenderer renderer, SceneObjectPart prim, - bool useTextures) - { - const float MIN_SIZE = 2f; - - if ((PCode)prim.Shape.PCode != PCode.Prim) - return; - if (prim.Scale.LengthSquared() < MIN_SIZE * MIN_SIZE) - return; - - Primitive omvPrim = prim.Shape.ToOmvPrimitive(prim.OffsetPosition, prim.RotationOffset); - FacetedMesh renderMesh = m_primMesher.GenerateFacetedMesh(omvPrim, DetailLevel.Medium); - if (renderMesh == null) - return; - - warp_Vector primPos = ConvertVector(prim.GetWorldPosition()); - warp_Quaternion primRot = ConvertQuaternion(prim.RotationOffset); - - warp_Matrix m = warp_Matrix.quaternionMatrix(primRot); - - if (prim.ParentID != 0) - { - SceneObjectGroup group = m_scene.SceneGraph.GetGroupByPrim(prim.LocalId); - if (group != null) - m.transform(warp_Matrix.quaternionMatrix(ConvertQuaternion(group.RootPart.RotationOffset))); - } - - warp_Vector primScale = ConvertVector(prim.Scale); - - string primID = prim.UUID.ToString(); - - // Create the prim faces - // TODO: Implement the useTextures flag behavior - for (int i = 0; i < renderMesh.Faces.Count; i++) - { - Face face = renderMesh.Faces[i]; - string meshName = primID + "-Face-" + i.ToString(); - - // Avoid adding duplicate meshes to the scene - if (renderer.Scene.objectData.ContainsKey(meshName)) - { - continue; - } - - warp_Object faceObj = new warp_Object(face.Vertices.Count, face.Indices.Count / 3); - - for (int j = 0; j < face.Vertices.Count; j++) - { - Vertex v = face.Vertices[j]; - - warp_Vector pos = ConvertVector(v.Position); - warp_Vector norm = ConvertVector(v.Normal); - - if (prim.Shape.SculptTexture == UUID.Zero) - norm = norm.reverse(); - warp_Vertex vert = new warp_Vertex(pos, norm, v.TexCoord.X, v.TexCoord.Y); - - faceObj.addVertex(vert); - } - - for (int j = 0; j < face.Indices.Count; j += 3) - { - faceObj.addTriangle( - face.Indices[j + 0], - face.Indices[j + 1], - face.Indices[j + 2]); - } - - Primitive.TextureEntryFace teFace = prim.Shape.Textures.GetFace((uint)i); - Color4 faceColor = GetFaceColor(teFace); - string materialName = GetOrCreateMaterial(renderer, faceColor); - - faceObj.transform(m); - faceObj.setPos(primPos); - faceObj.scaleSelf(primScale.x, primScale.y, primScale.z); - - renderer.Scene.addObject(meshName, faceObj); - - renderer.SetObjectMaterial(meshName, materialName); - } - } - - private Color4 GetFaceColor(Primitive.TextureEntryFace face) - { - Color4 color; - - if (face.TextureID == UUID.Zero) - return face.RGBA; - - if (!m_colors.TryGetValue(face.TextureID, out color)) - { - bool fetched = false; - - // Attempt to fetch the texture metadata - UUID metadataID = UUID.Combine(face.TextureID, TEXTURE_METADATA_MAGIC); - AssetBase metadata = m_scene.AssetService.GetCached(metadataID.ToString()); - if (metadata != null) - { - OSDMap map = null; - try { map = OSDParser.Deserialize(metadata.Data) as OSDMap; } catch { } - - if (map != null) - { - color = map["X-JPEG2000-RGBA"].AsColor4(); - fetched = true; - } - } - - if (!fetched) - { - // Fetch the texture, decode and get the average color, - // then save it to a temporary metadata asset - AssetBase textureAsset = m_scene.AssetService.Get(face.TextureID.ToString()); - if (textureAsset != null) - { - int width, height; - color = GetAverageColor(textureAsset.FullID, textureAsset.Data, out width, out height); - - OSDMap data = new OSDMap { { "X-JPEG2000-RGBA", OSD.FromColor4(color) } }; - metadata = new AssetBase - { - Data = System.Text.Encoding.UTF8.GetBytes(OSDParser.SerializeJsonString(data)), - Description = "Metadata for JPEG2000 texture " + face.TextureID.ToString(), - Flags = AssetFlags.Collectable, - FullID = metadataID, - ID = metadataID.ToString(), - Local = true, - Temporary = true, - Name = String.Empty, - Type = (sbyte)AssetType.Unknown - }; - m_scene.AssetService.Store(metadata); - } - else - { - color = new Color4(0.5f, 0.5f, 0.5f, 1.0f); - } - } - - m_colors[face.TextureID] = color; - } - - return color * face.RGBA; - } - - private string GetOrCreateMaterial(WarpRenderer renderer, Color4 color) - { - string name = color.ToString(); - - warp_Material material = renderer.Scene.material(name); - if (material != null) - return name; - - renderer.AddMaterial(name, ConvertColor(color)); - if (color.A < 1f) - renderer.Scene.material(name).setTransparency((byte)((1f - color.A) * 255f)); - return name; - } - - #endregion Rendering Methods - - #region Static Helpers - - private static warp_Vector ConvertVector(Vector3 vector) - { - return new warp_Vector(vector.X, vector.Z, vector.Y); - } - - private static warp_Quaternion ConvertQuaternion(Quaternion quat) - { - return new warp_Quaternion(quat.X, quat.Z, quat.Y, -quat.W); - } - - private static int ConvertColor(Color4 color) - { - int c = warp_Color.getColor((byte)(color.R * 255f), (byte)(color.G * 255f), (byte)(color.B * 255f)); - if (color.A < 1f) - c |= (byte)(color.A * 255f) << 24; - - return c; - } - - private static Vector3 SurfaceNormal(Vector3 c1, Vector3 c2, Vector3 c3) - { - Vector3 edge1 = new Vector3(c2.X - c1.X, c2.Y - c1.Y, c2.Z - c1.Z); - Vector3 edge2 = new Vector3(c3.X - c1.X, c3.Y - c1.Y, c3.Z - c1.Z); - - Vector3 normal = Vector3.Cross(edge1, edge2); - normal.Normalize(); - - return normal; - } - - public static Color4 GetAverageColor(UUID textureID, byte[] j2kData, out int width, out int height) - { - ulong r = 0; - ulong g = 0; - ulong b = 0; - ulong a = 0; - - using (MemoryStream stream = new MemoryStream(j2kData)) - { - try - { - int pixelBytes; - - using (Bitmap bitmap = (Bitmap)J2kImage.FromStream(stream)) - { - 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 - { - if (pixelBytes == 4) - { - for (int y = 0; y < height; y++) - { - 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++) - { - 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]; - } - } - } - } - } - - // Get the averages for each channel - const decimal OO_255 = 1m / 255m; - decimal totalPixels = (decimal)(width * height); - - decimal rm = ((decimal)r / totalPixels) * OO_255; - decimal gm = ((decimal)g / totalPixels) * OO_255; - decimal bm = ((decimal)b / totalPixels) * OO_255; - decimal am = ((decimal)a / totalPixels) * OO_255; - - if (pixelBytes == 3) - am = 1m; - - return new Color4((float)rm, (float)gm, (float)bm, (float)am); - } - catch (Exception ex) - { - m_log.WarnFormat("[MAPTILE]: 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); - } - } - } - - #endregion Static Helpers - } - - public static class ImageUtils - { - /// - /// Performs bilinear interpolation between four values - /// - /// First, or top left value - /// Second, or top right value - /// Third, or bottom left value - /// Fourth, or bottom right value - /// Interpolation value on the X axis, between 0.0 and 1.0 - /// Interpolation value on fht Y axis, between 0.0 and 1.0 - /// The bilinearly interpolated result - public static float Bilinear(float v00, float v01, float v10, float v11, float xPercent, float yPercent) - { - return Utils.Lerp(Utils.Lerp(v00, v01, xPercent), Utils.Lerp(v10, v11, xPercent), yPercent); - } - - /// - /// Performs a high quality image resize - /// - /// Image to resize - /// New width - /// New height - /// Resized image - public static Bitmap ResizeImage(Image image, int width, int height) - { - Bitmap result = new Bitmap(width, height); - - using (Graphics graphics = Graphics.FromImage(result)) - { - graphics.CompositingQuality = System.Drawing.Drawing2D.CompositingQuality.HighQuality; - graphics.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.HighQualityBicubic; - graphics.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality; - graphics.PixelOffsetMode = System.Drawing.Drawing2D.PixelOffsetMode.HighQuality; - - graphics.DrawImage(image, 0, 0, result.Width, result.Height); - } - - return result; - } - } -} \ No newline at end of file diff --git a/OpenSim/Region/CoreModules/World/Warp3DMap/Warp3DImageModule.cs b/OpenSim/Region/CoreModules/World/Warp3DMap/Warp3DImageModule.cs new file mode 100644 index 0000000..3538b46 --- /dev/null +++ b/OpenSim/Region/CoreModules/World/Warp3DMap/Warp3DImageModule.cs @@ -0,0 +1,674 @@ +/* + * Copyright (c) Contributors, http://opensimulator.org/ + * See CONTRIBUTORS.TXT for a full list of copyright holders. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the OpenSimulator Project nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +using System; +using System.Collections.Generic; +using System.Drawing; +using System.Drawing.Imaging; +using System.IO; +using System.Reflection; +using CSJ2K; +using Nini.Config; +using log4net; +using Rednettle.Warp3D; +using OpenMetaverse; +using OpenMetaverse.Imaging; +using OpenMetaverse.Rendering; +using OpenMetaverse.StructuredData; +using OpenSim.Framework; +using OpenSim.Region.Framework.Interfaces; +using OpenSim.Region.Framework.Scenes; +using OpenSim.Region.Physics.Manager; +using OpenSim.Services.Interfaces; + +using WarpRenderer = global::Warp3D.Warp3D; + +namespace OpenSim.Region.CoreModules.World.Warp3DMap +{ + public class Warp3DImageModule : IMapImageGenerator, INonSharedRegionModule + { + 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 Scene m_scene; + private IRendering m_primMesher; + private IConfigSource m_config; + private Dictionary m_colors = new Dictionary(); + private bool m_useAntiAliasing = false; // TODO: Make this a config option + private bool m_Enabled = false; + + #region IRegionModule Members + + public void Initialise(IConfigSource source) + { + m_config = source; + + IConfig startupConfig = m_config.Configs["Startup"]; + if (startupConfig.GetString("MapImageModule", "MapImageModule") != "Warp3DImageModule") + return; + + m_Enabled = true; + } + + public void AddRegion(Scene scene) + { + if (!m_Enabled) + return; + + m_scene = scene; + + List renderers = RenderingLoader.ListRenderers(Util.ExecutingDirectory()); + if (renderers.Count > 0) + { + m_primMesher = RenderingLoader.LoadRenderer(renderers[0]); + m_log.Debug("[MAPTILE]: Loaded prim mesher " + m_primMesher.ToString()); + } + else + { + m_log.Debug("[MAPTILE]: No prim mesher loaded, prim rendering will be disabled"); + } + + m_scene.RegisterModuleInterface(this); + } + + public void RegionLoaded(Scene scene) + { + } + + public void RemoveRegion(Scene scene) + { + } + + public void Close() + { + } + + public string Name + { + get { return "Warp3DImageModule"; } + } + + public Type ReplaceableInterface + { + get { return null; } + } + + #endregion + + #region IMapImageGenerator Members + + public Bitmap CreateMapTile() + { + Vector3 camPos = new Vector3(127.5f, 127.5f, 221.7025033688163f); + Viewport viewport = new Viewport(camPos, -Vector3.UnitZ, 1024f, 0.1f, (int)Constants.RegionSize, (int)Constants.RegionSize, (float)Constants.RegionSize, (float)Constants.RegionSize); + return CreateMapTile(viewport, false); + } + + public Bitmap CreateViewImage(Vector3 camPos, Vector3 camDir, float fov, int width, int height, bool useTextures) + { + Viewport viewport = new Viewport(camPos, camDir, fov, (float)Constants.RegionSize, 0.1f, width, height); + return CreateMapTile(viewport, useTextures); + } + + public Bitmap CreateMapTile(Viewport viewport, bool useTextures) + { + bool drawPrimVolume = true; + bool textureTerrain = true; + + try + { + IConfig startupConfig = m_config.Configs["Startup"]; + drawPrimVolume = startupConfig.GetBoolean("DrawPrimOnMapTile", drawPrimVolume); + textureTerrain = startupConfig.GetBoolean("TextureOnMapTile", textureTerrain); + } + catch + { + m_log.Warn("[MAPTILE]: Failed to load StartupConfig"); + } + + m_colors.Clear(); + + int width = viewport.Width; + int height = viewport.Height; + + if (m_useAntiAliasing) + { + width *= 2; + height *= 2; + } + + WarpRenderer renderer = new WarpRenderer(); + renderer.CreateScene(width, height); + renderer.Scene.autoCalcNormals = false; + + #region Camera + + warp_Vector pos = ConvertVector(viewport.Position); + pos.z -= 0.001f; // Works around an issue with the Warp3D camera + warp_Vector lookat = warp_Vector.add(ConvertVector(viewport.Position), ConvertVector(viewport.LookDirection)); + + renderer.Scene.defaultCamera.setPos(pos); + renderer.Scene.defaultCamera.lookAt(lookat); + + if (viewport.Orthographic) + { + renderer.Scene.defaultCamera.isOrthographic = true; + renderer.Scene.defaultCamera.orthoViewWidth = viewport.OrthoWindowWidth; + renderer.Scene.defaultCamera.orthoViewHeight = viewport.OrthoWindowHeight; + } + else + { + float fov = viewport.FieldOfView; + fov *= 1.75f; // FIXME: ??? + renderer.Scene.defaultCamera.setFov(fov); + } + + #endregion Camera + + renderer.Scene.addLight("Light1", new warp_Light(new warp_Vector(1.0f, 0.5f, 1f), 0xffffff, 0, 320, 40)); + renderer.Scene.addLight("Light2", new warp_Light(new warp_Vector(-1f, -1f, 1f), 0xffffff, 0, 100, 40)); + + CreateWater(renderer); + CreateTerrain(renderer, textureTerrain); + if (drawPrimVolume) + CreateAllPrims(renderer, useTextures); + + renderer.Render(); + Bitmap bitmap = renderer.Scene.getImage(); + + if (m_useAntiAliasing) + { + using (Bitmap origBitmap = bitmap) + bitmap = ImageUtils.ResizeImage(origBitmap, viewport.Width, viewport.Height); + } + + return bitmap; + } + + public byte[] WriteJpeg2000Image() + { + try + { + using (Bitmap mapbmp = CreateMapTile()) + return OpenJPEG.EncodeFromImage(mapbmp, true); + } + catch (Exception e) + { + // JPEG2000 encoder failed + m_log.Error("[MAPTILE]: Failed generating terrain map: " + e); + } + + return null; + } + + #endregion + + #region Rendering Methods + + private void CreateWater(WarpRenderer renderer) + { + float waterHeight = (float)m_scene.RegionInfo.RegionSettings.WaterHeight; + + renderer.AddPlane("Water", 256f * 0.5f); + renderer.Scene.sceneobject("Water").setPos(127.5f, waterHeight, 127.5f); + + renderer.AddMaterial("WaterColor", ConvertColor(WATER_COLOR)); + renderer.Scene.material("WaterColor").setReflectivity(0); // match water color with standard map module thanks lkalif + renderer.Scene.material("WaterColor").setTransparency((byte)((1f - WATER_COLOR.A) * 255f)); + renderer.SetObjectMaterial("Water", "WaterColor"); + } + + private void CreateTerrain(WarpRenderer renderer, bool textureTerrain) + { + ITerrainChannel terrain = m_scene.Heightmap; + float[] heightmap = terrain.GetFloatsSerialised(); + + warp_Object obj = new warp_Object(256 * 256, 255 * 255 * 2); + + for (int y = 0; y < 256; y++) + { + for (int x = 0; x < 256; x++) + { + int v = y * 256 + x; + float height = heightmap[v]; + + warp_Vector pos = ConvertVector(new Vector3(x, y, height)); + obj.addVertex(new warp_Vertex(pos, (float)x / 255f, (float)(255 - y) / 255f)); + } + } + + for (int y = 0; y < 256; y++) + { + for (int x = 0; x < 256; x++) + { + if (x < 255 && y < 255) + { + int v = y * 256 + x; + + // Normal + Vector3 v1 = new Vector3(x, y, heightmap[y * 256 + x]); + Vector3 v2 = new Vector3(x + 1, y, heightmap[y * 256 + x + 1]); + Vector3 v3 = new Vector3(x, y + 1, heightmap[(y + 1) * 256 + x]); + warp_Vector norm = ConvertVector(SurfaceNormal(v1, v2, v3)); + norm = norm.reverse(); + obj.vertex(v).n = norm; + + // Triangle 1 + obj.addTriangle( + v, + v + 1, + v + 256); + + // Triangle 2 + obj.addTriangle( + v + 256 + 1, + v + 256, + v + 1); + } + } + } + + renderer.Scene.addObject("Terrain", obj); + + UUID[] textureIDs = new UUID[4]; + float[] startHeights = new float[4]; + float[] heightRanges = new float[4]; + + RegionSettings regionInfo = m_scene.RegionInfo.RegionSettings; + + textureIDs[0] = regionInfo.TerrainTexture1; + textureIDs[1] = regionInfo.TerrainTexture2; + textureIDs[2] = regionInfo.TerrainTexture3; + textureIDs[3] = regionInfo.TerrainTexture4; + + startHeights[0] = (float)regionInfo.Elevation1SW; + startHeights[1] = (float)regionInfo.Elevation1NW; + startHeights[2] = (float)regionInfo.Elevation1SE; + startHeights[3] = (float)regionInfo.Elevation1NE; + + heightRanges[0] = (float)regionInfo.Elevation2SW; + heightRanges[1] = (float)regionInfo.Elevation2NW; + heightRanges[2] = (float)regionInfo.Elevation2SE; + heightRanges[3] = (float)regionInfo.Elevation2NE; + + uint globalX, globalY; + Utils.LongToUInts(m_scene.RegionInfo.RegionHandle, out globalX, out globalY); + + 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); + renderer.Scene.material("TerrainColor").setReflectivity(0); // reduces tile seams a bit thanks lkalif + renderer.SetObjectMaterial("Terrain", "TerrainColor"); + } + + private void CreateAllPrims(WarpRenderer renderer, bool useTextures) + { + if (m_primMesher == null) + return; + + m_scene.ForEachSOG( + delegate(SceneObjectGroup group) + { + CreatePrim(renderer, group.RootPart, useTextures); + foreach (SceneObjectPart child in group.Parts) + CreatePrim(renderer, child, useTextures); + } + ); + } + + private void CreatePrim(WarpRenderer renderer, SceneObjectPart prim, + bool useTextures) + { + const float MIN_SIZE = 2f; + + if ((PCode)prim.Shape.PCode != PCode.Prim) + return; + if (prim.Scale.LengthSquared() < MIN_SIZE * MIN_SIZE) + return; + + Primitive omvPrim = prim.Shape.ToOmvPrimitive(prim.OffsetPosition, prim.RotationOffset); + FacetedMesh renderMesh = m_primMesher.GenerateFacetedMesh(omvPrim, DetailLevel.Medium); + if (renderMesh == null) + return; + + warp_Vector primPos = ConvertVector(prim.GetWorldPosition()); + warp_Quaternion primRot = ConvertQuaternion(prim.RotationOffset); + + warp_Matrix m = warp_Matrix.quaternionMatrix(primRot); + + if (prim.ParentID != 0) + { + SceneObjectGroup group = m_scene.SceneGraph.GetGroupByPrim(prim.LocalId); + if (group != null) + m.transform(warp_Matrix.quaternionMatrix(ConvertQuaternion(group.RootPart.RotationOffset))); + } + + warp_Vector primScale = ConvertVector(prim.Scale); + + string primID = prim.UUID.ToString(); + + // Create the prim faces + // TODO: Implement the useTextures flag behavior + for (int i = 0; i < renderMesh.Faces.Count; i++) + { + Face face = renderMesh.Faces[i]; + string meshName = primID + "-Face-" + i.ToString(); + + // Avoid adding duplicate meshes to the scene + if (renderer.Scene.objectData.ContainsKey(meshName)) + { + continue; + } + + warp_Object faceObj = new warp_Object(face.Vertices.Count, face.Indices.Count / 3); + + for (int j = 0; j < face.Vertices.Count; j++) + { + Vertex v = face.Vertices[j]; + + warp_Vector pos = ConvertVector(v.Position); + warp_Vector norm = ConvertVector(v.Normal); + + if (prim.Shape.SculptTexture == UUID.Zero) + norm = norm.reverse(); + warp_Vertex vert = new warp_Vertex(pos, norm, v.TexCoord.X, v.TexCoord.Y); + + faceObj.addVertex(vert); + } + + for (int j = 0; j < face.Indices.Count; j += 3) + { + faceObj.addTriangle( + face.Indices[j + 0], + face.Indices[j + 1], + face.Indices[j + 2]); + } + + Primitive.TextureEntryFace teFace = prim.Shape.Textures.GetFace((uint)i); + Color4 faceColor = GetFaceColor(teFace); + string materialName = GetOrCreateMaterial(renderer, faceColor); + + faceObj.transform(m); + faceObj.setPos(primPos); + faceObj.scaleSelf(primScale.x, primScale.y, primScale.z); + + renderer.Scene.addObject(meshName, faceObj); + + renderer.SetObjectMaterial(meshName, materialName); + } + } + + private Color4 GetFaceColor(Primitive.TextureEntryFace face) + { + Color4 color; + + if (face.TextureID == UUID.Zero) + return face.RGBA; + + if (!m_colors.TryGetValue(face.TextureID, out color)) + { + bool fetched = false; + + // Attempt to fetch the texture metadata + UUID metadataID = UUID.Combine(face.TextureID, TEXTURE_METADATA_MAGIC); + AssetBase metadata = m_scene.AssetService.GetCached(metadataID.ToString()); + if (metadata != null) + { + OSDMap map = null; + try { map = OSDParser.Deserialize(metadata.Data) as OSDMap; } catch { } + + if (map != null) + { + color = map["X-JPEG2000-RGBA"].AsColor4(); + fetched = true; + } + } + + if (!fetched) + { + // Fetch the texture, decode and get the average color, + // then save it to a temporary metadata asset + AssetBase textureAsset = m_scene.AssetService.Get(face.TextureID.ToString()); + if (textureAsset != null) + { + int width, height; + color = GetAverageColor(textureAsset.FullID, textureAsset.Data, out width, out height); + + OSDMap data = new OSDMap { { "X-JPEG2000-RGBA", OSD.FromColor4(color) } }; + metadata = new AssetBase + { + Data = System.Text.Encoding.UTF8.GetBytes(OSDParser.SerializeJsonString(data)), + Description = "Metadata for JPEG2000 texture " + face.TextureID.ToString(), + Flags = AssetFlags.Collectable, + FullID = metadataID, + ID = metadataID.ToString(), + Local = true, + Temporary = true, + Name = String.Empty, + Type = (sbyte)AssetType.Unknown + }; + m_scene.AssetService.Store(metadata); + } + else + { + color = new Color4(0.5f, 0.5f, 0.5f, 1.0f); + } + } + + m_colors[face.TextureID] = color; + } + + return color * face.RGBA; + } + + private string GetOrCreateMaterial(WarpRenderer renderer, Color4 color) + { + string name = color.ToString(); + + warp_Material material = renderer.Scene.material(name); + if (material != null) + return name; + + renderer.AddMaterial(name, ConvertColor(color)); + if (color.A < 1f) + renderer.Scene.material(name).setTransparency((byte)((1f - color.A) * 255f)); + return name; + } + + #endregion Rendering Methods + + #region Static Helpers + + private static warp_Vector ConvertVector(Vector3 vector) + { + return new warp_Vector(vector.X, vector.Z, vector.Y); + } + + private static warp_Quaternion ConvertQuaternion(Quaternion quat) + { + return new warp_Quaternion(quat.X, quat.Z, quat.Y, -quat.W); + } + + private static int ConvertColor(Color4 color) + { + int c = warp_Color.getColor((byte)(color.R * 255f), (byte)(color.G * 255f), (byte)(color.B * 255f)); + if (color.A < 1f) + c |= (byte)(color.A * 255f) << 24; + + return c; + } + + private static Vector3 SurfaceNormal(Vector3 c1, Vector3 c2, Vector3 c3) + { + Vector3 edge1 = new Vector3(c2.X - c1.X, c2.Y - c1.Y, c2.Z - c1.Z); + Vector3 edge2 = new Vector3(c3.X - c1.X, c3.Y - c1.Y, c3.Z - c1.Z); + + Vector3 normal = Vector3.Cross(edge1, edge2); + normal.Normalize(); + + return normal; + } + + public static Color4 GetAverageColor(UUID textureID, byte[] j2kData, out int width, out int height) + { + ulong r = 0; + ulong g = 0; + ulong b = 0; + ulong a = 0; + + using (MemoryStream stream = new MemoryStream(j2kData)) + { + try + { + int pixelBytes; + + using (Bitmap bitmap = (Bitmap)J2kImage.FromStream(stream)) + { + 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 + { + if (pixelBytes == 4) + { + for (int y = 0; y < height; y++) + { + 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++) + { + 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]; + } + } + } + } + } + + // Get the averages for each channel + const decimal OO_255 = 1m / 255m; + decimal totalPixels = (decimal)(width * height); + + decimal rm = ((decimal)r / totalPixels) * OO_255; + decimal gm = ((decimal)g / totalPixels) * OO_255; + decimal bm = ((decimal)b / totalPixels) * OO_255; + decimal am = ((decimal)a / totalPixels) * OO_255; + + if (pixelBytes == 3) + am = 1m; + + return new Color4((float)rm, (float)gm, (float)bm, (float)am); + } + catch (Exception ex) + { + m_log.WarnFormat("[MAPTILE]: 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); + } + } + } + + #endregion Static Helpers + } + + public static class ImageUtils + { + /// + /// Performs bilinear interpolation between four values + /// + /// First, or top left value + /// Second, or top right value + /// Third, or bottom left value + /// Fourth, or bottom right value + /// Interpolation value on the X axis, between 0.0 and 1.0 + /// Interpolation value on fht Y axis, between 0.0 and 1.0 + /// The bilinearly interpolated result + public static float Bilinear(float v00, float v01, float v10, float v11, float xPercent, float yPercent) + { + return Utils.Lerp(Utils.Lerp(v00, v01, xPercent), Utils.Lerp(v10, v11, xPercent), yPercent); + } + + /// + /// Performs a high quality image resize + /// + /// Image to resize + /// New width + /// New height + /// Resized image + public static Bitmap ResizeImage(Image image, int width, int height) + { + Bitmap result = new Bitmap(width, height); + + using (Graphics graphics = Graphics.FromImage(result)) + { + graphics.CompositingQuality = System.Drawing.Drawing2D.CompositingQuality.HighQuality; + graphics.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.HighQualityBicubic; + graphics.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality; + graphics.PixelOffsetMode = System.Drawing.Drawing2D.PixelOffsetMode.HighQuality; + + graphics.DrawImage(image, 0, 0, result.Width, result.Height); + } + + return result; + } + } +} \ No newline at end of file -- cgit v1.1 From 514dd85199331d8246c3a0dfc9cf782f61f48ef6 Mon Sep 17 00:00:00 2001 From: Justin Clark-Casey (justincc) Date: Wed, 6 Jun 2012 04:18:38 +0100 Subject: minor: Change log messages on Warp3DImageModule to show they are from this module --- .../CoreModules/World/Warp3DMap/Warp3DImageModule.cs | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) (limited to 'OpenSim/Region') diff --git a/OpenSim/Region/CoreModules/World/Warp3DMap/Warp3DImageModule.cs b/OpenSim/Region/CoreModules/World/Warp3DMap/Warp3DImageModule.cs index 3538b46..9002a9f 100644 --- a/OpenSim/Region/CoreModules/World/Warp3DMap/Warp3DImageModule.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(); @@ -222,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; @@ -619,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); -- cgit v1.1