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