This patch spawns from this game engine issue:
[blender-staging.git] / source / gameengine / BlenderRoutines / BL_KetsjiEmbedStart.cpp
1 /**
2  * $Id$
3  *
4  * ***** BEGIN GPL/BL DUAL LICENSE BLOCK *****
5  *
6  * This program is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU General Public License
8  * as published by the Free Software Foundation; either version 2
9  * of the License, or (at your option) any later version. The Blender
10  * Foundation also sells licenses for use in proprietary software under
11  * the Blender License.  See http://www.blender.org/BL/ for information
12  * about this.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program; if not, write to the Free Software Foundation,
21  * Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
22  *
23  * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
24  * All rights reserved.
25  *
26  * The Original Code is: all of this file.
27  *
28  * Contributor(s): none yet.
29  *
30  * ***** END GPL/BL DUAL LICENSE BLOCK *****
31  * Blender's Ketsji startpoint
32  */
33
34 #ifdef HAVE_CONFIG_H
35 #include <config.h>
36 #endif
37
38 #include <signal.h>
39 #include <stdlib.h>
40
41 #ifdef WIN32
42 // don't show stl-warnings
43 #pragma warning (disable:4786)
44 #endif
45
46 #ifdef __APPLE__
47 #define GL_GLEXT_LEGACY 1
48 #endif 
49
50 #include "KX_BlenderGL.h"
51 #include "KX_BlenderCanvas.h"
52 #include "KX_BlenderKeyboardDevice.h"
53 #include "KX_BlenderMouseDevice.h"
54 #include "KX_BlenderRenderTools.h"
55 #include "KX_BlenderSystem.h"
56 #include "BL_Material.h"
57
58 #include "KX_KetsjiEngine.h"
59 #include "KX_BlenderSceneConverter.h"
60 #include "KX_PythonInit.h"
61 #include "KX_PyConstraintBinding.h"
62
63 #include "RAS_OpenGLRasterizer.h"
64 #include "RAS_VAOpenGLRasterizer.h"
65 #include "RAS_ListRasterizer.h"
66 #include "RAS_GLExtensionManager.h"
67
68 #include "NG_LoopBackNetworkDeviceInterface.h"
69 #include "SND_DeviceManager.h"
70
71 #include "SYS_System.h"
72
73         /***/
74
75 #include "DNA_view3d_types.h"
76 #include "DNA_screen_types.h"
77 #include "BKE_global.h"
78 #include "BIF_screen.h"
79 #include "BIF_scrarea.h"
80
81 #include "BKE_main.h"   
82 #include "BLI_blenlib.h"
83 #include "BLO_readfile.h"
84 #include "DNA_scene_types.h"
85         /***/
86
87 #ifdef __cplusplus
88 extern "C" {
89 #endif
90 #include "BSE_headerbuttons.h"
91 void update_for_newframe();
92 #ifdef __cplusplus
93 }
94 #endif
95
96 static BlendFileData *load_game_data(char *filename) {
97         BlendReadError error;
98         //this doesn't work anymore for relative paths, so use BLO_read_from_memory instead
99         //BlendFileData *bfd= BLO_read_from_file(filename, &error);
100         FILE* file = fopen(filename,"rb");
101         BlendFileData *bfd  = 0;
102         if (file)
103         {
104                 fseek(file, 0L, SEEK_END);
105                 int len= ftell(file);
106                 fseek(file, 0L, SEEK_SET);      
107                 char* filebuffer= new char[len];//MEM_mallocN(len, "text_buffer");
108                 int sizeread = fread(filebuffer,len,1,file);
109                 if (sizeread==1){
110                         bfd = BLO_read_from_memory(filebuffer, len, &error);
111                 } else {
112                         error = BRE_UNABLE_TO_READ;
113                 }
114                 fclose(file);
115                 // the memory is not released in BLO_read_from_memory, must do it here
116                 delete filebuffer;
117         } else {
118                 error = BRE_UNABLE_TO_OPEN;
119         }
120
121         if (!bfd) {
122                 printf("Loading %s failed: %s\n", filename, BLO_bre_as_string(error));
123         }
124         
125         return bfd;
126 }
127
128 extern "C" void StartKetsjiShell(struct ScrArea *area,
129                                                                  char* scenename,
130                                                                  struct Main* maggie1,
131                                                                  struct SpaceIpo *sipo,
132                                                                  int always_use_expand_framing)
133 {
134         int exitrequested = KX_EXIT_REQUEST_NO_REQUEST;
135         
136         Main* blenderdata = maggie1;
137
138         char* startscenename = scenename;
139         char pathname[160];
140         strcpy (pathname, blenderdata->name);
141         STR_String exitstring = "";
142         BlendFileData *bfd= NULL;
143
144         // Acquire Python's GIL (global interpreter lock)
145         // so we can safely run Python code and API calls
146         PyGILState_STATE gilstate = PyGILState_Ensure();
147
148         bgl::InitExtensions(1);
149         
150         do
151         {
152                 View3D *v3d= (View3D*) area->spacedata.first;
153                 
154                 // get some preferences
155                 SYS_SystemHandle syshandle = SYS_GetSystem();
156                 bool properties = (SYS_GetCommandLineInt(syshandle, "show_properties", 0) != 0);
157                 bool usefixed = (SYS_GetCommandLineInt(syshandle, "fixedtime", 0) != 0);
158                 bool profile = (SYS_GetCommandLineInt(syshandle, "show_profile", 0) != 0);
159                 bool frameRate = (SYS_GetCommandLineInt(syshandle, "show_framerate", 0) != 0);
160                 bool game2ipo = (SYS_GetCommandLineInt(syshandle, "game2ipo", 0) != 0);
161                 bool displaylists = (SYS_GetCommandLineInt(syshandle, "displaylists", 0) != 0);
162                 bool usemat = false;
163                 
164                 #if defined(GL_ARB_multitexture) && defined(WITH_GLEXT)
165                 if (!getenv("WITHOUT_GLEXT")) {
166                         if(bgl::RAS_EXT_support._ARB_multitexture && bgl::QueryVersion(1, 1)) {
167                                 usemat = (SYS_GetCommandLineInt(syshandle, "blender_material", 0) != 0);
168                                 int unitmax=0;
169                                 glGetIntegerv(GL_MAX_TEXTURE_UNITS_ARB, (GLint*)&unitmax);
170                                 bgl::max_texture_units = MAXTEX>unitmax?unitmax:MAXTEX;
171                                 //std::cout << "using(" << bgl::max_texture_units << ") of(" << unitmax << ") texture units." << std::endl;
172                         } else {
173                                 bgl::max_texture_units = 0;
174                         }
175                 }
176                 #endif
177
178
179                 // create the canvas, rasterizer and rendertools
180                 RAS_ICanvas* canvas = new KX_BlenderCanvas(area);
181                 canvas->SetMouseState(RAS_ICanvas::MOUSE_INVISIBLE);
182                 RAS_IRenderTools* rendertools = new KX_BlenderRenderTools();
183                 RAS_IRasterizer* rasterizer = NULL;
184                 
185                 // let's see if we want to use vertexarrays or not
186                 int usevta = SYS_GetCommandLineInt(syshandle,"vertexarrays",1);
187                 bool useVertexArrays = (usevta > 0);
188                 
189                 bool lock_arrays = (displaylists && useVertexArrays);
190
191                 if(displaylists){
192                         if (useVertexArrays) {
193                                 rasterizer = new RAS_ListRasterizer(canvas, true, lock_arrays);
194                         } else {
195                                 rasterizer = new RAS_ListRasterizer(canvas);
196                         }
197                 } else if (useVertexArrays && bgl::QueryVersion(1, 1))
198                         rasterizer = new RAS_VAOpenGLRasterizer(canvas, lock_arrays);
199                 else
200                         rasterizer = new RAS_OpenGLRasterizer(canvas);
201                 
202                 // create the inputdevices
203                 KX_BlenderKeyboardDevice* keyboarddevice = new KX_BlenderKeyboardDevice();
204                 KX_BlenderMouseDevice* mousedevice = new KX_BlenderMouseDevice();
205                 
206                 // create a networkdevice
207                 NG_NetworkDeviceInterface* networkdevice = new
208                         NG_LoopBackNetworkDeviceInterface();
209                 
210                 // get an audiodevice
211                 SND_DeviceManager::Subscribe();
212                 SND_IAudioDevice* audiodevice = SND_DeviceManager::Instance();
213                 audiodevice->UseCD();
214                 
215                 // create a ketsji/blendersystem (only needed for timing and stuff)
216                 KX_BlenderSystem* kxsystem = new KX_BlenderSystem();
217                 
218                 // create the ketsjiengine
219                 KX_KetsjiEngine* ketsjiengine = new KX_KetsjiEngine(kxsystem);
220                 
221                 // set the devices
222                 ketsjiengine->SetKeyboardDevice(keyboarddevice);
223                 ketsjiengine->SetMouseDevice(mousedevice);
224                 ketsjiengine->SetNetworkDevice(networkdevice);
225                 ketsjiengine->SetCanvas(canvas);
226                 ketsjiengine->SetRenderTools(rendertools);
227                 ketsjiengine->SetRasterizer(rasterizer);
228                 ketsjiengine->SetNetworkDevice(networkdevice);
229                 ketsjiengine->SetAudioDevice(audiodevice);
230                 ketsjiengine->SetUseFixedTime(usefixed);
231                 ketsjiengine->SetTimingDisplay(frameRate, profile, properties);
232
233                 
234         
235                 // some blender stuff
236                 MT_CmMatrix4x4 projmat;
237                 MT_CmMatrix4x4 viewmat;
238                 int i;
239                 
240                 for (i = 0; i < 16; i++)
241                 {
242                         float *viewmat_linear= (float*) v3d->viewmat;
243                         viewmat.setElem(i, viewmat_linear[i]);
244                 }
245                 for (i = 0; i < 16; i++)
246                 {
247                         float *projmat_linear = (float*) area->winmat;
248                         projmat.setElem(i, projmat_linear[i]);
249                 }
250                 
251                 float camzoom = (1.41421 + (v3d->camzoom / 50.0));
252                 camzoom *= camzoom;
253                 camzoom = 4.0 / camzoom;
254                 
255                 ketsjiengine->SetDrawType(v3d->drawtype);
256                 ketsjiengine->SetCameraZoom(camzoom);
257                 
258                 // if we got an exitcode 3 (KX_EXIT_REQUEST_START_OTHER_GAME) load a different file
259                 if (exitrequested == KX_EXIT_REQUEST_START_OTHER_GAME || exitrequested == KX_EXIT_REQUEST_RESTART_GAME)
260                 {
261                         exitrequested = KX_EXIT_REQUEST_NO_REQUEST;
262                         if (bfd) BLO_blendfiledata_free(bfd);
263                         
264                         char basedpath[240];
265                         // base the actuator filename with respect
266                         // to the original file working directory
267                         if (exitstring != "")
268                                 strcpy(basedpath, exitstring.Ptr());
269
270                         BLI_convertstringcode(basedpath, pathname, 0);
271                         bfd = load_game_data(basedpath);
272                         
273                         // if it wasn't loaded, try it forced relative
274                         if (!bfd)
275                         {
276                                 // just add "//" in front of it
277                                 char temppath[242];
278                                 strcpy(temppath, "//");
279                                 strcat(temppath, basedpath);
280                                 
281                                 BLI_convertstringcode(temppath, pathname, 0);
282                                 bfd = load_game_data(temppath);
283                         }
284                         
285                         // if we got a loaded blendfile, proceed
286                         if (bfd)
287                         {
288                                 blenderdata = bfd->main;
289                                 startscenename = bfd->curscene->id.name + 2;
290                         }
291                         // else forget it, we can't find it
292                         else
293                         {
294                                 exitrequested = KX_EXIT_REQUEST_QUIT_GAME;
295                         }
296                 }
297                 
298                 Scene *blscene = NULL;
299                 if (!bfd)
300                 {
301                         blscene = (Scene*) blenderdata->scene.first;
302                         for (Scene *sce= (Scene*) blenderdata->scene.first; sce; sce= (Scene*) sce->id.next)
303                         {
304                                 if (startscenename == (sce->id.name+2))
305                                 {
306                                         blscene = sce;
307                                         break;
308                                 }
309                         }
310                 } else {
311                         blscene = bfd->curscene;
312                 }
313
314                 if (blscene)
315                 {
316                         int startFrame = blscene->r.cfra;
317                         ketsjiengine->SetGame2IpoMode(game2ipo,startFrame);
318                 }
319
320
321                 // Quad buffered needs a special window.
322                 if (blscene->r.stereomode != RAS_IRasterizer::RAS_STEREO_QUADBUFFERED)
323                         rasterizer->SetStereoMode((RAS_IRasterizer::StereoMode) blscene->r.stereomode);
324                 
325                 if (exitrequested != KX_EXIT_REQUEST_QUIT_GAME)
326                 {
327                         if (v3d->persp != V3D_CAMOB)
328                         {
329                                 ketsjiengine->EnableCameraOverride(startscenename);
330                                 ketsjiengine->SetCameraOverrideUseOrtho((v3d->persp == V3D_ORTHO));
331                                 ketsjiengine->SetCameraOverrideProjectionMatrix(projmat);
332                                 ketsjiengine->SetCameraOverrideViewMatrix(viewmat);
333                         }
334                         
335                         // create a scene converter, create and convert the startingscene
336                         KX_ISceneConverter* sceneconverter = new KX_BlenderSceneConverter(blenderdata,sipo, ketsjiengine);
337                         ketsjiengine->SetSceneConverter(sceneconverter);
338                         sceneconverter->addInitFromFrame=false;
339                         if (always_use_expand_framing)
340                                 sceneconverter->SetAlwaysUseExpandFraming(true);
341                         
342                         if(usemat)
343                                 sceneconverter->SetMaterials(true);
344                                         
345                         KX_Scene* startscene = new KX_Scene(keyboarddevice,
346                                 mousedevice,
347                                 networkdevice,
348                                 audiodevice,
349                                 startscenename);
350                         
351                         // some python things
352                         PyObject* dictionaryobject = initGamePythonScripting("Ketsji", psl_Lowest);
353                         ketsjiengine->SetPythonDictionary(dictionaryobject);
354                         initRasterizer(rasterizer, canvas);
355                         PyObject *gameLogic = initGameLogic(startscene);
356                         initGameKeys();
357                         initPythonConstraintBinding();
358
359
360                         if (sceneconverter)
361                         {
362                                 // convert and add scene
363                                 sceneconverter->ConvertScene(
364                                         startscenename,
365                                         startscene,
366                                         dictionaryobject,
367                                         keyboarddevice,
368                                         rendertools,
369                                         canvas);
370                                 ketsjiengine->AddScene(startscene);
371                                 
372                                 // init the rasterizer
373                                 rasterizer->Init();
374                                 
375                                 // start the engine
376                                 ketsjiengine->StartEngine(true);
377                                 
378                                 // the mainloop
379                                 while (!exitrequested)
380                                 {
381                                         // first check if we want to exit
382                                         exitrequested = ketsjiengine->GetExitCode();
383                                         
384                                         // kick the engine
385                                         bool render = ketsjiengine->NextFrame();
386                                         
387                                         if (render)
388                                         {
389                                                 // render the frame
390                                                 ketsjiengine->Render();
391                                         }
392                                         
393                                         // test for the ESC key
394                                         while (qtest())
395                                         {
396                                                 short val; 
397                                                 unsigned short event = extern_qread(&val);
398                                                 
399                                                 if (keyboarddevice->ConvertBlenderEvent(event,val))
400                                                         exitrequested = KX_EXIT_REQUEST_BLENDER_ESC;
401                                                 
402                                                         /* Coordinate conversion... where
403                                                         * should this really be?
404                                                 */
405                                                 if (event==MOUSEX) {
406                                                         val = val - scrarea_get_win_x(area);
407                                                 } else if (event==MOUSEY) {
408                                                         val = scrarea_get_win_height(area) - (val - scrarea_get_win_y(area)) - 1;
409                                                 }
410                                                 
411                                                 mousedevice->ConvertBlenderEvent(event,val);
412                                         }
413                                 }
414                                 exitstring = ketsjiengine->GetExitString();
415                                 
416                                 // when exiting the mainloop
417                                 dictionaryClearByHand(gameLogic);
418                                 ketsjiengine->StopEngine();
419                                 exitGamePythonScripting();
420                                 networkdevice->Disconnect();
421                         }
422                         if (sceneconverter)
423                         {
424                                 delete sceneconverter;
425                                 sceneconverter = NULL;
426                         }
427                 }
428                 // set the cursor back to normal
429                 canvas->SetMouseState(RAS_ICanvas::MOUSE_NORMAL);
430                 
431                 // clean up some stuff
432                 audiodevice->StopCD();
433                 
434                 if (ketsjiengine)
435                 {
436                         delete ketsjiengine;
437                         ketsjiengine = NULL;
438                 }
439                 if (kxsystem)
440                 {
441                         delete kxsystem;
442                         kxsystem = NULL;
443                 }
444                 if (networkdevice)
445                 {
446                         delete networkdevice;
447                         networkdevice = NULL;
448                 }
449                 if (keyboarddevice)
450                 {
451                         delete keyboarddevice;
452                         keyboarddevice = NULL;
453                 }
454                 if (mousedevice)
455                 {
456                         delete mousedevice;
457                         mousedevice = NULL;
458                 }
459                 if (rasterizer)
460                 {
461                         delete rasterizer;
462                         rasterizer = NULL;
463                 }
464                 if (rendertools)
465                 {
466                         delete rendertools;
467                         rendertools = NULL;
468                 }
469                 if (canvas)
470                 {
471                         delete canvas;
472                         canvas = NULL;
473                 }
474                 SND_DeviceManager::Unsubscribe();
475         
476         } while (exitrequested == KX_EXIT_REQUEST_RESTART_GAME || exitrequested == KX_EXIT_REQUEST_START_OTHER_GAME);
477
478         if (bfd) BLO_blendfiledata_free(bfd);
479
480         // Release Python's GIL
481         PyGILState_Release(gilstate);
482 }
483
484 extern "C" void StartKetsjiShellSimulation(struct ScrArea *area,
485                                                                  char* scenename,
486                                                                  struct Main* maggie,
487                                                                  struct SpaceIpo *sipo,
488                                                                  int always_use_expand_framing)
489 {
490     int exitrequested = KX_EXIT_REQUEST_NO_REQUEST;
491
492         Main* blenderdata = maggie;
493
494         char* startscenename = scenename;
495         char pathname[160];
496         strcpy (pathname, maggie->name);
497         STR_String exitstring = "";
498         BlendFileData *bfd= NULL;
499
500         // Acquire Python's GIL (global interpreter lock)
501         // so we can safely run Python code and API calls
502         PyGILState_STATE gilstate = PyGILState_Ensure();
503
504         bgl::InitExtensions(1);
505
506         do
507         {
508
509                 // get some preferences
510                 SYS_SystemHandle syshandle = SYS_GetSystem();
511                 bool properties = (SYS_GetCommandLineInt(syshandle, "show_properties", 0) != 0);
512                 bool usefixed = (SYS_GetCommandLineInt(syshandle, "fixedtime", 0) != 0);
513                 bool profile = (SYS_GetCommandLineInt(syshandle, "show_profile", 0) != 0);
514                 bool frameRate = (SYS_GetCommandLineInt(syshandle, "show_framerate", 0) != 0);
515                 bool game2ipo = true;//(SYS_GetCommandLineInt(syshandle, "game2ipo", 0) != 0);
516                 bool displaylists = (SYS_GetCommandLineInt(syshandle, "displaylists", 0) != 0);
517                 bool usemat = false;
518
519                 // create the canvas, rasterizer and rendertools
520                 RAS_ICanvas* canvas = new KX_BlenderCanvas(area);
521                 //canvas->SetMouseState(RAS_ICanvas::MOUSE_INVISIBLE);
522                 RAS_IRenderTools* rendertools = new KX_BlenderRenderTools();
523                 RAS_IRasterizer* rasterizer = NULL;
524
525                 // let's see if we want to use vertexarrays or not
526                 int usevta = SYS_GetCommandLineInt(syshandle,"vertexarrays",1);
527                 bool useVertexArrays = (usevta > 0);
528
529                 bool lock_arrays = (displaylists && useVertexArrays);
530
531                 if(displaylists && !useVertexArrays)
532                         rasterizer = new RAS_ListRasterizer(canvas);
533                 else if (useVertexArrays && bgl::QueryVersion(1, 1))
534                         rasterizer = new RAS_VAOpenGLRasterizer(canvas, lock_arrays);
535                 else
536                         rasterizer = new RAS_OpenGLRasterizer(canvas);
537
538                 // create the inputdevices
539                 KX_BlenderKeyboardDevice* keyboarddevice = new KX_BlenderKeyboardDevice();
540                 KX_BlenderMouseDevice* mousedevice = new KX_BlenderMouseDevice();
541
542                 // create a networkdevice
543                 NG_NetworkDeviceInterface* networkdevice = new
544                         NG_LoopBackNetworkDeviceInterface();
545
546                 // get an audiodevice
547                 SND_DeviceManager::Subscribe();
548                 SND_IAudioDevice* audiodevice = SND_DeviceManager::Instance();
549                 audiodevice->UseCD();
550
551                 // create a ketsji/blendersystem (only needed for timing and stuff)
552                 KX_BlenderSystem* kxsystem = new KX_BlenderSystem();
553
554                 // create the ketsjiengine
555                 KX_KetsjiEngine* ketsjiengine = new KX_KetsjiEngine(kxsystem);
556
557                 int i;
558
559                 Scene *blscene = NULL;
560                 if (!bfd)
561                 {
562                         blscene = (Scene*) maggie->scene.first;
563                         for (Scene *sce= (Scene*) maggie->scene.first; sce; sce= (Scene*) sce->id.next)
564                         {
565                                 if (startscenename == (sce->id.name+2))
566                                 {
567                                         blscene = sce;
568                                         break;
569                                 }
570                         }
571                 } else {
572                         blscene = bfd->curscene;
573                 }
574         int cframe,startFrame;
575                 if (blscene)
576                 {
577                         cframe=blscene->r.cfra;
578                         startFrame = blscene->r.sfra;
579                         blscene->r.cfra=startFrame;
580                         update_for_newframe();
581                         ketsjiengine->SetGame2IpoMode(game2ipo,startFrame);
582                 }
583
584                 // Quad buffered needs a special window.
585                 if (blscene->r.stereomode != RAS_IRasterizer::RAS_STEREO_QUADBUFFERED)
586                         rasterizer->SetStereoMode((RAS_IRasterizer::StereoMode) blscene->r.stereomode);
587
588                 if (exitrequested != KX_EXIT_REQUEST_QUIT_GAME)
589                 {
590                         // create a scene converter, create and convert the startingscene
591                         KX_ISceneConverter* sceneconverter = new KX_BlenderSceneConverter(maggie,sipo, ketsjiengine);
592                         ketsjiengine->SetSceneConverter(sceneconverter);
593                         sceneconverter->addInitFromFrame=true;
594                         
595                         if (always_use_expand_framing)
596                                 sceneconverter->SetAlwaysUseExpandFraming(true);
597
598                         if(usemat)
599                                 sceneconverter->SetMaterials(true);
600
601                         KX_Scene* startscene = new KX_Scene(keyboarddevice,
602                                 mousedevice,
603                                 networkdevice,
604                                 audiodevice,
605                                 startscenename);
606                         // some python things
607                         PyObject* dictionaryobject = initGamePythonScripting("Ketsji", psl_Lowest);
608                         ketsjiengine->SetPythonDictionary(dictionaryobject);
609                         initRasterizer(rasterizer, canvas);
610                         PyObject *gameLogic = initGameLogic(startscene);
611                         initGameKeys();
612                         initPythonConstraintBinding();
613
614                         if (sceneconverter)
615                         {
616                                 // convert and add scene
617                                 sceneconverter->ConvertScene(
618                                         startscenename,
619                                         startscene,
620                                         dictionaryobject,
621                                         keyboarddevice,
622                                         rendertools,
623                                         canvas);
624                                 ketsjiengine->AddScene(startscene);
625
626                                 // start the engine
627                                 ketsjiengine->StartEngine(false);
628                                 
629                                 ketsjiengine->SetUseFixedTime(true);
630                                 
631                                 ketsjiengine->SetTicRate(
632                                         (double) blscene->r.frs_sec /
633                                         (double) blscene->r.frs_sec_base);
634
635                                 // the mainloop
636                                 while ((blscene->r.cfra<=blscene->r.efra)&&(!exitrequested))
637                                 {
638                     printf("frame %i\n",blscene->r.cfra);
639                     // first check if we want to exit
640                                         exitrequested = ketsjiengine->GetExitCode();
641         
642                                         // kick the engine
643                                         ketsjiengine->NextFrame();
644                                     blscene->r.cfra=blscene->r.cfra+1;
645                                     update_for_newframe();
646                                         
647                                 }
648                                 exitstring = ketsjiengine->GetExitString();
649                         }
650                         if (sceneconverter)
651                         {
652                                 delete sceneconverter;
653                                 sceneconverter = NULL;
654                         }
655                 }
656                 blscene->r.cfra=cframe;
657                 // set the cursor back to normal
658                 canvas->SetMouseState(RAS_ICanvas::MOUSE_NORMAL);
659
660                 // clean up some stuff
661                 audiodevice->StopCD();
662                 if (ketsjiengine)
663                 {
664                         delete ketsjiengine;
665                         ketsjiengine = NULL;
666                 }
667                 if (kxsystem)
668                 {
669                         delete kxsystem;
670                         kxsystem = NULL;
671                 }
672                 if (networkdevice)
673                 {
674                         delete networkdevice;
675                         networkdevice = NULL;
676                 }
677                 if (keyboarddevice)
678                 {
679                         delete keyboarddevice;
680                         keyboarddevice = NULL;
681                 }
682                 if (mousedevice)
683                 {
684                         delete mousedevice;
685                         mousedevice = NULL;
686                 }
687                 SND_DeviceManager::Unsubscribe();
688
689         } while (exitrequested == KX_EXIT_REQUEST_RESTART_GAME || exitrequested == KX_EXIT_REQUEST_START_OTHER_GAME);
690         if (bfd) BLO_blendfiledata_free(bfd);
691
692         // Release Python's GIL
693         PyGILState_Release(gilstate);
694 }