Logo Search packages:      
Sourcecode: blender version File versions

GPG_Application.cpp

/**
 * $Id: GPG_Application.cpp,v 1.13 2004/12/01 08:35:38 kester Exp $
 *
 * ***** BEGIN GPL/BL DUAL LICENSE BLOCK *****
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version. The Blender
 * Foundation also sells licenses for use in proprietary software under
 * the Blender License.  See http://www.blender.org/BL/ for information
 * about this.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software Foundation,
 * Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 *
 * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
 * All rights reserved.
 *
 * The Original Code is: all of this file.
 *
 * Contributor(s): none yet.
 *
 * ***** END GPL/BL DUAL LICENSE BLOCK *****
 * GHOST Blender Player application implementation file.
 */

#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

#ifdef WIN32
      #pragma warning (disable:4786) // suppress stl-MSVC debug info warning
      #include <windows.h>
#endif

#ifdef __APPLE__
#include <OpenGL/gl.h>
#include <OpenGL/glu.h>
#else
#include <GL/gl.h>
#include <GL/glu.h>
#endif

#include "GPG_Application.h"

#include <iostream>
#include <assert.h>

/**********************************
 * Begin Blender include block
 **********************************/
#ifdef __cplusplus
extern "C"
{
#endif  // __cplusplus
#include "BLI_blenlib.h"
#include "BLO_readfile.h"
#ifdef __cplusplus
}
#endif // __cplusplus
/**********************************
 * End Blender include block
 **********************************/


#include "SYS_System.h"
#include "KX_KetsjiEngine.h"

// include files needed by "KX_BlenderSceneConverter.h"
#include "GEN_Map.h"
#include "SCA_IActuator.h"
#include "RAS_MeshObject.h"
#include "RAS_OpenGLRasterizer.h"
#include "RAS_VAOpenGLRasterizer.h"
#include "RAS_GLExtensionManager.h"
#include "KX_PythonInit.h"
#include "KX_PyConstraintBinding.h"

#include "KX_BlenderSceneConverter.h"
#include "NG_LoopBackNetworkDeviceInterface.h"
#include "SND_DeviceManager.h"

#include "GPC_MouseDevice.h"
#include "GPC_RenderTools.h"
#include "GPG_Canvas.h" 
#include "GPG_KeyboardDevice.h"
#include "GPG_System.h"

#include "STR_String.h"

#include "GHOST_ISystem.h"
#include "GHOST_IEvent.h"
#include "GHOST_IEventConsumer.h"
#include "GHOST_IWindow.h"
#include "GHOST_Rect.h"


static void frameTimerProc(GHOST_ITimerTask* task, GHOST_TUns64 time);

static GHOST_ISystem* fSystem = 0;
static const int kTimerFreq = 10;

GPG_Application::GPG_Application(GHOST_ISystem* system, struct Main *maggie, STR_String startSceneName)
      : m_startSceneName(startSceneName), 
        m_maggie(maggie), 
        m_exitRequested(0),
        m_system(system), 
        m_mainWindow(0), 
        m_frameTimer(0), 
        m_cursor(GHOST_kStandardCursorFirstCursor),
        m_engineInitialized(0), 
        m_engineRunning(0), 
        m_ketsjiengine(0),
        m_kxsystem(0), 
        m_keyboard(0), 
        m_mouse(0), 
        m_canvas(0), 
        m_rendertools(0), 
        m_rasterizer(0), 
        m_sceneconverter(0),
        m_networkdevice(0), 
        m_audiodevice(0)
{
      fSystem = system;
}



GPG_Application::~GPG_Application(void)
{
      exitEngine();
      fSystem->disposeWindow(m_mainWindow);
}



bool GPG_Application::SetGameEngineData(struct Main *maggie, STR_String startSceneName)
{
      bool result = false;

      if (maggie != NULL && startSceneName != "")
      {
            m_maggie = maggie;
            m_startSceneName = startSceneName;
            result = true;
      }

      return result;
}


#ifdef WIN32
#define SCR_SAVE_MOUSE_MOVE_THRESHOLD 15

