4 * ***** BEGIN GPL LICENSE BLOCK *****
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.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software Foundation,
18 * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
20 * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
21 * All rights reserved.
23 * The Original Code is: all of this file.
25 * Contributor(s): none yet.
27 * ***** END GPL LICENSE BLOCK *****
28 * GHOST Blender Player application implementation file.
36 #pragma warning (disable:4786) // suppress stl-MSVC debug info warning
41 #include "GPU_extensions.h"
43 #include "GPG_Application.h"
46 #include <MT_assert.h>
49 /**********************************
50 * Begin Blender include block
51 **********************************/
56 #include "BLI_blenlib.h"
57 #include "BLO_readfile.h"
58 #include "BKE_global.h"
60 #include "IMB_imbuf.h"
61 #include "DNA_scene_types.h"
65 /**********************************
66 * End Blender include block
67 **********************************/
70 #include "SYS_System.h"
71 #include "KX_KetsjiEngine.h"
73 // include files needed by "KX_BlenderSceneConverter.h"
75 #include "SCA_IActuator.h"
76 #include "RAS_MeshObject.h"
77 #include "RAS_OpenGLRasterizer.h"
78 #include "RAS_VAOpenGLRasterizer.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
85 #include "KX_BlenderSceneConverter.h"
86 #include "NG_LoopBackNetworkDeviceInterface.h"
87 #include "SND_DeviceManager.h"
89 #include "GPC_MouseDevice.h"
90 #include "GPC_RenderTools.h"
91 #include "GPG_Canvas.h"
92 #include "GPG_KeyboardDevice.h"
93 #include "GPG_System.h"
95 #include "STR_String.h"
97 #include "GHOST_ISystem.h"
98 #include "GHOST_IEvent.h"
99 #include "GHOST_IEventConsumer.h"
100 #include "GHOST_IWindow.h"
101 #include "GHOST_Rect.h"
103 static void frameTimerProc(GHOST_ITimerTask* task, GHOST_TUns64 time);
105 static GHOST_ISystem* fSystem = 0;
106 static const int kTimerFreq = 10;
108 GPG_Application::GPG_Application(GHOST_ISystem* system)
109 : m_startSceneName(""),
116 m_cursor(GHOST_kStandardCursorFirstCursor),
117 m_engineInitialized(0),
132 m_pyGlobalDictString(0),
133 m_pyGlobalDictString_Length(0)
140 GPG_Application::~GPG_Application(void)
142 if(m_pyGlobalDictString) {
143 delete [] m_pyGlobalDictString;
144 m_pyGlobalDictString = 0;
145 m_pyGlobalDictString_Length = 0;
149 fSystem->disposeWindow(m_mainWindow);
154 bool GPG_Application::SetGameEngineData(struct Main* maggie, Scene *scene, int argc, char **argv)
158 if (maggie != NULL && scene != NULL)
162 m_startSceneName = scene->id.name+2;
163 m_startScene = scene;
167 /* Python needs these */
176 #define SCR_SAVE_MOUSE_MOVE_THRESHOLD 15
178 static HWND found_ghost_window_hwnd;
179 static GHOST_IWindow* ghost_window_to_find;
180 static WNDPROC ghost_wnd_proc;
181 static POINT scr_save_mouse_pos;
183 static LRESULT CALLBACK screenSaverWindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
192 LONG dx = scr_save_mouse_pos.x - pt.x;
193 LONG dy = scr_save_mouse_pos.y - pt.y;
194 if (abs(dx) > SCR_SAVE_MOUSE_MOVE_THRESHOLD
195 || abs(dy) > SCR_SAVE_MOUSE_MOVE_THRESHOLD)
199 scr_save_mouse_pos = pt;
209 PostMessage(hwnd,WM_CLOSE,0,0);
210 return CallWindowProc(ghost_wnd_proc, hwnd, uMsg, wParam, lParam);
213 BOOL CALLBACK findGhostWindowHWNDProc(HWND hwnd, LPARAM lParam)
215 GHOST_IWindow *p = (GHOST_IWindow*) GetWindowLongPtr(hwnd, GWLP_USERDATA);
217 if (p == ghost_window_to_find)
219 found_ghost_window_hwnd = hwnd;
225 static HWND findGhostWindowHWND(GHOST_IWindow* window)
227 found_ghost_window_hwnd = NULL;
228 ghost_window_to_find = window;
229 EnumWindows(findGhostWindowHWNDProc, NULL);
230 return found_ghost_window_hwnd;
233 bool GPG_Application::startScreenSaverPreview(
235 const bool stereoVisual,
236 const int stereoMode)
238 bool success = false;
241 if (GetWindowRect(parentWindow, &rc))
243 int windowWidth = rc.right - rc.left;
244 int windowHeight = rc.bottom - rc.top;
245 STR_String title = "";
247 m_mainWindow = fSystem->createWindow(title, 0, 0, windowWidth, windowHeight, GHOST_kWindowStateMinimized,
248 GHOST_kDrawingContextTypeOpenGL, stereoVisual);
250 printf("error: could not create main window\n");
254 HWND ghost_hwnd = findGhostWindowHWND(m_mainWindow);
256 printf("error: could find main window\n");
260 SetParent(ghost_hwnd, parentWindow);
261 LONG style = GetWindowLong(ghost_hwnd, GWL_STYLE);
262 LONG exstyle = GetWindowLong(ghost_hwnd, GWL_EXSTYLE);
264 RECT adjrc = { 0, 0, windowWidth, windowHeight };
265 AdjustWindowRectEx(&adjrc, style, FALSE, exstyle);
267 style = (style & (~(WS_POPUP|WS_OVERLAPPEDWINDOW|WS_OVERLAPPED|WS_CAPTION|WS_SYSMENU|WS_THICKFRAME|WS_MINIMIZEBOX|WS_MAXIMIZEBOX|WS_TILEDWINDOW ))) | WS_CHILD;
268 SetWindowLong(ghost_hwnd, GWL_STYLE, style);
269 SetWindowPos(ghost_hwnd, NULL, adjrc.left, adjrc.top, 0, 0, SWP_NOZORDER|SWP_NOSIZE|SWP_NOACTIVATE);
271 /* Check the size of the client rectangle of the window and resize the window
272 * so that the client rectangle has the size requested.
274 m_mainWindow->setClientSize(windowWidth, windowHeight);
276 success = initEngine(m_mainWindow, stereoMode);
278 success = startEngine();
285 bool GPG_Application::startScreenSaverFullScreen(
288 int bpp,int frequency,
289 const bool stereoVisual,
290 const int stereoMode)
292 bool ret = startFullScreen(width, height, bpp, frequency, stereoVisual, stereoMode);
295 HWND ghost_hwnd = findGhostWindowHWND(m_mainWindow);
296 if (ghost_hwnd != NULL)
298 GetCursorPos(&scr_save_mouse_pos);
299 ghost_wnd_proc = (WNDPROC) GetWindowLongPtr(ghost_hwnd, GWLP_WNDPROC);
300 SetWindowLongPtr(ghost_hwnd,GWLP_WNDPROC, (uintptr_t) screenSaverWindowProc);
308 bool GPG_Application::startWindow(STR_String& title,
313 const bool stereoVisual,
314 const int stereoMode)
317 // Create the main window
318 //STR_String title ("Blender Player - GHOST");
319 m_mainWindow = fSystem->createWindow(title, windowLeft, windowTop, windowWidth, windowHeight, GHOST_kWindowStateNormal,
320 GHOST_kDrawingContextTypeOpenGL, stereoVisual);
322 printf("error: could not create main window\n");
326 /* Check the size of the client rectangle of the window and resize the window
327 * so that the client rectangle has the size requested.
329 m_mainWindow->setClientSize(windowWidth, windowHeight);
330 m_mainWindow->setCursorVisibility(false);
332 success = initEngine(m_mainWindow, stereoMode);
334 success = startEngine();
339 bool GPG_Application::startEmbeddedWindow(STR_String& title,
340 const GHOST_TEmbedderWindowID parentWindow,
341 const bool stereoVisual,
342 const int stereoMode) {
344 m_mainWindow = fSystem->createWindow(title, 0, 0, 0, 0, GHOST_kWindowStateNormal,
345 GHOST_kDrawingContextTypeOpenGL, stereoVisual, parentWindow);
348 printf("error: could not create main window\n");
353 bool success = initEngine(m_mainWindow, stereoMode);
355 success = startEngine();
361 bool GPG_Application::startFullScreen(
364 int bpp,int frequency,
365 const bool stereoVisual,
366 const int stereoMode)
369 // Create the main window
370 GHOST_DisplaySetting setting;
371 setting.xPixels = width;
372 setting.yPixels = height;
374 setting.frequency = frequency;
376 fSystem->beginFullScreen(setting, &m_mainWindow, stereoVisual);
377 m_mainWindow->setCursorVisibility(false);
379 success = initEngine(m_mainWindow, stereoMode);
381 success = startEngine();
389 bool GPG_Application::StartGameEngine(int stereoMode)
391 bool success = initEngine(m_mainWindow, stereoMode);
394 success = startEngine();
401 void GPG_Application::StopGameEngine()
408 bool GPG_Application::processEvent(GHOST_IEvent* event)
412 switch (event->getType())
414 case GHOST_kEventUnknown:
417 case GHOST_kEventButtonDown:
418 handled = handleButton(event, true);
421 case GHOST_kEventButtonUp:
422 handled = handleButton(event, false);
425 case GHOST_kEventWheel:
426 handled = handleWheel(event);
429 case GHOST_kEventCursorMove:
430 handled = handleCursorMove(event);
433 case GHOST_kEventKeyDown:
434 handleKey(event, true);
437 case GHOST_kEventKeyUp:
438 handleKey(event, false);
442 case GHOST_kEventWindowClose:
443 m_exitRequested = KX_EXIT_REQUEST_OUTSIDE;
446 case GHOST_kEventWindowActivate:
449 case GHOST_kEventWindowDeactivate:
453 case GHOST_kEventWindowUpdate:
455 GHOST_IWindow* window = event->getWindow();
456 if (!m_system->validWindow(window)) break;
457 // Update the state of the game engine
458 if (m_kxsystem && !m_exitRequested)
460 // Proceed to next frame
461 window->activateDrawingContext();
463 // first check if we want to exit
464 m_exitRequested = m_ketsjiengine->GetExitCode();
467 bool renderFrame = m_ketsjiengine->NextFrame();
471 m_ketsjiengine->Render();
474 m_exitString = m_ketsjiengine->GetExitString();
478 case GHOST_kEventWindowSize:
480 GHOST_IWindow* window = event->getWindow();
481 if (!m_system->validWindow(window)) break;
484 window->getClientBounds(bnds);
485 m_canvas->Resize(bnds.getWidth(), bnds.getHeight());
499 int GPG_Application::getExitRequested(void)
501 return m_exitRequested;
506 const STR_String& GPG_Application::getExitString(void)
513 bool GPG_Application::initEngine(GHOST_IWindow* window, const int stereoMode)
515 if (!m_engineInitialized)
517 GPU_extensions_init();
518 bgl::InitExtensions(true);
520 // get and set the preferences
521 SYS_SystemHandle syshandle = SYS_GetSystem();
525 // SYS_WriteCommandLineInt(syshandle, "fixedtime", 0);
526 // SYS_WriteCommandLineInt(syshandle, "vertexarrays",1);
527 bool properties = (SYS_GetCommandLineInt(syshandle, "show_properties", 0) != 0);
528 bool profile = (SYS_GetCommandLineInt(syshandle, "show_profile", 0) != 0);
529 bool fixedFr = (G.fileflags & G_FILE_ENABLE_ALL_FRAMES);
531 bool showPhysics = (G.fileflags & G_FILE_SHOW_PHYSICS);
532 SYS_WriteCommandLineInt(syshandle, "show_physics", showPhysics);
534 bool fixed_framerate= (SYS_GetCommandLineInt(syshandle, "fixed_framerate", fixedFr) != 0);
535 bool frameRate = (SYS_GetCommandLineInt(syshandle, "show_framerate", 0) != 0);
536 bool useLists = (SYS_GetCommandLineInt(syshandle, "displaylists", G.fileflags & G_FILE_DISPLAY_LISTS) != 0);
537 bool nodepwarnings = (SYS_GetCommandLineInt(syshandle, "ignore_deprecation_warnings", 1) != 0);
539 if(GLEW_ARB_multitexture && GLEW_VERSION_1_1)
540 m_blendermat = (SYS_GetCommandLineInt(syshandle, "blender_material", 1) != 0);
542 if(GPU_extensions_minimum_support())
543 m_blenderglslmat = (SYS_GetCommandLineInt(syshandle, "blender_glsl_material", 1) != 0);
544 else if(G.fileflags & G_FILE_GAME_MAT_GLSL)
545 m_blendermat = false;
547 // create the canvas, rasterizer and rendertools
548 m_canvas = new GPG_Canvas(window);
553 m_rendertools = new GPC_RenderTools();
559 m_rasterizer = new RAS_ListRasterizer(m_canvas, true);
561 m_rasterizer = new RAS_ListRasterizer(m_canvas);
563 else if (GLEW_VERSION_1_1)
564 m_rasterizer = new RAS_VAOpenGLRasterizer(m_canvas);
566 m_rasterizer = new RAS_OpenGLRasterizer(m_canvas);
568 m_rasterizer->SetStereoMode((RAS_IRasterizer::StereoMode) stereoMode);
572 // create the inputdevices
573 m_keyboard = new GPG_KeyboardDevice();
577 m_mouse = new GPC_MouseDevice();
581 // create a networkdevice
582 m_networkdevice = new NG_LoopBackNetworkDeviceInterface();
583 if (!m_networkdevice)
586 // get an audiodevice
587 SND_DeviceManager::Subscribe();
588 m_audiodevice = SND_DeviceManager::Instance();
591 m_audiodevice->UseCD();
593 // create a ketsjisystem (only needed for timing and stuff)
594 m_kxsystem = new GPG_System (m_system);
598 // create the ketsjiengine
599 m_ketsjiengine = new KX_KetsjiEngine(m_kxsystem);
602 m_ketsjiengine->SetKeyboardDevice(m_keyboard);
603 m_ketsjiengine->SetMouseDevice(m_mouse);
604 m_ketsjiengine->SetNetworkDevice(m_networkdevice);
605 m_ketsjiengine->SetCanvas(m_canvas);
606 m_ketsjiengine->SetRenderTools(m_rendertools);
607 m_ketsjiengine->SetRasterizer(m_rasterizer);
608 m_ketsjiengine->SetNetworkDevice(m_networkdevice);
609 m_ketsjiengine->SetAudioDevice(m_audiodevice);
610 m_ketsjiengine->SetTimingDisplay(frameRate, false, false);
612 CValue::SetDeprecationWarnings(nodepwarnings);
614 m_ketsjiengine->SetUseFixedTime(fixed_framerate);
615 m_ketsjiengine->SetTimingDisplay(frameRate, profile, properties);
617 m_engineInitialized = true;
620 return m_engineInitialized;
623 delete m_audiodevice;
624 delete m_networkdevice;
628 delete m_rendertools;
631 m_rendertools = NULL;
635 m_networkdevice = NULL;
636 m_audiodevice = NULL;
643 bool GPG_Application::startEngine(void)
645 if (m_engineRunning) {
649 // Temporary hack to disable banner display for NaN approved content.
651 m_canvas->SetBannerDisplayEnabled(true);
653 cam = (Camera*)G.scene->camera->data;
655 if (((cam->flag) & 48)==48) {
656 m_canvas->SetBannerDisplayEnabled(false);
660 showError(CString("Camera data invalid."));
665 // create a scene converter, create and convert the stratingscene
666 m_sceneconverter = new KX_BlenderSceneConverter(m_maggie, m_ketsjiengine);
667 if (m_sceneconverter)
669 STR_String startscenename = m_startSceneName.Ptr();
670 m_ketsjiengine->SetSceneConverter(m_sceneconverter);
672 // if (always_use_expand_framing)
673 // sceneconverter->SetAlwaysUseExpandFraming(true);
674 if(m_blendermat && (G.fileflags & G_FILE_GAME_MAT))
675 m_sceneconverter->SetMaterials(true);
676 if(m_blenderglslmat && (G.fileflags & G_FILE_GAME_MAT_GLSL))
677 m_sceneconverter->SetGLSLMaterials(true);
679 KX_Scene* startscene = new KX_Scene(m_keyboard,
687 // some python things
688 PyObject* dictionaryobject = initGamePlayerPythonScripting("Ketsji", psl_Lowest, m_maggie, m_argc, m_argv);
689 m_ketsjiengine->SetPythonDictionary(dictionaryobject);
690 initRasterizer(m_rasterizer, m_canvas);
691 PyObject *gameLogic = initGameLogic(m_ketsjiengine, startscene);
692 PyDict_SetItemString(dictionaryobject, "GameLogic", gameLogic); // Same as importing the module
694 initPythonConstraintBinding();
702 //initialize Dome Settings
703 if(m_startScene->r.stereomode == RAS_IRasterizer::RAS_STEREO_DOME)
704 m_ketsjiengine->InitDome(m_startScene->r.domeres, m_startScene->r.domemode, m_startScene->r.domeangle, m_startScene->r.domeresbuf, m_startScene->r.dometilt, m_startScene->r.dometext);
706 // Set the GameLogic.globalDict from marshal'd data, so we can
707 // load new blend files and keep data in GameLogic.globalDict
708 loadGamePythonConfig(m_pyGlobalDictString, m_pyGlobalDictString_Length);
710 m_sceneconverter->ConvertScene(
716 m_ketsjiengine->AddScene(startscene);
718 // Create a timer that is used to kick the engine
720 m_frameTimer = m_system->installTimer(0, kTimerFreq, frameTimerProc, m_mainWindow);
722 m_rasterizer->Init();
723 m_ketsjiengine->StartEngine(true);
724 m_engineRunning = true;
726 // Set the animation playback rate for ipo's and actions
727 // the framerate below should patch with FPS macro defined in blendef.h
728 // Could be in StartEngine set the framerate, we need the scene to do this
729 m_ketsjiengine->SetAnimFrameRate( (((double) G.scene->r.frs_sec) / G.scene->r.frs_sec_base) );
733 if (!m_engineRunning)
738 return m_engineRunning;
742 void GPG_Application::stopEngine()
744 // GameLogic.globalDict gets converted into a buffer, and sorted in
745 // m_pyGlobalDictString so we can restore after python has stopped
746 // and started between .blend file loads.
747 if(m_pyGlobalDictString) {
748 delete [] m_pyGlobalDictString;
749 m_pyGlobalDictString = 0;
752 m_pyGlobalDictString_Length = saveGamePythonConfig(&m_pyGlobalDictString);
754 // when exiting the mainloop
755 exitGamePythonScripting();
756 m_ketsjiengine->StopEngine();
757 m_networkdevice->Disconnect();
759 if (m_sceneconverter) {
760 delete m_sceneconverter;
761 m_sceneconverter = 0;
763 if (m_system && m_frameTimer) {
764 m_system->removeTimer(m_frameTimer);
767 m_engineRunning = false;
771 void GPG_Application::exitEngine()
776 delete m_ketsjiengine;
786 SND_DeviceManager::Unsubscribe();
791 delete m_networkdevice;
811 delete m_rendertools;
821 #ifdef WITH_QUICKTIME
824 GPU_extensions_exit();
827 m_engineInitialized = false;
830 bool GPG_Application::handleWheel(GHOST_IEvent* event)
832 bool handled = false;
836 GHOST_TEventDataPtr eventData = ((GHOST_IEvent*)event)->getData();
837 GHOST_TEventWheelData* wheelData = static_cast<GHOST_TEventWheelData*>(eventData);
838 GPC_MouseDevice::TButtonId button;
839 if (wheelData->z > 0)
840 button = GPC_MouseDevice::buttonWheelUp;
842 button = GPC_MouseDevice::buttonWheelDown;
843 m_mouse->ConvertButtonEvent(button, true);
849 bool GPG_Application::handleButton(GHOST_IEvent* event, bool isDown)
851 bool handled = false;
855 GHOST_TEventDataPtr eventData = ((GHOST_IEvent*)event)->getData();
856 GHOST_TEventButtonData* buttonData = static_cast<GHOST_TEventButtonData*>(eventData);
857 GPC_MouseDevice::TButtonId button;
858 switch (buttonData->button)
860 case GHOST_kButtonMaskMiddle:
861 button = GPC_MouseDevice::buttonMiddle;
863 case GHOST_kButtonMaskRight:
864 button = GPC_MouseDevice::buttonRight;
866 case GHOST_kButtonMaskLeft:
868 button = GPC_MouseDevice::buttonLeft;
871 m_mouse->ConvertButtonEvent(button, isDown);
878 bool GPG_Application::handleCursorMove(GHOST_IEvent* event)
880 bool handled = false;
882 if (m_mouse && m_mainWindow)
884 GHOST_TEventDataPtr eventData = ((GHOST_IEvent*)event)->getData();
885 GHOST_TEventCursorData* cursorData = static_cast<GHOST_TEventCursorData*>(eventData);
887 m_mainWindow->screenToClient(cursorData->x, cursorData->y, x, y);
888 m_mouse->ConvertMoveEvent(x, y);
895 bool GPG_Application::handleKey(GHOST_IEvent* event, bool isDown)
897 bool handled = false;
901 GHOST_TEventDataPtr eventData = ((GHOST_IEvent*)event)->getData();
902 GHOST_TEventKeyData* keyData = static_cast<GHOST_TEventKeyData*>(eventData);
903 //no need for this test
904 //if (fSystem->getFullScreen()) {
905 if (keyData->key == GHOST_kKeyEsc && !m_keyboard->m_hookesc && !m_isEmbedded) {
906 m_exitRequested = KX_EXIT_REQUEST_OUTSIDE;
909 m_keyboard->ConvertEvent(keyData->key, isDown);
917 static void frameTimerProc(GHOST_ITimerTask* task, GHOST_TUns64 time)
919 GHOST_IWindow* window = (GHOST_IWindow*)task->getUserData();
920 if (fSystem->validWindow(window)) {
921 window->invalidate();