1 /**
2 Copyright: Copyright (c) 2015-2018 Andrey Penechko.
3 License: $(WEB boost.org/LICENSE_1_0.txt, Boost License 1.0).
4 Authors: Andrey Penechko.
5 */
6 
7 module voxelman.graphics.plugin;
8 
9 import pluginlib;
10 import voxelman.container.buffer;
11 import voxelman.globalconfig;
12 import voxelman.log;
13 import voxelman.math;
14 public import voxelman.graphics;
15 import voxelman.gui;
16 public import voxelman.text.linebuffer;
17 
18 import voxelman.config.configmanager;
19 import voxelman.core.config;
20 import voxelman.core.events;
21 import voxelman.eventdispatcher.plugin;
22 import voxelman.gui.plugin;
23 import voxelman.platform.iwindow;
24 import voxelman.platform.glfwwindow;
25 import voxelman.input.keybindingmanager;
26 
27 
28 class GraphicsResources : IResourceManager
29 {
30 	override string id() @property { return "voxelman.graphics.graphicsresources"; }
31 
32 	GuiContext guictx;
33 }
34 
35 final class GraphicsPlugin : IPlugin
36 {
37 private:
38 	uint vao;
39 	uint vbo;
40 	EventDispatcherPlugin evDispatcher;
41 	Matrix4f ortho_projection;
42 	GraphicsResources graphicsRes;
43 	GuiContext guictx;
44 
45 public:
46 	IWindow window;
47 	IRenderer renderer;
48 	ResourceManager resourceManager;
49 	RenderQueue renderQueue;
50 	LineBuffer debugText;
51 	bool showDebugInfo;
52 
53 	FpsCamera camera;
54 	Batch debugBatch;
55 	Buffer!ColoredVertex transparentBuffer;
56 	Batch2d overlayBatch;
57 
58 	SolidShader3d solidShader3d;
59 	TransparentShader3d transparentShader3d;
60 	SolidShader2d solidShader2d;
61 
62 	ConfigOption cameraSensivity;
63 	ConfigOption cameraFov;
64 	ConfigOption resolution;
65 	ConfigOption gl_debug_context;
66 	ConfigOption vsync;
67 
68 	mixin IdAndSemverFrom!"voxelman.graphics.plugininfo";
69 
70 	override void registerResourceManagers(void delegate(IResourceManager) registerResourceManager)
71 	{
72 		guictx = new GuiContext(&debugText);
73 
74 		resourceManager = new ResourceManager(BUILD_TO_ROOT_PATH~"res");
75 
76 		graphicsRes = new GraphicsResources;
77 		graphicsRes.guictx = guictx;
78 
79 		registerResourceManager(graphicsRes);
80 
81 		guictx.style.iconMap = resourceManager.loadNamedSpriteSheet("icons", resourceManager.texAtlas, ivec2(16, 16));
82 		guictx.style.iconPlaceholder = guictx.style.iconMap["no-icon"];
83 	}
84 
85 	override void registerResources(IResourceManagerRegistry resmanRegistry)
86 	{
87 		auto keyBindingMan = resmanRegistry.getResourceManager!KeyBindingManager;
88 		keyBindingMan.registerKeyBinding(new KeyBinding(KeyCode.KEY_F8, "key.showDebug", null, (s){showDebugInfo.toggle_bool;}));
89 
90 		auto config = resmanRegistry.getResourceManager!ConfigManager;
91 		cameraSensivity = config.registerOption!double("camera_sensivity", 0.4);
92 		cameraFov = config.registerOption!double("camera_fov", 60.0);
93 		resolution = config.registerOption!(int[])("resolution", [1280, 720]);
94 		gl_debug_context = config.registerOption!bool("gl_debug_context", false);
95 		vsync = config.registerOption!bool("vsync", true);
96 	}
97 
98 	override void preInit()
99 	{
100 		import voxelman.graphics.gl;
101 
102 		bool debugCtx = gl_debug_context.get!bool;
103 
104 		loadOpenGL();
105 
106 		window = new GlfwWindow();
107 		WindowParams windowParams;
108 		windowParams.size = ivec2(resolution.get!(int[]));
109 		windowParams.title = "Voxelman client";
110 		windowParams.openglDebugContext = debugCtx;
111 		window.init(windowParams);
112 		window.setVsync(vsync.get!bool);
113 
114 		reloadOpenGL();
115 
116 		if (debugCtx) setupGLDebugLogging();
117 
118 		// Bind events
119 		window.windowResized.connect(&windowResized);
120 		window.closePressed.connect(&closePressed);
121 
122 		window.mousePressed.connect(&guictx.pointerPressed);
123 		window.mouseReleased.connect(&guictx.pointerReleased);
124 		window.mouseMoved.connect(&guictx.pointerMoved);
125 		window.wheelScrolled.connect(&guictx.onScroll);
126 		window.keyPressed.connect(&guictx.onKeyPress);
127 		window.keyReleased.connect(&guictx.onKeyRelease);
128 		window.charEntered.connect(&guictx.onCharEnter);
129 		guictx.state.setClipboard = &window.clipboardString;
130 		guictx.state.getClipboard = &window.clipboardString;
131 
132 		renderer = new OglRenderer(window);
133 		renderQueue = new RenderQueue(resourceManager, renderer);
134 
135 		guictx.pointerMoved(window.mousePosition);
136 		guictx.style.defaultFont = renderQueue.defaultFont;
137 
138 		// Camera
139 		camera.move(vec3(0, 0, 0));
140 		camera.sensivity = cameraSensivity.get!float;
141 		camera.fov = cameraFov.get!float;
142 	}
143 
144 	override void init(IPluginManager pluginman)
145 	{
146 		evDispatcher = pluginman.getPlugin!EventDispatcherPlugin;
147 		auto gui = pluginman.getPlugin!GuiPlugin;
148 
149 		// events
150 		evDispatcher.subscribeToEvent(&onPreUpdateEvent);
151 		evDispatcher.subscribeToEvent(&onUpdateEvent);
152 		evDispatcher.subscribeToEvent(&onPostUpdateEvent);
153 		evDispatcher.subscribeToEvent(&draw);
154 		evDispatcher.subscribeToEvent(&onGameStopEvent);
155 
156 		// graphics
157 		glGenVertexArrays(1, &vao);
158 		glGenBuffers( 1, &vbo);
159 
160 		// - Setup shaders
161 		solidShader3d.compile(renderer);
162 		transparentShader3d.compile(renderer);
163 		solidShader2d.compile(renderer);
164 
165 		ortho_projection.arrayof =
166 		[
167 			 2f/1, 0f,   0f, 0f,
168 			 0f,  -2f/1, 0f, 0f,
169 			 0f,   0f,  -1f, 0f,
170 			-1f,   1f,   0f, 1f,
171 		];
172 	}
173 
174 	override void postInit()
175 	{
176 		renderQueue.reuploadTexture();
177 		renderer.setClearColor(165,211,238);
178 		camera.aspect = cast(float)renderer.framebufferSize.x/renderer.framebufferSize.y;
179 		updateOrtoMatrix();
180 	}
181 
182 	private void windowResized(ivec2 newSize)
183 	{
184 		camera.aspect = cast(float)newSize.x/newSize.y;
185 		updateOrtoMatrix();
186 		evDispatcher.postEvent(WindowResizedEvent(newSize));
187 	}
188 
189 	private void closePressed()
190 	{
191 		evDispatcher.postEvent(ClosePressedEvent());
192 	}
193 
194 	private void onGameStopEvent(ref GameStopEvent stopEvent)
195 	{
196 		window.releaseWindow;
197 	}
198 
199 	void resetCamera()
200 	{
201 		camera.position = vec3(0,0,0);
202 		camera.target = vec3(0,0,1);
203 		camera.heading = vec2(0, 0);
204 		camera.update();
205 	}
206 
207 	private void onPreUpdateEvent(ref PreUpdateEvent event)
208 	{
209 		window.processEvents();
210 		renderQueue.beginFrame();
211 	}
212 
213 	private void onUpdateEvent(ref UpdateEvent event)
214 	{
215 		guictx.state.canvasSize = renderer.framebufferSize;
216 		guictx.update(event.deltaTime, renderQueue);
217 		window.setCursorIcon(guictx.state.cursorIcon);
218 	}
219 
220 	private void onPostUpdateEvent(ref PostUpdateEvent event)
221 	{
222 		//if (showDebugInfo)
223 			drawDebugText();
224 		debugText.clear();
225 		renderQueue.endFrame();
226 	}
227 
228 	private void draw(ref RenderEvent event)
229 	{
230 		checkgl!glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);
231 		renderer.depthTest(true);
232 
233 		// 3d solid
234 		evDispatcher.postEvent(RenderSolid3dEvent(renderer));
235 
236 		// 3d transparent
237 		renderer.depthTest(false);
238 		renderer.alphaBlending(true);
239 
240 		evDispatcher.postEvent(RenderTransparent3dEvent(renderer));
241 
242 		// debug drawings
243 		transparentShader3d.bind;
244 		transparentShader3d.setMVP(Matrix4f.identity, camera.cameraMatrix, camera.perspective);
245 		transparentShader3d.setTransparency(0.3f);
246 
247 		drawBuffer(transparentBuffer.data, GL_TRIANGLES);
248 		transparentShader3d.unbind;
249 		transparentBuffer.clear();
250 
251 		draw(debugBatch);
252 		debugBatch.reset();
253 
254 		evDispatcher.postEvent(Render2Event(renderer));
255 
256 		draw(overlayBatch);
257 		overlayBatch.reset();
258 
259 		renderer.depthTest(true);
260 		//checkgl!glClear(GL_DEPTH_BUFFER_BIT);
261 		renderQueue.drawFrame();
262 
263 		evDispatcher.postEvent(Render3Event(renderer));
264 
265 		renderer.alphaBlending(false);
266 		renderer.flush();
267 	}
268 
269 	int overlayDepth = 1000;
270 	void drawDebugText()
271 	{
272 		auto pos = vec2(renderer.framebufferSize.x, 0);
273 
274 		auto mesherParams = renderQueue.startTextAt(pos + vec2(-5,5));
275 		mesherParams.depth = overlayDepth;
276 		mesherParams.color = cast(Color4ub)Colors.white;
277 
278 		//info(debugText.lines.data);
279 		mesherParams.meshText(debugText.lines.data);
280 		mesherParams.alignMeshedText(Alignment.max);
281 	}
282 
283 	void draw(Batch batch, Matrix4f modelMatrix = Matrix4f.identity)
284 	{
285 		solidShader3d.bind;
286 		solidShader3d.setMVP(modelMatrix, camera.cameraMatrix, camera.perspective);
287 
288 		drawBuffer(batch.triBuffer.data, GL_TRIANGLES);
289 		drawBuffer(batch.lineBuffer.data, GL_LINES);
290 		drawBuffer(batch.pointBuffer.data, GL_POINTS);
291 
292 		solidShader3d.unbind;
293 	}
294 
295 	void draw(ref Armature armature, Matrix4f modelMatrix = Matrix4f.identity)
296 	{
297 		solidShader3d.bind;
298 		solidShader3d.setMVP(modelMatrix, camera.cameraMatrix, camera.perspective);
299 
300 		drawBone(armature.root, modelMatrix);
301 
302 		solidShader3d.unbind;
303 	}
304 
305 	private void drawBone(ref Armature.Bone bone, Matrix4f model)
306 	{
307 		model = model * bone.transform;
308 		solidShader3d.setModel(model);
309 
310 		drawBuffer(bone.mesh.triBuffer.data, GL_TRIANGLES);
311 		drawBuffer(bone.mesh.lineBuffer.data, GL_LINES);
312 		drawBuffer(bone.mesh.pointBuffer.data, GL_POINTS);
313 
314 		foreach (child; bone.children.data)
315 			drawBone(child, model);
316 	}
317 
318 	void draw(Batch2d batch)
319 	{
320 		solidShader2d.bind;
321 		solidShader2d.setProjection(ortho_projection);
322 
323 		drawBuffer(batch.triBuffer.data, GL_TRIANGLES);
324 		drawBuffer(batch.lineBuffer.data, GL_LINES);
325 		drawBuffer(batch.pointBuffer.data, GL_POINTS);
326 
327 		solidShader2d.unbind;
328 	}
329 
330 	void drawBuffer3d(VertexType)(VertexType[] buffer, uint mode)
331 	{
332 		if (buffer.length == 0) return;
333 		solidShader3d.bind;
334 		solidShader3d.setMVP(Matrix4f.identity, camera.cameraMatrix, camera.perspective);
335 
336 		drawBuffer(buffer, mode);
337 
338 		solidShader3d.unbind;
339 	}
340 
341 private:
342 
343 	void updateOrtoMatrix()
344 	{
345 		renderer.setViewport(ivec2(0, 0), renderer.framebufferSize);
346 		ortho_projection.a11 =  2f/renderer.framebufferSize.x;
347 		ortho_projection.a22 = -2f/renderer.framebufferSize.y;
348 	}
349 
350 	void drawBuffer(VertexType)(VertexType[] buffer, uint mode)
351 	{
352 		if (buffer.length == 0) return;
353 		checkgl!glBindVertexArray(vao);
354 		checkgl!glBindBuffer(GL_ARRAY_BUFFER, vbo);
355 		checkgl!glBufferData(GL_ARRAY_BUFFER, buffer.length*VertexType.sizeof, buffer.ptr, GL_DYNAMIC_DRAW);
356 		VertexType.setAttributes();
357 		checkgl!glBindBuffer(GL_ARRAY_BUFFER, 0);
358 		checkgl!glDrawArrays(mode, 0, cast(uint)(buffer.length));
359 		checkgl!glBindVertexArray(0);
360 	}
361 }