1 /*
2 Minetest
3 Copyright (C) 2010-2013 celeron55, Perttu Ahola <celeron55@gmail.com>
4 Copyright (C) 2017 nerzhul, Loic Blot <loic.blot@unix-experience.fr>
5 
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU Lesser General Public License as published by
8 the Free Software Foundation; either version 2.1 of the License, or
9 (at your option) any later version.
10 
11 This program 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
14 GNU Lesser General Public License for more details.
15 
16 You should have received a copy of the GNU Lesser General Public License along
17 with this program; if not, write to the Free Software Foundation, Inc.,
18 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19 */
20 
21 #include <IrrlichtDevice.h>
22 #include <irrlicht.h>
23 #include "fontengine.h"
24 #include "client.h"
25 #include "clouds.h"
26 #include "util/numeric.h"
27 #include "guiscalingfilter.h"
28 #include "localplayer.h"
29 #include "client/hud.h"
30 #include "camera.h"
31 #include "minimap.h"
32 #include "clientmap.h"
33 #include "renderingengine.h"
34 #include "render/core.h"
35 #include "render/factory.h"
36 #include "inputhandler.h"
37 #include "gettext.h"
38 #include "../gui/guiSkin.h"
39 
40 #if !defined(_WIN32) && !defined(__APPLE__) && !defined(__ANDROID__) && \
41 		!defined(SERVER) && !defined(__HAIKU__)
42 #define XORG_USED
43 #endif
44 #ifdef XORG_USED
45 #include <X11/Xlib.h>
46 #include <X11/Xutil.h>
47 #include <X11/Xatom.h>
48 #endif
49 
50 #ifdef _WIN32
51 #include <windows.h>
52 #include <winuser.h>
53 #endif
54 
55 #if ENABLE_GLES
56 #include "filesys.h"
57 #endif
58 
59 RenderingEngine *RenderingEngine::s_singleton = nullptr;
60 
61 
createSkin(gui::IGUIEnvironment * environment,gui::EGUI_SKIN_TYPE type,video::IVideoDriver * driver)62 static gui::GUISkin *createSkin(gui::IGUIEnvironment *environment,
63 		gui::EGUI_SKIN_TYPE type, video::IVideoDriver *driver)
64 {
65 	gui::GUISkin *skin = new gui::GUISkin(type, driver);
66 
67 	gui::IGUIFont *builtinfont = environment->getBuiltInFont();
68 	gui::IGUIFontBitmap *bitfont = nullptr;
69 	if (builtinfont && builtinfont->getType() == gui::EGFT_BITMAP)
70 		bitfont = (gui::IGUIFontBitmap*)builtinfont;
71 
72 	gui::IGUISpriteBank *bank = 0;
73 	skin->setFont(builtinfont);
74 
75 	if (bitfont)
76 		bank = bitfont->getSpriteBank();
77 
78 	skin->setSpriteBank(bank);
79 
80 	return skin;
81 }
82 
83 
RenderingEngine(IEventReceiver * receiver)84 RenderingEngine::RenderingEngine(IEventReceiver *receiver)
85 {
86 	sanity_check(!s_singleton);
87 
88 	// Resolution selection
89 	bool fullscreen = g_settings->getBool("fullscreen");
90 	u16 screen_w = g_settings->getU16("screen_w");
91 	u16 screen_h = g_settings->getU16("screen_h");
92 
93 	// bpp, fsaa, vsync
94 	bool vsync = g_settings->getBool("vsync");
95 	u16 bits = g_settings->getU16("fullscreen_bpp");
96 	u16 fsaa = g_settings->getU16("fsaa");
97 
98 	// stereo buffer required for pageflip stereo
99 	bool stereo_buffer = g_settings->get("3d_mode") == "pageflip";
100 
101 	// Determine driver
102 	video::E_DRIVER_TYPE driverType = video::EDT_OPENGL;
103 	const std::string &driverstring = g_settings->get("video_driver");
104 	std::vector<video::E_DRIVER_TYPE> drivers =
105 			RenderingEngine::getSupportedVideoDrivers();
106 	u32 i;
107 	for (i = 0; i != drivers.size(); i++) {
108 		if (!strcasecmp(driverstring.c_str(),
109 				RenderingEngine::getVideoDriverName(drivers[i]))) {
110 			driverType = drivers[i];
111 			break;
112 		}
113 	}
114 	if (i == drivers.size()) {
115 		errorstream << "Invalid video_driver specified; "
116 			       "defaulting to opengl"
117 			    << std::endl;
118 	}
119 
120 	SIrrlichtCreationParameters params = SIrrlichtCreationParameters();
121 	params.DriverType = driverType;
122 	params.WindowSize = core::dimension2d<u32>(screen_w, screen_h);
123 	params.Bits = bits;
124 	params.AntiAlias = fsaa;
125 	params.Fullscreen = fullscreen;
126 	params.Stencilbuffer = false;
127 	params.Stereobuffer = stereo_buffer;
128 	params.Vsync = vsync;
129 	params.EventReceiver = receiver;
130 	params.HighPrecisionFPU = g_settings->getBool("high_precision_fpu");
131 	params.ZBufferBits = 24;
132 #ifdef __ANDROID__
133 	params.PrivateData = porting::app_global;
134 #endif
135 #if ENABLE_GLES
136 	// there is no standardized path for these on desktop
137 	std::string rel_path = std::string("client") + DIR_DELIM
138 			+ "shaders" + DIR_DELIM + "Irrlicht";
139 	params.OGLES2ShaderPath = (porting::path_share + DIR_DELIM + rel_path + DIR_DELIM).c_str();
140 #endif
141 
142 	m_device = createDeviceEx(params);
143 	driver = m_device->getVideoDriver();
144 
145 	s_singleton = this;
146 
147 	auto skin = createSkin(m_device->getGUIEnvironment(),
148 			gui::EGST_WINDOWS_METALLIC, driver);
149 	m_device->getGUIEnvironment()->setSkin(skin);
150 	skin->drop();
151 }
152 
~RenderingEngine()153 RenderingEngine::~RenderingEngine()
154 {
155 	core.reset();
156 	m_device->closeDevice();
157 	s_singleton = nullptr;
158 }
159 
getWindowSize() const160 v2u32 RenderingEngine::getWindowSize() const
161 {
162 	if (core)
163 		return core->getVirtualSize();
164 	return m_device->getVideoDriver()->getScreenSize();
165 }
166 
setResizable(bool resize)167 void RenderingEngine::setResizable(bool resize)
168 {
169 	m_device->setResizable(resize);
170 }
171 
print_video_modes()172 bool RenderingEngine::print_video_modes()
173 {
174 	IrrlichtDevice *nulldevice;
175 
176 	bool vsync = g_settings->getBool("vsync");
177 	u16 fsaa = g_settings->getU16("fsaa");
178 	MyEventReceiver *receiver = new MyEventReceiver();
179 
180 	SIrrlichtCreationParameters params = SIrrlichtCreationParameters();
181 	params.DriverType = video::EDT_NULL;
182 	params.WindowSize = core::dimension2d<u32>(640, 480);
183 	params.Bits = 24;
184 	params.AntiAlias = fsaa;
185 	params.Fullscreen = false;
186 	params.Stencilbuffer = false;
187 	params.Vsync = vsync;
188 	params.EventReceiver = receiver;
189 	params.HighPrecisionFPU = g_settings->getBool("high_precision_fpu");
190 
191 	nulldevice = createDeviceEx(params);
192 
193 	if (!nulldevice) {
194 		delete receiver;
195 		return false;
196 	}
197 
198 	std::cout << _("Available video modes (WxHxD):") << std::endl;
199 
200 	video::IVideoModeList *videomode_list = nulldevice->getVideoModeList();
201 
202 	if (videomode_list != NULL) {
203 		s32 videomode_count = videomode_list->getVideoModeCount();
204 		core::dimension2d<u32> videomode_res;
205 		s32 videomode_depth;
206 		for (s32 i = 0; i < videomode_count; ++i) {
207 			videomode_res = videomode_list->getVideoModeResolution(i);
208 			videomode_depth = videomode_list->getVideoModeDepth(i);
209 			std::cout << videomode_res.Width << "x" << videomode_res.Height
210 				  << "x" << videomode_depth << std::endl;
211 		}
212 
213 		std::cout << _("Active video mode (WxHxD):") << std::endl;
214 		videomode_res = videomode_list->getDesktopResolution();
215 		videomode_depth = videomode_list->getDesktopDepth();
216 		std::cout << videomode_res.Width << "x" << videomode_res.Height << "x"
217 			  << videomode_depth << std::endl;
218 	}
219 
220 	nulldevice->drop();
221 	delete receiver;
222 
223 	return videomode_list != NULL;
224 }
225 
setupTopLevelWindow(const std::string & name)226 bool RenderingEngine::setupTopLevelWindow(const std::string &name)
227 {
228 	// FIXME: It would make more sense for there to be a switch of some
229 	// sort here that would call the correct toplevel setup methods for
230 	// the environment Minetest is running in.
231 
232 	/* Setting Xorg properties for the top level window */
233 	setupTopLevelXorgWindow(name);
234 
235 	/* Setting general properties for the top level window */
236 	verbosestream << "Client: Configuring general top level"
237 		<< " window properties"
238 		<< std::endl;
239 	bool result = setWindowIcon();
240 
241 	return result;
242 }
243 
setupTopLevelXorgWindow(const std::string & name)244 void RenderingEngine::setupTopLevelXorgWindow(const std::string &name)
245 {
246 #ifdef XORG_USED
247 	const video::SExposedVideoData exposedData = driver->getExposedVideoData();
248 
249 	Display *x11_dpl = reinterpret_cast<Display *>(exposedData.OpenGLLinux.X11Display);
250 	if (x11_dpl == NULL) {
251 		warningstream << "Client: Could not find X11 Display in ExposedVideoData"
252 			<< std::endl;
253 		return;
254 	}
255 
256 	verbosestream << "Client: Configuring X11-specific top level"
257 		<< " window properties"
258 		<< std::endl;
259 
260 
261 	Window x11_win = reinterpret_cast<Window>(exposedData.OpenGLLinux.X11Window);
262 
263 	// Set application name and class hints. For now name and class are the same.
264 	XClassHint *classhint = XAllocClassHint();
265 	classhint->res_name = const_cast<char *>(name.c_str());
266 	classhint->res_class = const_cast<char *>(name.c_str());
267 
268 	XSetClassHint(x11_dpl, x11_win, classhint);
269 	XFree(classhint);
270 
271 	// FIXME: In the future WMNormalHints should be set ... e.g see the
272 	// gtk/gdk code (gdk/x11/gdksurface-x11.c) for the setup_top_level
273 	// method. But for now (as it would require some significant changes)
274 	// leave the code as is.
275 
276 	// The following is borrowed from the above gdk source for setting top
277 	// level windows. The source indicates and the Xlib docs suggest that
278 	// this will set the WM_CLIENT_MACHINE and WM_LOCAL_NAME. This will not
279 	// set the WM_CLIENT_MACHINE to a Fully Qualified Domain Name (FQDN) which is
280 	// required by the Extended Window Manager Hints (EWMH) spec when setting
281 	// the _NET_WM_PID (see further down) but running Minetest in an env
282 	// where the window manager is on another machine from Minetest (therefore
283 	// making the PID useless) is not expected to be a problem. Further
284 	// more, using gtk/gdk as the model it would seem that not using a FQDN is
285 	// not an issue for modern Xorg window managers.
286 
287 	verbosestream << "Client: Setting Xorg window manager Properties"
288 		<< std::endl;
289 
290 	XSetWMProperties (x11_dpl, x11_win, NULL, NULL, NULL, 0, NULL, NULL, NULL);
291 
292 	// Set the _NET_WM_PID window property according to the EWMH spec. _NET_WM_PID
293 	// (in conjunction with WM_CLIENT_MACHINE) can be used by window managers to
294 	// force a shutdown of an application if it doesn't respond to the destroy
295 	// window message.
296 
297 	verbosestream << "Client: Setting Xorg _NET_WM_PID extened window manager property"
298 		<< std::endl;
299 
300 	Atom NET_WM_PID = XInternAtom(x11_dpl, "_NET_WM_PID", false);
301 
302 	pid_t pid = getpid();
303 
304 	XChangeProperty(x11_dpl, x11_win, NET_WM_PID,
305 			XA_CARDINAL, 32, PropModeReplace,
306 			reinterpret_cast<unsigned char *>(&pid),1);
307 
308 	// Set the WM_CLIENT_LEADER window property here. Minetest has only one
309 	// window and that window will always be the leader.
310 
311 	verbosestream << "Client: Setting Xorg WM_CLIENT_LEADER property"
312 		<< std::endl;
313 
314 	Atom WM_CLIENT_LEADER = XInternAtom(x11_dpl, "WM_CLIENT_LEADER", false);
315 
316 	XChangeProperty (x11_dpl, x11_win, WM_CLIENT_LEADER,
317 		XA_WINDOW, 32, PropModeReplace,
318 		reinterpret_cast<unsigned char *>(&x11_win), 1);
319 #endif
320 }
321 
322 #ifdef _WIN32
getWindowHandle(irr::video::IVideoDriver * driver,HWND & hWnd)323 static bool getWindowHandle(irr::video::IVideoDriver *driver, HWND &hWnd)
324 {
325 	const video::SExposedVideoData exposedData = driver->getExposedVideoData();
326 
327 	switch (driver->getDriverType()) {
328 	case video::EDT_DIRECT3D8:
329 		hWnd = reinterpret_cast<HWND>(exposedData.D3D8.HWnd);
330 		break;
331 	case video::EDT_DIRECT3D9:
332 		hWnd = reinterpret_cast<HWND>(exposedData.D3D9.HWnd);
333 		break;
334 	case video::EDT_OPENGL:
335 		hWnd = reinterpret_cast<HWND>(exposedData.OpenGLWin32.HWnd);
336 		break;
337 	default:
338 		return false;
339 	}
340 
341 	return true;
342 }
343 #endif
344 
setWindowIcon()345 bool RenderingEngine::setWindowIcon()
346 {
347 #if defined(XORG_USED)
348 #if RUN_IN_PLACE
349 	return setXorgWindowIconFromPath(
350 			porting::path_share + "/misc/" PROJECT_NAME "-xorg-icon-128.png");
351 #else
352 	// We have semi-support for reading in-place data if we are
353 	// compiled with RUN_IN_PLACE. Don't break with this and
354 	// also try the path_share location.
355 	return setXorgWindowIconFromPath(
356 			       ICON_DIR "/hicolor/128x128/apps/" PROJECT_NAME ".png") ||
357 	       setXorgWindowIconFromPath(porting::path_share + "/misc/" PROJECT_NAME
358 							       "-xorg-icon-128.png");
359 #endif
360 #elif defined(_WIN32)
361 	HWND hWnd; // Window handle
362 	if (!getWindowHandle(driver, hWnd))
363 		return false;
364 
365 	// Load the ICON from resource file
366 	const HICON hicon = LoadIcon(GetModuleHandle(NULL),
367 			MAKEINTRESOURCE(130) // The ID of the ICON defined in
368 					     // winresource.rc
369 	);
370 
371 	if (hicon) {
372 		SendMessage(hWnd, WM_SETICON, ICON_BIG, reinterpret_cast<LPARAM>(hicon));
373 		SendMessage(hWnd, WM_SETICON, ICON_SMALL,
374 				reinterpret_cast<LPARAM>(hicon));
375 		return true;
376 	}
377 	return false;
378 #else
379 	return false;
380 #endif
381 }
382 
setXorgWindowIconFromPath(const std::string & icon_file)383 bool RenderingEngine::setXorgWindowIconFromPath(const std::string &icon_file)
384 {
385 #ifdef XORG_USED
386 
387 	video::IImageLoader *image_loader = NULL;
388 	u32 cnt = driver->getImageLoaderCount();
389 	for (u32 i = 0; i < cnt; i++) {
390 		if (driver->getImageLoader(i)->isALoadableFileExtension(
391 				    icon_file.c_str())) {
392 			image_loader = driver->getImageLoader(i);
393 			break;
394 		}
395 	}
396 
397 	if (!image_loader) {
398 		warningstream << "Could not find image loader for file '" << icon_file
399 			      << "'" << std::endl;
400 		return false;
401 	}
402 
403 	io::IReadFile *icon_f =
404 			m_device->getFileSystem()->createAndOpenFile(icon_file.c_str());
405 
406 	if (!icon_f) {
407 		warningstream << "Could not load icon file '" << icon_file << "'"
408 			      << std::endl;
409 		return false;
410 	}
411 
412 	video::IImage *img = image_loader->loadImage(icon_f);
413 
414 	if (!img) {
415 		warningstream << "Could not load icon file '" << icon_file << "'"
416 			      << std::endl;
417 		icon_f->drop();
418 		return false;
419 	}
420 
421 	u32 height = img->getDimension().Height;
422 	u32 width = img->getDimension().Width;
423 
424 	size_t icon_buffer_len = 2 + height * width;
425 	long *icon_buffer = new long[icon_buffer_len];
426 
427 	icon_buffer[0] = width;
428 	icon_buffer[1] = height;
429 
430 	for (u32 x = 0; x < width; x++) {
431 		for (u32 y = 0; y < height; y++) {
432 			video::SColor col = img->getPixel(x, y);
433 			long pixel_val = 0;
434 			pixel_val |= (u8)col.getAlpha() << 24;
435 			pixel_val |= (u8)col.getRed() << 16;
436 			pixel_val |= (u8)col.getGreen() << 8;
437 			pixel_val |= (u8)col.getBlue();
438 			icon_buffer[2 + x + y * width] = pixel_val;
439 		}
440 	}
441 
442 	img->drop();
443 	icon_f->drop();
444 
445 	const video::SExposedVideoData &video_data = driver->getExposedVideoData();
446 
447 	Display *x11_dpl = (Display *)video_data.OpenGLLinux.X11Display;
448 
449 	if (x11_dpl == NULL) {
450 		warningstream << "Could not find x11 display for setting its icon."
451 			      << std::endl;
452 		delete[] icon_buffer;
453 		return false;
454 	}
455 
456 	Window x11_win = (Window)video_data.OpenGLLinux.X11Window;
457 
458 	Atom net_wm_icon = XInternAtom(x11_dpl, "_NET_WM_ICON", False);
459 	Atom cardinal = XInternAtom(x11_dpl, "CARDINAL", False);
460 	XChangeProperty(x11_dpl, x11_win, net_wm_icon, cardinal, 32, PropModeReplace,
461 			(const unsigned char *)icon_buffer, icon_buffer_len);
462 
463 	delete[] icon_buffer;
464 
465 #endif
466 	return true;
467 }
468 
469 /*
470 	Draws a screen with a single text on it.
471 	Text will be removed when the screen is drawn the next time.
472 	Additionally, a progressbar can be drawn when percent is set between 0 and 100.
473 */
_draw_load_screen(const std::wstring & text,gui::IGUIEnvironment * guienv,ITextureSource * tsrc,float dtime,int percent,bool clouds)474 void RenderingEngine::_draw_load_screen(const std::wstring &text,
475 		gui::IGUIEnvironment *guienv, ITextureSource *tsrc, float dtime,
476 		int percent, bool clouds)
477 {
478 	v2u32 screensize = RenderingEngine::get_instance()->getWindowSize();
479 
480 	v2s32 textsize(g_fontengine->getTextWidth(text), g_fontengine->getLineHeight());
481 	v2s32 center(screensize.X / 2, screensize.Y / 2);
482 	core::rect<s32> textrect(center - textsize / 2, center + textsize / 2);
483 
484 	gui::IGUIStaticText *guitext =
485 			guienv->addStaticText(text.c_str(), textrect, false, false);
486 	guitext->setTextAlignment(gui::EGUIA_CENTER, gui::EGUIA_UPPERLEFT);
487 
488 	bool cloud_menu_background = clouds && g_settings->getBool("menu_clouds");
489 	if (cloud_menu_background) {
490 		g_menuclouds->step(dtime * 3);
491 		g_menuclouds->render();
492 		get_video_driver()->beginScene(
493 				true, true, video::SColor(255, 140, 186, 250));
494 		g_menucloudsmgr->drawAll();
495 	} else
496 		get_video_driver()->beginScene(true, true, video::SColor(255, 0, 0, 0));
497 
498 	// draw progress bar
499 	if ((percent >= 0) && (percent <= 100)) {
500 		video::ITexture *progress_img = tsrc->getTexture("progress_bar.png");
501 		video::ITexture *progress_img_bg =
502 				tsrc->getTexture("progress_bar_bg.png");
503 
504 		if (progress_img && progress_img_bg) {
505 #ifndef __ANDROID__
506 			const core::dimension2d<u32> &img_size =
507 					progress_img_bg->getSize();
508 			u32 imgW = rangelim(img_size.Width, 200, 600);
509 			u32 imgH = rangelim(img_size.Height, 24, 72);
510 #else
511 			const core::dimension2d<u32> img_size(256, 48);
512 			float imgRatio = (float)img_size.Height / img_size.Width;
513 			u32 imgW = screensize.X / 2.2f;
514 			u32 imgH = floor(imgW * imgRatio);
515 #endif
516 			v2s32 img_pos((screensize.X - imgW) / 2,
517 					(screensize.Y - imgH) / 2);
518 
519 			draw2DImageFilterScaled(get_video_driver(), progress_img_bg,
520 					core::rect<s32>(img_pos.X, img_pos.Y,
521 							img_pos.X + imgW,
522 							img_pos.Y + imgH),
523 					core::rect<s32>(0, 0, img_size.Width,
524 							img_size.Height),
525 					0, 0, true);
526 
527 			draw2DImageFilterScaled(get_video_driver(), progress_img,
528 					core::rect<s32>(img_pos.X, img_pos.Y,
529 							img_pos.X + (percent * imgW) / 100,
530 							img_pos.Y + imgH),
531 					core::rect<s32>(0, 0,
532 							(percent * img_size.Width) / 100,
533 							img_size.Height),
534 					0, 0, true);
535 		}
536 	}
537 
538 	guienv->drawAll();
539 	get_video_driver()->endScene();
540 	guitext->remove();
541 }
542 
543 /*
544 	Draws the menu scene including (optional) cloud background.
545 */
_draw_menu_scene(gui::IGUIEnvironment * guienv,float dtime,bool clouds)546 void RenderingEngine::_draw_menu_scene(gui::IGUIEnvironment *guienv,
547 		float dtime, bool clouds)
548 {
549 	bool cloud_menu_background = clouds && g_settings->getBool("menu_clouds");
550 	if (cloud_menu_background) {
551 		g_menuclouds->step(dtime * 3);
552 		g_menuclouds->render();
553 		get_video_driver()->beginScene(
554 				true, true, video::SColor(255, 140, 186, 250));
555 		g_menucloudsmgr->drawAll();
556 	} else
557 		get_video_driver()->beginScene(true, true, video::SColor(255, 0, 0, 0));
558 
559 	guienv->drawAll();
560 	get_video_driver()->endScene();
561 }
562 
getSupportedVideoModes()563 std::vector<core::vector3d<u32>> RenderingEngine::getSupportedVideoModes()
564 {
565 	IrrlichtDevice *nulldevice = createDevice(video::EDT_NULL);
566 	sanity_check(nulldevice);
567 
568 	std::vector<core::vector3d<u32>> mlist;
569 	video::IVideoModeList *modelist = nulldevice->getVideoModeList();
570 
571 	s32 num_modes = modelist->getVideoModeCount();
572 	for (s32 i = 0; i != num_modes; i++) {
573 		core::dimension2d<u32> mode_res = modelist->getVideoModeResolution(i);
574 		u32 mode_depth = (u32)modelist->getVideoModeDepth(i);
575 		mlist.emplace_back(mode_res.Width, mode_res.Height, mode_depth);
576 	}
577 
578 	nulldevice->drop();
579 	return mlist;
580 }
581 
getSupportedVideoDrivers()582 std::vector<irr::video::E_DRIVER_TYPE> RenderingEngine::getSupportedVideoDrivers()
583 {
584 	std::vector<irr::video::E_DRIVER_TYPE> drivers;
585 
586 	for (int i = 0; i != irr::video::EDT_COUNT; i++) {
587 		if (irr::IrrlichtDevice::isDriverSupported((irr::video::E_DRIVER_TYPE)i))
588 			drivers.push_back((irr::video::E_DRIVER_TYPE)i);
589 	}
590 
591 	return drivers;
592 }
593 
_initialize(Client * client,Hud * hud)594 void RenderingEngine::_initialize(Client *client, Hud *hud)
595 {
596 	const std::string &draw_mode = g_settings->get("3d_mode");
597 	core.reset(createRenderingCore(draw_mode, m_device, client, hud));
598 	core->initialize();
599 }
600 
_finalize()601 void RenderingEngine::_finalize()
602 {
603 	core.reset();
604 }
605 
_draw_scene(video::SColor skycolor,bool show_hud,bool show_minimap,bool draw_wield_tool,bool draw_crosshair)606 void RenderingEngine::_draw_scene(video::SColor skycolor, bool show_hud,
607 		bool show_minimap, bool draw_wield_tool, bool draw_crosshair)
608 {
609 	core->draw(skycolor, show_hud, show_minimap, draw_wield_tool, draw_crosshair);
610 }
611 
getVideoDriverName(irr::video::E_DRIVER_TYPE type)612 const char *RenderingEngine::getVideoDriverName(irr::video::E_DRIVER_TYPE type)
613 {
614 	static const char *driver_ids[] = {
615 			"null",
616 			"software",
617 			"burningsvideo",
618 			"direct3d8",
619 			"direct3d9",
620 			"opengl",
621 			"ogles1",
622 			"ogles2",
623 	};
624 
625 	return driver_ids[type];
626 }
627 
getVideoDriverFriendlyName(irr::video::E_DRIVER_TYPE type)628 const char *RenderingEngine::getVideoDriverFriendlyName(irr::video::E_DRIVER_TYPE type)
629 {
630 	static const char *driver_names[] = {
631 			"NULL Driver",
632 			"Software Renderer",
633 			"Burning's Video",
634 			"Direct3D 8",
635 			"Direct3D 9",
636 			"OpenGL",
637 			"OpenGL ES1",
638 			"OpenGL ES2",
639 	};
640 
641 	return driver_names[type];
642 }
643 
644 #ifndef __ANDROID__
645 #if defined(XORG_USED)
646 
calcDisplayDensity()647 static float calcDisplayDensity()
648 {
649 	const char *current_display = getenv("DISPLAY");
650 
651 	if (current_display != NULL) {
652 		Display *x11display = XOpenDisplay(current_display);
653 
654 		if (x11display != NULL) {
655 			/* try x direct */
656 			int dh = DisplayHeight(x11display, 0);
657 			int dw = DisplayWidth(x11display, 0);
658 			int dh_mm = DisplayHeightMM(x11display, 0);
659 			int dw_mm = DisplayWidthMM(x11display, 0);
660 			XCloseDisplay(x11display);
661 
662 			if (dh_mm != 0 && dw_mm != 0) {
663 				float dpi_height = floor(dh / (dh_mm * 0.039370) + 0.5);
664 				float dpi_width = floor(dw / (dw_mm * 0.039370) + 0.5);
665 				return std::max(dpi_height, dpi_width) / 96.0;
666 			}
667 		}
668 	}
669 
670 	/* return manually specified dpi */
671 	return g_settings->getFloat("screen_dpi") / 96.0;
672 }
673 
getDisplayDensity()674 float RenderingEngine::getDisplayDensity()
675 {
676 	static float cached_display_density = calcDisplayDensity();
677 	return cached_display_density;
678 }
679 
680 #elif defined(_WIN32)
681 
682 
calcDisplayDensity(irr::video::IVideoDriver * driver)683 static float calcDisplayDensity(irr::video::IVideoDriver *driver)
684 {
685 	HWND hWnd;
686 	if (getWindowHandle(driver, hWnd)) {
687 		HDC hdc = GetDC(hWnd);
688 		float dpi = GetDeviceCaps(hdc, LOGPIXELSX);
689 		ReleaseDC(hWnd, hdc);
690 		return dpi / 96.0f;
691 	}
692 
693 	/* return manually specified dpi */
694 	return g_settings->getFloat("screen_dpi") / 96.0f;
695 }
696 
getDisplayDensity()697 float RenderingEngine::getDisplayDensity()
698 {
699 	static bool cached = false;
700 	static float display_density;
701 	if (!cached) {
702 		display_density = calcDisplayDensity(get_video_driver());
703 		cached = true;
704 	}
705 	return display_density;
706 }
707 
708 #else
709 
getDisplayDensity()710 float RenderingEngine::getDisplayDensity()
711 {
712 	return g_settings->getFloat("screen_dpi") / 96.0;
713 }
714 
715 #endif
716 
getDisplaySize()717 v2u32 RenderingEngine::getDisplaySize()
718 {
719 	IrrlichtDevice *nulldevice = createDevice(video::EDT_NULL);
720 
721 	core::dimension2d<u32> deskres =
722 			nulldevice->getVideoModeList()->getDesktopResolution();
723 	nulldevice->drop();
724 
725 	return deskres;
726 }
727 
728 #else // __ANDROID__
getDisplayDensity()729 float RenderingEngine::getDisplayDensity()
730 {
731 	return porting::getDisplayDensity();
732 }
733 
getDisplaySize()734 v2u32 RenderingEngine::getDisplaySize()
735 {
736 	return porting::getDisplaySize();
737 }
738 #endif // __ANDROID__
739