1
0

Program.cs 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389
  1. using System;
  2. using System.Diagnostics;
  3. using System.Runtime.InteropServices;
  4. using ImGuiNET;
  5. using OpenTK.Windowing.Common;
  6. using OpenTK.Windowing.Desktop;
  7. using OpenTK.Windowing.GraphicsLibraryFramework;
  8. using ErrorCode = OpenTK.Windowing.GraphicsLibraryFramework.ErrorCode;
  9. using Vector2 = System.Numerics.Vector2;
  10. // ReSharper disable PossibleNullReferenceException
  11. namespace Pow3r
  12. {
  13. internal sealed unsafe partial class Program
  14. {
  15. private Renderer _renderer = Renderer.Veldrid;
  16. [UnmanagedCallersOnly]
  17. private static byte* GetClipboardTextCallback(void* userData)
  18. {
  19. return GLFW.GetClipboardStringRaw((Window*) userData);
  20. }
  21. [UnmanagedCallersOnly]
  22. private static void SetClipboardTextCallback(void* userData, byte* text)
  23. {
  24. GLFW.SetClipboardStringRaw((Window*) userData, text);
  25. }
  26. private static readonly GLFWCallbacks.ErrorCallback ErrorCallback = GlfwErrorCallback;
  27. private static void GlfwErrorCallback(ErrorCode error, string description)
  28. {
  29. Console.WriteLine($"{error}: {description}");
  30. }
  31. private bool[] _mouseJustPressed = new bool[5];
  32. private bool _fullscreen;
  33. private int _monitorIdx;
  34. private bool _vsync = true;
  35. private GameWindow _window;
  36. private readonly Stopwatch _stopwatch = new();
  37. private readonly Cursor*[] _cursors = new Cursor*[9];
  38. private readonly float[] _frameTimings = new float[180];
  39. private int _frameTimeIdx = 0;
  40. private int _tps = 60;
  41. private void Run(string[] args)
  42. {
  43. for (var i = 0; i < args.Length; i++)
  44. {
  45. if (args[i] == "--renderer")
  46. {
  47. _renderer = Enum.Parse<Renderer>(args[++i]);
  48. }
  49. else if (args[i] == "--veldrid")
  50. {
  51. _vdRenderer = Enum.Parse<VeldridRenderer>(args[++i]);
  52. }
  53. else if (args[i] == "--fullscreen")
  54. {
  55. _fullscreen = true;
  56. }
  57. else if (args[i] == "--monitor-idx")
  58. {
  59. _monitorIdx = int.Parse(args[++i]);
  60. }
  61. else if (args[i] == "--no-vsync")
  62. {
  63. _vsync = false;
  64. }
  65. else if (args[i] == "--help")
  66. {
  67. Console.WriteLine("--renderer <Veldrid|OpenGL>");
  68. Console.WriteLine("--veldrid <Vulkan|OpenGL|D3D11>");
  69. Console.WriteLine("--no-vsync");
  70. Console.WriteLine("--fullscreen");
  71. Console.WriteLine("--monitor-idx");
  72. Console.WriteLine("--help");
  73. return;
  74. }
  75. else
  76. {
  77. Console.WriteLine($"unknown arg \"{args[i]}\"");
  78. return;
  79. }
  80. }
  81. Console.WriteLine($"Renderer: {_renderer}");
  82. if (_renderer == Renderer.Veldrid)
  83. Console.WriteLine($"Veldrid API: {_vdRenderer}");
  84. Console.WriteLine($"Fullscreen: {_fullscreen}");
  85. Console.WriteLine($"VSync: {_vsync}");
  86. //NativeLibrary.Load("nvapi64.dll");
  87. GLFW.Init();
  88. GLFW.SetErrorCallback(ErrorCallback);
  89. // var sw = Stopwatch.StartNew();
  90. GLFW.WindowHint(WindowHintBool.SrgbCapable, true);
  91. var windowSettings = new NativeWindowSettings
  92. {
  93. Size = (1280, 720),
  94. WindowState = WindowState.Maximized,
  95. StartVisible = false,
  96. Title = "Pow3r"
  97. };
  98. var openGLBased = _renderer == Renderer.OpenGL ||
  99. (_renderer == Renderer.Veldrid && _vdRenderer == VeldridRenderer.OpenGL);
  100. if (openGLBased)
  101. {
  102. windowSettings.API = ContextAPI.OpenGL;
  103. if (_renderer == Renderer.Veldrid)
  104. {
  105. windowSettings.Profile = ContextProfile.Core;
  106. windowSettings.APIVersion = new Version(4, 6);
  107. windowSettings.Flags = ContextFlags.ForwardCompatible;
  108. }
  109. else
  110. {
  111. windowSettings.Profile = ContextProfile.Any;
  112. windowSettings.APIVersion = new Version(1, 5);
  113. }
  114. #if DEBUG
  115. windowSettings.Flags |= ContextFlags.Debug;
  116. #endif
  117. }
  118. else
  119. {
  120. windowSettings.API = ContextAPI.NoAPI;
  121. }
  122. _window = new GameWindow(GameWindowSettings.Default, windowSettings);
  123. // Console.WriteLine(sw.ElapsedMilliseconds);
  124. if (_fullscreen)
  125. {
  126. var monitors = GLFW.GetMonitors();
  127. var monitor = monitors[_monitorIdx];
  128. var monitorMode = GLFW.GetVideoMode(monitor);
  129. GLFW.SetWindowMonitor(
  130. _window.WindowPtr,
  131. monitor,
  132. 0, 0,
  133. monitorMode->Width,
  134. monitorMode->Height,
  135. monitorMode->RefreshRate);
  136. }
  137. if (openGLBased)
  138. {
  139. _window.VSync = _vsync ? VSyncMode.On : VSyncMode.Off;
  140. }
  141. var context = ImGui.CreateContext();
  142. ImGui.SetCurrentContext(context);
  143. ImGui.StyleColorsDark();
  144. var io = ImGui.GetIO();
  145. io.Fonts.AddFontDefault();
  146. delegate* unmanaged<void*, byte*> getClipboardCallback = &GetClipboardTextCallback;
  147. io.GetClipboardTextFn = (IntPtr) getClipboardCallback;
  148. delegate* unmanaged<void*, byte*, void> setClipboardCallback = &SetClipboardTextCallback;
  149. io.GetClipboardTextFn = (IntPtr) setClipboardCallback;
  150. io.ClipboardUserData = (IntPtr) _window.WindowPtr;
  151. io.BackendFlags |= ImGuiBackendFlags.RendererHasVtxOffset;
  152. io.BackendFlags |= ImGuiBackendFlags.HasSetMousePos;
  153. io.BackendFlags |= ImGuiBackendFlags.HasMouseCursors;
  154. io.KeyMap[(int) ImGuiKey.Tab] = (int) Keys.Tab;
  155. io.KeyMap[(int) ImGuiKey.LeftArrow] = (int) Keys.Left;
  156. io.KeyMap[(int) ImGuiKey.RightArrow] = (int) Keys.Right;
  157. io.KeyMap[(int) ImGuiKey.UpArrow] = (int) Keys.Up;
  158. io.KeyMap[(int) ImGuiKey.DownArrow] = (int) Keys.Down;
  159. io.KeyMap[(int) ImGuiKey.PageUp] = (int) Keys.PageUp;
  160. io.KeyMap[(int) ImGuiKey.PageDown] = (int) Keys.PageDown;
  161. io.KeyMap[(int) ImGuiKey.Home] = (int) Keys.Home;
  162. io.KeyMap[(int) ImGuiKey.End] = (int) Keys.End;
  163. io.KeyMap[(int) ImGuiKey.Delete] = (int) Keys.Delete;
  164. io.KeyMap[(int) ImGuiKey.Backspace] = (int) Keys.Backspace;
  165. io.KeyMap[(int) ImGuiKey.Enter] = (int) Keys.Enter;
  166. io.KeyMap[(int) ImGuiKey.Escape] = (int) Keys.Escape;
  167. io.KeyMap[(int) ImGuiKey.A] = (int) Keys.A;
  168. io.KeyMap[(int) ImGuiKey.C] = (int) Keys.C;
  169. io.KeyMap[(int) ImGuiKey.V] = (int) Keys.V;
  170. io.KeyMap[(int) ImGuiKey.X] = (int) Keys.X;
  171. io.KeyMap[(int) ImGuiKey.Y] = (int) Keys.Y;
  172. io.KeyMap[(int) ImGuiKey.Z] = (int) Keys.Z;
  173. _cursors[(int) ImGuiMouseCursor.Arrow] = GLFW.CreateStandardCursor(CursorShape.Arrow);
  174. _cursors[(int) ImGuiMouseCursor.TextInput] = GLFW.CreateStandardCursor(CursorShape.IBeam);
  175. _cursors[(int) ImGuiMouseCursor.ResizeNS] = GLFW.CreateStandardCursor(CursorShape.VResize);
  176. _cursors[(int) ImGuiMouseCursor.ResizeEW] = GLFW.CreateStandardCursor(CursorShape.HResize);
  177. _cursors[(int) ImGuiMouseCursor.Hand] = GLFW.CreateStandardCursor(CursorShape.Hand);
  178. _cursors[(int) ImGuiMouseCursor.ResizeAll] = GLFW.CreateStandardCursor(CursorShape.Arrow);
  179. _cursors[(int) ImGuiMouseCursor.ResizeNESW] = GLFW.CreateStandardCursor(CursorShape.Arrow);
  180. _cursors[(int) ImGuiMouseCursor.ResizeNWSE] = GLFW.CreateStandardCursor(CursorShape.Arrow);
  181. _cursors[(int) ImGuiMouseCursor.NotAllowed] = GLFW.CreateStandardCursor(CursorShape.Arrow);
  182. InitRenderer();
  183. _window.MouseDown += OnMouseDown;
  184. _window.TextInput += WindowOnTextInput;
  185. _window.MouseWheel += WindowOnMouseWheel;
  186. _window.KeyDown += args => KeyCallback(args, true);
  187. _window.KeyUp += args => KeyCallback(args, false);
  188. _stopwatch.Start();
  189. LoadFromDisk();
  190. _window.IsVisible = true;
  191. var lastTick = TimeSpan.Zero;
  192. var lastFrame = TimeSpan.Zero;
  193. var curTime = TimeSpan.Zero;
  194. while (!GLFW.WindowShouldClose(_window.WindowPtr))
  195. {
  196. NativeWindow.ProcessWindowEvents(false);
  197. var tickSpan = TimeSpan.FromSeconds(1f / _tps);
  198. while (curTime - lastTick > tickSpan)
  199. {
  200. lastTick += tickSpan;
  201. Tick((float) tickSpan.TotalSeconds);
  202. }
  203. _frameTimeIdx = (_frameTimeIdx + 1) % _frameTimings.Length;
  204. var dt = curTime - lastFrame;
  205. lastFrame = curTime;
  206. _frameTimings[_frameTimeIdx] = (float) dt.TotalMilliseconds;
  207. FrameUpdate((float) dt.TotalSeconds);
  208. Render();
  209. curTime = _stopwatch.Elapsed;
  210. }
  211. SaveToDisk();
  212. }
  213. private static void KeyCallback(KeyboardKeyEventArgs obj, bool down)
  214. {
  215. var io = ImGui.GetIO();
  216. if (obj.Key ==Keys.Unknown)
  217. return;
  218. var keyInt = (int) obj.Key;
  219. io.KeysDown[keyInt] = down;
  220. io.KeyCtrl = io.KeysDown[(int) Keys.LeftControl] || io.KeysDown[(int) Keys.RightControl];
  221. io.KeyShift = io.KeysDown[(int) Keys.LeftShift] || io.KeysDown[(int) Keys.RightShift];
  222. io.KeyAlt = io.KeysDown[(int) Keys.LeftAlt] || io.KeysDown[(int) Keys.RightAlt];
  223. }
  224. private static void WindowOnMouseWheel(MouseWheelEventArgs obj)
  225. {
  226. var io = ImGui.GetIO();
  227. io.MouseWheelH += obj.OffsetX;
  228. io.MouseWheel += obj.OffsetY;
  229. }
  230. private static void WindowOnTextInput(TextInputEventArgs obj)
  231. {
  232. var io = ImGui.GetIO();
  233. io.AddInputCharacter((uint) obj.Unicode);
  234. }
  235. private void OnMouseDown(MouseButtonEventArgs obj)
  236. {
  237. var button = (int) obj.Button;
  238. if (obj.IsPressed && button < _mouseJustPressed.Length)
  239. _mouseJustPressed[button] = true;
  240. }
  241. private void FrameUpdate(float dt)
  242. {
  243. //var sw = Stopwatch.StartNew();
  244. var io = ImGui.GetIO();
  245. GLFW.GetFramebufferSize(_window.WindowPtr, out var fbW, out var fbH);
  246. GLFW.GetWindowSize(_window.WindowPtr, out var wW, out var wH);
  247. io.DisplaySize = new Vector2(wW, wH);
  248. io.DisplayFramebufferScale = new Vector2(fbW / (float) wW, fbH / (float) wH);
  249. io.DeltaTime = dt;
  250. UpdateMouseState(io);
  251. UpdateCursorState(io);
  252. //Console.WriteLine($"INPUT: {sw.Elapsed.TotalMilliseconds}");
  253. ImGui.NewFrame();
  254. DoUI(dt);
  255. }
  256. private void UpdateCursorState(ImGuiIOPtr io)
  257. {
  258. var cursor = ImGui.GetMouseCursor();
  259. if (cursor == ImGuiMouseCursor.None)
  260. {
  261. GLFW.SetInputMode(_window.WindowPtr, CursorStateAttribute.Cursor, CursorModeValue.CursorHidden);
  262. }
  263. else
  264. {
  265. GLFW.SetCursor(_window.WindowPtr, _cursors[(int) cursor]);
  266. GLFW.SetInputMode(_window.WindowPtr, CursorStateAttribute.Cursor, CursorModeValue.CursorNormal);
  267. }
  268. }
  269. private void UpdateMouseState(ImGuiIOPtr io)
  270. {
  271. for (var i = 0; i < io.MouseDown.Count; i++)
  272. {
  273. io.MouseDown[i] = _mouseJustPressed[i] ||
  274. GLFW.GetMouseButton(_window.WindowPtr, (MouseButton) i) == InputAction.Press;
  275. _mouseJustPressed[i] = false;
  276. }
  277. var oldMousePos = io.MousePos;
  278. io.MousePos = new Vector2(float.PositiveInfinity, float.PositiveInfinity);
  279. var focused = _window.IsFocused;
  280. if (focused)
  281. {
  282. if (io.WantSetMousePos)
  283. {
  284. GLFW.SetCursorPos(_window.WindowPtr, oldMousePos.X, oldMousePos.Y);
  285. }
  286. else
  287. {
  288. GLFW.GetCursorPos(_window.WindowPtr, out var x, out var y);
  289. io.MousePos = new Vector2((float) x, (float) y);
  290. }
  291. }
  292. }
  293. private void InitRenderer()
  294. {
  295. switch (_renderer)
  296. {
  297. case Renderer.OpenGL:
  298. InitOpenGL();
  299. break;
  300. case Renderer.Veldrid:
  301. InitVeldrid();
  302. break;
  303. }
  304. }
  305. private void Render()
  306. {
  307. ImGui.Render();
  308. switch (_renderer)
  309. {
  310. case Renderer.OpenGL:
  311. RenderOpenGL();
  312. break;
  313. case Renderer.Veldrid:
  314. RenderVeldrid();
  315. break;
  316. }
  317. }
  318. private static void Main(string[] args)
  319. {
  320. new Program().Run(args);
  321. }
  322. public enum Renderer
  323. {
  324. OpenGL,
  325. Veldrid
  326. }
  327. }
  328. }