static HWND found_ghost_window_hwnd;
static GHOST_IWindow* ghost_window_to_find;
static WNDPROC ghost_wnd_proc;
static POINT scr_save_mouse_pos;

static LRESULT CALLBACK screenSaverWindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
      BOOL close = FALSE;
      switch (uMsg)
      {
            case WM_MOUSEMOVE:
            { 
                  POINT pt; 
                  GetCursorPos(&pt);
                  LONG dx = scr_save_mouse_pos.x - pt.x;
                  LONG dy = scr_save_mouse_pos.y - pt.y;
                  if (abs(dx) > SCR_SAVE_MOUSE_MOVE_THRESHOLD
                      || abs(dy) > SCR_SAVE_MOUSE_MOVE_THRESHOLD)
                  {
                        close = TRUE;
                  }
                  scr_save_mouse_pos = pt;
                  break;
            }
            case WM_LBUTTONDOWN: 
            case WM_MBUTTONDOWN: 
            case WM_RBUTTONDOWN: 
            case WM_KEYDOWN:
                  close = TRUE;
      }
      if (close)
            PostMessage(hwnd,WM_CLOSE,0,0);
      return CallWindowProc(ghost_wnd_proc, hwnd, uMsg, wParam, lParam);
}

BOOL CALLBACK findGhostWindowHWNDProc(HWND hwnd, LPARAM lParam)
{
      GHOST_IWindow *p = (GHOST_IWindow*) GetWindowLong(hwnd, GWL_USERDATA);
      BOOL ret = TRUE;
      if (p == ghost_window_to_find)
      {
            found_ghost_window_hwnd = hwnd;
            ret = FALSE;
      }
      return ret;
}

static HWND findGhostWindowHWND(GHOST_IWindow* window)
{
      found_ghost_window_hwnd = NULL;
      ghost_window_to_find = window;
      EnumWindows(findGhostWindowHWNDProc, NULL);
      return found_ghost_window_hwnd;
}

bool GPG_Application::startScreenSaverPreview(
      HWND parentWindow,
      const bool stereoVisual,
      const int stereoMode)
{
      bool success = false;

      RECT rc;
      if (GetWindowRect(parentWindow, &rc))
      {
            int windowWidth = rc.right - rc.left;
            int windowHeight = rc.bottom - rc.top;
            STR_String title = "";
                                          
            m_mainWindow = fSystem->createWindow(title, 0, 0, windowWidth, windowHeight, GHOST_kWindowStateMinimized,
                  GHOST_kDrawingContextTypeOpenGL, stereoVisual);
            if (!m_mainWindow) {
                  printf("error: could not create main window\n");
                  exit(-1);
            }

            HWND ghost_hwnd = findGhostWindowHWND(m_mainWindow);
            if (!ghost_hwnd) {
                  printf("error: could find main window\n");
                  exit(-1);
            }

            SetParent(ghost_hwnd, parentWindow);
            LONG style = GetWindowLong(ghost_hwnd, GWL_STYLE);
            LONG exstyle = GetWindowLong(ghost_hwnd, GWL_EXSTYLE);

            RECT adjrc = { 0, 0, windowWidth, windowHeight };
            AdjustWindowRectEx(&adjrc, style, FALSE, exstyle);

            style = (style & (~(WS_POPUP|WS_OVERLAPPEDWINDOW|WS_OVERLAPPED|WS_CAPTION|WS_SYSMENU|WS_THICKFRAME|WS_MINIMIZEBOX|WS_MAXIMIZEBOX|WS_TILEDWINDOW ))) | WS_CHILD;
            SetWindowLong(ghost_hwnd, GWL_STYLE, style);
            SetWindowPos(ghost_hwnd, NULL, adjrc.left, adjrc.top, 0, 0, SWP_NOZORDER|SWP_NOSIZE|SWP_NOACTIVATE);

            /* Check the size of the client rectangle of the window and resize the window
             * so that the client rectangle has the size requested.
             */
            m_mainWindow->setClientSize(windowWidth, windowHeight);

            success = initEngine(m_mainWindow, stereoMode);
            if (success) {
                  success = startEngine();
            }

      }
      return success;
}

