Add support for interlaced polarized 3d screens
[oweals/minetest.git] / src / drawscene.cpp
1 /*
2 Minetest
3 Copyright (C) 2010-2014 celeron55, Perttu Ahola <celeron55@gmail.com>
4
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU Lesser General Public License as published by
7 the Free Software Foundation; either version 2.1 of the License, or
8 (at your option) any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 GNU Lesser General Public License for more details.
14
15 You should have received a copy of the GNU Lesser General Public License along
16 with this program; if not, write to the Free Software Foundation, Inc.,
17 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 */
19
20 #include "drawscene.h"
21 #include "main.h" // for g_settings
22 #include "settings.h"
23 #include "clouds.h"
24 #include "util/timetaker.h"
25
26 typedef enum {
27         LEFT = -1,
28         RIGHT = 1,
29         EYECOUNT = 2
30 } paralax_sign;
31
32 void draw_selectionbox(video::IVideoDriver* driver, Hud& hud,
33                 std::vector<aabb3f>& hilightboxes, bool show_hud)
34 {
35         if (!show_hud)
36                 return;
37
38         video::SMaterial oldmaterial = driver->getMaterial2D();
39         video::SMaterial m;
40         m.Thickness = 3;
41         m.Lighting = false;
42         driver->setMaterial(m);
43         hud.drawSelectionBoxes(hilightboxes);
44         driver->setMaterial(oldmaterial);
45 }
46
47 void draw_anaglyph_3d_mode(Camera& camera, bool show_hud, Hud& hud,
48                 std::vector<aabb3f> hilightboxes, video::IVideoDriver* driver,
49                 scene::ISceneManager* smgr, bool draw_wield_tool, Client& client,
50                 gui::IGUIEnvironment* guienv )
51 {
52
53         /* preserve old setup*/
54         irr::core::vector3df oldPosition = camera.getCameraNode()->getPosition();
55         irr::core::vector3df oldTarget   = camera.getCameraNode()->getTarget();
56
57         irr::core::matrix4 startMatrix =
58                         camera.getCameraNode()->getAbsoluteTransformation();
59         irr::core::vector3df focusPoint = (camera.getCameraNode()->getTarget()
60                         - camera.getCameraNode()->getAbsolutePosition()).setLength(1)
61                         + camera.getCameraNode()->getAbsolutePosition();
62
63
64         //Left eye...
65         irr::core::vector3df leftEye;
66         irr::core::matrix4 leftMove;
67         leftMove.setTranslation(
68                         irr::core::vector3df(-g_settings->getFloat("3d_paralax_strength"),
69                                         0.0f, 0.0f));
70         leftEye = (startMatrix * leftMove).getTranslation();
71
72         //clear the depth buffer, and color
73         driver->beginScene( true, true, irr::video::SColor(0, 200, 200, 255));
74         driver->getOverrideMaterial().Material.ColorMask = irr::video::ECP_RED;
75         driver->getOverrideMaterial().EnableFlags = irr::video::EMF_COLOR_MASK;
76         driver->getOverrideMaterial().EnablePasses = irr::scene::ESNRP_SKY_BOX
77                         + irr::scene::ESNRP_SOLID + irr::scene::ESNRP_TRANSPARENT
78                         + irr::scene::ESNRP_TRANSPARENT_EFFECT + irr::scene::ESNRP_SHADOW;
79         camera.getCameraNode()->setPosition(leftEye);
80         camera.getCameraNode()->setTarget(focusPoint);
81         smgr->drawAll();
82         driver->setTransform(video::ETS_WORLD, core::IdentityMatrix);
83         if (show_hud)
84         {
85                 draw_selectionbox(driver, hud, hilightboxes, show_hud);
86
87                 if (draw_wield_tool)
88                         camera.drawWieldedTool(&leftMove);
89         }
90
91         guienv->drawAll();
92
93         //Right eye...
94         irr::core::vector3df rightEye;
95         irr::core::matrix4 rightMove;
96         rightMove.setTranslation(
97                         irr::core::vector3df(g_settings->getFloat("3d_paralax_strength"),
98                                         0.0f, 0.0f));
99         rightEye = (startMatrix * rightMove).getTranslation();
100
101         //clear the depth buffer
102         driver->clearZBuffer();
103         driver->getOverrideMaterial().Material.ColorMask = irr::video::ECP_GREEN
104                         + irr::video::ECP_BLUE;
105         driver->getOverrideMaterial().EnableFlags = irr::video::EMF_COLOR_MASK;
106         driver->getOverrideMaterial().EnablePasses = irr::scene::ESNRP_SKY_BOX
107                         + irr::scene::ESNRP_SOLID + irr::scene::ESNRP_TRANSPARENT
108                         + irr::scene::ESNRP_TRANSPARENT_EFFECT + irr::scene::ESNRP_SHADOW;
109         camera.getCameraNode()->setPosition(rightEye);
110         camera.getCameraNode()->setTarget(focusPoint);
111         smgr->drawAll();
112         driver->setTransform(video::ETS_WORLD, core::IdentityMatrix);
113         if (show_hud)
114         {
115                 draw_selectionbox(driver, hud, hilightboxes, show_hud);
116
117                 if (draw_wield_tool)
118                         camera.drawWieldedTool(&rightMove);
119         }
120
121         guienv->drawAll();
122
123         driver->getOverrideMaterial().Material.ColorMask = irr::video::ECP_ALL;
124         driver->getOverrideMaterial().EnableFlags = 0;
125         driver->getOverrideMaterial().EnablePasses = 0;
126         camera.getCameraNode()->setPosition(oldPosition);
127         camera.getCameraNode()->setTarget(oldTarget);
128 }
129
130 void init_texture(video::IVideoDriver* driver, const v2u32& screensize,
131                 video::ITexture** texture)
132 {
133         static v2u32 last_screensize = v2u32(0,0);
134
135         if (( *texture == NULL ) || (screensize != last_screensize))
136         {
137                 if (*texture != NULL)
138                 {
139                         driver->removeTexture(*texture);
140                 }
141                 *texture = driver->addRenderTargetTexture(
142                                 core::dimension2d<u32>(screensize.X, screensize.Y));
143                 last_screensize = screensize;
144         }
145 }
146
147 video::ITexture* draw_image(const v2u32& screensize,
148                 paralax_sign psign, const irr::core::matrix4& startMatrix,
149                 const irr::core::vector3df& focusPoint, bool show_hud,
150                 video::IVideoDriver* driver, Camera& camera, scene::ISceneManager* smgr,
151                 Hud& hud, std::vector<aabb3f>& hilightboxes,
152                 bool draw_wield_tool, Client& client, gui::IGUIEnvironment* guienv,
153                 video::SColor skycolor )
154 {
155         static video::ITexture* images[2] = { NULL, NULL };
156
157         video::ITexture* image = NULL;
158
159         if (psign == RIGHT)
160         {
161                 init_texture(driver, screensize, &images[1]);
162                 image = images[1];
163         } else {
164                 init_texture(driver, screensize, &images[0]);
165                 image = images[0];
166         }
167
168         driver->setRenderTarget(image, true, true,
169                         irr::video::SColor(255,
170                                         skycolor.getRed(), skycolor.getGreen(), skycolor.getBlue()));
171
172         irr::core::vector3df eye_pos;
173         irr::core::matrix4 movement;
174         movement.setTranslation(
175                         irr::core::vector3df((int) psign *
176                                         g_settings->getFloat("3d_paralax_strength"), 0.0f, 0.0f));
177         eye_pos = (startMatrix * movement).getTranslation();
178
179         //clear the depth buffer
180         driver->clearZBuffer();
181         camera.getCameraNode()->setPosition(eye_pos);
182         camera.getCameraNode()->setTarget(focusPoint);
183         smgr->drawAll();
184
185         driver->setTransform(video::ETS_WORLD, core::IdentityMatrix);
186
187         if (show_hud)
188         {
189                 draw_selectionbox(driver, hud, hilightboxes, show_hud);
190
191                 if (draw_wield_tool)
192                         camera.drawWieldedTool(&movement);
193         }
194
195         guienv->drawAll();
196
197         /* switch back to real renderer */
198         driver->setRenderTarget(0, true, true,
199                         irr::video::SColor(0,
200                                         skycolor.getRed(), skycolor.getGreen(), skycolor.getBlue()));
201
202         return image;
203 }
204
205 video::ITexture*  draw_hud(video::IVideoDriver* driver, const v2u32& screensize,
206                 bool show_hud, Hud& hud, Client& client, bool draw_crosshair,
207                 video::SColor skycolor, gui::IGUIEnvironment* guienv, Camera& camera )
208 {
209         static video::ITexture* image = NULL;
210         init_texture(driver, screensize, &image);
211         driver->setRenderTarget(image, true, true,
212                         irr::video::SColor(255,0,0,0));
213
214         if (show_hud)
215         {
216                 if (draw_crosshair)
217                         hud.drawCrosshair();
218                 hud.drawHotbar(client.getPlayerItem());
219                 hud.drawLuaElements(camera.getOffset());
220
221                 guienv->drawAll();
222         }
223
224         driver->setRenderTarget(0, true, true,
225                         irr::video::SColor(0,
226                                         skycolor.getRed(), skycolor.getGreen(), skycolor.getBlue()));
227
228         return image;
229 }
230
231 void draw_interlaced_3d_mode(Camera& camera, bool show_hud,
232                 Hud& hud, std::vector<aabb3f> hilightboxes, video::IVideoDriver* driver,
233                 scene::ISceneManager* smgr, const v2u32& screensize,
234                 bool draw_wield_tool, Client& client, gui::IGUIEnvironment* guienv,
235                 video::SColor skycolor )
236 {
237         /* save current info */
238         irr::core::vector3df oldPosition = camera.getCameraNode()->getPosition();
239         irr::core::vector3df oldTarget = camera.getCameraNode()->getTarget();
240         irr::core::matrix4 startMatrix =
241                         camera.getCameraNode()->getAbsoluteTransformation();
242         irr::core::vector3df focusPoint = (camera.getCameraNode()->getTarget()
243                         - camera.getCameraNode()->getAbsolutePosition()).setLength(1)
244                         + camera.getCameraNode()->getAbsolutePosition();
245
246         /* create left view */
247         video::ITexture* left_image = draw_image(screensize, LEFT, startMatrix,
248                         focusPoint, show_hud, driver, camera, smgr, hud, hilightboxes,
249                         draw_wield_tool, client, guienv, skycolor);
250
251         //Right eye...
252         irr::core::vector3df rightEye;
253         irr::core::matrix4 rightMove;
254         rightMove.setTranslation(
255                         irr::core::vector3df(g_settings->getFloat("3d_paralax_strength"),
256                                         0.0f, 0.0f));
257         rightEye = (startMatrix * rightMove).getTranslation();
258
259         //clear the depth buffer
260         driver->clearZBuffer();
261         camera.getCameraNode()->setPosition(rightEye);
262         camera.getCameraNode()->setTarget(focusPoint);
263         smgr->drawAll();
264
265         driver->setTransform(video::ETS_WORLD, core::IdentityMatrix);
266
267         if (show_hud)
268         {
269                 draw_selectionbox(driver, hud, hilightboxes, show_hud);
270
271                 if(draw_wield_tool)
272                         camera.drawWieldedTool(&rightMove);
273         }
274         guienv->drawAll();
275
276         for (unsigned int i = 0; i < screensize.Y; i+=2 ) {
277                 driver->draw2DImage(left_image, irr::core::position2d<s32>(0, screensize.Y-i),
278                                 irr::core::rect<s32>(0, i,screensize.X, i+1), 0,
279                                 irr::video::SColor(255, 255, 255, 255),
280                                 false);
281         }
282
283         /* cleanup */
284         camera.getCameraNode()->setPosition(oldPosition);
285         camera.getCameraNode()->setTarget(oldTarget);
286 }
287
288 void draw_sidebyside_3d_mode(Camera& camera, bool show_hud,
289                 Hud& hud, std::vector<aabb3f> hilightboxes, video::IVideoDriver* driver,
290                 scene::ISceneManager* smgr, const v2u32& screensize,
291                 bool draw_wield_tool, Client& client, gui::IGUIEnvironment* guienv,
292                 video::SColor skycolor )
293 {
294         /* save current info */
295         irr::core::vector3df oldPosition = camera.getCameraNode()->getPosition();
296         irr::core::vector3df oldTarget = camera.getCameraNode()->getTarget();
297         irr::core::matrix4 startMatrix =
298                         camera.getCameraNode()->getAbsoluteTransformation();
299         irr::core::vector3df focusPoint = (camera.getCameraNode()->getTarget()
300                         - camera.getCameraNode()->getAbsolutePosition()).setLength(1)
301                         + camera.getCameraNode()->getAbsolutePosition();
302
303         /* create left view */
304         video::ITexture* left_image = draw_image(screensize, LEFT, startMatrix,
305                         focusPoint, show_hud, driver, camera, smgr, hud, hilightboxes,
306                         draw_wield_tool, client, guienv, skycolor);
307
308         /* create right view */
309         video::ITexture* right_image = draw_image(screensize, RIGHT, startMatrix,
310                         focusPoint, show_hud, driver, camera, smgr, hud, hilightboxes,
311                         draw_wield_tool, client, guienv, skycolor);
312
313         /* create hud overlay */
314         video::ITexture* hudtexture = draw_hud(driver, screensize, show_hud, hud, client,
315                         false, skycolor, guienv, camera );
316         driver->makeColorKeyTexture(hudtexture, irr::video::SColor(255, 0, 0, 0));
317         //makeColorKeyTexture mirrors texture so we do it twice to get it right again
318         driver->makeColorKeyTexture(hudtexture, irr::video::SColor(255, 0, 0, 0));
319
320         driver->draw2DImage(left_image,
321                         irr::core::rect<s32>(0, 0, screensize.X/2, screensize.Y),
322                         irr::core::rect<s32>(0, 0, screensize.X, screensize.Y), 0, 0, false);
323
324         driver->draw2DImage(hudtexture,
325                         irr::core::rect<s32>(0, 0, screensize.X/2, screensize.Y),
326                         irr::core::rect<s32>(0, 0, screensize.X, screensize.Y), 0, 0, true);
327
328         driver->draw2DImage(right_image,
329                         irr::core::rect<s32>(screensize.X/2, 0, screensize.X, screensize.Y),
330                         irr::core::rect<s32>(0, 0, screensize.X, screensize.Y), 0, 0, false);
331
332         driver->draw2DImage(hudtexture,
333                         irr::core::rect<s32>(screensize.X/2, 0, screensize.X, screensize.Y),
334                         irr::core::rect<s32>(0, 0, screensize.X, screensize.Y), 0, 0, true);
335
336         left_image = NULL;
337         right_image = NULL;
338
339         /* cleanup */
340         camera.getCameraNode()->setPosition(oldPosition);
341         camera.getCameraNode()->setTarget(oldTarget);
342 }
343
344 void draw_top_bottom_3d_mode(Camera& camera, bool show_hud,
345                 Hud& hud, std::vector<aabb3f> hilightboxes, video::IVideoDriver* driver,
346                 scene::ISceneManager* smgr, const v2u32& screensize,
347                 bool draw_wield_tool, Client& client, gui::IGUIEnvironment* guienv,
348                 video::SColor skycolor )
349 {
350         /* save current info */
351         irr::core::vector3df oldPosition = camera.getCameraNode()->getPosition();
352         irr::core::vector3df oldTarget = camera.getCameraNode()->getTarget();
353         irr::core::matrix4 startMatrix =
354                         camera.getCameraNode()->getAbsoluteTransformation();
355         irr::core::vector3df focusPoint = (camera.getCameraNode()->getTarget()
356                         - camera.getCameraNode()->getAbsolutePosition()).setLength(1)
357                         + camera.getCameraNode()->getAbsolutePosition();
358
359         /* create left view */
360         video::ITexture* left_image = draw_image(screensize, LEFT, startMatrix,
361                         focusPoint, show_hud, driver, camera, smgr, hud, hilightboxes,
362                         draw_wield_tool, client, guienv, skycolor);
363
364         /* create right view */
365         video::ITexture* right_image = draw_image(screensize, RIGHT, startMatrix,
366                         focusPoint, show_hud, driver, camera, smgr, hud, hilightboxes,
367                         draw_wield_tool, client, guienv, skycolor);
368
369         /* create hud overlay */
370         video::ITexture* hudtexture = draw_hud(driver, screensize, show_hud, hud, client,
371                         false, skycolor, guienv, camera );
372         driver->makeColorKeyTexture(hudtexture, irr::video::SColor(255, 0, 0, 0));
373         //makeColorKeyTexture mirrors texture so we do it twice to get it right again
374         driver->makeColorKeyTexture(hudtexture, irr::video::SColor(255, 0, 0, 0));
375
376         driver->draw2DImage(left_image,
377                         irr::core::rect<s32>(0, 0, screensize.X, screensize.Y/2),
378                         irr::core::rect<s32>(0, 0, screensize.X, screensize.Y), 0, 0, false);
379
380         driver->draw2DImage(hudtexture,
381                         irr::core::rect<s32>(0, 0, screensize.X, screensize.Y/2),
382                         irr::core::rect<s32>(0, 0, screensize.X, screensize.Y), 0, 0, true);
383
384         driver->draw2DImage(right_image,
385                         irr::core::rect<s32>(0, screensize.Y/2, screensize.X, screensize.Y),
386                         irr::core::rect<s32>(0, 0, screensize.X, screensize.Y), 0, 0, false);
387
388         driver->draw2DImage(hudtexture,
389                         irr::core::rect<s32>(0, screensize.Y/2, screensize.X, screensize.Y),
390                         irr::core::rect<s32>(0, 0, screensize.X, screensize.Y), 0, 0, true);
391
392         left_image = NULL;
393         right_image = NULL;
394
395         /* cleanup */
396         camera.getCameraNode()->setPosition(oldPosition);
397         camera.getCameraNode()->setTarget(oldTarget);
398 }
399
400 void draw_plain(Camera& camera, bool show_hud, Hud& hud,
401                 std::vector<aabb3f> hilightboxes, video::IVideoDriver* driver,
402                 bool draw_wield_tool, Client& client, gui::IGUIEnvironment* guienv)
403 {
404
405         draw_selectionbox(driver, hud, hilightboxes, show_hud);
406
407         if(draw_wield_tool)
408                 camera.drawWieldedTool();
409 }
410
411 void draw_scene(video::IVideoDriver* driver, scene::ISceneManager* smgr,
412                 Camera& camera, Client& client, LocalPlayer* player, Hud& hud,
413                 gui::IGUIEnvironment* guienv, std::vector<aabb3f> hilightboxes,
414                 const v2u32& screensize, video::SColor skycolor, bool show_hud)
415 {
416         //TODO check if usefull
417         u32 scenetime = 0;
418         {
419                 TimeTaker timer("smgr");
420
421                 bool draw_wield_tool = (show_hud &&
422                                 (player->hud_flags & HUD_FLAG_WIELDITEM_VISIBLE) &&
423                                 camera.getCameraMode() < CAMERA_MODE_THIRD );
424
425                 bool draw_crosshair = ((player->hud_flags & HUD_FLAG_CROSSHAIR_VISIBLE) &&
426                                 (camera.getCameraMode() != CAMERA_MODE_THIRD_FRONT));
427
428                 std::string draw_mode = g_settings->get("3d_mode");
429
430                 smgr->drawAll();
431
432                 if (draw_mode == "anaglyph")
433                 {
434                         draw_anaglyph_3d_mode(camera, show_hud, hud, hilightboxes, driver,
435                                         smgr, draw_wield_tool, client, guienv);
436                         draw_crosshair = false;
437                 }
438                 else if (draw_mode == "interlaced")
439                 {
440                         draw_interlaced_3d_mode(camera, show_hud, hud, hilightboxes, driver,
441                                         smgr, screensize, draw_wield_tool, client, guienv, skycolor);
442                         draw_crosshair = false;
443                 }
444                 else if (draw_mode == "sidebyside")
445                 {
446                         draw_sidebyside_3d_mode(camera, show_hud, hud, hilightboxes, driver,
447                                         smgr, screensize, draw_wield_tool, client, guienv, skycolor);
448                         show_hud = false;
449                 }
450                 else if (draw_mode == "topbottom")
451                 {
452                         draw_top_bottom_3d_mode(camera, show_hud, hud, hilightboxes, driver,
453                                         smgr, screensize, draw_wield_tool, client, guienv, skycolor);
454                         show_hud = false;
455                 }
456                 else {
457                         draw_plain(camera, show_hud, hud, hilightboxes, driver,
458                                         draw_wield_tool, client, guienv);
459                 }
460                 //TODO how to make those 3d too
461                 if (show_hud)
462                 {
463                         if (draw_crosshair)
464                                 hud.drawCrosshair();
465                         hud.drawHotbar(client.getPlayerItem());
466                         hud.drawLuaElements(camera.getOffset());
467
468                         guienv->drawAll();
469                 }
470
471                 scenetime = timer.stop(true);
472         }
473
474 }
475
476 /*
477         Draws a screen with a single text on it.
478         Text will be removed when the screen is drawn the next time.
479         Additionally, a progressbar can be drawn when percent is set between 0 and 100.
480 */
481 /*gui::IGUIStaticText **/
482 void draw_load_screen(const std::wstring &text, IrrlichtDevice* device,
483                 gui::IGUIEnvironment* guienv, gui::IGUIFont* font, float dtime,
484                 int percent, bool clouds )
485 {
486         video::IVideoDriver* driver = device->getVideoDriver();
487         v2u32 screensize = driver->getScreenSize();
488         const wchar_t *loadingtext = text.c_str();
489         core::vector2d<u32> textsize_u = font->getDimension(loadingtext);
490         core::vector2d<s32> textsize(textsize_u.X,textsize_u.Y);
491         core::vector2d<s32> center(screensize.X/2, screensize.Y/2);
492         core::rect<s32> textrect(center - textsize/2, center + textsize/2);
493
494         gui::IGUIStaticText *guitext = guienv->addStaticText(
495                         loadingtext, textrect, false, false);
496         guitext->setTextAlignment(gui::EGUIA_CENTER, gui::EGUIA_UPPERLEFT);
497
498         bool cloud_menu_background = clouds && g_settings->getBool("menu_clouds");
499         if (cloud_menu_background)
500         {
501                 g_menuclouds->step(dtime*3);
502                 g_menuclouds->render();
503                 driver->beginScene(true, true, video::SColor(255,140,186,250));
504                 g_menucloudsmgr->drawAll();
505         }
506         else
507                 driver->beginScene(true, true, video::SColor(255,0,0,0));
508
509         if (percent >= 0 && percent <= 100) // draw progress bar
510         {
511                 core::vector2d<s32> barsize(256,32);
512                 core::rect<s32> barrect(center-barsize/2, center+barsize/2);
513                 driver->draw2DRectangle(video::SColor(255,255,255,255),barrect, NULL); // border
514                 driver->draw2DRectangle(video::SColor(255,64,64,64), core::rect<s32> (
515                                 barrect.UpperLeftCorner+1,
516                                 barrect.LowerRightCorner-1), NULL); // black inside the bar
517                 driver->draw2DRectangle(video::SColor(255,128,128,128), core::rect<s32> (
518                                 barrect.UpperLeftCorner+1,
519                                 core::vector2d<s32>(
520                                         barrect.LowerRightCorner.X-(barsize.X-1)+percent*(barsize.X-2)/100,
521                                         barrect.LowerRightCorner.Y-1)), NULL); // the actual progress
522         }
523         guienv->drawAll();
524         driver->endScene();
525
526         guitext->remove();
527
528         //return guitext;
529 }