0e45b2a90f389db100644e8ebeeafe3c7f6c2894
[blender.git] / source / gameengine / GamePlayer / ghost / GPG_Application.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  * GHOST Blender Player application implementation file.
32  */
33
34 #ifdef HAVE_CONFIG_H
35 #include <config.h>
36 #endif
37
38 #ifdef WIN32
39         #pragma warning (disable:4786) // suppress stl-MSVC debug info warning
40         #include <windows.h>
41 #endif
42
43 #ifdef __APPLE__
44 #include <OpenGL/gl.h>
45 #include <OpenGL/glu.h>
46 #else
47 #include <GL/gl.h>
48 #include <GL/glu.h>
49 #endif
50
51 #include "GPG_Application.h"
52
53 #include <iostream>
54 #include <assert.h>
55
56 /**********************************
57  * Begin Blender include block
58  **********************************/
59 #ifdef __cplusplus
60 extern "C"
61 {
62 #endif  // __cplusplus
63 #include "BLI_blenlib.h"
64 #include "BLO_readfile.h"
65 #ifdef __cplusplus
66 }
67 #endif // __cplusplus
68 /**********************************
69  * End Blender include block
70  **********************************/
71
72
73 #include "SYS_System.h"
74 #include "KX_KetsjiEngine.h"
75
76 // include files needed by "KX_BlenderSceneConverter.h"
77 #include "GEN_Map.h"
78 #include "SCA_IActuator.h"
79 #include "RAS_MeshObject.h"
80 #include "RAS_OpenGLRasterizer.h"
81 #include "RAS_VAOpenGLRasterizer.h"
82 #include "RAS_GLExtensionManager.h"
83 #include "KX_PythonInit.h"
84 #include "KX_PyConstraintBinding.h"
85
86 #include "KX_BlenderSceneConverter.h"
87 #include "NG_LoopBackNetworkDeviceInterface.h"
88 #include "SND_DeviceManager.h"
89
90 #include "GPC_MouseDevice.h"
91 #include "GPC_RenderTools.h"
92 #include "GPG_Canvas.h" 
93 #include "GPG_KeyboardDevice.h"
94 #include "GPG_System.h"
95
96 #include "STR_String.h"
97
98 #include "GHOST_ISystem.h"
99 #include "GHOST_IEvent.h"
100 #include "GHOST_IEventConsumer.h"
101 #include "GHOST_IWindow.h"
102 #include "GHOST_Rect.h"
103
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, struct Main *maggie, STR_String startSceneName)
111         : m_startSceneName(startSceneName), 
112           m_maggie(maggie), 
113           m_exitRequested(0),
114           m_system(system), 
115           m_mainWindow(0), 
116           m_frameTimer(0), 
117           m_cursor(GHOST_kStandardCursorFirstCursor),
118           m_engineInitialized(0), 
119           m_engineRunning(0), 
120           m_ketsjiengine(0),
121           m_kxsystem(0), 
122           m_keyboard(0), 
123           m_mouse(0), 
124           m_canvas(0), 
125           m_rendertools(0), 
126           m_rasterizer(0), 
127           m_sceneconverter(0),
128           m_networkdevice(0), 
129           m_audiodevice(0)
130 {
131         fSystem = system;
132 }
133
134
135
136 GPG_Application::~GPG_Application(void)
137 {
138         exitEngine();
139         fSystem->disposeWindow(m_mainWindow);
140 }
141
142
143
144 bool GPG_Application::SetGameEngineData(struct Main *maggie, STR_String startSceneName)
145 {
146         bool result = false;
147
148         if (maggie != NULL && startSceneName != "")
149         {
150                 m_maggie = maggie;
151                 m_startSceneName = startSceneName;
152                 result = true;
153         }
154
155         return result;
156 }
157
158
159
160 bool GPG_Application::startWindow(STR_String& title,
161         int windowLeft,
162         int windowTop,
163         int windowWidth,
164         int windowHeight,
165         const bool stereoVisual,
166         const int stereoMode)
167 {
168         bool success;
169         // Create the main window
170         //STR_String title ("Blender Player - GHOST");
171         m_mainWindow = fSystem->createWindow(title, windowLeft, windowTop, windowWidth, windowHeight, GHOST_kWindowStateNormal,
172                 GHOST_kDrawingContextTypeOpenGL, stereoVisual);
173         if (!m_mainWindow) {
174                 printf("error: could not create main window\n");
175                 exit(-1);
176         }
177
178         /* Check the size of the client rectangle of the window and resize the window
179          * so that the client rectangle has the size requested.
180          */
181         m_mainWindow->setClientSize(windowWidth, windowHeight);
182         m_mainWindow->setCursorVisibility(false);
183
184         success = initEngine(m_mainWindow, stereoMode);
185         if (success) {
186                 success = startEngine();
187         }
188         return success;
189 }
190
191
192
193 bool GPG_Application::startFullScreen(
194                 int width,
195                 int height,
196                 int bpp,int frequency,
197                 const bool stereoVisual,
198                 const int stereoMode)
199 {
200         bool success;
201         // Create the main window
202         GHOST_DisplaySetting setting;
203         setting.xPixels = width;
204         setting.yPixels = height;
205         setting.bpp = bpp;
206         setting.frequency = frequency;
207
208         fSystem->beginFullScreen(setting, &m_mainWindow, stereoVisual);
209         m_mainWindow->setCursorVisibility(false);
210
211         success = initEngine(m_mainWindow, stereoMode);
212         if (success) {
213                 success = startEngine();
214         }
215         return success;
216 }
217
218
219
220 bool GPG_Application::StartGameEngine(int stereoMode)
221 {
222         bool success = initEngine(m_mainWindow, stereoMode);
223         
224         if (success)
225                 success = startEngine();
226
227         return success;
228 }
229
230
231
232 void GPG_Application::StopGameEngine()
233 {
234         exitEngine();
235 }
236
237
238
239 bool GPG_Application::processEvent(GHOST_IEvent* event)
240 {
241         bool handled = true;
242
243         switch (event->getType())
244         {
245                 case GHOST_kEventUnknown:
246                         break;
247
248                 case GHOST_kEventButtonDown:
249                         handled = handleButton(event, true);
250                         break;
251
252                 case GHOST_kEventButtonUp:
253                         handled = handleButton(event, false);
254                         break;
255                         
256                 case GHOST_kEventWheel:
257                         handled = handleWheel(event);
258                         break;
259
260                 case GHOST_kEventCursorMove:
261                         handled = handleCursorMove(event);
262                         break;
263
264                 case GHOST_kEventKeyDown:
265                         handleKey(event, true);
266                         break;
267
268                 case GHOST_kEventKeyUp:
269                         handleKey(event, false);
270                         break;
271
272
273                 case GHOST_kEventWindowClose:
274                         m_exitRequested = KX_EXIT_REQUEST_OUTSIDE;
275                         break;
276
277                 case GHOST_kEventWindowActivate:
278                         handled = false;
279                         break;
280                 case GHOST_kEventWindowDeactivate:
281                         handled = false;
282                         break;
283
284                 case GHOST_kEventWindowUpdate:
285                         {
286                                 GHOST_IWindow* window = event->getWindow();
287                                 if (!m_system->validWindow(window)) break;
288                                 // Update the state of the game engine
289                                 if (m_kxsystem && !m_exitRequested)
290                                 {
291                                         // Proceed to next frame
292                                         window->activateDrawingContext();
293
294                                         // first check if we want to exit
295                                         m_exitRequested = m_ketsjiengine->GetExitCode();
296                                         
297                                         // kick the engine
298                                         m_ketsjiengine->NextFrame();
299                                         
300                                         // render the frame
301                                         m_ketsjiengine->Render();
302                                 }
303                                 m_exitString = m_ketsjiengine->GetExitString();
304                         }
305                         break;
306                 
307                 case GHOST_kEventWindowSize:
308                         {
309                         GHOST_IWindow* window = event->getWindow();
310                         if (!m_system->validWindow(window)) break;
311                         if (m_canvas) {
312                                 GHOST_Rect bnds;
313                                 window->getClientBounds(bnds);
314                                 m_canvas->Resize(bnds.getWidth(), bnds.getHeight());
315                         }
316                         }
317                         break;
318                 
319                 default:
320                         handled = false;
321                         break;
322         }
323         return handled;
324 }
325
326
327
328 int GPG_Application::getExitRequested(void)
329 {
330         return m_exitRequested;
331 }
332
333
334
335 const STR_String& GPG_Application::getExitString(void)
336 {
337         return m_exitString;
338 }
339
340
341
342 bool GPG_Application::initEngine(GHOST_IWindow* window, const int stereoMode)
343 {
344         if (!m_engineInitialized)
345         {
346                 bgl::InitExtensions(1);
347
348                 // get and set the preferences
349                 SYS_SystemHandle syshandle = SYS_GetSystem();
350                 if (!syshandle)
351                         return false;
352                 
353                 // SYS_WriteCommandLineInt(syshandle, "fixedtime", 0);
354                 // SYS_WriteCommandLineInt(syshandle, "vertexarrays",1);                
355                 bool properties = (SYS_GetCommandLineInt(syshandle, "show_properties", 0) != 0);
356                 bool profile = (SYS_GetCommandLineInt(syshandle, "show_profile", 0) != 0);
357                 bool frameRate = (SYS_GetCommandLineInt(syshandle, "show_framerate", 0) != 0);
358                 bool useVertexArrays = SYS_GetCommandLineInt(syshandle,"vertexarrays",1) != 0;
359                 // create the canvas, rasterizer and rendertools
360                 m_canvas = new GPG_Canvas(window);
361                 if (!m_canvas)
362                         return false;
363                                 
364                 m_canvas->Init();                               
365                 m_rendertools = new GPC_RenderTools();
366                 if (!m_rendertools)
367                         goto initFailed;
368                                         
369                 if (useVertexArrays && bgl::QueryVersion(1, 1))
370                         m_rasterizer = new RAS_VAOpenGLRasterizer(m_canvas);
371                 else
372                         m_rasterizer = new RAS_OpenGLRasterizer(m_canvas);
373                 m_rasterizer->SetStereoMode((RAS_IRasterizer::StereoMode) stereoMode);
374                 if (!m_rasterizer)
375                         goto initFailed;
376                                                 
377                 // create the inputdevices
378                 m_keyboard = new GPG_KeyboardDevice();
379                 if (!m_keyboard)
380                         goto initFailed;
381                         
382                 m_mouse = new GPC_MouseDevice();
383                 if (!m_mouse)
384                         goto initFailed;
385                         
386                 // create a networkdevice
387                 m_networkdevice = new NG_LoopBackNetworkDeviceInterface();
388                 if (!m_networkdevice)
389                         goto initFailed;
390                         
391                 // get an audiodevice
392                 SND_DeviceManager::Subscribe();
393                 m_audiodevice = SND_DeviceManager::Instance();
394                 if (!m_audiodevice)
395                         goto initFailed;
396                 m_audiodevice->UseCD();
397                 
398                 // create a ketsjisystem (only needed for timing and stuff)
399                 m_kxsystem = new GPG_System (m_system);
400                 if (!m_kxsystem)
401                         goto initFailed;
402                 
403                 // create the ketsjiengine
404                 m_ketsjiengine = new KX_KetsjiEngine(m_kxsystem);
405                 
406                 // set the devices
407                 m_ketsjiengine->SetKeyboardDevice(m_keyboard);
408                 m_ketsjiengine->SetMouseDevice(m_mouse);
409                 m_ketsjiengine->SetNetworkDevice(m_networkdevice);
410                 m_ketsjiengine->SetCanvas(m_canvas);
411                 m_ketsjiengine->SetRenderTools(m_rendertools);
412                 m_ketsjiengine->SetRasterizer(m_rasterizer);
413                 m_ketsjiengine->SetNetworkDevice(m_networkdevice);
414                 m_ketsjiengine->SetAudioDevice(m_audiodevice);
415                 m_ketsjiengine->SetTimingDisplay(frameRate, false, false);
416
417                 m_ketsjiengine->SetUseFixedTime(false);
418                 m_ketsjiengine->SetTimingDisplay(frameRate, profile, properties);
419
420                 m_engineInitialized = true;
421         }
422
423         return m_engineInitialized;
424 initFailed:
425         delete m_kxsystem;
426         delete m_audiodevice;
427         delete m_networkdevice;
428         delete m_mouse;
429         delete m_keyboard;
430         delete m_rasterizer;
431         delete m_rendertools;
432         delete m_canvas;
433         m_canvas = NULL;
434         m_rendertools = NULL;
435         m_rasterizer = NULL;
436         m_keyboard = NULL;
437         m_mouse = NULL;
438         m_networkdevice = NULL;
439         m_audiodevice = NULL;
440         m_kxsystem = NULL;
441         return false;
442 }
443
444
445
446 bool GPG_Application::startEngine(void)
447 {
448         if (m_engineRunning) {
449                 return false;
450         }
451         
452         // Temporary hack to disable banner display for NaN approved content.
453         /*
454         m_canvas->SetBannerDisplayEnabled(true);        
455         Camera* cam;
456         cam = (Camera*)G.scene->camera->data;
457         if (cam) {
458         if (((cam->flag) & 48)==48) {
459         m_canvas->SetBannerDisplayEnabled(false);
460         }
461         }
462         else {
463         showError(CString("Camera data invalid."));
464         return false;
465         }
466         */
467         
468         // create a scene converter, create and convert the stratingscene
469         m_sceneconverter = new KX_BlenderSceneConverter(m_maggie, m_ketsjiengine);
470         if (m_sceneconverter)
471         {
472                 STR_String startscenename = m_startSceneName.Ptr();
473                 m_ketsjiengine->SetSceneConverter(m_sceneconverter);
474                 
475                 //      if (always_use_expand_framing)
476                 //              sceneconverter->SetAlwaysUseExpandFraming(true);
477                 
478
479                 KX_Scene* startscene = new KX_Scene(m_keyboard,
480                         m_mouse,
481                         m_networkdevice,
482                         m_audiodevice,
483                         startscenename);
484                 
485                 // some python things
486                 PyObject* m_dictionaryobject = initGamePlayerPythonScripting("Ketsji", psl_Lowest);
487                 m_ketsjiengine->SetPythonDictionary(m_dictionaryobject);
488                 initRasterizer(m_rasterizer, m_canvas);
489                 initGameLogic(startscene);
490                 initGameKeys();
491                 initPythonConstraintBinding();
492                 
493                 m_sceneconverter->ConvertScene(
494                         startscenename,
495                         startscene,
496                         m_dictionaryobject,
497                         m_keyboard,
498                         m_rendertools,
499                         m_canvas);
500                 m_ketsjiengine->AddScene(startscene);
501                 
502                 // Create a timer that is used to kick the engine
503                 if (!m_frameTimer) {
504                         m_frameTimer = m_system->installTimer(0, kTimerFreq, frameTimerProc, m_mainWindow);
505                 }
506                 m_rasterizer->Init();
507                 m_ketsjiengine->StartEngine();
508                 m_engineRunning = true;
509                 
510         }
511         
512         if (!m_engineRunning)
513         {
514                 stopEngine();
515         }
516         
517         return m_engineRunning;
518 }
519
520
521 void GPG_Application::stopEngine()
522 {
523         // when exiting the mainloop
524         exitGamePythonScripting();
525         m_ketsjiengine->StopEngine();
526         m_networkdevice->Disconnect();
527
528         if (m_sceneconverter) {
529                 delete m_sceneconverter;
530                 m_sceneconverter = 0;
531         }
532         if (m_system && m_frameTimer) {
533                 m_system->removeTimer(m_frameTimer);
534                 m_frameTimer = 0;
535         }
536         m_engineRunning = false;
537 }
538
539
540 void GPG_Application::exitEngine()
541 {
542         if (m_ketsjiengine)
543         {
544                 stopEngine();
545                 delete m_ketsjiengine;
546                 m_ketsjiengine = 0;
547         }
548         if (m_kxsystem)
549         {
550                 delete m_kxsystem;
551                 m_kxsystem = 0;
552         }
553         if (m_audiodevice)
554         {
555                 SND_DeviceManager::Unsubscribe();
556                 m_audiodevice = 0;
557         }
558         if (m_networkdevice)
559         {
560                 delete m_networkdevice;
561                 m_networkdevice = 0;
562         }
563         if (m_mouse)
564         {
565                 delete m_mouse;
566                 m_mouse = 0;
567         }
568         if (m_keyboard)
569         {
570                 delete m_keyboard;
571                 m_keyboard = 0;
572         }
573         if (m_rasterizer)
574         {
575                 delete m_rasterizer;
576                 m_rasterizer = 0;
577         }
578         if (m_rendertools)
579         {
580                 delete m_rendertools;
581                 m_rendertools = 0;
582         }
583         if (m_canvas)
584         {
585                 delete m_canvas;
586                 m_canvas = 0;
587         }
588
589         m_exitRequested = 0;
590         m_engineInitialized = false;
591 }
592
593 bool GPG_Application::handleWheel(GHOST_IEvent* event)
594 {
595         bool handled = false;
596         assert(event);
597         if (m_mouse) 
598         {
599                 GHOST_TEventDataPtr eventData = ((GHOST_IEvent*)event)->getData();
600                 GHOST_TEventWheelData* wheelData = static_cast<GHOST_TEventWheelData*>(eventData);
601                 GPC_MouseDevice::TButtonId button;
602                 if (wheelData->z > 0)
603                         button = GPC_MouseDevice::buttonWheelUp;
604                 else
605                         button = GPC_MouseDevice::buttonWheelDown;
606                 m_mouse->ConvertButtonEvent(button, true);
607                 handled = true;
608         }
609         return handled;
610 }
611
612 bool GPG_Application::handleButton(GHOST_IEvent* event, bool isDown)
613 {
614         bool handled = false;
615         assert(event);
616         if (m_mouse) 
617         {
618                 GHOST_TEventDataPtr eventData = ((GHOST_IEvent*)event)->getData();
619                 GHOST_TEventButtonData* buttonData = static_cast<GHOST_TEventButtonData*>(eventData);
620                 GPC_MouseDevice::TButtonId button;
621                 switch (buttonData->button)
622                 {
623                 case GHOST_kButtonMaskMiddle:
624                         button = GPC_MouseDevice::buttonMiddle;
625                         break;
626                 case GHOST_kButtonMaskRight:
627                         button = GPC_MouseDevice::buttonRight;
628                         break;
629                 case GHOST_kButtonMaskLeft:
630                 default:
631                         button = GPC_MouseDevice::buttonLeft;
632                         break;
633                 }
634                 m_mouse->ConvertButtonEvent(button, isDown);
635                 handled = true;
636         }
637         return handled;
638 }
639
640
641 bool GPG_Application::handleCursorMove(GHOST_IEvent* event)
642 {
643         bool handled = false;
644         assert(event);
645         if (m_mouse && m_mainWindow)
646         {
647                 GHOST_TEventDataPtr eventData = ((GHOST_IEvent*)event)->getData();
648                 GHOST_TEventCursorData* cursorData = static_cast<GHOST_TEventCursorData*>(eventData);
649                 GHOST_TInt32 x, y;
650                 m_mainWindow->screenToClient(cursorData->x, cursorData->y, x, y);
651                 m_mouse->ConvertMoveEvent(x, y);
652                 handled = true;
653         }
654         return handled;
655 }
656
657
658 bool GPG_Application::handleKey(GHOST_IEvent* event, bool isDown)
659 {
660         bool handled = false;
661         assert(event);
662         if (m_keyboard)
663         {
664                 GHOST_TEventDataPtr eventData = ((GHOST_IEvent*)event)->getData();
665                 GHOST_TEventKeyData* keyData = static_cast<GHOST_TEventKeyData*>(eventData);
666                 if (fSystem->getFullScreen()) {
667                         if (keyData->key == GHOST_kKeyEsc) {
668                                 m_exitRequested = KX_EXIT_REQUEST_OUTSIDE;
669                         }
670                 }
671                 m_keyboard->ConvertEvent(keyData->key, isDown);
672                 handled = true;
673         }
674         return handled;
675 }
676
677
678
679 static void frameTimerProc(GHOST_ITimerTask* task, GHOST_TUns64 time)
680 {
681         GHOST_IWindow* window = (GHOST_IWindow*)task->getUserData();
682         if (fSystem->validWindow(window)) {
683                 window->invalidate();
684         }
685 }