bool GPG_Application::startScreenSaverFullScreen(
            int width,
            int height,
            int bpp,int frequency,
            const bool stereoVisual,
            const int stereoMode)
{
      bool ret = startFullScreen(width, height, bpp, frequency, stereoVisual, stereoMode);
      if (ret)
      {
            HWND ghost_hwnd = findGhostWindowHWND(m_mainWindow);
            if (ghost_hwnd != NULL)
            {
                  GetCursorPos(&scr_save_mouse_pos);
                  ghost_wnd_proc = (WNDPROC) GetWindowLong(ghost_hwnd, GWL_WNDPROC);
                  SetWindowLong(ghost_hwnd,GWL_WNDPROC, (LONG) screenSaverWindowProc);
            }
      }
      return ret;
}

#endif

bool GPG_Application::startWindow(STR_String& title,
      int windowLeft,
      int windowTop,
      int windowWidth,
      int windowHeight,
      const bool stereoVisual,
      const int stereoMode)
{
      bool success;
      // Create the main window
      //STR_String title ("Blender Player - GHOST");
      m_mainWindow = fSystem->createWindow(title, windowLeft, windowTop, windowWidth, windowHeight, GHOST_kWindowStateNormal,
            GHOST_kDrawingContextTypeOpenGL, stereoVisual);
      if (!m_mainWindow) {
            printf("error: could not create main window\n");
            exit(-1);
      }

      /* Check the size of the client rectangle of the window and resize the window
       * so that the client rectangle has the size requested.
       */
      m_mainWindow->setClientSize(windowWidth, windowHeight);
      m_mainWindow->setCursorVisibility(false);

      success = initEngine(m_mainWindow, stereoMode);
      if (success) {
            success = startEngine();
      }
      return success;
}



bool GPG_Application::startFullScreen(
            int width,
            int height,
            int bpp,int frequency,
            const bool stereoVisual,
            const int stereoMode)
{
      bool success;
      // Create the main window
      GHOST_DisplaySetting setting;
      setting.xPixels = width;
      setting.yPixels = height;
      setting.bpp = bpp;
      setting.frequency = frequency;

      fSystem->beginFullScreen(setting, &m_mainWindow, stereoVisual);
      m_mainWindow->setCursorVisibility(false);

      success = initEngine(m_mainWindow, stereoMode);
      if (success) {
            success = startEngine();
      }
      return success;
}



bool GPG_Application::StartGameEngine(int stereoMode)
{
      bool success = initEngine(m_mainWindow, stereoMode);
      
      if (success)
            success = startEngine();

      return success;
}



void GPG_Application::StopGameEngine()
{
      exitEngine();
}



bool GPG_Application::processEvent(GHOST_IEvent* event)
{
      bool handled = true;

      switch (event->getType())
      {
            case GHOST_kEventUnknown:
                  break;

            case GHOST_kEventButtonDown:
                  handled = handleButton(event, true);
                  break;

            case GHOST_kEventButtonUp:
                  handled = handleButton(event, false);
                  break;
                  
            case GHOST_kEventWheel:
                  handled = handleWheel(event);
                  break;

            case GHOST_kEventCursorMove:
                  handled = handleCursorMove(event);
                  break;

            case GHOST_kEventKeyDown:
                  handleKey(event, true);
                  break;

            case GHOST_kEventKeyUp:
                  handleKey(event, false);
                  break;


            case GHOST_kEventWindowClose:
                  m_exitRequested = KX_EXIT_REQUEST_OUTSIDE;
                  break;

            case GHOST_kEventWindowActivate:
                  handled = false;
                  break;
            case GHOST_kEventWindowDeactivate:
                  handled = false;
                  break;

            case GHOST_kEventWindowUpdate:
                  {
                        GHOST_IWindow* window = event->getWindow();
                        if (!m_system->validWindow(window)) break;
                        // Update the state of the game engine
                        if (m_kxsystem && !m_exitRequested)
                        {
                              // Proceed to next frame
                              window->activateDrawingContext();

                              // first check if we want to exit
                              m_exitRequested = m_ketsjiengine->GetExitCode();
                              
                              // kick the engine
                              m_ketsjiengine->NextFrame();
                              
                              // render the frame
                              m_ketsjiengine->Render();
                        }
                        m_exitString = m_ketsjiengine->GetExitString();
                  }
                  break;
            
            case GHOST_kEventWindowSize:
                  {
                  GHOST_IWindow* window = event->getWindow();
                  if (!m_system->validWindow(window)) break;
                  if (m_canvas) {
                        GHOST_Rect bnds;
                        window->getClientBounds(bnds);
                        m_canvas->Resize(bnds.getWidth(), bnds.getHeight());
                  }
                  }
                  break;
            
            default:
                  handled = false;
                  break;
      }
      return handled;
}



