using System.Numerics; using ImGuiNET; using NResUI.Abstractions; using NResUI.Rendering.Viewport; using NResUI.Rendering.Viewport.Meshes; using Silk.NET.OpenGL; using Silk.NET.Windowing; namespace NResUI.ImGuiUI; public sealed class ViewportPanel : IImGuiPanel { private readonly ViewportRenderer _renderer; private readonly ViewportScene _scene; private readonly ViewportCamera _camera = new(); private readonly ViewportInputController _inputController = new(); public ViewportPanel(GL gl, IWindow window) { var cubeMesh = PrimitiveMeshes.CreateCube(gl); var gridMesh = PrimitiveMeshes.CreateWorldGrid(gl); _scene = ViewportScene.CreateDefaultCubeScene(cubeMesh, gridMesh); _renderer = new ViewportRenderer(gl, window); } public void OnImGuiRender() { if (!ImGui.Begin("Viewport")) { ImGui.End(); return; } DrawSelectionStatus(); DrawViewportToolbar(); DrawDebugControls(); var imageSize = ImGui.GetContentRegionAvail(); if (imageSize.X < 32 || imageSize.Y < 32) { ImGui.TextDisabled("Viewport is too small."); ImGui.End(); return; } var textureId = _renderer.Render( width: Math.Max(1, (int)imageSize.X), height: Math.Max(1, (int)imageSize.Y), scene: _scene, camera: _camera); ImGui.Image( (IntPtr)textureId, imageSize, new Vector2(0, 1), new Vector2(1, 0)); var viewportHovered = ImGui.IsItemHovered(); var viewportFocused = ImGui.IsWindowFocused(ImGuiFocusedFlags.RootAndChildWindows); var imageMin = ImGui.GetItemRectMin(); DrawViewportHelpOverlay(); _inputController.Handle( _scene, _camera, viewportHovered, viewportFocused, imageMin, imageSize); ImGui.End(); } private void DrawSelectionStatus() { var selectedPiece = _scene.SelectedPiece; if (selectedPiece != null) ImGui.Text($"Selected: {selectedPiece.Name}"); else ImGui.TextDisabled("Selected: none"); } private void DrawViewportToolbar() { if (ImGui.Button("Reset view")) _camera.Reset(); ImGui.SameLine(); if (ImGui.Button("Frame selected")) { if (_scene.TryGetSelectedPieceWorldBounds(out var selectedBounds)) _camera.FrameBounds(selectedBounds.Min, selectedBounds.Max); } ImGui.SameLine(); if (ImGui.Button("Frame all")) { if (_scene.TryGetSceneWorldBounds(out var sceneBounds)) _camera.FrameBounds(sceneBounds.Min, sceneBounds.Max); else _camera.Reset(); } } private void DrawDebugControls() { var grid = _scene.Grid; if (grid != null) { var isVisible = grid.IsVisible; if (ImGui.Checkbox("Grid", ref isVisible)) grid.IsVisible = isVisible; } ImGui.SameLine(); var showOriginAxes = _scene.Debug.ShowOriginAxes; if (ImGui.Checkbox("Origin axes", ref showOriginAxes)) _scene.Debug.ShowOriginAxes = showOriginAxes; ImGui.SameLine(); var showPieceOrigins = _scene.Debug.ShowPieceOrigins; if (ImGui.Checkbox("Piece axes", ref showPieceOrigins)) _scene.Debug.ShowPieceOrigins = showPieceOrigins; ImGui.SameLine(); var showSelectedBounds = _scene.Debug.ShowSelectedBounds; if (ImGui.Checkbox("Selected bounds", ref showSelectedBounds)) _scene.Debug.ShowSelectedBounds = showSelectedBounds; ImGui.SameLine(); var showSceneBounds = _scene.Debug.ShowSceneBounds; if (ImGui.Checkbox("Scene bounds", ref showSceneBounds)) _scene.Debug.ShowSceneBounds = showSceneBounds; ImGui.SameLine(); var wireframe = _scene.Debug.Wireframe; if (ImGui.Checkbox("Wireframe", ref wireframe)) _scene.Debug.Wireframe = wireframe; } private static void DrawViewportHelpOverlay() { var drawList = ImGui.GetWindowDrawList(); var imageMin = ImGui.GetItemRectMin(); var imageMax = ImGui.GetItemRectMax(); const string helpText = "LMB click: select / deselect\n" + "MMB drag: rotate\n" + "RMB drag or Alt+MMB: pan\n" + "Mouse wheel: zoom\n" + "F: frame selected Home: frame all"; var textSize = ImGui.CalcTextSize(helpText); var padding = new Vector2(8.0f, 6.0f); var boxMin = new Vector2( imageMin.X + 8.0f, imageMax.Y - textSize.Y - padding.Y * 2.0f - 8.0f); var boxMax = new Vector2( boxMin.X + textSize.X + padding.X * 2.0f, boxMin.Y + textSize.Y + padding.Y * 2.0f); drawList.AddRectFilled( boxMin, boxMax, ImGui.GetColorU32(new Vector4(0.0f, 0.0f, 0.0f, 0.45f)), 4.0f); drawList.AddText( boxMin + padding, ImGui.GetColorU32(new Vector4(1.0f, 1.0f, 1.0f, 0.9f)), helpText); } }