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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, 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.
31 /** \file gameengine/GamePlayer/ghost/GPG_Application.cpp
37 #pragma warning (disable:4786) // suppress stl-MSVC debug info warning
42 #include "GPU_extensions.h"
44 #include "GPG_Application.h"
47 #include <MT_assert.h>
50 /**********************************
51 * Begin Blender include block
52 **********************************/
57 #include "BLI_blenlib.h"
58 #include "BLO_readfile.h"
59 #include "BKE_global.h"
61 #include "BKE_sound.h"
62 #include "IMB_imbuf.h"
63 #include "DNA_scene_types.h"
67 /**********************************
68 * End Blender include block
69 **********************************/
72 #include "BL_System.h"
73 #include "KX_KetsjiEngine.h"
75 // include files needed by "KX_BlenderSceneConverter.h"
77 #include "SCA_IActuator.h"
78 #include "RAS_MeshObject.h"
79 #include "RAS_OpenGLRasterizer.h"
80 #include "RAS_VAOpenGLRasterizer.h"
81 #include "RAS_ListRasterizer.h"
82 #include "RAS_GLExtensionManager.h"
83 #include "KX_PythonInit.h"
84 #include "KX_PyConstraintBinding.h"
85 #include "BL_Material.h" // MAXTEX
87 #include "KX_BlenderSceneConverter.h"
88 #include "NG_LoopBackNetworkDeviceInterface.h"
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"
96 #include "STR_String.h"
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"
104 static void frameTimerProc(GHOST_ITimerTask* task, GHOST_TUns64 time);
106 static GHOST_ISystem* fSystem = 0;
107 static const int kTimerFreq = 10;
109 GPG_Application::GPG_Application(GHOST_ISystem* system)
110 : m_startSceneName(""),
117 m_cursor(GHOST_kStandardCursorFirstCursor),
118 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)
160 // XXX G.scene = scene;
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,
237 const GHOST_TUns16 samples)
239 bool success = false;
242 if (GetWindowRect(parentWindow, &rc))
244 int windowWidth = rc.right - rc.left;
245 int windowHeight = rc.bottom - rc.top;
246 STR_String title = "";
248 m_mainWindow = fSystem->createWindow(title, 0, 0, windowWidth, windowHeight, GHOST_kWindowStateMinimized,
249 GHOST_kDrawingContextTypeOpenGL, stereoVisual, samples);
251 printf("error: could not create main window\n");
255 HWND ghost_hwnd = findGhostWindowHWND(m_mainWindow);
257 printf("error: could find main window\n");
261 SetParent(ghost_hwnd, parentWindow);
262 LONG style = GetWindowLong(ghost_hwnd, GWL_STYLE);
263 LONG exstyle = GetWindowLong(ghost_hwnd, GWL_EXSTYLE);
265 RECT adjrc = { 0, 0, windowWidth, windowHeight };
266 AdjustWindowRectEx(&adjrc, style, FALSE, exstyle);
268 style = (style & (~(WS_POPUP|WS_OVERLAPPEDWINDOW|WS_OVERLAPPED|WS_CAPTION|WS_SYSMENU|WS_THICKFRAME|WS_MINIMIZEBOX|WS_MAXIMIZEBOX|WS_TILEDWINDOW ))) | WS_CHILD;
269 SetWindowLong(ghost_hwnd, GWL_STYLE, style);
270 SetWindowPos(ghost_hwnd, NULL, adjrc.left, adjrc.top, 0, 0, SWP_NOZORDER|SWP_NOSIZE|SWP_NOACTIVATE);
272 /* Check the size of the client rectangle of the window and resize the window
273 * so that the client rectangle has the size requested.
275 m_mainWindow->setClientSize(windowWidth, windowHeight);
277 success = initEngine(m_mainWindow, stereoMode);
279 success = startEngine();
286 bool GPG_Application::startScreenSaverFullScreen(
289 int bpp,int frequency,
290 const bool stereoVisual,
291 const int stereoMode,
292 const GHOST_TUns16 samples)
294 bool ret = startFullScreen(width, height, bpp, frequency, stereoVisual, stereoMode, samples);
297 HWND ghost_hwnd = findGhostWindowHWND(m_mainWindow);
298 if (ghost_hwnd != NULL)
300 GetCursorPos(&scr_save_mouse_pos);
301 ghost_wnd_proc = (WNDPROC) GetWindowLongPtr(ghost_hwnd, GWLP_WNDPROC);
302 SetWindowLongPtr(ghost_hwnd,GWLP_WNDPROC, (uintptr_t) screenSaverWindowProc);
310 bool GPG_Application::startWindow(STR_String& title,
315 const bool stereoVisual,
316 const int stereoMode,
317 const GHOST_TUns16 samples)
320 // Create the main window
321 //STR_String title ("Blender Player - GHOST");
322 m_mainWindow = fSystem->createWindow(title, windowLeft, windowTop, windowWidth, windowHeight, GHOST_kWindowStateNormal,
323 GHOST_kDrawingContextTypeOpenGL, stereoVisual, samples);
325 printf("error: could not create main window\n");
329 /* Check the size of the client rectangle of the window and resize the window
330 * so that the client rectangle has the size requested.
332 m_mainWindow->setClientSize(windowWidth, windowHeight);
333 m_mainWindow->setCursorVisibility(false);
335 success = initEngine(m_mainWindow, stereoMode);
337 success = startEngine();
342 bool GPG_Application::startEmbeddedWindow(STR_String& title,
343 const GHOST_TEmbedderWindowID parentWindow,
344 const bool stereoVisual,
345 const int stereoMode,
346 const GHOST_TUns16 samples) {
347 GHOST_TWindowState state = GHOST_kWindowStateNormal;
348 if (parentWindow != 0)
349 state = GHOST_kWindowStateEmbedded;
350 m_mainWindow = fSystem->createWindow(title, 0, 0, 0, 0, state,
351 GHOST_kDrawingContextTypeOpenGL, stereoVisual, samples, parentWindow);
354 printf("error: could not create main window\n");
359 bool success = initEngine(m_mainWindow, stereoMode);
361 success = startEngine();
367 bool GPG_Application::startFullScreen(
370 int bpp,int frequency,
371 const bool stereoVisual,
372 const int stereoMode,
373 const GHOST_TUns16 samples)
376 // Create the main window
377 GHOST_DisplaySetting setting;
378 setting.xPixels = width;
379 setting.yPixels = height;
381 setting.frequency = frequency;
383 fSystem->beginFullScreen(setting, &m_mainWindow, stereoVisual);
384 m_mainWindow->setCursorVisibility(false);
385 m_mainWindow->setState(GHOST_kWindowStateFullScreen);
387 success = initEngine(m_mainWindow, stereoMode);
389 success = startEngine();
397 bool GPG_Application::StartGameEngine(int stereoMode)
399 bool success = initEngine(m_mainWindow, stereoMode);
402 success = startEngine();
409 void GPG_Application::StopGameEngine()
416 bool GPG_Application::processEvent(GHOST_IEvent* event)
420 switch (event->getType())
422 case GHOST_kEventUnknown:
425 case GHOST_kEventButtonDown:
426 handled = handleButton(event, true);
429 case GHOST_kEventButtonUp:
430 handled = handleButton(event, false);
433 case GHOST_kEventWheel:
434 handled = handleWheel(event);
437 case GHOST_kEventCursorMove:
438 handled = handleCursorMove(event);
441 case GHOST_kEventKeyDown:
442 handleKey(event, true);
445 case GHOST_kEventKeyUp:
446 handleKey(event, false);
450 case GHOST_kEventWindowClose:
451 m_exitRequested = KX_EXIT_REQUEST_OUTSIDE;
454 case GHOST_kEventWindowActivate:
457 case GHOST_kEventWindowDeactivate:
461 case GHOST_kEventWindowUpdate:
463 GHOST_IWindow* window = event->getWindow();
464 if (!m_system->validWindow(window)) break;
465 // Update the state of the game engine
466 if (m_kxsystem && !m_exitRequested)
468 // Proceed to next frame
469 window->activateDrawingContext();
471 // first check if we want to exit
472 m_exitRequested = m_ketsjiengine->GetExitCode();
475 bool renderFrame = m_ketsjiengine->NextFrame();
479 m_ketsjiengine->Render();
482 m_exitString = m_ketsjiengine->GetExitString();
486 case GHOST_kEventWindowSize:
488 GHOST_IWindow* window = event->getWindow();
489 if (!m_system->validWindow(window)) break;
492 window->getClientBounds(bnds);
493 m_canvas->Resize(bnds.getWidth(), bnds.getHeight());
507 int GPG_Application::getExitRequested(void)
509 return m_exitRequested;
514 const STR_String& GPG_Application::getExitString(void)
521 bool GPG_Application::initEngine(GHOST_IWindow* window, const int stereoMode)
523 if (!m_engineInitialized)
525 GPU_extensions_init();
526 bgl::InitExtensions(true);
528 // get and set the preferences
529 SYS_SystemHandle syshandle = SYS_GetSystem();
533 // SYS_WriteCommandLineInt(syshandle, "fixedtime", 0);
534 // SYS_WriteCommandLineInt(syshandle, "vertexarrays",1);
535 GameData *gm= &m_startScene->gm;
536 bool properties = (SYS_GetCommandLineInt(syshandle, "show_properties", 0) != 0);
537 bool profile = (SYS_GetCommandLineInt(syshandle, "show_profile", 0) != 0);
538 bool fixedFr = (gm->flag & GAME_ENABLE_ALL_FRAMES);
540 bool showPhysics = (gm->flag & GAME_SHOW_PHYSICS);
541 SYS_WriteCommandLineInt(syshandle, "show_physics", showPhysics);
543 bool fixed_framerate= (SYS_GetCommandLineInt(syshandle, "fixed_framerate", fixedFr) != 0);
544 bool frameRate = (SYS_GetCommandLineInt(syshandle, "show_framerate", 0) != 0);
545 bool useLists = (SYS_GetCommandLineInt(syshandle, "displaylists", gm->flag & GAME_DISPLAY_LISTS) != 0);
546 bool nodepwarnings = (SYS_GetCommandLineInt(syshandle, "ignore_deprecation_warnings", 1) != 0);
548 if(GLEW_ARB_multitexture && GLEW_VERSION_1_1)
549 m_blendermat = (SYS_GetCommandLineInt(syshandle, "blender_material", 1) != 0);
551 if(GPU_glsl_support())
552 m_blenderglslmat = (SYS_GetCommandLineInt(syshandle, "blender_glsl_material", 1) != 0);
553 else if(gm->matmode == GAME_MAT_GLSL)
554 m_blendermat = false;
556 // create the canvas, rasterizer and rendertools
557 m_canvas = new GPG_Canvas(window);
562 if (gm->flag & GAME_SHOW_MOUSE)
563 m_canvas->SetMouseState(RAS_ICanvas::MOUSE_NORMAL);
565 m_rendertools = new GPC_RenderTools();
571 m_rasterizer = new RAS_ListRasterizer(m_canvas, true);
573 m_rasterizer = new RAS_ListRasterizer(m_canvas);
575 else if (GLEW_VERSION_1_1)
576 m_rasterizer = new RAS_VAOpenGLRasterizer(m_canvas);
578 m_rasterizer = new RAS_OpenGLRasterizer(m_canvas);
580 /* Stereo parameters - Eye Separation from the UI - stereomode from the command-line/UI */
581 m_rasterizer->SetStereoMode((RAS_IRasterizer::StereoMode) stereoMode);
582 m_rasterizer->SetEyeSeparation(m_startScene->gm.eyeseparation);
587 // create the inputdevices
588 m_keyboard = new GPG_KeyboardDevice();
592 m_mouse = new GPC_MouseDevice();
596 // create a networkdevice
597 m_networkdevice = new NG_LoopBackNetworkDeviceInterface();
598 if (!m_networkdevice)
601 sound_init(m_maggie);
603 // create a ketsjisystem (only needed for timing and stuff)
604 m_kxsystem = new GPG_System (m_system);
608 // create the ketsjiengine
609 m_ketsjiengine = new KX_KetsjiEngine(m_kxsystem);
612 m_ketsjiengine->SetKeyboardDevice(m_keyboard);
613 m_ketsjiengine->SetMouseDevice(m_mouse);
614 m_ketsjiengine->SetNetworkDevice(m_networkdevice);
615 m_ketsjiengine->SetCanvas(m_canvas);
616 m_ketsjiengine->SetRenderTools(m_rendertools);
617 m_ketsjiengine->SetRasterizer(m_rasterizer);
618 m_ketsjiengine->SetNetworkDevice(m_networkdevice);
620 m_ketsjiengine->SetTimingDisplay(frameRate, false, false);
622 CValue::SetDeprecationWarnings(nodepwarnings);
627 m_ketsjiengine->SetUseFixedTime(fixed_framerate);
628 m_ketsjiengine->SetTimingDisplay(frameRate, profile, properties);
630 m_engineInitialized = true;
633 return m_engineInitialized;
637 delete m_networkdevice;
641 delete m_rendertools;
644 m_rendertools = NULL;
648 m_networkdevice = NULL;
655 bool GPG_Application::startEngine(void)
657 if (m_engineRunning) {
661 // Temporary hack to disable banner display for NaN approved content.
663 m_canvas->SetBannerDisplayEnabled(true);
665 cam = (Camera*)scene->camera->data;
667 if (((cam->flag) & 48)==48) {
668 m_canvas->SetBannerDisplayEnabled(false);
672 showError(CString("Camera data invalid."));
677 // create a scene converter, create and convert the stratingscene
678 m_sceneconverter = new KX_BlenderSceneConverter(m_maggie, m_ketsjiengine);
679 if (m_sceneconverter)
681 STR_String startscenename = m_startSceneName.Ptr();
682 m_ketsjiengine->SetSceneConverter(m_sceneconverter);
684 // if (always_use_expand_framing)
685 // sceneconverter->SetAlwaysUseExpandFraming(true);
686 if(m_blendermat && (m_startScene->gm.matmode != GAME_MAT_TEXFACE))
687 m_sceneconverter->SetMaterials(true);
688 if(m_blenderglslmat && (m_startScene->gm.matmode == GAME_MAT_GLSL))
689 m_sceneconverter->SetGLSLMaterials(true);
691 KX_Scene* startscene = new KX_Scene(m_keyboard,
699 // some python things
700 PyObject *gameLogic, *gameLogic_keys;
701 setupGamePython(m_ketsjiengine, startscene, m_maggie, NULL, &gameLogic, &gameLogic_keys, m_argc, m_argv);
702 #endif // WITH_PYTHON
704 //initialize Dome Settings
705 if(m_startScene->gm.stereoflag == STEREO_DOME)
706 m_ketsjiengine->InitDome(m_startScene->gm.dome.res, m_startScene->gm.dome.mode, m_startScene->gm.dome.angle, m_startScene->gm.dome.resbuf, m_startScene->gm.dome.tilt, m_startScene->gm.dome.warptext);
709 // Set the GameLogic.globalDict from marshal'd data, so we can
710 // load new blend files and keep data in GameLogic.globalDict
711 loadGamePythonConfig(m_pyGlobalDictString, m_pyGlobalDictString_Length);
713 m_sceneconverter->ConvertScene(
717 m_ketsjiengine->AddScene(startscene);
719 // Create a timer that is used to kick the engine
721 m_frameTimer = m_system->installTimer(0, kTimerFreq, frameTimerProc, m_mainWindow);
723 m_rasterizer->Init();
724 m_ketsjiengine->StartEngine(true);
725 m_engineRunning = true;
727 // Set the animation playback rate for ipo's and actions
728 // the framerate below should patch with FPS macro defined in blendef.h
729 // Could be in StartEngine set the framerate, we need the scene to do this
730 Scene *scene= startscene->GetBlenderScene(); // needed for macro
731 m_ketsjiengine->SetAnimFrameRate(FPS);
734 if (!m_engineRunning)
739 return m_engineRunning;
743 void GPG_Application::stopEngine()
746 // GameLogic.globalDict gets converted into a buffer, and sorted in
747 // m_pyGlobalDictString so we can restore after python has stopped
748 // and started between .blend file loads.
749 if(m_pyGlobalDictString) {
750 delete [] m_pyGlobalDictString;
751 m_pyGlobalDictString = 0;
754 m_pyGlobalDictString_Length = saveGamePythonConfig(&m_pyGlobalDictString);
756 // when exiting the mainloop
757 exitGamePythonScripting();
760 m_ketsjiengine->StopEngine();
761 m_networkdevice->Disconnect();
763 if (m_sceneconverter) {
764 delete m_sceneconverter;
765 m_sceneconverter = 0;
767 if (m_system && m_frameTimer) {
768 m_system->removeTimer(m_frameTimer);
771 m_engineRunning = false;
775 void GPG_Application::exitEngine()
781 delete m_ketsjiengine;
791 delete m_networkdevice;
811 delete m_rendertools;
820 GPU_extensions_exit();
823 m_engineInitialized = false;
826 bool GPG_Application::handleWheel(GHOST_IEvent* event)
828 bool handled = false;
832 GHOST_TEventDataPtr eventData = ((GHOST_IEvent*)event)->getData();
833 GHOST_TEventWheelData* wheelData = static_cast<GHOST_TEventWheelData*>(eventData);
834 GPC_MouseDevice::TButtonId button;
835 if (wheelData->z > 0)
836 button = GPC_MouseDevice::buttonWheelUp;
838 button = GPC_MouseDevice::buttonWheelDown;
839 m_mouse->ConvertButtonEvent(button, true);
845 bool GPG_Application::handleButton(GHOST_IEvent* event, bool isDown)
847 bool handled = false;
851 GHOST_TEventDataPtr eventData = ((GHOST_IEvent*)event)->getData();
852 GHOST_TEventButtonData* buttonData = static_cast<GHOST_TEventButtonData*>(eventData);
853 GPC_MouseDevice::TButtonId button;
854 switch (buttonData->button)
856 case GHOST_kButtonMaskMiddle:
857 button = GPC_MouseDevice::buttonMiddle;
859 case GHOST_kButtonMaskRight:
860 button = GPC_MouseDevice::buttonRight;
862 case GHOST_kButtonMaskLeft:
864 button = GPC_MouseDevice::buttonLeft;
867 m_mouse->ConvertButtonEvent(button, isDown);
874 bool GPG_Application::handleCursorMove(GHOST_IEvent* event)
876 bool handled = false;
878 if (m_mouse && m_mainWindow)
880 GHOST_TEventDataPtr eventData = ((GHOST_IEvent*)event)->getData();
881 GHOST_TEventCursorData* cursorData = static_cast<GHOST_TEventCursorData*>(eventData);
883 m_mainWindow->screenToClient(cursorData->x, cursorData->y, x, y);
884 m_mouse->ConvertMoveEvent(x, y);
891 bool GPG_Application::handleKey(GHOST_IEvent* event, bool isDown)
893 bool handled = false;
897 GHOST_TEventDataPtr eventData = ((GHOST_IEvent*)event)->getData();
898 GHOST_TEventKeyData* keyData = static_cast<GHOST_TEventKeyData*>(eventData);
899 //no need for this test
900 //if (fSystem->getFullScreen()) {
901 if (keyData->key == GHOST_kKeyEsc && !m_keyboard->m_hookesc && !m_isEmbedded) {
902 m_exitRequested = KX_EXIT_REQUEST_OUTSIDE;
905 m_keyboard->ConvertEvent(keyData->key, isDown);
913 static void frameTimerProc(GHOST_ITimerTask* task, GHOST_TUns64 time)
915 GHOST_IWindow* window = (GHOST_IWindow*)task->getUserData();
916 if (fSystem->validWindow(window)) {
917 window->invalidate();