diff --git a/src/DotRecast.Recast.Demo/Draw/NavMeshRenderer.cs b/src/DotRecast.Recast.Demo/Draw/NavMeshRenderer.cs index 335664f..93943af 100644 --- a/src/DotRecast.Recast.Demo/Draw/NavMeshRenderer.cs +++ b/src/DotRecast.Recast.Demo/Draw/NavMeshRenderer.cs @@ -19,12 +19,11 @@ freely, subject to the following restrictions: */ using System.Collections.Generic; -using System.Linq; using DotRecast.Core; using DotRecast.Detour; using DotRecast.Recast.Demo.Builder; using DotRecast.Recast.Demo.Geom; -using DotRecast.Recast.Demo.Settings; +using DotRecast.Recast.Demo.UI; namespace DotRecast.Recast.Demo.Draw; diff --git a/src/DotRecast.Recast.Demo/Program.cs b/src/DotRecast.Recast.Demo/Program.cs index 4faba5b..6428385 100644 --- a/src/DotRecast.Recast.Demo/Program.cs +++ b/src/DotRecast.Recast.Demo/Program.cs @@ -19,7 +19,7 @@ public static class Program Directory.SetCurrentDirectory(workingDirectory); } - var format = "{Timestamp:yyyy-MM-dd HH:mm:ss.fff} [{Level:u3}] {Message:lj} [{MemberName}()] [{ThreadName}:{ThreadId}] at {FilePath}:{LineNumber} {NewLine}{Exception}"; + var format = "{Timestamp:yyyy-MM-dd HH:mm:ss.fff} [{Level:u3}] {Message:lj} [{ThreadName}:{ThreadId}]{NewLine}{Exception}"; Log.Logger = new LoggerConfiguration() .MinimumLevel.Verbose() .Enrich.WithThreadId() diff --git a/src/DotRecast.Recast.Demo/RecastDemo.cs b/src/DotRecast.Recast.Demo/RecastDemo.cs index f4a5ae8..876df68 100644 --- a/src/DotRecast.Recast.Demo/RecastDemo.cs +++ b/src/DotRecast.Recast.Demo/RecastDemo.cs @@ -38,7 +38,7 @@ using DotRecast.Detour.Io; using DotRecast.Recast.Demo.Builder; using DotRecast.Recast.Demo.Draw; using DotRecast.Recast.Demo.Geom; -using DotRecast.Recast.Demo.Settings; + using DotRecast.Recast.Demo.Tools; using DotRecast.Recast.Demo.UI; using static DotRecast.Core.RecastMath; @@ -50,7 +50,7 @@ public class RecastDemo { private static readonly ILogger Logger = Log.ForContext(); - private RcViewSystem _viewSys; + private RcCanvas _canvas; private IWindow window; private IInputContext _input; private ImGuiController _imgui; @@ -107,8 +107,11 @@ public class RecastDemo private int[] viewport; private bool markerPositionSet; private Vector3f markerPosition = new Vector3f(); + private ToolsView toolsUI; private RcSettingsView settingsUI; + private RcLogView logUI; + private long prevFrameTime; private RecastDebugDraw dd; @@ -252,9 +255,6 @@ public class RecastDemo private IWindow CreateWindow() { var monitor = Window.Platforms.First().GetMainMonitor(); - // // if (monitors.limit() > 1) { - // // monitor = monitors[1]; - // // } var resolution = monitor.VideoMode.Resolution.Value; float aspect = 16.0f / 9.0f; @@ -358,7 +358,6 @@ public class RecastDemo mice.MouseMove += OnMouseMoved; } - _gl = window.CreateOpenGL(); dd = new RecastDebugDraw(_gl); @@ -367,28 +366,7 @@ public class RecastDemo dd.init(camr); _imgui = new ImGuiController(_gl, window, _input); - - - // // if (capabilities.OpenGL43) { - // // GL43.glDebugMessageControl(GL43.GL_DEBUG_SOURCE_API, GL43.GL_DEBUG_TYPE_OTHER, - // // GL43.GL_DEBUG_SEVERITY_NOTIFICATION, - // // (int[]) null, false); - // // } else if (capabilities.GL_ARB_debug_output) { - // // ARBDebugOutput.glDebugMessageControlARB(ARBDebugOutput.GL_DEBUG_SOURCE_API_ARB, - // // ARBDebugOutput.GL_DEBUG_TYPE_OTHER_ARB, ARBDebugOutput.GL_DEBUG_SEVERITY_LOW_ARB, (int[]) null, false); - // // } - var vendor = _gl.GetStringS(GLEnum.Vendor); - Logger.Debug(vendor); - - var version = _gl.GetStringS(GLEnum.Version); - Logger.Debug(version); - - var renderGl = _gl.GetStringS(GLEnum.Renderer); - Logger.Debug(renderGl); - - var glslString = _gl.GetStringS(GLEnum.ShadingLanguageVersion); - Logger.Debug(glslString); - + settingsUI = new RcSettingsView(); toolsUI = new ToolsView( new TestNavmeshTool(), @@ -398,8 +376,20 @@ public class RecastDemo new JumpLinkBuilderTool(), new DynamicUpdateTool() ); + logUI = new RcLogView(); + + _canvas = new RcCanvas(window, settingsUI, toolsUI, logUI); + + var vendor = _gl.GetStringS(GLEnum.Vendor); + var version = _gl.GetStringS(GLEnum.Version); + var renderGl = _gl.GetStringS(GLEnum.Renderer); + var glslString = _gl.GetStringS(GLEnum.ShadingLanguageVersion); + + Logger.Debug(vendor); + Logger.Debug(version); + Logger.Debug(renderGl); + Logger.Debug(glslString); - _viewSys = new RcViewSystem(window, _input, settingsUI, toolsUI); DemoInputGeomProvider geom = loadInputMesh(Loader.ToBytes("nav_test.obj")); sample = new Sample(geom, ImmutableArray.Empty, null, settingsUI, dd); @@ -537,6 +527,8 @@ public class RecastDemo float m_detailSampleMaxError = settingsUI.getDetailSampleMaxError(); int m_tileSize = settingsUI.getTileSize(); long t = FrequencyWatch.Ticks; + + Logger.Information($"build"); Tuple, NavMesh> buildResult; if (settingsUI.isTiled()) @@ -559,8 +551,22 @@ public class RecastDemo sample.update(sample.getInputGeom(), buildResult.Item1, buildResult.Item2); sample.setChanged(false); settingsUI.setBuildTime((FrequencyWatch.Ticks - t) / TimeSpan.TicksPerMillisecond); - settingsUI.setBuildTelemetry(buildResult.Item1.Select(x => x.getTelemetry()).ToList()); + //settingsUI.setBuildTelemetry(buildResult.Item1.Select(x => x.getTelemetry()).ToList()); toolsUI.setSample(sample); + + Logger.Information($"build times"); + Logger.Information($"-----------------------------------------"); + var telemetries = buildResult.Item1 + .Select(x => x.getTelemetry()) + .SelectMany(x => x.ToList()) + .GroupBy(x => x.Item1) + .ToImmutableSortedDictionary(x => x.Key, x => x.Sum(y => y.Item2)); + + foreach (var (key, millis) in telemetries) + { + Logger.Information($"{key}: {millis} ms"); + } + } } else @@ -706,7 +712,7 @@ public class RecastDemo io.DisplayFramebufferScale = Vector2.One; io.DeltaTime = (float)dt; - //window.DoEvents(); + _canvas.Update(dt); _imgui.Update((float)dt); } @@ -736,8 +742,8 @@ public class RecastDemo dd.fog(false); - _viewSys.Draw(); - _mouseOverMenu = _viewSys.IsMouseOverUI(); + _canvas.Draw(dt); + _mouseOverMenu = _canvas.IsMouseOverUI(); _imgui.Render(); window.SwapBuffers(); diff --git a/src/DotRecast.Recast.Demo/Sample.cs b/src/DotRecast.Recast.Demo/Sample.cs index bdc6060..79e4386 100644 --- a/src/DotRecast.Recast.Demo/Sample.cs +++ b/src/DotRecast.Recast.Demo/Sample.cs @@ -22,7 +22,8 @@ using System.Collections.Generic; using DotRecast.Detour; using DotRecast.Recast.Demo.Draw; using DotRecast.Recast.Demo.Geom; -using DotRecast.Recast.Demo.Settings; + +using DotRecast.Recast.Demo.UI; namespace DotRecast.Recast.Demo; diff --git a/src/DotRecast.Recast.Demo/Tools/ConsoleTextWriterHook.cs b/src/DotRecast.Recast.Demo/Tools/ConsoleTextWriterHook.cs new file mode 100644 index 0000000..14563c8 --- /dev/null +++ b/src/DotRecast.Recast.Demo/Tools/ConsoleTextWriterHook.cs @@ -0,0 +1,22 @@ +using System; +using System.IO; +using System.Text; + +namespace DotRecast.Recast.Demo.Tools; + +public class ConsoleTextWriterHook : TextWriter +{ + public override Encoding Encoding => Encoding.UTF8; + private readonly Action _event; + + public ConsoleTextWriterHook(Action relay) + { + _event = relay; + } + + public override void Write(char[] buffer, int index, int count) + { + var s = new string(new Span(buffer, index, count)); + _event?.Invoke(s); + } +} \ No newline at end of file diff --git a/src/DotRecast.Recast.Demo/UI/IRcView.cs b/src/DotRecast.Recast.Demo/UI/IRcView.cs index b66c1cc..94a2f03 100644 --- a/src/DotRecast.Recast.Demo/UI/IRcView.cs +++ b/src/DotRecast.Recast.Demo/UI/IRcView.cs @@ -16,12 +16,12 @@ freely, subject to the following restrictions: 3. This notice may not be removed or altered from any source distribution. */ -using Silk.NET.Windowing; - namespace DotRecast.Recast.Demo.UI; public interface IRcView { + void Bind(RcCanvas canvas); bool IsMouseInside(); - void Draw(); + void Update(double dt); + void Draw(double dt); } \ No newline at end of file diff --git a/src/DotRecast.Recast.Demo/UI/RcViewSystem.cs b/src/DotRecast.Recast.Demo/UI/RcCanvas.cs similarity index 83% rename from src/DotRecast.Recast.Demo/UI/RcViewSystem.cs rename to src/DotRecast.Recast.Demo/UI/RcCanvas.cs index 4aead3a..bc97392 100644 --- a/src/DotRecast.Recast.Demo/UI/RcViewSystem.cs +++ b/src/DotRecast.Recast.Demo/UI/RcCanvas.cs @@ -16,29 +16,40 @@ freely, subject to the following restrictions: 3. This notice may not be removed or altered from any source distribution. */ +using System.Numerics; using ImGuiNET; using Serilog; using Serilog.Core; using Silk.NET.Input; +using Silk.NET.Maths; using Silk.NET.OpenGL; using Silk.NET.Windowing; namespace DotRecast.Recast.Demo.UI; -public class RcViewSystem +public class RcCanvas { private static readonly ILogger Logger = Log.ForContext(); - + + private readonly IWindow _window; private readonly IRcView[] _views; private bool _mouseOverUI; public bool IsMouseOverUI() => _mouseOverUI; - public RcViewSystem(IWindow window, IInputContext input, params IRcView[] views) + public Vector2D Size => _window.Size; + + public RcCanvas(IWindow window, params IRcView[] views) { + _window = window; + _views = views; + foreach (var view in _views) + { + view.Bind(this); + } + // setupClipboard(window); // glfwSetCharCallback(window, (w, codepoint) => nk_input_unicode(ctx, codepoint)); // glContext = new NuklearGL(this); - _views = views; } @@ -86,13 +97,21 @@ public class RcViewSystem // nk_input_end(ctx); } - public void Draw() + public void Update(double dt) + { + foreach (var view in _views) + { + view.Update(dt); + } + } + + public void Draw(double dt) { _mouseOverUI = false; - foreach (IRcView m in _views) + foreach (var view in _views) { - m.Draw(); - _mouseOverUI |= m.IsMouseInside(); + view.Draw(dt); + _mouseOverUI |= view.IsMouseInside(); // if (_mouseOverUI) // { // Logger.Information("mouse hover!"); diff --git a/src/DotRecast.Recast.Demo/UI/RcLogView.cs b/src/DotRecast.Recast.Demo/UI/RcLogView.cs new file mode 100644 index 0000000..c5a484f --- /dev/null +++ b/src/DotRecast.Recast.Demo/UI/RcLogView.cs @@ -0,0 +1,130 @@ +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Numerics; +using System.Text; +using DotRecast.Recast.Demo.Tools; +using ImGuiNET; + +namespace DotRecast.Recast.Demo.UI; + +public class RcLogView : IRcView +{ + private RcCanvas _canvas; + private bool _mouseInside; + + private List _lines = new(); + private readonly ConcurrentQueue _output = new(); + private readonly StringBuilder _outputStringBuilder = new(); + + private readonly ConcurrentQueue _error = new(); + private readonly StringBuilder _errorStringBuilder = new(); + + + + public RcLogView() + { + Console.SetOut(new ConsoleTextWriterHook(OnOut)); + Console.SetError(new ConsoleTextWriterHook(OnError)); + } + + private void OnOut(string log) + { + _output.Enqueue(log); + } + + private void OnError(string log) + { + _error.Enqueue(log); + } + + public void Clear() + { + _lines.Clear(); + } + + private void MergeLines(ConcurrentQueue queue, StringBuilder builder) + { + while (queue.TryDequeue(out var s)) + { + if (s != "\r\n") + { + builder.Append(s); + } + else + { + _lines.Add(builder.ToString()); + builder.Clear(); + } + } + } + + + public void Bind(RcCanvas canvas) + { + _canvas = canvas; + } + + public void Update(double dt) + { + MergeLines(_output, _outputStringBuilder); + MergeLines(_error, _errorStringBuilder); + + // buffer + if (10240 < _lines.Count) + { + _lines.RemoveRange(0, _lines.Count - 8196); + } + } + + public bool IsMouseInside() => _mouseInside; + + public void Draw(double dt) + { + int otherWidth = 310; + int height = 234; + var width = _canvas.Size.X - (otherWidth * 2); + //var posX = _canvas.Size.X - width; + ImGui.SetNextWindowPos(new Vector2(otherWidth, _canvas.Size.Y - height)); + ImGui.SetNextWindowSize(new Vector2(width, height)); + if (!ImGui.Begin("Log", ImGuiWindowFlags.NoMove | ImGuiWindowFlags.NoResize)) + { + ImGui.End(); + return; + } + + + if (ImGui.BeginChild("scrolling", Vector2.Zero, false, ImGuiWindowFlags.HorizontalScrollbar)) + { + _mouseInside = ImGui.IsWindowHovered(); + + ImGui.PushStyleVar(ImGuiStyleVar.ItemSpacing, Vector2.Zero); + + unsafe + { + var clipper = new ImGuiListClipperPtr(ImGuiNative.ImGuiListClipper_ImGuiListClipper()); + clipper.Begin(_lines.Count); + while (clipper.Step()) + { + for (int lineNo = clipper.DisplayStart; lineNo < clipper.DisplayEnd; lineNo++) + { + ImGui.TextUnformatted(_lines[lineNo]); + } + } + + clipper.End(); + clipper.Destroy(); + } + + ImGui.PopStyleVar(); + + if (ImGui.GetScrollY() >= ImGui.GetScrollMaxY()) + { + ImGui.SetScrollHereY(1.0f); + } + } + + ImGui.EndChild(); + ImGui.End(); + } +} \ No newline at end of file diff --git a/src/DotRecast.Recast.Demo/Settings/RcSettingsView.cs b/src/DotRecast.Recast.Demo/UI/RcSettingsView.cs similarity index 92% rename from src/DotRecast.Recast.Demo/Settings/RcSettingsView.cs rename to src/DotRecast.Recast.Demo/UI/RcSettingsView.cs index 8a1e05d..96f94b7 100644 --- a/src/DotRecast.Recast.Demo/Settings/RcSettingsView.cs +++ b/src/DotRecast.Recast.Demo/UI/RcSettingsView.cs @@ -20,13 +20,14 @@ using System; using System.Collections.Generic; using System.IO; using System.Linq; +using System.Numerics; using DotRecast.Core; using DotRecast.Recast.Demo.Draw; using DotRecast.Recast.Demo.UI; using ImGuiNET; using Silk.NET.Windowing; -namespace DotRecast.Recast.Demo.Settings; +namespace DotRecast.Recast.Demo.UI; public class RcSettingsView : IRcView { @@ -63,7 +64,6 @@ public class RcSettingsView : IRcView // public readonly NkColor transparent = NkColor.create(); private bool buildTriggered; private long buildTime; - private Dictionary telemetries = new(); private readonly int[] voxels = new int[2]; private readonly int[] tiles = new int[2]; private int maxTiles; @@ -77,14 +77,32 @@ public class RcSettingsView : IRcView private bool _mouseInside; public bool IsMouseInside() => _mouseInside; - public void Draw() + + private RcCanvas _canvas; + + public void Bind(RcCanvas canvas) + { + _canvas = canvas; + } + + public void Update(double dt) { - ImGui.Begin("Properties"); - _mouseInside = ImGui.IsWindowHovered(); + } + + public void Draw(double dt) + { + int width = 310; + var posX = _canvas.Size.X - width; + ImGui.SetNextWindowPos(new Vector2(posX, 0)); + ImGui.SetNextWindowSize(new Vector2(width, _canvas.Size.Y)); + ImGui.Begin("Properties", ImGuiWindowFlags.NoMove | ImGuiWindowFlags.NoResize); + + _mouseInside = ImGui.IsWindowHovered(); + ImGui.Text("Input Mesh"); ImGui.Separator(); - + const string strLoadSourceGeom = "Load Source Geom..."; if (ImGui.Button(strLoadSourceGeom)) { @@ -101,13 +119,14 @@ public class RcSettingsView : IRcView meshInputFilePath = picker.SelectedFile; ImFilePicker.RemoveFilePicker(strLoadSourceGeom); } + ImGui.EndPopup(); } else { meshInputTrigerred = false; } - + ImGui.Text($"Verts: {voxels[0]} Tris: {voxels[1]}"); ImGui.NewLine(); @@ -142,7 +161,7 @@ public class RcSettingsView : IRcView ImGui.RadioButton(label, ref partitioningIdx, partition.Idx); }); ImGui.NewLine(); - + ImGui.Text("Filtering"); ImGui.Separator(); ImGui.Checkbox("Low Hanging Obstacles", ref filterLowHangingObstacles); @@ -162,7 +181,7 @@ public class RcSettingsView : IRcView ImGui.SliderFloat("Sample Distance", ref detailSampleDist, 0f, 16f, "%.1f"); ImGui.SliderFloat("Max Sample Error", ref detailSampleMaxError, 0f, 16f, "%.1f"); ImGui.NewLine(); - + ImGui.Text("Tiling"); ImGui.Separator(); ImGui.Checkbox("Enable", ref tiled); @@ -171,18 +190,16 @@ public class RcSettingsView : IRcView if (0 < (tileSize % 16)) tileSize = tileSize + (16 - (tileSize % 16)); ImGui.SliderInt("Tile Size", ref tileSize, 16, 1024); - + ImGui.Text($"Tiles {tiles[0]} x {tiles[1]}"); ImGui.Text($"Max Tiles {maxTiles}"); ImGui.Text($"Max Polys {maxPolys}"); } + ImGui.NewLine(); ImGui.Text($"Build Time: {buildTime} ms"); - foreach (var (key, millis) in telemetries) - { - ImGui.Text($"{key}: {millis} ms"); - } + ImGui.Separator(); buildTriggered = ImGui.Button("Build"); const string strLoadNavMesh = "Load Nav Mesh..."; @@ -200,20 +217,18 @@ public class RcSettingsView : IRcView Console.WriteLine(picker.SelectedFile); ImFilePicker.RemoveFilePicker(strLoadNavMesh); } + ImGui.EndPopup(); } - + ImGui.NewLine(); - + ImGui.Text("Draw"); ImGui.Separator(); - - DrawMode.Values.forEach(dm => - { - ImGui.RadioButton(dm.Text, ref drawMode, dm.Idx); - }); + + DrawMode.Values.forEach(dm => { ImGui.RadioButton(dm.Text, ref drawMode, dm.Idx); }); ImGui.NewLine(); - + ImGui.End(); } @@ -286,16 +301,7 @@ public class RcSettingsView : IRcView { this.buildTime = buildTime; } - - public void setBuildTelemetry(IList telemetries) - { - this.telemetries = telemetries - .SelectMany(x => x.ToList()) - .GroupBy(x => x.Item1) - .ToDictionary(x => x.Key, x => x.Sum(y => y.Item2)); - - } - + public DrawMode getDrawMode() { return DrawMode.Values[drawMode]; diff --git a/src/DotRecast.Recast.Demo/Tools/ToolsView.cs b/src/DotRecast.Recast.Demo/UI/ToolsView.cs similarity index 81% rename from src/DotRecast.Recast.Demo/Tools/ToolsView.cs rename to src/DotRecast.Recast.Demo/UI/ToolsView.cs index 4d6d31a..b15d1ad 100644 --- a/src/DotRecast.Recast.Demo/Tools/ToolsView.cs +++ b/src/DotRecast.Recast.Demo/UI/ToolsView.cs @@ -17,12 +17,13 @@ freely, subject to the following restrictions: 3. This notice may not be removed or altered from any source distribution. */ +using System.Numerics; using DotRecast.Core; +using DotRecast.Recast.Demo.Tools; using DotRecast.Recast.Demo.UI; using ImGuiNET; -using Silk.NET.Windowing; -namespace DotRecast.Recast.Demo.Tools; +namespace DotRecast.Recast.Demo.UI; public class ToolsView : IRcView { @@ -36,22 +37,38 @@ public class ToolsView : IRcView { this.tools = tools; } - + private bool _mouseInside; public bool IsMouseInside() => _mouseInside; - public void Draw() + private RcCanvas _canvas; + + public void Bind(RcCanvas canvas) + { + _canvas = canvas; + } + + public void Update(double dt) { - ImGui.Begin("Tools"); - _mouseInside = ImGui.IsWindowHovered(); + } + + public void Draw(double dt) + { + int width = 310; + ImGui.SetNextWindowPos(new Vector2(0, 0)); + ImGui.SetNextWindowSize(new Vector2(width, _canvas.Size.Y)); + ImGui.Begin("Tools", ImGuiWindowFlags.NoMove | ImGuiWindowFlags.NoResize); + _mouseInside = ImGui.IsWindowHovered(); + for (int i = 0; i < tools.Length; ++i) { var tool = tools[i]; ImGui.RadioButton(tool.getName(), ref _currentToolIdx, i); } + ImGui.NewLine(); - + if (0 > _currentToolIdx || _currentToolIdx >= tools.Length) { ImGui.End();