int GPG_Application::getExitRequested(void)
{
      return m_exitRequested;
}



const STR_String& GPG_Application::getExitString(void)
{
      return m_exitString;
}



bool GPG_Application::initEngine(GHOST_IWindow* window, const int stereoMode)
{
      if (!m_engineInitialized)
      {
            bgl::InitExtensions(1);

            // get and set the preferences
            SYS_SystemHandle syshandle = SYS_GetSystem();
            if (!syshandle)
                  return false;
            
            // SYS_WriteCommandLineInt(syshandle, "fixedtime", 0);
            // SYS_WriteCommandLineInt(syshandle, "vertexarrays",1);          
            bool properties   = (SYS_GetCommandLineInt(syshandle, "show_properties", 0) != 0);
            bool profile = (SYS_GetCommandLineInt(syshandle, "show_profile", 0) != 0);
            bool frameRate = (SYS_GetCommandLineInt(syshandle, "show_framerate", 0) != 0);
            bool useVertexArrays = SYS_GetCommandLineInt(syshandle,"vertexarrays",1) != 0;
            // create the canvas, rasterizer and rendertools
            m_canvas = new GPG_Canvas(window);
            if (!m_canvas)
                  return false;
                        
            m_canvas->Init();                   
            m_rendertools = new GPC_RenderTools();
            if (!m_rendertools)
                  goto initFailed;
                              
            if (useVertexArrays && bgl::QueryVersion(1, 1))
                  m_rasterizer = new RAS_VAOpenGLRasterizer(m_canvas);
            else
                  m_rasterizer = new RAS_OpenGLRasterizer(m_canvas);
            m_rasterizer->SetStereoMode((RAS_IRasterizer::StereoMode) stereoMode);
            if (!m_rasterizer)
                  goto initFailed;
                                    
            // create the inputdevices
            m_keyboard = new GPG_KeyboardDevice();
            if (!m_keyboard)
                  goto initFailed;
                  
            m_mouse = new GPC_MouseDevice();
            if (!m_mouse)
                  goto initFailed;
                  
            // create a networkdevice
            m_networkdevice = new NG_LoopBackNetworkDeviceInterface();
            if (!m_networkdevice)
                  goto initFailed;
                  
            // get an audiodevice
            SND_DeviceManager::Subscribe();
            m_audiodevice = SND_DeviceManager::Instance();
            if (!m_audiodevice)
                  goto initFailed;
            m_audiodevice->UseCD();
            
            // create a ketsjisystem (only needed for timing and stuff)
            m_kxsystem = new GPG_System (m_system);
            if (!m_kxsystem)
                  goto initFailed;
            
            // create the ketsjiengine
            m_ketsjiengine = new KX_KetsjiEngine(m_kxsystem);
            
            // set the devices
            m_ketsjiengine->SetKeyboardDevice(m_keyboard);
            m_ketsjiengine->SetMouseDevice(m_mouse);
            m_ketsjiengine->SetNetworkDevice(m_networkdevice);
            m_ketsjiengine->SetCanvas(m_canvas);
            m_ketsjiengine->SetRenderTools(m_rendertools);
            m_ketsjiengine->SetRasterizer(m_rasterizer);
            m_ketsjiengine->SetNetworkDevice(m_networkdevice);
            m_ketsjiengine->SetAudioDevice(m_audiodevice);
            m_ketsjiengine->SetTimingDisplay(frameRate, false, false);

            m_ketsjiengine->SetUseFixedTime(false);
            m_ketsjiengine->SetTimingDisplay(frameRate, profile, properties);

            m_engineInitialized = true;
      }

      return m_engineInitialized;
initFailed:
      delete m_kxsystem;
      delete m_audiodevice;
      delete m_networkdevice;
      delete m_mouse;
      delete m_keyboard;
      delete m_rasterizer;
      delete m_rendertools;
      delete m_canvas;
      m_canvas = NULL;
      m_rendertools = NULL;
      m_rasterizer = NULL;
      m_keyboard = NULL;
      m_mouse = NULL;
      m_networkdevice = NULL;
      m_audiodevice = NULL;
      m_kxsystem = NULL;
      return false;
}



