f0a7bd47ca3f00b34165f7da73c575da6afbee81
[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, 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 GHOST_TUns16 samples)
329 {
330         GHOST_GLSettings glSettings = {0};
331         bool success;
332         // Create the main window
333         //STR_String title ("Blender Player - GHOST");
334         if (stereoVisual)
335                 glSettings.flags |= GHOST_glStereoVisual;
336         glSettings.numOfAASamples = samples;
337
338         m_mainWindow = fSystem->createWindow(title, windowLeft, windowTop, windowWidth, windowHeight, GHOST_kWindowStateNormal,
339                                              GHOST_kDrawingContextTypeOpenGL, glSettings);
340         if (!m_mainWindow) {
341                 printf("error: could not create main window\n");
342                 exit(-1);
343         }
344
345         /* Check the size of the client rectangle of the window and resize the window
346          * so that the client rectangle has the size requested.
347          */
348         m_mainWindow->setClientSize(windowWidth, windowHeight);
349         m_mainWindow->setCursorVisibility(false);
350
351         success = initEngine(m_mainWindow, stereoMode);
352         if (success) {
353                 success = startEngine();
354         }
355         return success;
356 }
357
358 bool GPG_Application::startEmbeddedWindow(
359         STR_String& title,
360         const GHOST_TEmbedderWindowID parentWindow,
361         const bool stereoVisual,
362         const int stereoMode,
363         const GHOST_TUns16 samples)
364 {
365         GHOST_TWindowState state = GHOST_kWindowStateNormal;
366         GHOST_GLSettings glSettings = {0};
367
368         if (stereoVisual)
369                 glSettings.flags |= GHOST_glStereoVisual;
370         glSettings.numOfAASamples = samples;
371
372         if (parentWindow != 0)
373                 state = GHOST_kWindowStateEmbedded;
374         m_mainWindow = fSystem->createWindow(title, 0, 0, 0, 0, state,
375                                              GHOST_kDrawingContextTypeOpenGL, glSettings, parentWindow);
376
377         if (!m_mainWindow) {
378                 printf("error: could not create main window\n");
379                 exit(-1);
380         }
381         m_isEmbedded = true;
382
383         bool success = initEngine(m_mainWindow, stereoMode);
384         if (success) {
385                 success = startEngine();
386         }
387         return success;
388 }
389
390
391 bool GPG_Application::startFullScreen(
392         int width,
393         int height,
394         int bpp,int frequency,
395         const bool stereoVisual,
396         const int stereoMode,
397         const GHOST_TUns16 samples,
398         bool useDesktop)
399 {
400         bool success;
401         GHOST_TUns32 sysWidth=0, sysHeight=0;
402         fSystem->getMainDisplayDimensions(sysWidth, sysHeight);
403         // Create the main window
404         GHOST_DisplaySetting setting;
405         setting.xPixels = (useDesktop) ? sysWidth : width;
406         setting.yPixels = (useDesktop) ? sysHeight : height;
407         setting.bpp = bpp;
408         setting.frequency = frequency;
409
410         fSystem->beginFullScreen(setting, &m_mainWindow, stereoVisual, samples);
411         m_mainWindow->setCursorVisibility(false);
412         /* note that X11 ignores this (it uses a window internally for fullscreen) */
413         m_mainWindow->setState(GHOST_kWindowStateFullScreen);
414
415         success = initEngine(m_mainWindow, stereoMode);
416         if (success) {
417                 success = startEngine();
418         }
419         return success;
420 }
421
422
423
424
425 bool GPG_Application::StartGameEngine(int stereoMode)
426 {
427         bool success = initEngine(m_mainWindow, stereoMode);
428         
429         if (success)
430                 success = startEngine();
431
432         return success;
433 }
434
435
436
437 void GPG_Application::StopGameEngine()
438 {
439         exitEngine();
440 }
441
442
443
444 bool GPG_Application::processEvent(GHOST_IEvent* event)
445 {
446         bool handled = true;
447
448         switch (event->getType())
449         {
450                 case GHOST_kEventUnknown:
451                         break;
452
453                 case GHOST_kEventButtonDown:
454                         handled = handleButton(event, true);
455                         break;
456
457                 case GHOST_kEventButtonUp:
458                         handled = handleButton(event, false);
459                         break;
460                         
461                 case GHOST_kEventWheel:
462                         handled = handleWheel(event);
463                         break;
464
465                 case GHOST_kEventCursorMove:
466                         handled = handleCursorMove(event);
467                         break;
468
469                 case GHOST_kEventKeyDown:
470                         handleKey(event, true);
471                         break;
472
473                 case GHOST_kEventKeyUp:
474                         handleKey(event, false);
475                         break;
476
477
478                 case GHOST_kEventWindowClose:
479                 case GHOST_kEventQuit:
480                         m_exitRequested = KX_EXIT_REQUEST_OUTSIDE;
481                         break;
482
483                 case GHOST_kEventWindowActivate:
484                         handled = false;
485                         break;
486                 case GHOST_kEventWindowDeactivate:
487                         handled = false;
488                         break;
489
490                 // The player now runs as often as it can (repsecting vsync and fixedtime).
491                 // This allows the player to break 100fps, but this code is being left here
492                 // as reference. (see EngineNextFrame)
493                 //case GHOST_kEventWindowUpdate:
494                 //      {
495                 //              GHOST_IWindow* window = event->getWindow();
496                 //              if (!m_system->validWindow(window)) break;
497                 //              // Update the state of the game engine
498                 //              if (m_kxsystem && !m_exitRequested)
499                 //              {
500                 //                      // Proceed to next frame
501                 //                      window->activateDrawingContext();
502
503                 //                      // first check if we want to exit
504                 //                      m_exitRequested = m_ketsjiengine->GetExitCode();
505                 //
506                 //                      // kick the engine
507                 //                      bool renderFrame = m_ketsjiengine->NextFrame();
508                 //                      if (renderFrame)
509                 //                      {
510                 //                              // render the frame
511                 //                              m_ketsjiengine->Render();
512                 //                      }
513                 //              }
514                 //              m_exitString = m_ketsjiengine->GetExitString();
515                 //      }
516                 //      break;
517                 //
518                 case GHOST_kEventWindowSize:
519                         {
520                         GHOST_IWindow* window = event->getWindow();
521                         if (!m_system->validWindow(window)) break;
522                         if (m_canvas) {
523                                 GHOST_Rect bnds;
524                                 window->getClientBounds(bnds);
525                                 m_canvas->Resize(bnds.getWidth(), bnds.getHeight());
526                                 m_ketsjiengine->Resize();
527                         }
528                         }
529                         break;
530                 
531                 default:
532                         handled = false;
533                         break;
534         }
535         return handled;
536 }
537
538
539
540 int GPG_Application::getExitRequested(void)
541 {
542         return m_exitRequested;
543 }
544
545
546 GlobalSettings* GPG_Application::getGlobalSettings(void)
547 {
548         return m_ketsjiengine->GetGlobalSettings();
549 }
550
551
552
553 const STR_String& GPG_Application::getExitString(void)
554 {
555         return m_exitString;
556 }
557
558
559
560 bool GPG_Application::initEngine(GHOST_IWindow* window, const int stereoMode)
561 {
562         if (!m_engineInitialized)
563         {
564                 GPU_init();
565
566                 // get and set the preferences
567                 SYS_SystemHandle syshandle = SYS_GetSystem();
568                 if (!syshandle)
569                         return false;
570                 
571                 // SYS_WriteCommandLineInt(syshandle, "fixedtime", 0);
572                 // SYS_WriteCommandLineInt(syshandle, "vertexarrays",1);
573                 GameData *gm= &m_startScene->gm;
574                 bool properties = (SYS_GetCommandLineInt(syshandle, "show_properties", 0) != 0);
575                 bool profile = (SYS_GetCommandLineInt(syshandle, "show_profile", 0) != 0);
576
577                 bool showPhysics = (gm->flag & GAME_SHOW_PHYSICS);
578                 SYS_WriteCommandLineInt(syshandle, "show_physics", showPhysics);
579
580                 bool fixed_framerate= (SYS_GetCommandLineInt(syshandle, "fixedtime", (gm->flag & GAME_ENABLE_ALL_FRAMES)) != 0);
581                 bool frameRate = (SYS_GetCommandLineInt(syshandle, "show_framerate", 0) != 0);
582                 bool useLists = (SYS_GetCommandLineInt(syshandle, "displaylists", gm->flag & GAME_DISPLAY_LISTS) != 0) && GPU_display_list_support();
583                 bool nodepwarnings = (SYS_GetCommandLineInt(syshandle, "ignore_deprecation_warnings", 1) != 0);
584                 bool restrictAnimFPS = (gm->flag & GAME_RESTRICT_ANIM_UPDATES) != 0;
585
586                 if (GLEW_ARB_multitexture && GLEW_VERSION_1_1)
587                         m_blendermat = (SYS_GetCommandLineInt(syshandle, "blender_material", 1) != 0);
588
589                 if (GPU_glsl_support())
590                         m_blenderglslmat = (SYS_GetCommandLineInt(syshandle, "blender_glsl_material", 1) != 0);
591                 else if (m_globalSettings->matmode == GAME_MAT_GLSL)
592                         m_blendermat = false;
593
594                 // create the canvas, rasterizer and rendertools
595                 m_canvas = new GPG_Canvas(window);
596                 if (!m_canvas)
597                         return false;
598
599                 if (gm->vsync == VSYNC_ADAPTIVE)
600                         m_canvas->SetSwapInterval(-1);
601                 else
602                         m_canvas->SetSwapInterval((gm->vsync == VSYNC_ON) ? 1 : 0);
603
604                 m_canvas->Init();
605                 if (gm->flag & GAME_SHOW_MOUSE)
606                         m_canvas->SetMouseState(RAS_ICanvas::MOUSE_NORMAL);
607                 
608                 RAS_STORAGE_TYPE raster_storage = RAS_AUTO_STORAGE;
609
610                 if (gm->raster_storage == RAS_STORE_VBO) {
611                         raster_storage = RAS_VBO;
612                 }
613                 else if (gm->raster_storage == RAS_STORE_VA) {
614                         raster_storage = RAS_VA;
615                 }
616                 //Don't use displaylists with VBOs
617                 //If auto starts using VBOs, make sure to check for that here
618                 if (useLists && raster_storage != RAS_VBO)
619                         m_rasterizer = new RAS_ListRasterizer(m_canvas, true, raster_storage);
620                 else
621                         m_rasterizer = new RAS_OpenGLRasterizer(m_canvas, raster_storage);
622
623                 /* Stereo parameters - Eye Separation from the UI - stereomode from the command-line/UI */
624                 m_rasterizer->SetStereoMode((RAS_IRasterizer::StereoMode) stereoMode);
625                 m_rasterizer->SetEyeSeparation(m_startScene->gm.eyeseparation);
626                 
627                 if (!m_rasterizer)
628                         goto initFailed;
629                                                 
630                 // create the inputdevices
631                 m_keyboard = new GPG_KeyboardDevice();
632                 if (!m_keyboard)
633                         goto initFailed;
634                         
635                 m_mouse = new GPC_MouseDevice();
636                 if (!m_mouse)
637                         goto initFailed;
638                         
639                 // create a networkdevice
640                 m_networkdevice = new NG_LoopBackNetworkDeviceInterface();
641                 if (!m_networkdevice)
642                         goto initFailed;
643                         
644                 BKE_sound_init(m_maggie);
645
646                 // create a ketsjisystem (only needed for timing and stuff)
647                 m_kxsystem = new GPG_System (m_system);
648                 if (!m_kxsystem)
649                         goto initFailed;
650                 
651                 // create the ketsjiengine
652                 m_ketsjiengine = new KX_KetsjiEngine(m_kxsystem);
653                 
654                 // set the devices
655                 m_ketsjiengine->SetKeyboardDevice(m_keyboard);
656                 m_ketsjiengine->SetMouseDevice(m_mouse);
657                 m_ketsjiengine->SetNetworkDevice(m_networkdevice);
658                 m_ketsjiengine->SetCanvas(m_canvas);
659                 m_ketsjiengine->SetRasterizer(m_rasterizer);
660
661                 KX_KetsjiEngine::SetExitKey(ConvertKeyCode(gm->exitkey));
662 #ifdef WITH_PYTHON
663                 CValue::SetDeprecationWarnings(nodepwarnings);
664 #else
665                 (void)nodepwarnings;
666 #endif
667
668                 m_ketsjiengine->SetUseFixedTime(fixed_framerate);
669                 m_ketsjiengine->SetTimingDisplay(frameRate, profile, properties);
670                 m_ketsjiengine->SetRestrictAnimationFPS(restrictAnimFPS);
671
672                 //set the global settings (carried over if restart/load new files)
673                 m_ketsjiengine->SetGlobalSettings(m_globalSettings);
674
675                 m_engineInitialized = true;
676         }
677
678         return m_engineInitialized;
679 initFailed:
680         BKE_sound_exit();
681         delete m_kxsystem;
682         delete m_networkdevice;
683         delete m_mouse;
684         delete m_keyboard;
685         delete m_rasterizer;
686         delete m_canvas;
687         m_canvas = NULL;
688         m_rasterizer = NULL;
689         m_keyboard = NULL;
690         m_mouse = NULL;
691         m_networkdevice = NULL;
692         m_kxsystem = NULL;
693         return false;
694 }
695
696
697
698 bool GPG_Application::startEngine(void)
699 {
700         if (m_engineRunning) {
701                 return false;
702         }
703         
704         // Temporary hack to disable banner display for NaN approved content.
705         /*
706         m_canvas->SetBannerDisplayEnabled(true);
707         Camera* cam;
708         cam = (Camera*)scene->camera->data;
709         if (cam) {
710         if (((cam->flag) & 48)==48) {
711         m_canvas->SetBannerDisplayEnabled(false);
712         }
713         }
714         else {
715         showError(CString("Camera data invalid."));
716         return false;
717         }
718         */
719         
720         // create a scene converter, create and convert the stratingscene
721         m_sceneconverter = new KX_BlenderSceneConverter(m_maggie, m_ketsjiengine);
722         if (m_sceneconverter)
723         {
724                 STR_String m_kxStartScenename = m_startSceneName.Ptr();
725                 m_ketsjiengine->SetSceneConverter(m_sceneconverter);
726
727                 //      if (always_use_expand_framing)
728                 //              sceneconverter->SetAlwaysUseExpandFraming(true);
729                 if (m_blendermat)
730                         m_sceneconverter->SetMaterials(true);
731                 if (m_blenderglslmat && (m_globalSettings->matmode == GAME_MAT_GLSL))
732                         m_sceneconverter->SetGLSLMaterials(true);
733                 if (m_startScene->gm.flag & GAME_NO_MATERIAL_CACHING)
734                         m_sceneconverter->SetCacheMaterials(false);
735
736                 m_kxStartScene = new KX_Scene(m_keyboard,
737                         m_mouse,
738                         m_networkdevice,
739                         m_kxStartScenename,
740                         m_startScene,
741                         m_canvas);
742                 
743 #ifdef WITH_PYTHON
744                         // some python things
745                         PyObject *gameLogic, *gameLogic_keys;
746                         setupGamePython(m_ketsjiengine, m_kxStartScene, m_maggie, NULL, &gameLogic, &gameLogic_keys, m_argc, m_argv);
747 #endif // WITH_PYTHON
748
749                 //initialize Dome Settings
750                 if (m_startScene->gm.stereoflag == STEREO_DOME)
751                         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);
752
753                 // initialize 3D Audio Settings
754                 AUD_Device* device = BKE_sound_get_device();
755                 AUD_Device_setSpeedOfSound(device, m_startScene->audio.speed_of_sound);
756                 AUD_Device_setDopplerFactor(device, m_startScene->audio.doppler_factor);
757                 AUD_Device_setDistanceModel(device, AUD_DistanceModel(m_startScene->audio.distance_model));
758
759 #ifdef WITH_PYTHON
760                 // Set the GameLogic.globalDict from marshal'd data, so we can
761                 // load new blend files and keep data in GameLogic.globalDict
762                 loadGamePythonConfig(m_pyGlobalDictString, m_pyGlobalDictString_Length);
763 #endif
764                 m_sceneconverter->ConvertScene(
765                         m_kxStartScene,
766                         m_rasterizer,
767                         m_canvas);
768                 m_ketsjiengine->AddScene(m_kxStartScene);
769                 
770                 // Create a timer that is used to kick the engine
771                 if (!m_frameTimer) {
772                         m_frameTimer = m_system->installTimer(0, kTimerFreq, frameTimerProc, m_mainWindow);
773                 }
774                 m_rasterizer->Init();
775                 m_ketsjiengine->StartEngine(true);
776                 m_engineRunning = true;
777                 
778                 // Set the animation playback rate for ipo's and actions
779                 // the framerate below should patch with FPS macro defined in blendef.h
780                 // Could be in StartEngine set the framerate, we need the scene to do this
781                 Scene *scene= m_kxStartScene->GetBlenderScene(); // needed for macro
782                 m_ketsjiengine->SetAnimFrameRate(FPS);
783         }
784         
785         if (!m_engineRunning)
786         {
787                 stopEngine();
788         }
789         
790         return m_engineRunning;
791 }
792
793
794 void GPG_Application::stopEngine()
795 {
796 #ifdef WITH_PYTHON
797         // GameLogic.globalDict gets converted into a buffer, and sorted in
798         // m_pyGlobalDictString so we can restore after python has stopped
799         // and started between .blend file loads.
800         if (m_pyGlobalDictString) {
801                 delete [] m_pyGlobalDictString;
802                 m_pyGlobalDictString = 0;
803         }
804
805         m_pyGlobalDictString_Length = saveGamePythonConfig(&m_pyGlobalDictString);
806 #endif
807         
808         m_ketsjiengine->StopEngine();
809         m_networkdevice->Disconnect();
810
811         if (m_sceneconverter) {
812                 delete m_sceneconverter;
813                 m_sceneconverter = 0;
814         }
815         if (m_system && m_frameTimer) {
816                 m_system->removeTimer(m_frameTimer);
817                 m_frameTimer = 0;
818         }
819
820         m_engineRunning = false;
821 }
822
823 void GPG_Application::EngineNextFrame()
824 {
825         // Update the state of the game engine
826         if (m_kxsystem && !m_exitRequested)
827         {
828                 // Proceed to next frame
829                 if (m_mainWindow)
830                         m_mainWindow->activateDrawingContext();
831
832                 // first check if we want to exit
833                 m_exitRequested = m_ketsjiengine->GetExitCode();
834                 
835                 // kick the engine
836                 bool renderFrame = m_ketsjiengine->NextFrame();
837                 if (renderFrame && m_mainWindow)
838                 {
839                         // render the frame
840                         m_ketsjiengine->Render();
841                 }
842         }
843         m_exitString = m_ketsjiengine->GetExitString();
844 }
845
846 void GPG_Application::exitEngine()
847 {
848         // We only want to kill the engine if it has been initialized
849         if (!m_engineInitialized)
850                 return;
851
852         BKE_sound_exit();
853         if (m_ketsjiengine)
854         {
855                 stopEngine();
856                 delete m_ketsjiengine;
857                 m_ketsjiengine = 0;
858         }
859         if (m_kxsystem)
860         {
861                 delete m_kxsystem;
862                 m_kxsystem = 0;
863         }
864         if (m_networkdevice)
865         {
866                 delete m_networkdevice;
867                 m_networkdevice = 0;
868         }
869         if (m_mouse)
870         {
871                 delete m_mouse;
872                 m_mouse = 0;
873         }
874         if (m_keyboard)
875         {
876                 delete m_keyboard;
877                 m_keyboard = 0;
878         }
879         if (m_rasterizer)
880         {
881                 delete m_rasterizer;
882                 m_rasterizer = 0;
883         }
884         if (m_canvas)
885         {
886                 delete m_canvas;
887                 m_canvas = 0;
888         }
889
890         GPU_exit();
891
892 #ifdef WITH_PYTHON
893         // Call this after we're sure nothing needs Python anymore (e.g., destructors)
894         exitGamePlayerPythonScripting();
895 #endif
896
897         m_exitRequested = 0;
898         m_engineInitialized = false;
899 }
900
901 bool GPG_Application::handleWheel(GHOST_IEvent* event)
902 {
903         bool handled = false;
904         MT_assert(event);
905         if (m_mouse) 
906         {
907                 GHOST_TEventDataPtr eventData = ((GHOST_IEvent*)event)->getData();
908                 GHOST_TEventWheelData* wheelData = static_cast<GHOST_TEventWheelData*>(eventData);
909                 GPC_MouseDevice::TButtonId button;
910                 if (wheelData->z > 0)
911                         button = GPC_MouseDevice::buttonWheelUp;
912                 else
913                         button = GPC_MouseDevice::buttonWheelDown;
914                 m_mouse->ConvertButtonEvent(button, true);
915                 handled = true;
916         }
917         return handled;
918 }
919
920 bool GPG_Application::handleButton(GHOST_IEvent* event, bool isDown)
921 {
922         bool handled = false;
923         MT_assert(event);
924         if (m_mouse) 
925         {
926                 GHOST_TEventDataPtr eventData = ((GHOST_IEvent*)event)->getData();
927                 GHOST_TEventButtonData* buttonData = static_cast<GHOST_TEventButtonData*>(eventData);
928                 GPC_MouseDevice::TButtonId button;
929                 switch (buttonData->button)
930                 {
931                 case GHOST_kButtonMaskMiddle:
932                         button = GPC_MouseDevice::buttonMiddle;
933                         break;
934                 case GHOST_kButtonMaskRight:
935                         button = GPC_MouseDevice::buttonRight;
936                         break;
937                 case GHOST_kButtonMaskLeft:
938                 default:
939                         button = GPC_MouseDevice::buttonLeft;
940                         break;
941                 }
942                 m_mouse->ConvertButtonEvent(button, isDown);
943                 handled = true;
944         }
945         return handled;
946 }
947
948
949 bool GPG_Application::handleCursorMove(GHOST_IEvent* event)
950 {
951         bool handled = false;
952         MT_assert(event);
953         if (m_mouse && m_mainWindow)
954         {
955                 GHOST_TEventDataPtr eventData = ((GHOST_IEvent*)event)->getData();
956                 GHOST_TEventCursorData* cursorData = static_cast<GHOST_TEventCursorData*>(eventData);
957                 GHOST_TInt32 x, y;
958                 m_mainWindow->screenToClient(cursorData->x, cursorData->y, x, y);
959                 m_mouse->ConvertMoveEvent(x, y);
960                 handled = true;
961         }
962         return handled;
963 }
964
965
966 bool GPG_Application::handleKey(GHOST_IEvent* event, bool isDown)
967 {
968         bool handled = false;
969         MT_assert(event);
970         if (m_keyboard)
971         {
972                 GHOST_TEventDataPtr eventData = ((GHOST_IEvent*)event)->getData();
973                 GHOST_TEventKeyData* keyData = static_cast<GHOST_TEventKeyData*>(eventData);
974                 unsigned int unicode = keyData->utf8_buf[0] ? BLI_str_utf8_as_unicode(keyData->utf8_buf) : keyData->ascii;
975
976                 if (m_keyboard->ToNative(keyData->key) == KX_KetsjiEngine::GetExitKey() && !m_keyboard->m_hookesc && !m_isEmbedded) {
977                         m_exitRequested = KX_EXIT_REQUEST_OUTSIDE;
978                 }
979                 m_keyboard->ConvertEvent(keyData->key, isDown, unicode);
980                 handled = true;
981         }
982         return handled;
983 }
984
985
986
987 static void frameTimerProc(GHOST_ITimerTask* task, GHOST_TUns64 time)
988 {
989         GHOST_IWindow* window = (GHOST_IWindow*)task->getUserData();
990         if (fSystem->validWindow(window)) {
991                 window->invalidate();
992         }
993 }