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