bool GPG_Application::startEngine(void)
{
      if (m_engineRunning) {
            return false;
      }
      
      // Temporary hack to disable banner display for NaN approved content.
      /*
      m_canvas->SetBannerDisplayEnabled(true);  
      Camera* cam;
      cam = (Camera*)G.scene->camera->data;
      if (cam) {
      if (((cam->flag) & 48)==48) {
      m_canvas->SetBannerDisplayEnabled(false);
      }
      }
      else {
      showError(CString("Camera data invalid."));
      return false;
      }
      */
      
      // create a scene converter, create and convert the stratingscene
      m_sceneconverter = new KX_BlenderSceneConverter(m_maggie, m_ketsjiengine);
      if (m_sceneconverter)
      {
            STR_String startscenename = m_startSceneName.Ptr();
            m_ketsjiengine->SetSceneConverter(m_sceneconverter);
            
            //    if (always_use_expand_framing)
            //          sceneconverter->SetAlwaysUseExpandFraming(true);
            

            KX_Scene* startscene = new KX_Scene(m_keyboard,
                  m_mouse,
                  m_networkdevice,
                  m_audiodevice,
                  startscenename);
            
            // some python things
            PyObject* m_dictionaryobject = initGamePlayerPythonScripting("Ketsji", psl_Lowest);
            m_ketsjiengine->SetPythonDictionary(m_dictionaryobject);
            initRasterizer(m_rasterizer, m_canvas);
            initGameLogic(startscene);
            initGameKeys();
            initPythonConstraintBinding();
            
            m_sceneconverter->ConvertScene(
                  startscenename,
                  startscene,
                  m_dictionaryobject,
                  m_keyboard,
                  m_rendertools,
                  m_canvas);
            m_ketsjiengine->AddScene(startscene);
            
            // Create a timer that is used to kick the engine
            if (!m_frameTimer) {
                  m_frameTimer = m_system->installTimer(0, kTimerFreq, frameTimerProc, m_mainWindow);
            }
            m_rasterizer->Init();
            m_ketsjiengine->StartEngine();
            m_engineRunning = true;
            
      }
      
      if (!m_engineRunning)
      {
            stopEngine();
      }
      
      return m_engineRunning;
}


void GPG_Application::stopEngine()
{
      // when exiting the mainloop
      exitGamePythonScripting();
      m_ketsjiengine->StopEngine();
      m_networkdevice->Disconnect();

      if (m_sceneconverter) {
            delete m_sceneconverter;
            m_sceneconverter = 0;
      }
      if (m_system && m_frameTimer) {
            m_system->removeTimer(m_frameTimer);
            m_frameTimer = 0;
      }
      m_engineRunning = false;
}


