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 module dagon.core.application;
29 
30 import std.stdio;
31 import std.conv;
32 import std.getopt;
33 import std..string;
34 import std.file;
35 import core.stdc.stdlib;
36 
37 import derelict.sdl2.sdl;
38 import derelict.opengl;
39 import derelict.freetype.ft;
40 
41 import dagon.core.event;
42 
43 void exitWithError(string message)
44 {
45     writeln(message);
46     core.stdc.stdlib.exit(1);
47 }
48 
49 enum DagonEvent
50 {
51     Exit = -1
52 }
53 
54 class Application: EventListener
55 {
56     uint width;
57     uint height;
58     SDL_Window* window = null;
59     SDL_GLContext glcontext;
60     string libdir;
61     
62     this(uint w, uint h, bool fullscreen, string windowTitle, string[] args)
63     {
64         try
65         { 
66             getopt(
67                 args,
68                 "libdir", &libdir,
69             );
70         }
71         catch(Exception)
72         {
73         }
74 
75         DerelictGL3.load();
76         if (libdir.length)
77         {
78             version(linux)
79             {
80                 DerelictSDL2.load(format("%s/libSDL2-2.0.so", libdir));
81                 DerelictFT.load(format("%s/libfreetype.so", libdir));
82             }
83             version(Windows)
84             {
85                 version(X86)
86                 {
87                     DerelictSDL2.load(format("%s/SDL2.dll", libdir));
88                     DerelictFT.load(format("%s/freetype281.dll", libdir));
89                 }
90                 version(X86_64)
91                 {
92                     DerelictSDL2.load(format("%s/SDL2.dll", libdir));
93                     DerelictFT.load(format("%s/freetype281.dll", libdir));
94                 }
95             }
96         }
97         else
98         {  
99             version(linux)
100             {
101                 version(X86)
102                 {
103                     if (exists("lib/x86/libSDL2-2.0.so"))
104                         DerelictSDL2.load("lib/x86/libSDL2-2.0.so");
105                     else
106                         DerelictSDL2.load();
107 
108                     if (exists("lib/x86/libfreetype.so"))
109                         DerelictFT.load("lib/x86/libfreetype.so");
110                     else
111                         DerelictFT.load();
112                 }
113                 version(X86_64)
114                 {
115                     if (exists("lib/x64/libSDL2-2.0.so"))
116                         DerelictSDL2.load("lib/x64/libSDL2-2.0.so");
117                     else
118                         DerelictSDL2.load();
119 
120                     if (exists("lib/x64/libfreetype.so"))
121                         DerelictFT.load("lib/x64/libfreetype.so");
122                     else
123                         DerelictFT.load();
124                 }
125             }
126             version(Windows)
127             {
128                 version(X86)
129                 {
130                     if (exists("lib/x86/SDL2.dll"))
131                         DerelictSDL2.load("lib/x86/SDL2.dll");
132                     else
133                         DerelictSDL2.load();
134 
135                     if (exists("lib/x86/freetype281.dll"))
136                         DerelictFT.load("lib/x86/freetype281.dll");
137                     else
138                         DerelictFT.load();
139                 }
140                 version(X86_64)
141                 {
142                     if (exists("lib/x64/SDL2.dll"))
143                         DerelictSDL2.load("lib/x64/SDL2.dll");
144                     else
145                         DerelictSDL2.load();
146 
147                     if (exists("lib/x64/freetype281.dll"))
148                         DerelictFT.load("lib/x64/freetype281.dll");
149                     else
150                         DerelictFT.load();
151                 }
152             }
153         }
154         
155         version(FreeBSD)
156         {
157             DerelictSDL2.load();
158             DerelictFT.load();
159         }
160 
161         version(OSX)
162         {
163             DerelictSDL2.load();
164             DerelictFT.load();
165         }
166 
167         if (SDL_Init(SDL_INIT_EVERYTHING) == -1)
168             exitWithError("Failed to init SDL: " ~ to!string(SDL_GetError()));
169             
170         width = w;
171         height = h;
172 
173         SDL_GL_SetAttribute(SDL_GL_ACCELERATED_VISUAL, 1);        
174        
175         SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE);
176         SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 4);
177         SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 0);
178         SDL_GL_SetAttribute(SDL_GL_CONTEXT_FLAGS, SDL_GL_CONTEXT_FORWARD_COMPATIBLE_FLAG);
179         SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
180         SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24);
181         SDL_GL_SetAttribute(SDL_GL_STENCIL_SIZE, 8);
182 
183         window = SDL_CreateWindow(toStringz(windowTitle), SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, width, height, SDL_WINDOW_SHOWN | SDL_WINDOW_OPENGL);
184         if (window is null)
185             exitWithError("Failed to create window: " ~ to!string(SDL_GetError()));
186             
187         SDL_GL_SetSwapInterval(1);
188             
189         glcontext = SDL_GL_CreateContext(window);
190         if (glcontext is null)
191             exitWithError("Failed to create GL context: " ~ to!string(SDL_GetError()));
192             
193         SDL_GL_MakeCurrent(window, glcontext);
194 
195         GLVersion loadedVersion = DerelictGL3.reload();
196         writeln("OpenGL version loaded: ", loadedVersion);
197         if (loadedVersion < GLVersion.gl40)
198         {
199             exitWithError("Sorry, Dagon requires OpenGL 4.0!");
200         }
201         
202         if (fullscreen)
203             SDL_SetWindowFullscreen(window, SDL_WINDOW_FULLSCREEN);
204             
205         EventManager eventManager = new EventManager(window, width, height);
206         super(eventManager, null);
207             
208         // Initialize OpenGL
209         glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
210         glClearDepth(1.0);
211         glDepthFunc(GL_LESS);
212         glEnable(GL_DEPTH_TEST);
213         glEnable(GL_POLYGON_OFFSET_FILL);
214         
215         checkGLError();
216     }
217 
218     override void onUserEvent(int code)
219     {
220         if (code == DagonEvent.Exit)
221         {
222             exit();
223         }
224     }
225     
226     void onUpdate(double dt)
227     {
228         // Override me
229     }
230     
231     void onRender()
232     {
233         // Override me
234     }
235     
236     void checkGLError()
237     {
238         GLenum error = GL_NO_ERROR;
239         error = glGetError();
240         if (error != GL_NO_ERROR)
241         {
242             writeln("OpenGL error: ", error);
243             eventManager.running = false;
244         }
245     }
246     
247     void run()
248     {
249         while(eventManager.running)
250         {
251             beginRender();
252             onUpdate(eventManager.deltaTime);
253             onRender();
254             endRender();
255         }
256     }
257 
258     void beginRender()
259     {
260         eventManager.update();
261         processEvents();
262     }
263 
264     void endRender()
265     {
266         debug checkGLError();            
267         SDL_GL_SwapWindow(window);
268     }
269     
270     void exit()
271     {
272         eventManager.running = false;
273     }
274     
275     ~this()
276     {
277         SDL_GL_DeleteContext(glcontext);
278         SDL_DestroyWindow(window);
279         SDL_Quit();
280     }
281 }