Files
tbd-station-14/Pow3r/Program.cs
2022-11-16 12:58:00 -08:00

390 lines
14 KiB
C#

using System;
using System.Diagnostics;
using System.Runtime.InteropServices;
using ImGuiNET;
using OpenTK.Windowing.Common;
using OpenTK.Windowing.Desktop;
using OpenTK.Windowing.GraphicsLibraryFramework;
using ErrorCode = OpenTK.Windowing.GraphicsLibraryFramework.ErrorCode;
using Vector2 = System.Numerics.Vector2;
// ReSharper disable PossibleNullReferenceException
namespace Pow3r
{
internal sealed unsafe partial class Program
{
private Renderer _renderer = Renderer.Veldrid;
[UnmanagedCallersOnly]
private static byte* GetClipboardTextCallback(void* userData)
{
return GLFW.GetClipboardStringRaw((Window*) userData);
}
[UnmanagedCallersOnly]
private static void SetClipboardTextCallback(void* userData, byte* text)
{
GLFW.SetClipboardStringRaw((Window*) userData, text);
}
private static readonly GLFWCallbacks.ErrorCallback ErrorCallback = GlfwErrorCallback;
private static void GlfwErrorCallback(ErrorCode error, string description)
{
Console.WriteLine($"{error}: {description}");
}
private bool[] _mouseJustPressed = new bool[5];
private bool _fullscreen;
private int _monitorIdx;
private bool _vsync = true;
private GameWindow _window;
private readonly Stopwatch _stopwatch = new();
private readonly Cursor*[] _cursors = new Cursor*[9];
private readonly float[] _frameTimings = new float[180];
private int _frameTimeIdx = 0;
private int _tps = 60;
private void Run(string[] args)
{
for (var i = 0; i < args.Length; i++)
{
if (args[i] == "--renderer")
{
_renderer = Enum.Parse<Renderer>(args[++i]);
}
else if (args[i] == "--veldrid")
{
_vdRenderer = Enum.Parse<VeldridRenderer>(args[++i]);
}
else if (args[i] == "--fullscreen")
{
_fullscreen = true;
}
else if (args[i] == "--monitor-idx")
{
_monitorIdx = int.Parse(args[++i]);
}
else if (args[i] == "--no-vsync")
{
_vsync = false;
}
else if (args[i] == "--help")
{
Console.WriteLine("--renderer <Veldrid|OpenGL>");
Console.WriteLine("--veldrid <Vulkan|OpenGL|D3D11>");
Console.WriteLine("--no-vsync");
Console.WriteLine("--fullscreen");
Console.WriteLine("--monitor-idx");
Console.WriteLine("--help");
return;
}
else
{
Console.WriteLine($"unknown arg \"{args[i]}\"");
return;
}
}
Console.WriteLine($"Renderer: {_renderer}");
if (_renderer == Renderer.Veldrid)
Console.WriteLine($"Veldrid API: {_vdRenderer}");
Console.WriteLine($"Fullscreen: {_fullscreen}");
Console.WriteLine($"VSync: {_vsync}");
//NativeLibrary.Load("nvapi64.dll");
GLFW.Init();
GLFW.SetErrorCallback(ErrorCallback);
// var sw = Stopwatch.StartNew();
GLFW.WindowHint(WindowHintBool.SrgbCapable, true);
var windowSettings = new NativeWindowSettings
{
Size = (1280, 720),
WindowState = WindowState.Maximized,
StartVisible = false,
Title = "Pow3r"
};
var openGLBased = _renderer == Renderer.OpenGL ||
(_renderer == Renderer.Veldrid && _vdRenderer == VeldridRenderer.OpenGL);
if (openGLBased)
{
windowSettings.API = ContextAPI.OpenGL;
if (_renderer == Renderer.Veldrid)
{
windowSettings.Profile = ContextProfile.Core;
windowSettings.APIVersion = new Version(4, 6);
windowSettings.Flags = ContextFlags.ForwardCompatible;
}
else
{
windowSettings.Profile = ContextProfile.Any;
windowSettings.APIVersion = new Version(1, 5);
}
#if DEBUG
windowSettings.Flags |= ContextFlags.Debug;
#endif
}
else
{
windowSettings.API = ContextAPI.NoAPI;
}
_window = new GameWindow(GameWindowSettings.Default, windowSettings);
// Console.WriteLine(sw.ElapsedMilliseconds);
if (_fullscreen)
{
var monitors = GLFW.GetMonitors();
var monitor = monitors[_monitorIdx];
var monitorMode = GLFW.GetVideoMode(monitor);
GLFW.SetWindowMonitor(
_window.WindowPtr,
monitor,
0, 0,
monitorMode->Width,
monitorMode->Height,
monitorMode->RefreshRate);
}
if (openGLBased)
{
_window.VSync = _vsync ? VSyncMode.On : VSyncMode.Off;
}
var context = ImGui.CreateContext();
ImGui.SetCurrentContext(context);
ImGui.StyleColorsDark();
var io = ImGui.GetIO();
io.Fonts.AddFontDefault();
delegate* unmanaged<void*, byte*> getClipboardCallback = &GetClipboardTextCallback;
io.GetClipboardTextFn = (IntPtr) getClipboardCallback;
delegate* unmanaged<void*, byte*, void> setClipboardCallback = &SetClipboardTextCallback;
io.GetClipboardTextFn = (IntPtr) setClipboardCallback;
io.ClipboardUserData = (IntPtr) _window.WindowPtr;
io.BackendFlags |= ImGuiBackendFlags.RendererHasVtxOffset;
io.BackendFlags |= ImGuiBackendFlags.HasSetMousePos;
io.BackendFlags |= ImGuiBackendFlags.HasMouseCursors;
io.KeyMap[(int) ImGuiKey.Tab] = (int) Keys.Tab;
io.KeyMap[(int) ImGuiKey.LeftArrow] = (int) Keys.Left;
io.KeyMap[(int) ImGuiKey.RightArrow] = (int) Keys.Right;
io.KeyMap[(int) ImGuiKey.UpArrow] = (int) Keys.Up;
io.KeyMap[(int) ImGuiKey.DownArrow] = (int) Keys.Down;
io.KeyMap[(int) ImGuiKey.PageUp] = (int) Keys.PageUp;
io.KeyMap[(int) ImGuiKey.PageDown] = (int) Keys.PageDown;
io.KeyMap[(int) ImGuiKey.Home] = (int) Keys.Home;
io.KeyMap[(int) ImGuiKey.End] = (int) Keys.End;
io.KeyMap[(int) ImGuiKey.Delete] = (int) Keys.Delete;
io.KeyMap[(int) ImGuiKey.Backspace] = (int) Keys.Backspace;
io.KeyMap[(int) ImGuiKey.Enter] = (int) Keys.Enter;
io.KeyMap[(int) ImGuiKey.Escape] = (int) Keys.Escape;
io.KeyMap[(int) ImGuiKey.A] = (int) Keys.A;
io.KeyMap[(int) ImGuiKey.C] = (int) Keys.C;
io.KeyMap[(int) ImGuiKey.V] = (int) Keys.V;
io.KeyMap[(int) ImGuiKey.X] = (int) Keys.X;
io.KeyMap[(int) ImGuiKey.Y] = (int) Keys.Y;
io.KeyMap[(int) ImGuiKey.Z] = (int) Keys.Z;
_cursors[(int) ImGuiMouseCursor.Arrow] = GLFW.CreateStandardCursor(CursorShape.Arrow);
_cursors[(int) ImGuiMouseCursor.TextInput] = GLFW.CreateStandardCursor(CursorShape.IBeam);
_cursors[(int) ImGuiMouseCursor.ResizeNS] = GLFW.CreateStandardCursor(CursorShape.VResize);
_cursors[(int) ImGuiMouseCursor.ResizeEW] = GLFW.CreateStandardCursor(CursorShape.HResize);
_cursors[(int) ImGuiMouseCursor.Hand] = GLFW.CreateStandardCursor(CursorShape.Hand);
_cursors[(int) ImGuiMouseCursor.ResizeAll] = GLFW.CreateStandardCursor(CursorShape.Arrow);
_cursors[(int) ImGuiMouseCursor.ResizeNESW] = GLFW.CreateStandardCursor(CursorShape.Arrow);
_cursors[(int) ImGuiMouseCursor.ResizeNWSE] = GLFW.CreateStandardCursor(CursorShape.Arrow);
_cursors[(int) ImGuiMouseCursor.NotAllowed] = GLFW.CreateStandardCursor(CursorShape.Arrow);
InitRenderer();
_window.MouseDown += OnMouseDown;
_window.TextInput += WindowOnTextInput;
_window.MouseWheel += WindowOnMouseWheel;
_window.KeyDown += args => KeyCallback(args, true);
_window.KeyUp += args => KeyCallback(args, false);
_stopwatch.Start();
LoadFromDisk();
_window.IsVisible = true;
var lastTick = TimeSpan.Zero;
var lastFrame = TimeSpan.Zero;
var curTime = TimeSpan.Zero;
while (!GLFW.WindowShouldClose(_window.WindowPtr))
{
NativeWindow.ProcessWindowEvents(false);
var tickSpan = TimeSpan.FromSeconds(1f / _tps);
while (curTime - lastTick > tickSpan)
{
lastTick += tickSpan;
Tick((float) tickSpan.TotalSeconds);
}
_frameTimeIdx = (_frameTimeIdx + 1) % _frameTimings.Length;
var dt = curTime - lastFrame;
lastFrame = curTime;
_frameTimings[_frameTimeIdx] = (float) dt.TotalMilliseconds;
FrameUpdate((float) dt.TotalSeconds);
Render();
curTime = _stopwatch.Elapsed;
}
SaveToDisk();
}
private static void KeyCallback(KeyboardKeyEventArgs obj, bool down)
{
var io = ImGui.GetIO();
if (obj.Key ==Keys.Unknown)
return;
var keyInt = (int) obj.Key;
io.KeysDown[keyInt] = down;
io.KeyCtrl = io.KeysDown[(int) Keys.LeftControl] || io.KeysDown[(int) Keys.RightControl];
io.KeyShift = io.KeysDown[(int) Keys.LeftShift] || io.KeysDown[(int) Keys.RightShift];
io.KeyAlt = io.KeysDown[(int) Keys.LeftAlt] || io.KeysDown[(int) Keys.RightAlt];
}
private static void WindowOnMouseWheel(MouseWheelEventArgs obj)
{
var io = ImGui.GetIO();
io.MouseWheelH += obj.OffsetX;
io.MouseWheel += obj.OffsetY;
}
private static void WindowOnTextInput(TextInputEventArgs obj)
{
var io = ImGui.GetIO();
io.AddInputCharacter((uint) obj.Unicode);
}
private void OnMouseDown(MouseButtonEventArgs obj)
{
var button = (int) obj.Button;
if (obj.IsPressed && button < _mouseJustPressed.Length)
_mouseJustPressed[button] = true;
}
private void FrameUpdate(float dt)
{
//var sw = Stopwatch.StartNew();
var io = ImGui.GetIO();
GLFW.GetFramebufferSize(_window.WindowPtr, out var fbW, out var fbH);
GLFW.GetWindowSize(_window.WindowPtr, out var wW, out var wH);
io.DisplaySize = new Vector2(wW, wH);
io.DisplayFramebufferScale = new Vector2(fbW / (float) wW, fbH / (float) wH);
io.DeltaTime = dt;
UpdateMouseState(io);
UpdateCursorState(io);
//Console.WriteLine($"INPUT: {sw.Elapsed.TotalMilliseconds}");
ImGui.NewFrame();
DoUI(dt);
}
private void UpdateCursorState(ImGuiIOPtr io)
{
var cursor = ImGui.GetMouseCursor();
if (cursor == ImGuiMouseCursor.None)
{
GLFW.SetInputMode(_window.WindowPtr, CursorStateAttribute.Cursor, CursorModeValue.CursorHidden);
}
else
{
GLFW.SetCursor(_window.WindowPtr, _cursors[(int) cursor]);
GLFW.SetInputMode(_window.WindowPtr, CursorStateAttribute.Cursor, CursorModeValue.CursorNormal);
}
}
private void UpdateMouseState(ImGuiIOPtr io)
{
for (var i = 0; i < io.MouseDown.Count; i++)
{
io.MouseDown[i] = _mouseJustPressed[i] ||
GLFW.GetMouseButton(_window.WindowPtr, (MouseButton) i) == InputAction.Press;
_mouseJustPressed[i] = false;
}
var oldMousePos = io.MousePos;
io.MousePos = new Vector2(float.PositiveInfinity, float.PositiveInfinity);
var focused = _window.IsFocused;
if (focused)
{
if (io.WantSetMousePos)
{
GLFW.SetCursorPos(_window.WindowPtr, oldMousePos.X, oldMousePos.Y);
}
else
{
GLFW.GetCursorPos(_window.WindowPtr, out var x, out var y);
io.MousePos = new Vector2((float) x, (float) y);
}
}
}
private void InitRenderer()
{
switch (_renderer)
{
case Renderer.OpenGL:
InitOpenGL();
break;
case Renderer.Veldrid:
InitVeldrid();
break;
}
}
private void Render()
{
ImGui.Render();
switch (_renderer)
{
case Renderer.OpenGL:
RenderOpenGL();
break;
case Renderer.Veldrid:
RenderVeldrid();
break;
}
}
private static void Main(string[] args)
{
new Program().Run(args);
}
public enum Renderer
{
OpenGL,
Veldrid
}
}
}