void GPG_Application::exitEngine()
{
      if (m_ketsjiengine)
      {
            stopEngine();
            delete m_ketsjiengine;
            m_ketsjiengine = 0;
      }
      if (m_kxsystem)
      {
            delete m_kxsystem;
            m_kxsystem = 0;
      }
      if (m_audiodevice)
      {
            SND_DeviceManager::Unsubscribe();
            m_audiodevice = 0;
      }
      if (m_networkdevice)
      {
            delete m_networkdevice;
            m_networkdevice = 0;
      }
      if (m_mouse)
      {
            delete m_mouse;
            m_mouse = 0;
      }
      if (m_keyboard)
      {
            delete m_keyboard;
            m_keyboard = 0;
      }
      if (m_rasterizer)
      {
            delete m_rasterizer;
            m_rasterizer = 0;
      }
      if (m_rendertools)
      {
            delete m_rendertools;
            m_rendertools = 0;
      }
      if (m_canvas)
      {
            delete m_canvas;
            m_canvas = 0;
      }

      m_exitRequested = 0;
      m_engineInitialized = false;
}

bool GPG_Application::handleWheel(GHOST_IEvent* event)
{
      bool handled = false;
      assert(event);
      if (m_mouse) 
      {
            GHOST_TEventDataPtr eventData = ((GHOST_IEvent*)event)->getData();
            GHOST_TEventWheelData* wheelData = static_cast<GHOST_TEventWheelData*>(eventData);
            GPC_MouseDevice::TButtonId button;
            if (wheelData->z > 0)
                  button = GPC_MouseDevice::buttonWheelUp;
            else
                  button = GPC_MouseDevice::buttonWheelDown;
            m_mouse->ConvertButtonEvent(button, true);
            handled = true;
      }
      return handled;
}

bool GPG_Application::handleButton(GHOST_IEvent* event, bool isDown)
{
      bool handled = false;
      assert(event);
      if (m_mouse) 
      {
            GHOST_TEventDataPtr eventData = ((GHOST_IEvent*)event)->getData();
            GHOST_TEventButtonData* buttonData = static_cast<GHOST_TEventButtonData*>(eventData);
            GPC_MouseDevice::TButtonId button;
            switch (buttonData->button)
            {
            case GHOST_kButtonMaskMiddle:
                  button = GPC_MouseDevice::buttonMiddle;
                  break;
            case GHOST_kButtonMaskRight:
                  button = GPC_MouseDevice::buttonRight;
                  break;
            case GHOST_kButtonMaskLeft:
            default:
                  button = GPC_MouseDevice::buttonLeft;
                  break;
            }
            m_mouse->ConvertButtonEvent(button, isDown);
            handled = true;
      }
      return handled;
}


bool GPG_Application::handleCursorMove(GHOST_IEvent* event)
{
      bool handled = false;
      assert(event);
      if (m_mouse && m_mainWindow)
      {
            GHOST_TEventDataPtr eventData = ((GHOST_IEvent*)event)->getData();
            GHOST_TEventCursorData* cursorData = static_cast<GHOST_TEventCursorData*>(eventData);
            GHOST_TInt32 x, y;
            m_mainWindow->screenToClient(cursorData->x, cursorData->y, x, y);
            m_mouse->ConvertMoveEvent(x, y);
            handled = true;
      }
      return handled;
}


bool GPG_Application::handleKey(GHOST_IEvent* event, bool isDown)
{
      bool handled = false;
      assert(event);
      if (m_keyboard)
      {
            GHOST_TEventDataPtr eventData = ((GHOST_IEvent*)event)->getData();
            GHOST_TEventKeyData* keyData = static_cast<GHOST_TEventKeyData*>(eventData);
            if (fSystem->getFullScreen()) {
                  if (keyData->key == GHOST_kKeyEsc) {
                        m_exitRequested = KX_EXIT_REQUEST_OUTSIDE;
                  }
            }
            m_keyboard->ConvertEvent(keyData->key, isDown);
            handled = true;
      }
      return handled;
}



static void frameTimerProc(GHOST_ITimerTask* task, GHOST_TUns64 time)
{
      GHOST_IWindow* window = (GHOST_IWindow*)task->getUserData();
      if (fSystem->validWindow(window)) {
            window->invalidate();
      }
}

Generated by  Doxygen 1.6.0   Back to index