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 }