BGE: alpha on frame buffer and precedence of MSAA over swap.
[blender.git] / source / gameengine / GamePlayer / ghost / GPG_Application.cpp
1 /*
2  * ***** BEGIN GPL LICENSE BLOCK *****
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU General Public License
6  * as published by the Free Software Foundation; either version 2
7  * of the License, or (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software Foundation,
16  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17  *
18  * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
19  * All rights reserved.
20  *
21  * The Original Code is: all of this file.
22  *
23  * Contributor(s): none yet.
24  *
25  * ***** END GPL LICENSE BLOCK *****
26  * GHOST Blender Player application implementation file.
27  */
28
29 /** \file gameengine/GamePlayer/ghost/GPG_Application.cpp
30  *  \ingroup player
31  */
32
33
34 #ifdef WIN32
35 #  pragma warning (disable:4786) // suppress stl-MSVC debug info warning
36 #  include <windows.h>
37 #endif
38
39 #include "glew-mx.h"
40 #include "GPU_extensions.h"
41 #include "GPU_init_exit.h"
42
43 #include "GPG_Application.h"
44 #include "BL_BlenderDataConversion.h"
45
46 #include <iostream>
47 #include <MT_assert.h>
48 #include <stdlib.h>
49
50 /**********************************
51  * Begin Blender include block
52  **********************************/
53 #ifdef __cplusplus
54 extern "C"
55 {
56 #endif  // __cplusplus
57 #include "BLI_blenlib.h"
58 #include "BLO_readfile.h"
59 #include "BKE_global.h"
60 #include "BKE_main.h"
61 #include "BKE_sound.h"
62 #include "IMB_imbuf.h"
63 #include "DNA_scene_types.h"
64 #ifdef __cplusplus
65 }
66 #endif // __cplusplus
67 /**********************************
68  * End Blender include block
69  **********************************/
70
71
72 #include "BL_System.h"
73 #include "KX_KetsjiEngine.h"
74
75 // include files needed by "KX_BlenderSceneConverter.h"
76 #include "CTR_Map.h"
77 #include "SCA_IActuator.h"
78 #include "RAS_MeshObject.h"
79 #include "RAS_OpenGLRasterizer.h"
80 #include "RAS_ListRasterizer.h"
81 #include "KX_PythonInit.h"
82 #include "KX_PyConstraintBinding.h"
83 #include "BL_Material.h" // MAXTEX
84
85 #include "KX_BlenderSceneConverter.h"
86 #include "NG_LoopBackNetworkDeviceInterface.h"
87
88 #include "GPC_MouseDevice.h"
89 #include "GPG_Canvas.h" 
90 #include "GPG_KeyboardDevice.h"
91 #include "GPG_System.h"
92
93 #include "STR_String.h"
94
95 #include "GHOST_ISystem.h"
96 #include "GHOST_IEvent.h"
97 #include "GHOST_IEventConsumer.h"
98 #include "GHOST_IWindow.h"
99 #include "GHOST_Rect.h"
100
101 #ifdef WITH_AUDASPACE
102 #  include AUD_DEVICE_H
103 #endif
104
105 static void frameTimerProc(GHOST_ITimerTask* task, GHOST_TUns64 time);
106
107 static GHOST_ISystem* fSystem = 0;
108 static const int kTimerFreq = 10;
109
110 GPG_Application::GPG_Application(GHOST_ISystem* system)
111         : m_startSceneName(""), 
112           m_startScene(0),
113           m_maggie(0),
114           m_kxStartScene(NULL),
115           m_exitRequested(0),
116           m_system(system), 
117           m_mainWindow(0), 
118           m_frameTimer(0), 
119           m_cursor(GHOST_kStandardCursorFirstCursor),
120           m_engineInitialized(0), 
121           m_engineRunning(0), 
122           m_isEmbedded(false),
123           m_ketsjiengine(0),
124           m_kxsystem(0), 
125           m_keyboard(0), 
126           m_mouse(0), 
127           m_canvas(0),
128           m_rasterizer(0), 
129           m_sceneconverter(0),
130           m_networkdevice(0),
131           m_blendermat(0),
132           m_blenderglslmat(0),
133           m_pyGlobalDictString(0),
134           m_pyGlobalDictString_Length(0)
135 {
136         fSystem = system;
137 }
138
139
140
141 GPG_Application::~GPG_Application(void)
142 {
143         if (m_pyGlobalDictString) {
144                 delete [] m_pyGlobalDictString;
145                 m_pyGlobalDictString = 0;
146                 m_pyGlobalDictString_Length = 0;
147         }
148
149         exitEngine();
150         fSystem->disposeWindow(m_mainWindow);
151 }
152
153
154
155 bool GPG_Application::SetGameEngineData(struct Main* maggie, Scene *scene, GlobalSettings *gs, int argc, char **argv)
156 {
157         bool result = false;
158
159         if (maggie != NULL && scene != NULL)
160         {
161 // XXX          G.scene = scene;
162                 m_maggie = maggie;
163                 m_startSceneName = scene->id.name+2;
164                 m_startScene = scene;
165                 result = true;
166         }
167         
168         /* Python needs these */
169         m_argc= argc;
170         m_argv= argv;
171
172         /* Global Settings */
173         m_globalSettings= gs;
174
175         return result;
176 }
177
178
179 #ifdef WIN32
180 #define SCR_SAVE_MOUSE_MOVE_THRESHOLD 15
181
182 static HWND found_ghost_window_hwnd;
183 static GHOST_IWindow* ghost_window_to_find;
184 static WNDPROC ghost_wnd_proc;
185 static POINT scr_save_mouse_pos;
186
187 static LRESULT CALLBACK screenSaverWindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
188 {
189         BOOL close = false;
190         switch (uMsg)
191         {
192                 case WM_MOUSEMOVE:
193                 { 
194                         POINT pt; 
195                         GetCursorPos(&pt);
196                         LONG dx = scr_save_mouse_pos.x - pt.x;
197                         LONG dy = scr_save_mouse_pos.y - pt.y;
198                         if (abs(dx) > SCR_SAVE_MOUSE_MOVE_THRESHOLD
199                                 || abs(dy) > SCR_SAVE_MOUSE_MOVE_THRESHOLD)
200                         {
201                                 close = true;
202                         }
203                         scr_save_mouse_pos = pt;
204                         break;
205                 }
206                 case WM_LBUTTONDOWN: 
207                 case WM_MBUTTONDOWN: 
208                 case WM_RBUTTONDOWN: 
209                 case WM_KEYDOWN:
210                         close = true;
211         }
212         if (close)
213                 PostMessage(hwnd,WM_CLOSE,0,0);
214         return CallWindowProc(ghost_wnd_proc, hwnd, uMsg, wParam, lParam);
215 }
216
217 BOOL CALLBACK findGhostWindowHWNDProc(HWND hwnd, LPARAM lParam)
218 {
219         GHOST_IWindow *p = (GHOST_IWindow*) GetWindowLongPtr(hwnd, GWLP_USERDATA);
220         BOOL ret = true;
221         if (p == ghost_window_to_find)
222         {
223                 found_ghost_window_hwnd = hwnd;
224                 ret = false;
225         }
226         return ret;
227 }
228
229 static HWND findGhostWindowHWND(GHOST_IWindow* window)
230 {
231         found_ghost_window_hwnd = NULL;
232         ghost_window_to_find = window;
233         EnumWindows(findGhostWindowHWNDProc, NULL);
234         return found_ghost_window_hwnd;
235 }
236
237 bool GPG_Application::startScreenSaverPreview(
238         HWND parentWindow,
239         const bool stereoVisual,
240         const int stereoMode,
241         const GHOST_TUns16 samples)
242 {
243         bool success = false;
244
245         RECT rc;
246         if (GetWindowRect(parentWindow, &rc))
247         {
248                 int windowWidth = rc.right - rc.left;
249                 int windowHeight = rc.bottom - rc.top;
250                 STR_String title = "";
251                 GHOST_GLSettings glSettings = {0};
252
253                 if (stereoVisual) {
254                         glSettings.flags |= GHOST_glStereoVisual;
255                 }
256                 glSettings.numOfAASamples = samples;
257
258                 m_mainWindow = fSystem->createWindow(title, 0, 0, windowWidth, windowHeight, GHOST_kWindowStateMinimized,
259                                                      GHOST_kDrawingContextTypeOpenGL, glSettings);
260                 if (!m_mainWindow) {
261                         printf("error: could not create main window\n");
262                         exit(-1);
263                 }
264
265                 HWND ghost_hwnd = findGhostWindowHWND(m_mainWindow);
266                 if (!ghost_hwnd) {
267                         printf("error: could find main window\n");
268                         exit(-1);
269                 }
270
271                 SetParent(ghost_hwnd, parentWindow);
272                 LONG_PTR style = GetWindowLongPtr(ghost_hwnd, GWL_STYLE);
273                 LONG_PTR exstyle = GetWindowLongPtr(ghost_hwnd, GWL_EXSTYLE);
274
275                 RECT adjrc = { 0, 0, windowWidth, windowHeight };
276                 AdjustWindowRectEx(&adjrc, style, false, exstyle);
277
278                 style = (style & (~(WS_POPUP|WS_OVERLAPPEDWINDOW|WS_OVERLAPPED|WS_CAPTION|WS_SYSMENU|WS_THICKFRAME|WS_MINIMIZEBOX|WS_MAXIMIZEBOX|WS_TILEDWINDOW ))) | WS_CHILD;
279                 SetWindowLongPtr(ghost_hwnd, GWL_STYLE, style);
280                 SetWindowPos(ghost_hwnd, NULL, adjrc.left, adjrc.top, 0, 0, SWP_NOZORDER|SWP_NOSIZE|SWP_NOACTIVATE);
281
282                 /* Check the size of the client rectangle of the window and resize the window
283                  * so that the client rectangle has the size requested.
284                  */
285                 m_mainWindow->setClientSize(windowWidth, windowHeight);
286
287                 success = initEngine(m_mainWindow, stereoMode);
288                 if (success) {
289                         success = startEngine();
290                 }
291
292         }
293         return success;
294 }
295
296 bool GPG_Application::startScreenSaverFullScreen(
297                 int width,
298                 int height,
299                 int bpp,int frequency,
300                 const bool stereoVisual,
301                 const int stereoMode,
302                 const GHOST_TUns16 samples)
303 {
304         bool ret = startFullScreen(width, height, bpp, frequency, stereoVisual, stereoMode, 0, samples);
305         if (ret)
306         {
307                 HWND ghost_hwnd = findGhostWindowHWND(m_mainWindow);
308                 if (ghost_hwnd != NULL)
309                 {
310                         GetCursorPos(&scr_save_mouse_pos);
311                         ghost_wnd_proc = (WNDPROC) GetWindowLongPtr(ghost_hwnd, GWLP_WNDPROC);
312                         SetWindowLongPtr(ghost_hwnd,GWLP_WNDPROC, (uintptr_t) screenSaverWindowProc);
313                 }
314         }
315         return ret;
316 }
317
318 #endif
319
320 bool GPG_Application::startWindow(
321         STR_String& title,
322         int windowLeft,
323         int windowTop,
324         int windowWidth,
325         int windowHeight,
326         const bool stereoVisual,
327         const int stereoMode,
328                 const int alphaBackground,
329         const GHOST_TUns16 samples)
330 {
331         GHOST_GLSettings glSettings = {0};
332         bool success;
333         // Create the main window
334         //STR_String title ("Blender Player - GHOST");
335         if (stereoVisual)
336                 glSettings.flags |= GHOST_glStereoVisual;
337         if (alphaBackground)
338                 glSettings.flags |= GHOST_glAlphaBackground;
339         glSettings.numOfAASamples = samples;
340
341         m_mainWindow = fSystem->createWindow(title, windowLeft, windowTop, windowWidth, windowHeight, GHOST_kWindowStateNormal,
342                                              GHOST_kDrawingContextTypeOpenGL, glSettings);
343         if (!m_mainWindow) {
344                 printf("error: could not create main window\n");
345                 exit(-1);
346         }
347
348         /* Check the size of the client rectangle of the window and resize the window
349          * so that the client rectangle has the size requested.
350          */
351         m_mainWindow->setClientSize(windowWidth, windowHeight);
352         m_mainWindow->setCursorVisibility(false);
353
354         success = initEngine(m_mainWindow, stereoMode);
355         if (success) {
356                 success = startEngine();
357         }
358         return success;
359 }
360
361 bool GPG_Application::startEmbeddedWindow(
362         STR_String& title,
363         const GHOST_TEmbedderWindowID parentWindow,
364         const bool stereoVisual,
365         const int stereoMode,
366                 const int alphaBackground,
367         const GHOST_TUns16 samples)
368 {
369         GHOST_TWindowState state = GHOST_kWindowStateNormal;
370         GHOST_GLSettings glSettings = {0};
371
372         if (stereoVisual)
373                 glSettings.flags |= GHOST_glStereoVisual;
374         if (alphaBackground)
375                 glSettings.flags |= GHOST_glAlphaBackground;
376         glSettings.numOfAASamples = samples;
377
378         if (parentWindow != 0)
379                 state = GHOST_kWindowStateEmbedded;
380         m_mainWindow = fSystem->createWindow(title, 0, 0, 0, 0, state,
381                                              GHOST_kDrawingContextTypeOpenGL, glSettings, parentWindow);
382
383         if (!m_mainWindow) {
384                 printf("error: could not create main window\n");
385                 exit(-1);
386         }
387         m_isEmbedded = true;
388
389         bool success = initEngine(m_mainWindow, stereoMode);
390         if (success) {
391                 success = startEngine();
392         }
393         return success;
394 }
395
396
397 bool GPG_Application::startFullScreen(
398         int width,
399         int height,
400         int bpp,int frequency,
401         const bool stereoVisual,
402         const int stereoMode,
403         const int alphaBackground,
404         const GHOST_TUns16 samples,
405         bool useDesktop)
406 {
407         bool success;
408         GHOST_TUns32 sysWidth=0, sysHeight=0;
409         fSystem->getMainDisplayDimensions(sysWidth, sysHeight);
410         // Create the main window
411         GHOST_DisplaySetting setting;
412         setting.xPixels = (useDesktop) ? sysWidth : width;
413         setting.yPixels = (useDesktop) ? sysHeight : height;
414         setting.bpp = bpp;
415         setting.frequency = frequency;
416
417         fSystem->beginFullScreen(setting, &m_mainWindow, stereoVisual, alphaBackground, samples);
418         m_mainWindow->setCursorVisibility(false);
419         /* note that X11 ignores this (it uses a window internally for fullscreen) */
420         m_mainWindow->setState(GHOST_kWindowStateFullScreen);
421
422         success = initEngine(m_mainWindow, stereoMode);
423         if (success) {
424                 success = startEngine();
425         }
426         return success;
427 }
428
429
430
431
432 bool GPG_Application::StartGameEngine(int stereoMode)
433 {
434         bool success = initEngine(m_mainWindow, stereoMode);
435         
436         if (success)
437                 success = startEngine();
438
439         return success;
440 }
441
442
443
444 void GPG_Application::StopGameEngine()
445 {
446         exitEngine();
447 }
448
449
450
451 bool GPG_Application::processEvent(GHOST_IEvent* event)
452 {
453         bool handled = true;
454
455         switch (event->getType())
456         {
457                 case GHOST_kEventUnknown:
458                         break;
459
460                 case GHOST_kEventButtonDown:
461                         handled = handleButton(event, true);
462                         break;
463
464                 case GHOST_kEventButtonUp:
465                         handled = handleButton(event, false);
466                         break;
467                         
468                 case GHOST_kEventWheel:
469                         handled = handleWheel(event);
470                         break;
471
472                 case GHOST_kEventCursorMove:
473                         handled = handleCursorMove(event);
474                         break;
475
476                 case GHOST_kEventKeyDown:
477                         handleKey(event, true);
478                         break;
479
480                 case GHOST_kEventKeyUp:
481                         handleKey(event, false);
482                         break;
483
484
485                 case GHOST_kEventWindowClose:
486                 case GHOST_kEventQuit:
487                         m_exitRequested = KX_EXIT_REQUEST_OUTSIDE;
488                         break;
489
490                 case GHOST_kEventWindowActivate:
491                         handled = false;
492                         break;
493                 case GHOST_kEventWindowDeactivate:
494                         handled = false;
495                         break;
496
497                 // The player now runs as often as it can (repsecting vsync and fixedtime).
498                 // This allows the player to break 100fps, but this code is being left here
499                 // as reference. (see EngineNextFrame)
500                 //case GHOST_kEventWindowUpdate:
501                 //      {
502                 //              GHOST_IWindow* window = event->getWindow();
503                 //              if (!m_system->validWindow(window)) break;
504                 //              // Update the state of the game engine
505                 //              if (m_kxsystem && !m_exitRequested)
506                 //              {
507                 //                      // Proceed to next frame
508                 //                      window->activateDrawingContext();
509
510                 //                      // first check if we want to exit
511                 //                      m_exitRequested = m_ketsjiengine->GetExitCode();
512                 //
513                 //                      // kick the engine
514                 //                      bool renderFrame = m_ketsjiengine->NextFrame();
515                 //                      if (renderFrame)
516                 //                      {
517                 //                              // render the frame
518                 //                              m_ketsjiengine->Render();
519                 //                      }
520                 //              }
521                 //              m_exitString = m_ketsjiengine->GetExitString();
522                 //      }
523                 //      break;
524                 //
525                 case GHOST_kEventWindowSize:
526                         {
527                         GHOST_IWindow* window = event->getWindow();
528                         if (!m_system->validWindow(window)) break;
529                         if (m_canvas) {
530                                 GHOST_Rect bnds;
531                                 window->getClientBounds(bnds);
532                                 m_canvas->Resize(bnds.getWidth(), bnds.getHeight());
533                                 m_ketsjiengine->Resize();
534                         }
535                         }
536                         break;
537                 
538                 default:
539                         handled = false;
540                         break;
541         }
542         return handled;
543 }
544
545
546
547 int GPG_Application::getExitRequested(void)
548 {
549         return m_exitRequested;
550 }
551
552
553 GlobalSettings* GPG_Application::getGlobalSettings(void)
554 {
555         return m_ketsjiengine->GetGlobalSettings();
556 }
557
558
559
560 const STR_String& GPG_Application::getExitString(void)
561 {
562         return m_exitString;
563 }
564
565
566
567 bool GPG_Application::initEngine(GHOST_IWindow* window, const int stereoMode)
568 {
569         if (!m_engineInitialized)
570         {
571                 GPU_init();
572
573                 // get and set the preferences
574                 SYS_SystemHandle syshandle = SYS_GetSystem();
575                 if (!syshandle)
576                         return false;
577                 
578                 // SYS_WriteCommandLineInt(syshandle, "fixedtime", 0);
579                 // SYS_WriteCommandLineInt(syshandle, "vertexarrays",1);
580                 GameData *gm= &m_startScene->gm;
581                 bool properties = (SYS_GetCommandLineInt(syshandle, "show_properties", 0) != 0);
582                 bool profile = (SYS_GetCommandLineInt(syshandle, "show_profile", 0) != 0);
583
584                 bool showPhysics = (gm->flag & GAME_SHOW_PHYSICS);
585                 SYS_WriteCommandLineInt(syshandle, "show_physics", showPhysics);
586
587                 bool fixed_framerate= (SYS_GetCommandLineInt(syshandle, "fixedtime", (gm->flag & GAME_ENABLE_ALL_FRAMES)) != 0);
588                 bool frameRate = (SYS_GetCommandLineInt(syshandle, "show_framerate", 0) != 0);
589                 bool useLists = (SYS_GetCommandLineInt(syshandle, "displaylists", gm->flag & GAME_DISPLAY_LISTS) != 0) && GPU_display_list_support();
590                 bool nodepwarnings = (SYS_GetCommandLineInt(syshandle, "ignore_deprecation_warnings", 1) != 0);
591                 bool restrictAnimFPS = (gm->flag & GAME_RESTRICT_ANIM_UPDATES) != 0;
592
593                 if (GLEW_ARB_multitexture && GLEW_VERSION_1_1)
594                         m_blendermat = (SYS_GetCommandLineInt(syshandle, "blender_material", 1) != 0);
595
596                 if (GPU_glsl_support())
597                         m_blenderglslmat = (SYS_GetCommandLineInt(syshandle, "blender_glsl_material", 1) != 0);
598                 else if (m_globalSettings->matmode == GAME_MAT_GLSL)
599                         m_blendermat = false;
600
601                 // create the canvas, rasterizer and rendertools
602                 m_canvas = new GPG_Canvas(window);
603                 if (!m_canvas)
604                         return false;
605
606                 if (gm->vsync == VSYNC_ADAPTIVE)
607                         m_canvas->SetSwapInterval(-1);
608                 else
609                         m_canvas->SetSwapInterval((gm->vsync == VSYNC_ON) ? 1 : 0);
610
611                 m_canvas->Init();
612                 if (gm->flag & GAME_SHOW_MOUSE)
613                         m_canvas->SetMouseState(RAS_ICanvas::MOUSE_NORMAL);
614                 
615                 RAS_STORAGE_TYPE raster_storage = RAS_AUTO_STORAGE;
616
617                 if (gm->raster_storage == RAS_STORE_VBO) {
618                         raster_storage = RAS_VBO;
619                 }
620                 else if (gm->raster_storage == RAS_STORE_VA) {
621                         raster_storage = RAS_VA;
622                 }
623                 //Don't use displaylists with VBOs
624                 //If auto starts using VBOs, make sure to check for that here
625                 if (useLists && raster_storage != RAS_VBO)
626                         m_rasterizer = new RAS_ListRasterizer(m_canvas, true, raster_storage);
627                 else
628                         m_rasterizer = new RAS_OpenGLRasterizer(m_canvas, raster_storage);
629
630                 /* Stereo parameters - Eye Separation from the UI - stereomode from the command-line/UI */
631                 m_rasterizer->SetStereoMode((RAS_IRasterizer::StereoMode) stereoMode);
632                 m_rasterizer->SetEyeSeparation(m_startScene->gm.eyeseparation);
633                 
634                 if (!m_rasterizer)
635                         goto initFailed;
636                                                 
637                 // create the inputdevices
638                 m_keyboard = new GPG_KeyboardDevice();
639                 if (!m_keyboard)
640                         goto initFailed;
641                         
642                 m_mouse = new GPC_MouseDevice();
643                 if (!m_mouse)
644                         goto initFailed;
645                         
646                 // create a networkdevice
647                 m_networkdevice = new NG_LoopBackNetworkDeviceInterface();
648                 if (!m_networkdevice)
649                         goto initFailed;
650                         
651                 BKE_sound_init(m_maggie);
652
653                 // create a ketsjisystem (only needed for timing and stuff)
654                 m_kxsystem = new GPG_System (m_system);
655                 if (!m_kxsystem)
656                         goto initFailed;
657                 
658                 // create the ketsjiengine
659                 m_ketsjiengine = new KX_KetsjiEngine(m_kxsystem);
660                 
661                 // set the devices
662                 m_ketsjiengine->SetKeyboardDevice(m_keyboard);
663                 m_ketsjiengine->SetMouseDevice(m_mouse);
664                 m_ketsjiengine->SetNetworkDevice(m_networkdevice);
665                 m_ketsjiengine->SetCanvas(m_canvas);
666                 m_ketsjiengine->SetRasterizer(m_rasterizer);
667
668                 KX_KetsjiEngine::SetExitKey(ConvertKeyCode(gm->exitkey));
669 #ifdef WITH_PYTHON
670                 CValue::SetDeprecationWarnings(nodepwarnings);
671 #else
672                 (void)nodepwarnings;
673 #endif
674
675                 m_ketsjiengine->SetUseFixedTime(fixed_framerate);
676                 m_ketsjiengine->SetTimingDisplay(frameRate, profile, properties);
677                 m_ketsjiengine->SetRestrictAnimationFPS(restrictAnimFPS);
678
679                 //set the global settings (carried over if restart/load new files)
680                 m_ketsjiengine->SetGlobalSettings(m_globalSettings);
681
682                 m_engineInitialized = true;
683         }
684
685         return m_engineInitialized;
686 initFailed:
687         BKE_sound_exit();
688         delete m_kxsystem;
689         delete m_networkdevice;
690         delete m_mouse;
691         delete m_keyboard;
692         delete m_rasterizer;
693         delete m_canvas;
694         m_canvas = NULL;
695         m_rasterizer = NULL;
696         m_keyboard = NULL;
697         m_mouse = NULL;
698         m_networkdevice = NULL;
699         m_kxsystem = NULL;
700         return false;
701 }
702
703
704
705 bool GPG_Application::startEngine(void)
706 {
707         if (m_engineRunning) {
708                 return false;
709         }
710         
711         // Temporary hack to disable banner display for NaN approved content.
712         /*
713         m_canvas->SetBannerDisplayEnabled(true);
714         Camera* cam;
715         cam = (Camera*)scene->camera->data;
716         if (cam) {
717         if (((cam->flag) & 48)==48) {
718         m_canvas->SetBannerDisplayEnabled(false);
719         }
720         }
721         else {
722         showError(CString("Camera data invalid."));
723         return false;
724         }
725         */
726         
727         // create a scene converter, create and convert the stratingscene
728         m_sceneconverter = new KX_BlenderSceneConverter(m_maggie, m_ketsjiengine);
729         if (m_sceneconverter)
730         {
731                 STR_String m_kxStartScenename = m_startSceneName.Ptr();
732                 m_ketsjiengine->SetSceneConverter(m_sceneconverter);
733
734                 //      if (always_use_expand_framing)
735                 //              sceneconverter->SetAlwaysUseExpandFraming(true);
736                 if (m_blendermat)
737                         m_sceneconverter->SetMaterials(true);
738                 if (m_blenderglslmat && (m_globalSettings->matmode == GAME_MAT_GLSL))
739                         m_sceneconverter->SetGLSLMaterials(true);
740                 if (m_startScene->gm.flag & GAME_NO_MATERIAL_CACHING)
741                         m_sceneconverter->SetCacheMaterials(false);
742
743                 m_kxStartScene = new KX_Scene(m_keyboard,
744                         m_mouse,
745                         m_networkdevice,
746                         m_kxStartScenename,
747                         m_startScene,
748                         m_canvas);
749                 
750 #ifdef WITH_PYTHON
751                         // some python things
752                         PyObject *gameLogic, *gameLogic_keys;
753                         setupGamePython(m_ketsjiengine, m_kxStartScene, m_maggie, NULL, &gameLogic, &gameLogic_keys, m_argc, m_argv);
754 #endif // WITH_PYTHON
755
756                 //initialize Dome Settings
757                 if (m_startScene->gm.stereoflag == STEREO_DOME)
758                         m_ketsjiengine->InitDome(m_startScene->gm.dome.res, m_startScene->gm.dome.mode, m_startScene->gm.dome.angle, m_startScene->gm.dome.resbuf, m_startScene->gm.dome.tilt, m_startScene->gm.dome.warptext);
759
760                 // initialize 3D Audio Settings
761                 AUD_Device* device = BKE_sound_get_device();
762                 AUD_Device_setSpeedOfSound(device, m_startScene->audio.speed_of_sound);
763                 AUD_Device_setDopplerFactor(device, m_startScene->audio.doppler_factor);
764                 AUD_Device_setDistanceModel(device, AUD_DistanceModel(m_startScene->audio.distance_model));
765
766 #ifdef WITH_PYTHON
767                 // Set the GameLogic.globalDict from marshal'd data, so we can
768                 // load new blend files and keep data in GameLogic.globalDict
769                 loadGamePythonConfig(m_pyGlobalDictString, m_pyGlobalDictString_Length);
770 #endif
771                 m_sceneconverter->ConvertScene(
772                         m_kxStartScene,
773                         m_rasterizer,
774                         m_canvas);
775                 m_ketsjiengine->AddScene(m_kxStartScene);
776                 
777                 // Create a timer that is used to kick the engine
778                 if (!m_frameTimer) {
779                         m_frameTimer = m_system->installTimer(0, kTimerFreq, frameTimerProc, m_mainWindow);
780                 }
781                 m_rasterizer->Init();
782                 m_ketsjiengine->StartEngine(true);
783                 m_engineRunning = true;
784                 
785                 // Set the animation playback rate for ipo's and actions
786                 // the framerate below should patch with FPS macro defined in blendef.h
787                 // Could be in StartEngine set the framerate, we need the scene to do this
788                 Scene *scene= m_kxStartScene->GetBlenderScene(); // needed for macro
789                 m_ketsjiengine->SetAnimFrameRate(FPS);
790         }
791         
792         if (!m_engineRunning)
793         {
794                 stopEngine();
795         }
796         
797         return m_engineRunning;
798 }
799
800
801 void GPG_Application::stopEngine()
802 {
803 #ifdef WITH_PYTHON
804         // GameLogic.globalDict gets converted into a buffer, and sorted in
805         // m_pyGlobalDictString so we can restore after python has stopped
806         // and started between .blend file loads.
807         if (m_pyGlobalDictString) {
808                 delete [] m_pyGlobalDictString;
809                 m_pyGlobalDictString = 0;
810         }
811
812         m_pyGlobalDictString_Length = saveGamePythonConfig(&m_pyGlobalDictString);
813 #endif
814         
815         m_ketsjiengine->StopEngine();
816         m_networkdevice->Disconnect();
817
818         if (m_sceneconverter) {
819                 delete m_sceneconverter;
820                 m_sceneconverter = 0;
821         }
822         if (m_system && m_frameTimer) {
823                 m_system->removeTimer(m_frameTimer);
824                 m_frameTimer = 0;
825         }
826
827         m_engineRunning = false;
828 }
829
830 void GPG_Application::EngineNextFrame()
831 {
832         // Update the state of the game engine
833         if (m_kxsystem && !m_exitRequested)
834         {
835                 // Proceed to next frame
836                 if (m_mainWindow)
837                         m_mainWindow->activateDrawingContext();
838
839                 // first check if we want to exit
840                 m_exitRequested = m_ketsjiengine->GetExitCode();
841                 
842                 // kick the engine
843                 bool renderFrame = m_ketsjiengine->NextFrame();
844                 if (renderFrame && m_mainWindow)
845                 {
846                         // render the frame
847                         m_ketsjiengine->Render();
848                 }
849         }
850         m_exitString = m_ketsjiengine->GetExitString();
851 }
852
853 void GPG_Application::exitEngine()
854 {
855         // We only want to kill the engine if it has been initialized
856         if (!m_engineInitialized)
857                 return;
858
859         BKE_sound_exit();
860         if (m_ketsjiengine)
861         {
862                 stopEngine();
863                 delete m_ketsjiengine;
864                 m_ketsjiengine = 0;
865         }
866         if (m_kxsystem)
867         {
868                 delete m_kxsystem;
869                 m_kxsystem = 0;
870         }
871         if (m_networkdevice)
872         {
873                 delete m_networkdevice;
874                 m_networkdevice = 0;
875         }
876         if (m_mouse)
877         {
878                 delete m_mouse;
879                 m_mouse = 0;
880         }
881         if (m_keyboard)
882         {
883                 delete m_keyboard;
884                 m_keyboard = 0;
885         }
886         if (m_rasterizer)
887         {
888                 delete m_rasterizer;
889                 m_rasterizer = 0;
890         }
891         if (m_canvas)
892         {
893                 delete m_canvas;
894                 m_canvas = 0;
895         }
896
897         GPU_exit();
898
899 #ifdef WITH_PYTHON
900         // Call this after we're sure nothing needs Python anymore (e.g., destructors)
901         exitGamePlayerPythonScripting();
902 #endif
903
904         m_exitRequested = 0;
905         m_engineInitialized = false;
906 }
907
908 bool GPG_Application::handleWheel(GHOST_IEvent* event)
909 {
910         bool handled = false;
911         MT_assert(event);
912         if (m_mouse) 
913         {
914                 GHOST_TEventDataPtr eventData = ((GHOST_IEvent*)event)->getData();
915                 GHOST_TEventWheelData* wheelData = static_cast<GHOST_TEventWheelData*>(eventData);
916                 GPC_MouseDevice::TButtonId button;
917                 if (wheelData->z > 0)
918                         button = GPC_MouseDevice::buttonWheelUp;
919                 else
920                         button = GPC_MouseDevice::buttonWheelDown;
921                 m_mouse->ConvertButtonEvent(button, true);
922                 handled = true;
923         }
924         return handled;
925 }
926
927 bool GPG_Application::handleButton(GHOST_IEvent* event, bool isDown)
928 {
929         bool handled = false;
930         MT_assert(event);
931         if (m_mouse) 
932         {
933                 GHOST_TEventDataPtr eventData = ((GHOST_IEvent*)event)->getData();
934                 GHOST_TEventButtonData* buttonData = static_cast<GHOST_TEventButtonData*>(eventData);
935                 GPC_MouseDevice::TButtonId button;
936                 switch (buttonData->button)
937                 {
938                 case GHOST_kButtonMaskMiddle:
939                         button = GPC_MouseDevice::buttonMiddle;
940                         break;
941                 case GHOST_kButtonMaskRight:
942                         button = GPC_MouseDevice::buttonRight;
943                         break;
944                 case GHOST_kButtonMaskLeft:
945                 default:
946                         button = GPC_MouseDevice::buttonLeft;
947                         break;
948                 }
949                 m_mouse->ConvertButtonEvent(button, isDown);
950                 handled = true;
951         }
952         return handled;
953 }
954
955
956 bool GPG_Application::handleCursorMove(GHOST_IEvent* event)
957 {
958         bool handled = false;
959         MT_assert(event);
960         if (m_mouse && m_mainWindow)
961         {
962                 GHOST_TEventDataPtr eventData = ((GHOST_IEvent*)event)->getData();
963                 GHOST_TEventCursorData* cursorData = static_cast<GHOST_TEventCursorData*>(eventData);
964                 GHOST_TInt32 x, y;
965                 m_mainWindow->screenToClient(cursorData->x, cursorData->y, x, y);
966                 m_mouse->ConvertMoveEvent(x, y);
967                 handled = true;
968         }
969         return handled;
970 }
971
972
973 bool GPG_Application::handleKey(GHOST_IEvent* event, bool isDown)
974 {
975         bool handled = false;
976         MT_assert(event);
977         if (m_keyboard)
978         {
979                 GHOST_TEventDataPtr eventData = ((GHOST_IEvent*)event)->getData();
980                 GHOST_TEventKeyData* keyData = static_cast<GHOST_TEventKeyData*>(eventData);
981                 unsigned int unicode = keyData->utf8_buf[0] ? BLI_str_utf8_as_unicode(keyData->utf8_buf) : keyData->ascii;
982
983                 if (m_keyboard->ToNative(keyData->key) == KX_KetsjiEngine::GetExitKey() && !m_keyboard->m_hookesc && !m_isEmbedded) {
984                         m_exitRequested = KX_EXIT_REQUEST_OUTSIDE;
985                 }
986                 m_keyboard->ConvertEvent(keyData->key, isDown, unicode);
987                 handled = true;
988         }
989         return handled;
990 }
991
992
993
994 static void frameTimerProc(GHOST_ITimerTask* task, GHOST_TUns64 time)
995 {
996         GHOST_IWindow* window = (GHOST_IWindow*)task->getUserData();
997         if (fSystem->validWindow(window)) {
998                 window->invalidate();
999         }
1000 }