1 /** 2 Copyright: Copyright (c) 2017-2018 Andrey Penechko. 3 License: $(WEB boost.org/LICENSE_1_0.txt, Boost License 1.0). 4 Authors: Andrey Penechko. 5 */ 6 module voxelman.gui.guiapp; 7 8 import std.stdio : writefln, writeln; 9 import core.time : MonoTime, Duration, usecs, dur; 10 import std..string : format; 11 import voxelman.container.gapbuffer; 12 import voxelman.graphics; 13 import voxelman.gui; 14 import voxelman.math; 15 import voxelman.platform; 16 import voxelman.text.linebuffer; 17 import voxelman.text.scale; 18 import voxelman.utils.fpshelper; 19 20 class GuiApp 21 { 22 FpsHelper fpsHelper; 23 GuiContext guictx; 24 IRenderer renderer; 25 IWindow window; 26 RenderQueue renderQueue; 27 LineBuffer debugText; 28 29 bool isClosePressed; 30 bool limitFps = true; 31 int maxFps = 40; 32 double updateTime = 0; 33 bool showDebugInfo; 34 35 string title; 36 ivec2 windowSize; 37 38 this(string title, ivec2 windowSize) 39 { 40 this.title = title; 41 this.windowSize = windowSize; 42 } 43 44 void run(string[] args) 45 { 46 import core.thread : Thread; 47 48 load(args, "./"); 49 50 MonoTime prevTime = MonoTime.currTime; 51 52 bool isRunning = true; 53 54 void checkForClose() 55 { 56 if (isClosePressed) 57 isRunning = false; 58 } 59 60 while(isRunning) 61 { 62 MonoTime newTime = MonoTime.currTime; 63 double delta = (newTime - prevTime).total!"usecs" / 1_000_000.0; 64 prevTime = newTime; 65 66 fpsHelper.update(delta, updateTime); 67 update(delta); 68 69 Duration updateTimeDur = MonoTime.currTime - newTime; 70 updateTime = updateTimeDur.total!"usecs" / 1_000_000.0; 71 72 if (limitFps) { 73 Duration targetFrameTime = (1_000_000 / maxFps).usecs; 74 Duration sleepTime = targetFrameTime - updateTimeDur; 75 if (sleepTime > Duration.zero) 76 Thread.sleep(sleepTime); 77 } 78 79 checkForClose(); 80 } 81 82 stop(); 83 } 84 85 void load(string[] args, string resourcePath) 86 { 87 import std..string : fromStringz; 88 import voxelman.graphics.gl; 89 90 loadOpenGL(); 91 92 window = new GlfwWindow(); 93 WindowParams windowParams; 94 windowParams.size = windowSize; 95 windowParams.title = title; 96 window.init(windowParams); 97 98 reloadOpenGL(); 99 100 renderer = new OglRenderer(window); 101 auto resourceManager = new ResourceManager(resourcePath); 102 renderQueue = new RenderQueue(resourceManager, renderer); 103 guictx = new GuiContext(&debugText); 104 guictx.pointerMoved(window.mousePosition); 105 guictx.style.defaultFont = renderQueue.defaultFont; 106 107 window.mousePressed.connect(&guictx.pointerPressed); 108 window.mouseReleased.connect(&guictx.pointerReleased); 109 window.mouseMoved.connect(&guictx.pointerMoved); 110 window.wheelScrolled.connect(&guictx.onScroll); 111 window.keyPressed.connect(&guictx.onKeyPress); 112 window.keyReleased.connect(&guictx.onKeyRelease); 113 window.charEntered.connect(&guictx.onCharEnter); 114 guictx.state.setClipboard = &window.clipboardString; 115 guictx.state.getClipboard = &window.clipboardString; 116 117 renderer.setClearColor(255, 255, 255); 118 119 // Bind events 120 window.windowResized.connect(&windowResized); 121 window.closePressed.connect(&closePressed); 122 123 guictx.style.iconMap = resourceManager.loadNamedSpriteSheet("icons", resourceManager.texAtlas, ivec2(16, 16)); 124 guictx.style.iconPlaceholder = guictx.style.iconMap["no-icon"]; 125 126 renderQueue.reuploadTexture(); 127 } 128 129 void update(double delta) 130 { 131 window.processEvents(); 132 renderQueue.beginFrame(); 133 134 userPreUpdate(delta); 135 136 guictx.state.canvasSize = renderer.framebufferSize; 137 guictx.update(delta, renderQueue); 138 window.setCursorIcon(guictx.state.cursorIcon); 139 140 userPostUpdate(delta); 141 142 if (showDebugInfo) drawDebugText(); 143 debugText.clear(); 144 renderQueue.endFrame(); 145 146 // render 147 checkgl!glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT); 148 renderQueue.drawFrame(); 149 renderer.flush(); 150 } 151 152 int overlayDepth = 1000; 153 void drawDebugText() 154 { 155 auto pos = vec2(renderer.framebufferSize.x, 0); 156 157 auto mesherParams = renderQueue.startTextAt(pos + vec2(-5,5)); 158 mesherParams.depth = overlayDepth; 159 160 mesherParams.meshText(debugText.lines.data); 161 mesherParams.alignMeshedText(Alignment.max); 162 163 renderQueue.drawRectFill(vec2(mesherParams.origin)-vec2(5,5), mesherParams.size + vec2(10,10), overlayDepth-1, Colors.white); 164 renderQueue.drawRectLine(vec2(mesherParams.origin)-vec2(6,6), mesherParams.size + vec2(12,12), overlayDepth-1, Colors.black); 165 } 166 167 void userPreUpdate(double delta) {} 168 void userPostUpdate(double delta) {} 169 170 void stop() 171 { 172 window.releaseWindow; 173 } 174 175 void windowResized(ivec2 newSize) 176 { 177 renderer.setViewport(ivec2(0, 0), renderer.framebufferSize); 178 } 179 180 void closePressed() 181 { 182 isClosePressed = true; 183 } 184 } 185