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