1 /***************************************************************************
2  *   Copyright (C) 2005-2019 by the FIFE team                              *
3  *   http://www.fifengine.net                                              *
4  *   This file is part of FIFE.                                            *
5  *                                                                         *
6  *   FIFE is free software; you can redistribute it and/or                 *
7  *   modify it under the terms of the GNU Lesser General Public            *
8  *   License as published by the Free Software Foundation; either          *
9  *   version 2.1 of the License, or (at your option) any later version.    *
10  *                                                                         *
11  *   This library is distributed in the hope that it will be useful,       *
12  *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
13  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU     *
14  *   Lesser General Public License for more details.                       *
15  *                                                                         *
16  *   You should have received a copy of the GNU Lesser General Public      *
17  *   License along with this library; if not, write to the                 *
18  *   Free Software Foundation, Inc.,                                       *
19  *   51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA          *
20  ***************************************************************************/
21 
22 // Standard C++ library includes
23 #include <iostream>
24 #include <algorithm>
25 
26 // 3rd party library includes
27 #include <SDL.h>
28 #include <SDL_ttf.h>
29 
30 // FIFE includes
31 // These includes are split up in two parts, separated by one empty line
32 // First block: files included from the FIFE root src directory
33 // Second block: files included from the same folder
34 #include "util/base/exception.h"
35 #include "util/log/logger.h"
36 #include "util/time/timemanager.h"
37 #include "audio/soundmanager.h"
38 #include "gui/guimanager.h"
39 #include "vfs/vfs.h"
40 #include "vfs/vfsdirectory.h"
41 #include "vfs/directoryprovider.h"
42 #include "vfs/zip/zipprovider.h"
43 #include "eventchannel/eventmanager.h"
44 #include "video/imagemanager.h"
45 #include "video/animationmanager.h"
46 #include "audio/soundclipmanager.h"
47 #include "video/renderbackend.h"
48 #include "video/cursor.h"
49 #include "video/devicecaps.h"
50 #ifdef HAVE_OPENGL
51 #include "video/opengl/fife_opengl.h"
52 #include "video/opengl/renderbackendopengl.h"
53 #endif
54 #include "video/sdl/renderbackendsdl.h"
55 #include "loaders/native/video/imageloader.h"
56 #include "loaders/native/audio/ogg_loader.h"
57 #include "model/model.h"
58 #include "pathfinder/routepather/routepather.h"
59 #include "model/metamodel/grids/hexgrid.h"
60 #include "model/metamodel/grids/squaregrid.h"
61 #include "view/renderers/quadtreerenderer.h"
62 #include "view/renderers/gridrenderer.h"
63 #include "view/renderers/instancerenderer.h"
64 #include "view/renderers/coordinaterenderer.h"
65 #include "view/renderers/floatingtextrenderer.h"
66 #include "view/renderers/cellselectionrenderer.h"
67 #include "view/renderers/blockinginforenderer.h"
68 #include "view/renderers/genericrenderer.h"
69 #include "view/renderers/targetrenderer.h"
70 #include "view/renderers/lightrenderer.h"
71 #include "view/renderers/offrenderer.h"
72 #include "view/renderers/cellrenderer.h"
73 #include "video/image.h"
74 #include "engine.h"
75 #include "version.h"
76 
77 #ifdef USE_COCOA
78 
79 #include <objc/message.h>
80 #include <dlfcn.h>
81 
main(int32_t argc,char ** argv)82 int32_t main(int32_t argc, char **argv)
83 {
84     return 0;
85 }
86 #endif
87 
88 namespace FIFE {
89 	static Logger _log(LM_CONTROLLER);
90 
Engine()91 	Engine::Engine():
92 		m_renderbackend(0),
93 		m_guimanager(0),
94 		m_eventmanager(0),
95 		m_soundmanager(0),
96 		m_timemanager(0),
97 		m_imagemanager(0),
98 		m_animationmanager(0),
99 		m_soundclipmanager(0),
100 		m_vfs(0),
101 		m_model(0),
102 		m_logmanager(0),
103 		m_cursor(0),
104 		m_destroyed(false),
105 		m_settings(),
106 		m_devcaps(),
107 		m_offrenderer(0),
108 		m_targetrenderer(0),
109 		m_changelisteners() {
110 #ifdef USE_COCOA
111 		// The next lines ensure that Cocoa is initialzed correctly.
112 		// This is needed for SDL to function properly on MAC OS X.
113 		void* cocoa_lib;
114 		cocoa_lib = dlopen( "/System/Library/Frameworks/Cocoa.framework/Cocoa", RTLD_LAZY );
115 		void (*nsappload)(void);
116 		nsappload = (void(*)()) dlsym( cocoa_lib, "NSApplicationLoad");
117 		nsappload();
118 
119 		// Create an autorelease pool, so autoreleased SDL objects don't leak.
120 #ifdef OSX_109
121 		Class NSAutoreleasePool = objc_getClass("NSAutoreleasePool");
122 		m_autoreleasePool = class_createInstance(NSAutoreleasePool, 0);
123 #else
124 		objc_object *NSAutoreleasePool = objc_getClass("NSAutoreleasePool");
125 		m_autoreleasePool =
126 			objc_msgSend(NSAutoreleasePool, sel_registerName("new"));
127 #endif
128 #endif
129 		m_logmanager = LogManager::instance();
130 	}
131 
getSettings()132 	EngineSettings& Engine::getSettings() {
133 		return m_settings;
134 	}
135 
getDeviceCaps() const136 	const DeviceCaps& Engine::getDeviceCaps() const {
137 		return m_devcaps;
138 	}
139 
changeScreenMode(const ScreenMode & mode)140 	void Engine::changeScreenMode(const ScreenMode& mode){
141 		m_cursor->invalidate();
142 
143 		m_imagemanager->invalidateAll();
144 
145 		// recreate main screen
146 		m_renderbackend->createMainScreen(mode,	m_settings.getWindowTitle(), m_settings.getWindowIcon());
147 
148 		if (m_guimanager) {
149 			m_guimanager->resizeTopContainer(0,0,mode.getWidth(), mode.getHeight());
150 		}
151 
152 		std::vector<IEngineChangeListener*>::iterator i = m_changelisteners.begin();
153 		while (i != m_changelisteners.end()) {
154 			(*i)->onScreenModeChanged(mode);
155 			++i;
156 		}
157 	}
158 
init()159 	void Engine::init() {
160 		m_destroyed = false;
161 
162 		FL_LOG(_log, LMsg("Fifengine v") << FIFE::getVersion());
163 		FL_LOG(_log, "================== Engine initialize start =================");
164 		m_timemanager = new TimeManager();
165 		FL_LOG(_log, "Time manager created");
166 
167 		FL_LOG(_log, "Creating VFS");
168 		m_vfs = new VFS();
169 
170 		FL_LOG(_log, "Adding root directory to VFS");
171 		m_vfs->addSource( new VFSDirectory(m_vfs) );
172 		m_vfs->addProvider( new DirectoryProvider() );
173 
174 		FL_LOG(_log, "Adding zip provider to VFS");
175 		m_vfs->addProvider( new ZipProvider() );
176 
177 		//m_vfs->addProvider(ProviderDAT2());
178 		//m_vfs->addProvider(ProviderDAT1());
179 		FL_LOG(_log, "Engine pre-init done");
180 
181 		// If failed to init SDL throw exception.
182 		if (SDL_Init(SDL_INIT_NOPARACHUTE | SDL_INIT_TIMER) < 0) {
183 			throw SDLException(SDL_GetError());
184 		}
185 
186 		TTF_Init();
187 
188 		FL_LOG(_log, "Creating event manager");
189 		m_eventmanager = new EventManager();
190 		m_eventmanager->setMouseSensitivity(m_settings.getMouseSensitivity());
191 		m_eventmanager->setMouseAccelerationEnabled(m_settings.isMouseAccelerationEnabled());
192 		m_eventmanager->setJoystickSupport(m_settings.isJoystickSupport());
193 
194 		FL_LOG(_log, "Creating resource managers");
195 
196 		m_imagemanager = new ImageManager();
197 		m_animationmanager = new AnimationManager();
198 		m_soundclipmanager = new SoundClipManager();
199 
200 		FL_LOG(_log, "Creating render backend");
201 		std::string rbackend(m_settings.getRenderBackend());
202 		if (rbackend == "SDL") {
203 			m_renderbackend = new RenderBackendSDL(m_settings.getColorKey());
204 			FL_LOG(_log, "SDL Render backend created");
205 		} else {
206 #ifdef HAVE_OPENGL
207 			m_renderbackend = new RenderBackendOpenGL(m_settings.getColorKey());
208 			FL_LOG(_log, "OpenGL Render backend created");
209 #else
210 			m_renderbackend = new RenderBackendSDL(m_settings.getColorKey());
211 			// Remember  the choice so we pick the right graphics class.
212 			rbackend = "SDL";
213 			FL_WARN(_log, "Tried to select OpenGL, even though it is not compiled into the engine. Falling back to SDL Render backend");
214 #endif
215 		}
216 		FL_LOG(_log, "Initializing render backend");
217 		m_renderbackend->setColorKeyEnabled(m_settings.isColorKeyEnabled());
218 		// we always set this to false
219 		//m_renderbackend->setAlphaOptimizerEnabled(false);
220 		m_renderbackend->setImageCompressingEnabled(m_settings.isGLCompressImages());
221 		m_renderbackend->setFramebufferEnabled(m_settings.isGLUseFramebuffer());
222 		m_renderbackend->setNPOTEnabled(m_settings.isGLUseNPOT());
223 		m_renderbackend->setTextureFiltering(m_settings.getGLTextureFiltering());
224 		m_renderbackend->setMipmappingEnabled(m_settings.isGLUseMipmapping());
225 		m_renderbackend->setMonochromeEnabled(m_settings.isGLUseMonochrome());
226 		m_renderbackend->setDepthBufferEnabled(m_settings.isGLUseDepthBuffer());
227 		m_renderbackend->setAlphaTestValue(m_settings.getGLAlphaTestValue());
228 		m_renderbackend->setVSyncEnabled(m_settings.isVSync());
229 		if (m_settings.isFrameLimitEnabled()) {
230 			m_renderbackend->setFrameLimitEnabled(true);
231 			m_renderbackend->setFrameLimit(m_settings.getFrameLimit());
232 		}
233 
234 		std::string driver = m_settings.getVideoDriver();
235 		if (driver != ""){
236 			std::vector<std::string> drivers = m_devcaps.getAvailableVideoDrivers();
237 			if (std::find (drivers.begin(), drivers.end(), driver) == drivers.end()) {
238 				FL_WARN(_log, "Selected video driver is not supported for your Operating System!  Reverting to default driver.");
239 				driver = "";
240 			}
241 			m_devcaps.setVideoDriverName(driver);
242 		}
243 		// init backend with selected video driver or default
244 		m_renderbackend->init(driver);
245 
246 		// in case of SDL we use this to create the SDL_Renderer
247 		driver = m_settings.getSDLDriver();
248 		if (driver != ""){
249 			std::vector<std::string> drivers = m_devcaps.getAvailableRenderDrivers();
250 			if (std::find (drivers.begin(), drivers.end(), driver) == drivers.end()) {
251 				FL_WARN(_log, "Selected render driver is not supported for your Operating System!  Reverting to default driver.");
252 				driver = "";
253 			}
254 			m_devcaps.setRenderDriverName(driver);
255 		}
256 
257 		FL_LOG(_log, "Querying device capabilities");
258 		m_devcaps.fillDeviceCaps();
259 
260 		uint16_t bpp = m_settings.getBitsPerPixel();
261 
262 		m_screenMode = m_devcaps.getNearestScreenMode(
263 			m_settings.getScreenWidth(),
264 			m_settings.getScreenHeight(),
265 			bpp,
266 			rbackend,
267 			m_settings.isFullScreen(),
268 			m_settings.getRefreshRate(),
269 			m_settings.getDisplay());
270 
271 		FL_LOG(_log, "Creating main screen");
272 		m_renderbackend->createMainScreen(
273 			m_screenMode,
274 			m_settings.getWindowTitle(),
275 			m_settings.getWindowIcon());
276 		FL_LOG(_log, "Main screen created");
277 
278 #ifdef HAVE_OPENGL
279 		if (m_settings.getLightingModel() != 0) {
280 			m_renderbackend->setLightingModel(m_settings.getLightingModel());
281 		}
282 
283 #endif
284 		FL_LOG(_log, "Creating sound manager");
285 		m_soundmanager = new SoundManager();
286 		m_soundmanager->setVolume(static_cast<float>(m_settings.getInitialVolume()) / 10);
287 
288 		FL_LOG(_log, "Creating renderers");
289 		m_offrenderer = new OffRenderer(m_renderbackend);
290 		m_targetrenderer = new TargetRenderer(m_renderbackend);
291 		m_renderers.push_back(new InstanceRenderer(m_renderbackend, 10));
292 		m_renderers.push_back(new GridRenderer(m_renderbackend, 20));
293 		m_renderers.push_back(new CellSelectionRenderer(m_renderbackend, 30));
294 		m_renderers.push_back(new BlockingInfoRenderer(m_renderbackend, 40));
295 		m_renderers.push_back(new FloatingTextRenderer(m_renderbackend, 50));
296 		m_renderers.push_back(new QuadTreeRenderer(m_renderbackend, 60));
297 		m_renderers.push_back(new CoordinateRenderer(m_renderbackend, 70));
298 		m_renderers.push_back(new GenericRenderer(m_renderbackend, 80));
299 		m_renderers.push_back(new LightRenderer(m_renderbackend, 90));
300 		m_renderers.push_back(new CellRenderer(m_renderbackend, 100));
301 
302 		FL_LOG(_log, "Creating model");
303 		m_model = new Model(m_renderbackend, m_renderers);
304 		FL_LOG(_log, "Adding pathers to model");
305 		m_model->adoptPather(new RoutePather());
306 		FL_LOG(_log, "Adding grid prototypes to model");
307 		m_model->adoptCellGrid(new SquareGrid());
308 		m_model->adoptCellGrid(new HexGrid(false));
309 		m_model->adoptCellGrid(new HexGrid(true));
310 
311 		m_cursor = new Cursor(m_renderbackend);
312 		m_cursor->setNativeImageCursorEnabled(m_settings.isNativeImageCursorEnabled());
313 		FL_LOG(_log, "Engine initialized");
314 	}
315 
~Engine()316 	Engine::~Engine() {
317 		if( !m_destroyed ) {
318 			destroy();
319 		}
320 	}
321 
destroy()322 	void Engine::destroy() {
323 		FL_LOG(_log, "Destructing engine");
324  		delete m_cursor;
325 		delete m_model;
326 		delete m_soundmanager;
327 		delete m_guimanager;
328 
329 		delete m_animationmanager;
330 		delete m_imagemanager;
331 		delete m_soundclipmanager;
332 		delete m_eventmanager;
333 
334 		// properly remove all the renderers created during init
335 		delete m_offrenderer;
336 		delete m_targetrenderer;
337 		std::vector<RendererBase*>::iterator rendererIter = m_renderers.begin();
338 		for ( ; rendererIter != m_renderers.end(); ++rendererIter)
339 		{
340 			delete *rendererIter;
341 		}
342 		m_renderers.clear();
343 
344 		delete m_renderbackend;
345 		delete m_vfs;
346 		delete m_timemanager;
347 
348 		TTF_Quit();
349 		SDL_Quit();
350 
351 #ifdef USE_COCOA
352 		objc_msgSend(m_autoreleasePool, sel_registerName("release"));
353 #endif
354 
355 		FL_LOG(_log, "================== Engine destructed ==================");
356 		m_destroyed = true;
357 		//delete m_logmanager;
358 	}
initializePumping()359 	void Engine::initializePumping() {
360 		m_eventmanager->processEvents();
361 	}
362 
pump()363 	void Engine::pump() {
364 		m_renderbackend->startFrame();
365 		m_eventmanager->processEvents();
366 		m_timemanager->update();
367 		m_soundmanager->update();
368 
369 		m_targetrenderer->render();
370 		if (m_model->getActiveCameraCount() == 0) {
371 			m_renderbackend->clearBackBuffer();
372 			m_offrenderer->render();
373 		} else {
374 			m_model->update();
375 		}
376 
377 		if (m_guimanager) {
378 			m_guimanager->turn();
379 		}
380 
381 		m_cursor->draw();
382 		m_renderbackend->endFrame();
383 	}
384 
finalizePumping()385 	void Engine::finalizePumping() {
386 		// nothing here at the moment..
387 	}
388 
addChangeListener(IEngineChangeListener * listener)389 	void Engine::addChangeListener(IEngineChangeListener* listener) {
390 		m_changelisteners.push_back(listener);
391 	}
392 
removeChangeListener(IEngineChangeListener * listener)393 	void Engine::removeChangeListener(IEngineChangeListener* listener) {
394 		std::vector<IEngineChangeListener*>::iterator i = m_changelisteners.begin();
395 		while (i != m_changelisteners.end()) {
396 			if ((*i) == listener) {
397 				m_changelisteners.erase(i);
398 				return;
399 			}
400 			++i;
401 		}
402 	}
403 }
404