1 /* 2 Copyright (c) 2017-2018 Timur Gafarov 3 4 Boost Software License - Version 1.0 - August 17th, 2003 5 Permission is hereby granted, free of charge, to any person or organization 6 obtaining a copy of the software and accompanying documentation covered by 7 this license (the "Software") to use, reproduce, display, distribute, 8 execute, and transmit the Software, and to prepare derivative works of the 9 Software, and to permit third-parties to whom the Software is furnished to 10 do so, all subject to the following: 11 12 The copyright notices in the Software and this entire statement, including 13 the above license grant, this restriction and the following disclaimer, 14 must be included in all copies of the Software, in whole or in part, and 15 all derivative works of the Software, unless such copies or derivative 16 works are solely in the form of machine-executable object code generated by 17 a source language processor. 18 19 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT 22 SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE 23 FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, 24 ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 25 DEALINGS IN THE SOFTWARE. 26 */ 27 28 /++ 29 Base class to inherit Dagon applications from. 30 +/ 31 module dagon.core.application; 32 33 import std.stdio; 34 import std.conv; 35 import std.getopt; 36 import std.string; 37 import std.file; 38 import core.stdc.stdlib; 39 40 import dlib.core.memory; 41 import dagon.core.libs; 42 import dagon.core.event; 43 44 void exitWithError(string message) 45 { 46 writeln(message); 47 core.stdc.stdlib.exit(1); 48 } 49 50 enum DagonEvent 51 { 52 Exit = -1 53 } 54 55 enum string[GLenum] GLErrorStrings = [ 56 GL_NO_ERROR: "GL_NO_ERROR", 57 GL_INVALID_ENUM: "GL_INVALID_ENUM", 58 GL_INVALID_VALUE: "GL_INVALID_VALUE", 59 GL_INVALID_OPERATION: "GL_INVALID_OPERATION", 60 GL_INVALID_FRAMEBUFFER_OPERATION: "GL_INVALID_FRAMEBUFFER_OPERATION", 61 GL_OUT_OF_MEMORY: "GL_OUT_OF_MEMORY" 62 ]; 63 64 extern(System) nothrow void messageCallback( 65 GLenum source, 66 GLenum type, 67 GLuint id, 68 GLenum severity, 69 GLsizei length, 70 const GLchar* message, 71 const GLvoid* userParam) 72 { 73 string msg = "%stype = 0x%x, severity = 0x%x, message = %s\n"; 74 string err = "OpenGL error: "; 75 string empty = ""; 76 if (severity != GL_DEBUG_SEVERITY_NOTIFICATION) 77 printf(msg.ptr, (type == GL_DEBUG_TYPE_ERROR ? err.ptr : empty.ptr), type, severity, message); 78 } 79 80 /++ 81 Base class to inherit Dagon applications from. 82 `Application` wraps SDL2 window, loads dynamic link libraries using Derelict, 83 is responsible for initializing OpenGL context and doing main game loop. 84 +/ 85 class Application: EventListener 86 { 87 uint width; 88 uint height; 89 SDL_Window* window = null; 90 SDL_GLContext glcontext; 91 string libdir; 92 93 private EventManager _eventManager; 94 95 /++ 96 Constructor. 97 * `winWidth` - window width 98 * `winHeight` - window height 99 * `fullscreen` - if true, the application will run in fullscreen mode 100 * `windowTitle` - window title 101 * `args` - command line arguments 102 +/ 103 this(uint winWidth, uint winHeight, bool fullscreen, string windowTitle, string[] args) 104 { 105 version(NoFreetype) 106 { 107 } 108 else 109 { 110 FreetypeSupport ftsup = loadFreetype(); 111 if (ftsup != freetypeSupport) 112 { 113 if (ftsup == FreetypeSupport.badLibrary) 114 writeln("Warning: failed to load some Freetype functions. It seems that you have an old version of Freetype. Dagon will try to use it, but it is recommended to install Freetype 2.8.1 or higher"); 115 else 116 exitWithError("Error: Freetype library is not found. Please, install Freetype 2.8.1"); 117 } 118 } 119 120 version(NoNuklear) 121 { 122 } 123 else 124 { 125 NuklearSupport nuksup = loadNuklear(); 126 if (nuksup != NuklearSupport.Nuklear4) 127 { 128 exitWithError("Error: Nuklear library is not found. Please, install Nuklear."); 129 } 130 } 131 132 SDLSupport sdlsup = loadSDL(); 133 if (sdlsup != sdlSupport) 134 { 135 if (sdlsup == SDLSupport.badLibrary) 136 writeln("Warning: failed to load some SDL functions. It seems that you have an old version of SDL. Dagon will try to use it, but it is recommended to install SDL 2.0.5 or higher"); 137 else 138 exitWithError("Error: SDL library is not found. Please, install SDL 2.0.5"); 139 } 140 141 if (SDL_Init(SDL_INIT_EVERYTHING) == -1) 142 exitWithError("Error: failed to init SDL: " ~ to!string(SDL_GetError())); 143 144 width = winWidth; 145 height = winHeight; 146 147 SDL_GL_SetAttribute(SDL_GL_ACCELERATED_VISUAL, 1); 148 149 SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE); 150 SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 4); 151 SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 0); 152 SDL_GL_SetAttribute(SDL_GL_CONTEXT_FLAGS, SDL_GL_CONTEXT_FORWARD_COMPATIBLE_FLAG); 153 SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1); 154 SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24); 155 SDL_GL_SetAttribute(SDL_GL_STENCIL_SIZE, 8); 156 157 window = SDL_CreateWindow(toStringz(windowTitle), 158 SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, width, height, SDL_WINDOW_SHOWN | SDL_WINDOW_OPENGL); 159 if (window is null) 160 exitWithError("Error: failed to create window: " ~ to!string(SDL_GetError())); 161 162 SDL_GL_SetSwapInterval(1); 163 164 glcontext = SDL_GL_CreateContext(window); 165 if (glcontext is null) 166 exitWithError("Error: failed to create OpenGL context: " ~ to!string(SDL_GetError())); 167 168 SDL_GL_MakeCurrent(window, glcontext); 169 170 GLSupport glsup = loadOpenGL(); 171 if (isOpenGLLoaded()) 172 { 173 if (glsup < GLSupport.gl40) 174 { 175 exitWithError("Error: Dagon requires OpenGL 4.0, but it seems that your graphics card does not support it"); 176 } 177 } 178 else 179 { 180 exitWithError("Error: failed to load OpenGL functions. Please, update graphics card driver and make sure it supports OpenGL 4.0"); 181 } 182 183 if (fullscreen) 184 SDL_SetWindowFullscreen(window, SDL_WINDOW_FULLSCREEN); 185 186 _eventManager = New!EventManager(window, width, height); 187 super(_eventManager, null); 188 189 // Initialize OpenGL 190 glClearColor(0.0f, 0.0f, 0.0f, 1.0f); 191 glClearDepth(1.0); 192 glDepthFunc(GL_LESS); 193 glEnable(GL_DEPTH_TEST); 194 glEnable(GL_POLYGON_OFFSET_FILL); 195 glCullFace(GL_BACK); 196 glEnable(GL_TEXTURE_CUBE_MAP_SEAMLESS); 197 198 glClear(GL_COLOR_BUFFER_BIT); 199 SDL_GL_SwapWindow(window); 200 201 debug 202 { 203 if (hasKHRDebug) 204 { 205 glEnable(GL_DEBUG_OUTPUT); 206 glDebugMessageCallback(&messageCallback, null); 207 } 208 else 209 { 210 writeln("GL_KHR_debug is not supported, debug output is not available"); 211 } 212 } 213 } 214 215 override void onUserEvent(int code) 216 { 217 if (code == DagonEvent.Exit) 218 { 219 exit(); 220 } 221 } 222 223 void onUpdate(double dt) 224 { 225 // Override me 226 } 227 228 void onRender() 229 { 230 // Override me 231 } 232 233 void checkGLError() 234 { 235 GLenum error = GL_NO_ERROR; 236 error = glGetError(); 237 if (error != GL_NO_ERROR) 238 { 239 writefln("OpenGL error %s: %s", error, GLErrorStrings[error]); 240 //eventManager.running = false; 241 } 242 } 243 244 void run() 245 { 246 while(eventManager.running) 247 { 248 beginRender(); 249 onUpdate(eventManager.deltaTime); 250 onRender(); 251 endRender(); 252 } 253 } 254 255 void beginRender() 256 { 257 eventManager.update(); 258 processEvents(); 259 } 260 261 void endRender() 262 { 263 debug checkGLError(); 264 SDL_GL_SwapWindow(window); 265 } 266 267 void exit() 268 { 269 eventManager.exit(); 270 } 271 272 ~this() 273 { 274 SDL_GL_DeleteContext(glcontext); 275 SDL_DestroyWindow(window); 276 SDL_Quit(); 277 Delete(_eventManager); 278 } 279 }