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