/* * ***** BEGIN GPL 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. * * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, 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 LICENSE BLOCK ***** * The engine ties all game modules together. */ /** \file gameengine/Ketsji/KX_KetsjiEngine.cpp * \ingroup ketsji */ #ifdef _MSC_VER # pragma warning (disable:4786) #endif #include #include #include "KX_KetsjiEngine.h" #include "ListValue.h" #include "IntValue.h" #include "VectorValue.h" #include "BoolValue.h" #include "FloatValue.h" #include "RAS_BucketManager.h" #include "RAS_Rect.h" #include "RAS_IRasterizer.h" #include "RAS_IRenderTools.h" #include "RAS_ICanvas.h" #include "MT_Vector3.h" #include "MT_Transform.h" #include "SCA_IInputDevice.h" #include "KX_Camera.h" #include "KX_Dome.h" #include "KX_Light.h" #include "KX_PythonInit.h" #include "KX_PyConstraintBinding.h" #include "PHY_IPhysicsEnvironment.h" #ifdef WITH_AUDASPACE # include "AUD_C-API.h" # include "AUD_I3DDevice.h" #endif #include "NG_NetworkScene.h" #include "NG_NetworkDeviceInterface.h" #include "KX_WorldInfo.h" #include "KX_ISceneConverter.h" #include "KX_TimeCategoryLogger.h" #include "RAS_FramingManager.h" #include "DNA_world_types.h" #include "DNA_scene_types.h" #include "KX_NavMeshObject.h" // If define: little test for Nzc: guarded drawing. If the canvas is // not valid, skip rendering this frame. //#define NZC_GUARDED_OUTPUT #define DEFAULT_LOGIC_TIC_RATE 60.0 //#define DEFAULT_PHYSICS_TIC_RATE 60.0 #ifdef FREE_WINDOWS /* XXX mingw64 (gcc 4.7.0) defines a macro for DrawText that translates to DrawTextA. Not good */ #ifdef DrawText #undef DrawText #endif #endif const char KX_KetsjiEngine::m_profileLabels[tc_numCategories][15] = { "Physics:", // tc_physics "Logic:", // tc_logic "Animations:", // tc_animations "Network:", // tc_network "Scenegraph:", // tc_scenegraph "Rasterizer:", // tc_rasterizer "Services:", // tc_services "Overhead:", // tc_overhead "Outside:", // tc_outside "GPU Latency:" // tc_latency }; double KX_KetsjiEngine::m_ticrate = DEFAULT_LOGIC_TIC_RATE; int KX_KetsjiEngine::m_maxLogicFrame = 5; int KX_KetsjiEngine::m_maxPhysicsFrame = 5; double KX_KetsjiEngine::m_anim_framerate = 25.0; double KX_KetsjiEngine::m_suspendedtime = 0.0; double KX_KetsjiEngine::m_suspendeddelta = 0.0; double KX_KetsjiEngine::m_average_framerate = 0.0; bool KX_KetsjiEngine::m_restrict_anim_fps = false; short KX_KetsjiEngine::m_exitkey = 130; //ESC Key /** * Constructor of the Ketsji Engine */ KX_KetsjiEngine::KX_KetsjiEngine(KX_ISystem* system) : m_canvas(NULL), m_rasterizer(NULL), m_kxsystem(system), m_rendertools(NULL), m_sceneconverter(NULL), m_networkdevice(NULL), #ifdef WITH_PYTHON m_pythondictionary(NULL), #endif m_keyboarddevice(NULL), m_mousedevice(NULL), m_bInitialized(false), m_activecam(0), m_bFixedTime(false), m_firstframe(true), m_frameTime(0.f), m_clockTime(0.f), m_previousClockTime(0.f), m_previousAnimTime(0.f), m_exitcode(KX_EXIT_REQUEST_NO_REQUEST), m_exitstring(""), m_drawingmode(5), m_cameraZoom(1.0), m_overrideCam(false), m_overrideCamUseOrtho(false), m_overrideCamNear(0.0), m_overrideCamFar(0.0), m_stereo(false), m_curreye(0), m_logger(NULL), // Set up timing info display variables m_show_framerate(false), m_show_profile(false), m_showProperties(false), m_showBackground(false), m_show_debug_properties(false), m_animation_record(false), // Default behavior is to hide the cursor every frame. m_hideCursor(false), m_overrideFrameColor(false), m_overrideFrameColorR(0.0), m_overrideFrameColorG(0.0), m_overrideFrameColorB(0.0), m_usedome(false) { // Initialize the time logger m_logger = new KX_TimeCategoryLogger (25); for (int i = tc_first; i < tc_numCategories; i++) m_logger->AddCategory((KX_TimeCategory)i); #ifdef WITH_PYTHON m_pyprofiledict = PyDict_New(); #endif } /** * Destructor of the Ketsji Engine, release all memory */ KX_KetsjiEngine::~KX_KetsjiEngine() { delete m_logger; if (m_usedome) delete m_dome; #ifdef WITH_PYTHON Py_CLEAR(m_pyprofiledict); #endif } void KX_KetsjiEngine::SetKeyboardDevice(SCA_IInputDevice* keyboarddevice) { MT_assert(keyboarddevice); m_keyboarddevice = keyboarddevice; } void KX_KetsjiEngine::SetMouseDevice(SCA_IInputDevice* mousedevice) { MT_assert(mousedevice); m_mousedevice = mousedevice; } void KX_KetsjiEngine::SetNetworkDevice(NG_NetworkDeviceInterface* networkdevice) { MT_assert(networkdevice); m_networkdevice = networkdevice; } void KX_KetsjiEngine::SetCanvas(RAS_ICanvas* canvas) { MT_assert(canvas); m_canvas = canvas; } void KX_KetsjiEngine::SetRenderTools(RAS_IRenderTools* rendertools) { MT_assert(rendertools); m_rendertools = rendertools; } void KX_KetsjiEngine::SetRasterizer(RAS_IRasterizer* rasterizer) { MT_assert(rasterizer); m_rasterizer = rasterizer; } #ifdef WITH_PYTHON /* * At the moment the bge.logic module is imported into 'pythondictionary' after this function is called. * if this function ever changes to assign a copy, make sure the game logic module is imported into this dictionary before hand. */ void KX_KetsjiEngine::SetPyNamespace(PyObject *pythondictionary) { MT_assert(pythondictionary); m_pythondictionary = pythondictionary; } PyObject* KX_KetsjiEngine::GetPyProfileDict() { Py_INCREF(m_pyprofiledict); return m_pyprofiledict; } #endif void KX_KetsjiEngine::SetSceneConverter(KX_ISceneConverter* sceneconverter) { MT_assert(sceneconverter); m_sceneconverter = sceneconverter; } void KX_KetsjiEngine::InitDome(short res, short mode, short angle, float resbuf, short tilt, struct Text* text) { m_dome = new KX_Dome(m_canvas, m_rasterizer, m_rendertools,this, res, mode, angle, resbuf, tilt, text); m_usedome = true; } void KX_KetsjiEngine::RenderDome() { const GLint *viewport = m_canvas->GetViewPort(); m_dome->SetViewPort(viewport); KX_Scene* firstscene = *m_scenes.begin(); const RAS_FrameSettings &framesettings = firstscene->GetFramingType(); m_logger->StartLog(tc_rasterizer, m_kxsystem->GetTimeInSeconds(), true); // hiding mouse cursor each frame // (came back when going out of focus and then back in again) if (m_hideCursor) m_canvas->SetMouseState(RAS_ICanvas::MOUSE_INVISIBLE); // clear the entire game screen with the border color // only once per frame m_canvas->BeginDraw(); // BeginFrame() sets the actual drawing area. You can use a part of the window if (!BeginFrame()) return; KX_SceneList::iterator sceneit; KX_Scene* scene = NULL; int n_renders=m_dome->GetNumberRenders();// usually 4 or 6 for (int i=0;iClearBuffer(RAS_ICanvas::COLOR_BUFFER|RAS_ICanvas::DEPTH_BUFFER); for (sceneit = m_scenes.begin();sceneit != m_scenes.end(); sceneit++) // for each scene, call the proceed functions { scene = *sceneit; KX_Camera* cam = scene->GetActiveCamera(); m_rendertools->BeginFrame(m_rasterizer); // pass the scene's worldsettings to the rasterizer SetWorldSettings(scene->GetWorldInfo()); // shadow buffers if (i == 0) { RenderShadowBuffers(scene); } // Avoid drawing the scene with the active camera twice when it's viewport is enabled if (cam && !cam->GetViewport()) { if (scene->IsClearingZBuffer()) m_rasterizer->ClearDepthBuffer(); m_rendertools->SetAuxilaryClientInfo(scene); // do the rendering m_dome->RenderDomeFrame(scene,cam, i); //render all the font objects for this scene scene->RenderFonts(); } list* cameras = scene->GetCameras(); // Draw the scene once for each camera with an enabled viewport list::iterator it = cameras->begin(); while (it != cameras->end()) { if ((*it)->GetViewport()) { if (scene->IsClearingZBuffer()) m_rasterizer->ClearDepthBuffer(); m_rendertools->SetAuxilaryClientInfo(scene); // do the rendering m_dome->RenderDomeFrame(scene, (*it),i); //render all the font objects for this scene scene->RenderFonts(); } it++; } // Part of PostRenderScene() m_rendertools->MotionBlur(m_rasterizer); scene->Render2DFilters(m_canvas); // no RunDrawingCallBacks // no FlushDebugLines } m_dome->BindImages(i); } m_canvas->EndFrame();//XXX do we really need that? m_canvas->SetViewPort(0, 0, m_canvas->GetWidth(), m_canvas->GetHeight()); if (m_overrideFrameColor) //XXX why do we want { // Do not use the framing bar color set in the Blender scenes m_canvas->ClearColor( m_overrideFrameColorR, m_overrideFrameColorG, m_overrideFrameColorB, 1.0 ); } else { // Use the framing bar color set in the Blender scenes m_canvas->ClearColor( framesettings.BarRed(), framesettings.BarGreen(), framesettings.BarBlue(), 1.0 ); } m_dome->Draw(); // Draw Callback for the last scene #ifdef WITH_PYTHON scene->RunDrawingCallbacks(scene->GetPostDrawCB()); #endif EndFrame(); } /** * Ketsji Init(), Initializes data-structures and converts data from * Blender into Ketsji native (realtime) format also sets up the * graphics context */ void KX_KetsjiEngine::StartEngine(bool clearIpo) { m_clockTime = m_kxsystem->GetTimeInSeconds(); m_frameTime = m_kxsystem->GetTimeInSeconds(); m_previousClockTime = m_kxsystem->GetTimeInSeconds(); m_firstframe = true; m_bInitialized = true; // there is always one scene enabled at startup Scene* scene = m_scenes[0]->GetBlenderScene(); if (scene) { m_ticrate = scene->gm.ticrate ? scene->gm.ticrate : DEFAULT_LOGIC_TIC_RATE; m_maxLogicFrame = scene->gm.maxlogicstep ? scene->gm.maxlogicstep : 5; m_maxPhysicsFrame = scene->gm.maxphystep ? scene->gm.maxlogicstep : 5; } else { m_ticrate = DEFAULT_LOGIC_TIC_RATE; m_maxLogicFrame = 5; m_maxPhysicsFrame = 5; } if (m_animation_record) { m_sceneconverter->ResetPhysicsObjectsAnimationIpo(clearIpo); m_sceneconverter->WritePhysicsObjectToAnimationIpo(m_currentFrame); } } void KX_KetsjiEngine::ClearFrame() { // clear unless we're drawing overlapping stereo if (m_rasterizer->InterlacedStereo() && m_rasterizer->GetEye() == RAS_IRasterizer::RAS_STEREO_RIGHTEYE) return; // clear the viewports with the background color of the first scene bool doclear = false; KX_SceneList::iterator sceneit; RAS_Rect clearvp, area, viewport; for (sceneit = m_scenes.begin(); sceneit != m_scenes.end(); sceneit++) { KX_Scene* scene = *sceneit; //const RAS_FrameSettings &framesettings = scene->GetFramingType(); list* cameras = scene->GetCameras(); list::iterator it; for (it = cameras->begin(); it != cameras->end(); it++) { GetSceneViewport(scene, (*it), area, viewport); if (!doclear) { clearvp = viewport; doclear = true; } else { if (viewport.GetLeft() < clearvp.GetLeft()) clearvp.SetLeft(viewport.GetLeft()); if (viewport.GetBottom() < clearvp.GetBottom()) clearvp.SetBottom(viewport.GetBottom()); if (viewport.GetRight() > clearvp.GetRight()) clearvp.SetRight(viewport.GetRight()); if (viewport.GetTop() > clearvp.GetTop()) clearvp.SetTop(viewport.GetTop()); } } } if (doclear) { KX_Scene* firstscene = *m_scenes.begin(); SetBackGround(firstscene->GetWorldInfo()); m_canvas->SetViewPort(clearvp.GetLeft(), clearvp.GetBottom(), clearvp.GetRight(), clearvp.GetTop()); m_rasterizer->ClearColorBuffer(); } } bool KX_KetsjiEngine::BeginFrame() { // set the area used for rendering (stereo can assign only a subset) m_rasterizer->SetRenderArea(); if (m_canvas->BeginDraw()) { ClearFrame(); m_rasterizer->BeginFrame(m_drawingmode , m_kxsystem->GetTimeInSeconds()); m_rendertools->BeginFrame(m_rasterizer); return true; } return false; } void KX_KetsjiEngine::EndFrame() { m_rendertools->MotionBlur(m_rasterizer); // Show profiling info m_logger->StartLog(tc_overhead, m_kxsystem->GetTimeInSeconds(), true); if (m_show_framerate || m_show_profile || (m_show_debug_properties)) { RenderDebugProperties(); } double tottime = m_logger->GetAverage(), time; if (tottime < 1e-6) tottime = 1e-6; #ifdef WITH_PYTHON for (int i = tc_first; i < tc_numCategories; ++i) { time = m_logger->GetAverage((KX_TimeCategory)i); PyObject *val = PyTuple_New(2); PyTuple_SetItem(val, 0, PyFloat_FromDouble(time*1000.f)); PyTuple_SetItem(val, 1, PyFloat_FromDouble(time/tottime * 100.f)); PyDict_SetItemString(m_pyprofiledict, m_profileLabels[i], val); Py_DECREF(val); } #endif m_average_framerate = 1.0/tottime; // Go to next profiling measurement, time spend after this call is shown in the next frame. m_logger->NextMeasurement(m_kxsystem->GetTimeInSeconds()); m_logger->StartLog(tc_rasterizer, m_kxsystem->GetTimeInSeconds(), true); m_rasterizer->EndFrame(); // swap backbuffer (drawing into this buffer) <-> front/visible buffer m_logger->StartLog(tc_latency, m_kxsystem->GetTimeInSeconds(), true); m_rasterizer->SwapBuffers(); m_logger->StartLog(tc_rasterizer, m_kxsystem->GetTimeInSeconds(), true); m_rendertools->EndFrame(m_rasterizer); m_canvas->EndDraw(); } //#include "PIL_time.h" //#include "LinearMath/btQuickprof.h" bool KX_KetsjiEngine::NextFrame() { double timestep = 1.0/m_ticrate; double framestep = timestep; // static hidden::Clock sClock; m_logger->StartLog(tc_services, m_kxsystem->GetTimeInSeconds(),true); //float dt = sClock.getTimeMicroseconds() * 0.000001f; //sClock.reset(); if (m_bFixedTime) { m_clockTime += timestep; } else { // m_clockTime += dt; m_clockTime = m_kxsystem->GetTimeInSeconds(); } double deltatime = m_clockTime - m_frameTime; if (deltatime<0.f) { // We got here too quickly, which means there is nothing todo, just return and don't render. // Not sure if this is the best fix, but it seems to stop the jumping framerate issue (#33088) return false; } // Compute the number of logic frames to do each update (fixed tic bricks) int frames =int(deltatime*m_ticrate+1e-6); // if (frames>1) // printf("****************************************"); // printf("dt = %f, deltatime = %f, frames = %d\n",dt, deltatime,frames); // if (!frames) // PIL_sleep_ms(1); KX_SceneList::iterator sceneit; if (frames>m_maxPhysicsFrame) { // printf("framedOut: %d\n",frames); m_frameTime+=(frames-m_maxPhysicsFrame)*timestep; frames = m_maxPhysicsFrame; } bool doRender = frames>0; if (frames > m_maxLogicFrame) { framestep = (frames*timestep)/m_maxLogicFrame; frames = m_maxLogicFrame; } while (frames) { m_frameTime += framestep; m_sceneconverter->MergeAsyncLoads(); for (sceneit = m_scenes.begin();sceneit != m_scenes.end(); ++sceneit) // for each scene, call the proceed functions { KX_Scene* scene = *sceneit; /* Suspension holds the physics and logic processing for an * entire scene. Objects can be suspended individually, and * the settings for that precede the logic and physics * update. */ m_logger->StartLog(tc_logic, m_kxsystem->GetTimeInSeconds(), true); m_sceneconverter->resetNoneDynamicObjectToIpo();//this is for none dynamic objects with ipo scene->UpdateObjectActivity(); if (!scene->IsSuspended()) { // if the scene was suspended recalcutlate the delta tu "curtime" m_suspendedtime = scene->getSuspendedTime(); if (scene->getSuspendedTime()!=0.0) scene->setSuspendedDelta(scene->getSuspendedDelta()+m_clockTime-scene->getSuspendedTime()); m_suspendeddelta = scene->getSuspendedDelta(); m_logger->StartLog(tc_network, m_kxsystem->GetTimeInSeconds(), true); SG_SetActiveStage(SG_STAGE_NETWORK); scene->GetNetworkScene()->proceed(m_frameTime); //m_logger->StartLog(tc_scenegraph, m_kxsystem->GetTimeInSeconds(), true); //SG_SetActiveStage(SG_STAGE_NETWORK_UPDATE); //scene->UpdateParents(m_frameTime); m_logger->StartLog(tc_physics, m_kxsystem->GetTimeInSeconds(), true); SG_SetActiveStage(SG_STAGE_PHYSICS1); // set Python hooks for each scene #ifdef WITH_PYTHON PHY_SetActiveEnvironment(scene->GetPhysicsEnvironment()); #endif KX_SetActiveScene(scene); scene->GetPhysicsEnvironment()->endFrame(); // Update scenegraph after physics step. This maps physics calculations // into node positions. //m_logger->StartLog(tc_scenegraph, m_kxsystem->GetTimeInSeconds(), true); //SG_SetActiveStage(SG_STAGE_PHYSICS1_UPDATE); //scene->UpdateParents(m_frameTime); // Process sensors, and controllers m_logger->StartLog(tc_logic, m_kxsystem->GetTimeInSeconds(), true); SG_SetActiveStage(SG_STAGE_CONTROLLER); scene->LogicBeginFrame(m_frameTime); // Scenegraph needs to be updated again, because Logic Controllers // can affect the local matrices. m_logger->StartLog(tc_scenegraph, m_kxsystem->GetTimeInSeconds(), true); SG_SetActiveStage(SG_STAGE_CONTROLLER_UPDATE); scene->UpdateParents(m_frameTime); // Process actuators // Do some cleanup work for this logic frame m_logger->StartLog(tc_logic, m_kxsystem->GetTimeInSeconds(), true); SG_SetActiveStage(SG_STAGE_ACTUATOR); scene->LogicUpdateFrame(m_frameTime, true); scene->LogicEndFrame(); // Actuators can affect the scenegraph m_logger->StartLog(tc_scenegraph, m_kxsystem->GetTimeInSeconds(), true); SG_SetActiveStage(SG_STAGE_ACTUATOR_UPDATE); scene->UpdateParents(m_frameTime); if (!GetRestrictAnimationFPS()) { m_logger->StartLog(tc_animations, m_kxsystem->GetTimeInSeconds(), true); SG_SetActiveStage(SG_STAGE_ANIMATION_UPDATE); scene->UpdateAnimations(m_frameTime); } m_logger->StartLog(tc_physics, m_kxsystem->GetTimeInSeconds(), true); SG_SetActiveStage(SG_STAGE_PHYSICS2); scene->GetPhysicsEnvironment()->beginFrame(); // Perform physics calculations on the scene. This can involve // many iterations of the physics solver. scene->GetPhysicsEnvironment()->proceedDeltaTime(m_frameTime,timestep,framestep);//m_deltatimerealDeltaTime); m_logger->StartLog(tc_scenegraph, m_kxsystem->GetTimeInSeconds(), true); SG_SetActiveStage(SG_STAGE_PHYSICS2_UPDATE); scene->UpdateParents(m_frameTime); if (m_animation_record) { m_sceneconverter->WritePhysicsObjectToAnimationIpo(++m_currentFrame); } scene->setSuspendedTime(0.0); } // suspended else if (scene->getSuspendedTime()==0.0) scene->setSuspendedTime(m_clockTime); m_logger->StartLog(tc_services, m_kxsystem->GetTimeInSeconds(), true); } // update system devices m_logger->StartLog(tc_logic, m_kxsystem->GetTimeInSeconds(), true); if (m_keyboarddevice) m_keyboarddevice->NextFrame(); if (m_mousedevice) m_mousedevice->NextFrame(); if (m_networkdevice) m_networkdevice->NextFrame(); // scene management ProcessScheduledScenes(); frames--; } bool bUseAsyncLogicBricks= false;//true; if (bUseAsyncLogicBricks) { // Logic update sub frame: this will let some logic bricks run at the // full frame rate. for (sceneit = m_scenes.begin();sceneit != m_scenes.end(); ++sceneit) // for each scene, call the proceed functions { KX_Scene* scene = *sceneit; if (!scene->IsSuspended()) { // if the scene was suspended recalcutlate the delta tu "curtime" m_suspendedtime = scene->getSuspendedTime(); if (scene->getSuspendedTime()!=0.0) scene->setSuspendedDelta(scene->getSuspendedDelta()+m_clockTime-scene->getSuspendedTime()); m_suspendeddelta = scene->getSuspendedDelta(); // set Python hooks for each scene #ifdef WITH_PYTHON PHY_SetActiveEnvironment(scene->GetPhysicsEnvironment()); #endif KX_SetActiveScene(scene); m_logger->StartLog(tc_scenegraph, m_kxsystem->GetTimeInSeconds(), true); SG_SetActiveStage(SG_STAGE_PHYSICS1); scene->UpdateParents(m_clockTime); // Perform physics calculations on the scene. This can involve // many iterations of the physics solver. m_logger->StartLog(tc_physics, m_kxsystem->GetTimeInSeconds(), true); scene->GetPhysicsEnvironment()->proceedDeltaTime(m_clockTime,timestep,timestep); // Update scenegraph after physics step. This maps physics calculations // into node positions. m_logger->StartLog(tc_scenegraph, m_kxsystem->GetTimeInSeconds(), true); SG_SetActiveStage(SG_STAGE_PHYSICS2); scene->UpdateParents(m_clockTime); // Do some cleanup work for this logic frame m_logger->StartLog(tc_logic, m_kxsystem->GetTimeInSeconds(), true); scene->LogicUpdateFrame(m_clockTime, false); // Actuators can affect the scenegraph m_logger->StartLog(tc_scenegraph, m_kxsystem->GetTimeInSeconds(), true); SG_SetActiveStage(SG_STAGE_ACTUATOR); scene->UpdateParents(m_clockTime); scene->setSuspendedTime(0.0); } // suspended else if (scene->getSuspendedTime()==0.0) scene->setSuspendedTime(m_clockTime); m_logger->StartLog(tc_services, m_kxsystem->GetTimeInSeconds(), true); } } // Handle the animations independently of the logic time step if (GetRestrictAnimationFPS()) { double clocktime = m_kxsystem->GetTimeInSeconds(); m_logger->StartLog(tc_animations, clocktime, true); SG_SetActiveStage(SG_STAGE_ANIMATION_UPDATE); double anim_timestep = 1.0/KX_GetActiveScene()->GetAnimationFPS(); if (clocktime - m_previousAnimTime > anim_timestep) { // Sanity/debug print to make sure we're actually going at the fps we want (should be close to anim_timestep) // printf("Anim fps: %f\n", 1.0/(m_clockTime - m_previousAnimTime)); m_previousAnimTime = clocktime; for (sceneit = m_scenes.begin();sceneit != m_scenes.end(); ++sceneit) { (*sceneit)->UpdateAnimations(clocktime); } } } // Start logging time spend outside main loop m_logger->StartLog(tc_outside, m_kxsystem->GetTimeInSeconds(), true); return doRender; } void KX_KetsjiEngine::Render() { if (m_usedome) { RenderDome(); return; } KX_Scene* firstscene = *m_scenes.begin(); const RAS_FrameSettings &framesettings = firstscene->GetFramingType(); m_logger->StartLog(tc_rasterizer, m_kxsystem->GetTimeInSeconds(), true); SG_SetActiveStage(SG_STAGE_RENDER); // hiding mouse cursor each frame // (came back when going out of focus and then back in again) if (m_hideCursor) m_canvas->SetMouseState(RAS_ICanvas::MOUSE_INVISIBLE); // clear the entire game screen with the border color // only once per frame m_canvas->BeginDraw(); if (m_drawingmode == RAS_IRasterizer::KX_TEXTURED) { m_canvas->SetViewPort(0, 0, m_canvas->GetWidth(), m_canvas->GetHeight()); if (m_overrideFrameColor) { // Do not use the framing bar color set in the Blender scenes m_canvas->ClearColor( m_overrideFrameColorR, m_overrideFrameColorG, m_overrideFrameColorB, 1.0 ); } else { // Use the framing bar color set in the Blender scenes m_canvas->ClearColor( framesettings.BarRed(), framesettings.BarGreen(), framesettings.BarBlue(), 1.0 ); } // clear the -whole- viewport m_canvas->ClearBuffer(RAS_ICanvas::COLOR_BUFFER|RAS_ICanvas::DEPTH_BUFFER); } m_rasterizer->SetEye(RAS_IRasterizer::RAS_STEREO_LEFTEYE); // BeginFrame() sets the actual drawing area. You can use a part of the window if (!BeginFrame()) return; KX_SceneList::iterator sceneit; for (sceneit = m_scenes.begin();sceneit != m_scenes.end(); sceneit++) // for each scene, call the proceed functions { KX_Scene* scene = *sceneit; KX_Camera* cam = scene->GetActiveCamera(); // pass the scene's worldsettings to the rasterizer SetWorldSettings(scene->GetWorldInfo()); // this is now done incrementatlly in KX_Scene::CalculateVisibleMeshes //scene->UpdateMeshTransformations(); // shadow buffers RenderShadowBuffers(scene); // Avoid drawing the scene with the active camera twice when it's viewport is enabled if (cam && !cam->GetViewport()) { if (scene->IsClearingZBuffer()) m_rasterizer->ClearDepthBuffer(); m_rendertools->SetAuxilaryClientInfo(scene); // do the rendering RenderFrame(scene, cam); } list* cameras = scene->GetCameras(); // Draw the scene once for each camera with an enabled viewport list::iterator it = cameras->begin(); while (it != cameras->end()) { if ((*it)->GetViewport()) { if (scene->IsClearingZBuffer()) m_rasterizer->ClearDepthBuffer(); m_rendertools->SetAuxilaryClientInfo(scene); // do the rendering RenderFrame(scene, (*it)); } it++; } PostRenderScene(scene); } // only one place that checks for stereo if (m_rasterizer->Stereo()) { m_rasterizer->SetEye(RAS_IRasterizer::RAS_STEREO_RIGHTEYE); if (!BeginFrame()) return; for (sceneit = m_scenes.begin();sceneit != m_scenes.end(); sceneit++) // for each scene, call the proceed functions { KX_Scene* scene = *sceneit; KX_Camera* cam = scene->GetActiveCamera(); // pass the scene's worldsettings to the rasterizer SetWorldSettings(scene->GetWorldInfo()); if (scene->IsClearingZBuffer()) m_rasterizer->ClearDepthBuffer(); //pass the scene, for picking and raycasting (shadows) m_rendertools->SetAuxilaryClientInfo(scene); // do the rendering //RenderFrame(scene); RenderFrame(scene, cam); list* cameras = scene->GetCameras(); // Draw the scene once for each camera with an enabled viewport list::iterator it = cameras->begin(); while (it != cameras->end()) { if ((*it)->GetViewport()) { if (scene->IsClearingZBuffer()) m_rasterizer->ClearDepthBuffer(); m_rendertools->SetAuxilaryClientInfo(scene); // do the rendering RenderFrame(scene, (*it)); } it++; } PostRenderScene(scene); } } // if (m_rasterizer->Stereo()) EndFrame(); } void KX_KetsjiEngine::RequestExit(int exitrequestmode) { m_exitcode = exitrequestmode; } void KX_KetsjiEngine::SetNameNextGame(const STR_String& nextgame) { m_exitstring = nextgame; } int KX_KetsjiEngine::GetExitCode() { // if a gameactuator has set an exitcode or if there are no scenes left if (!m_exitcode) { if (m_scenes.begin()==m_scenes.end()) m_exitcode = KX_EXIT_REQUEST_NO_SCENES_LEFT; } // check if the window has been closed. if (!m_exitcode) { //if (!m_canvas->Check()) { // m_exitcode = KX_EXIT_REQUEST_OUTSIDE; //} } return m_exitcode; } const STR_String& KX_KetsjiEngine::GetExitString() { return m_exitstring; } void KX_KetsjiEngine::SetBackGround(KX_WorldInfo* wi) { if (wi->hasWorld()) { if (m_drawingmode == RAS_IRasterizer::KX_TEXTURED) { m_rasterizer->SetBackColor( wi->getBackColorRed(), wi->getBackColorGreen(), wi->getBackColorBlue(), 0.0 ); } } } void KX_KetsjiEngine::SetWorldSettings(KX_WorldInfo* wi) { if (wi->hasWorld()) { // ... m_rasterizer->SetAmbientColor( wi->getAmbientColorRed(), wi->getAmbientColorGreen(), wi->getAmbientColorBlue() ); if (m_drawingmode >= RAS_IRasterizer::KX_SOLID) { if (wi->hasMist()) { m_rasterizer->SetFog( wi->getMistStart(), wi->getMistDistance(), wi->getMistColorRed(), wi->getMistColorGreen(), wi->getMistColorBlue() ); } } } } void KX_KetsjiEngine::SetDrawType(int drawingmode) { m_drawingmode = drawingmode; } void KX_KetsjiEngine::EnableCameraOverride(const STR_String& forscene) { m_overrideCam = true; m_overrideSceneName = forscene; } void KX_KetsjiEngine::SetCameraZoom(float camzoom) { m_cameraZoom = camzoom; } void KX_KetsjiEngine::SetCameraOverrideUseOrtho(bool useOrtho) { m_overrideCamUseOrtho = useOrtho; } void KX_KetsjiEngine::SetCameraOverrideProjectionMatrix(const MT_CmMatrix4x4& mat) { m_overrideCamProjMat = mat; } void KX_KetsjiEngine::SetCameraOverrideViewMatrix(const MT_CmMatrix4x4& mat) { m_overrideCamViewMat = mat; } void KX_KetsjiEngine::SetCameraOverrideClipping(float nearfrust, float farfrust) { m_overrideCamNear = nearfrust; m_overrideCamFar = farfrust; } void KX_KetsjiEngine::SetCameraOverrideLens(float lens) { m_overrideCamLens = lens; } void KX_KetsjiEngine::GetSceneViewport(KX_Scene *scene, KX_Camera* cam, RAS_Rect& area, RAS_Rect& viewport) { // In this function we make sure the rasterizer settings are upto // date. We compute the viewport so that logic // using this information is upto date. // Note we postpone computation of the projection matrix // so that we are using the latest camera position. if (cam->GetViewport()) { RAS_Rect userviewport; userviewport.SetLeft(cam->GetViewportLeft()); userviewport.SetBottom(cam->GetViewportBottom()); userviewport.SetRight(cam->GetViewportRight()); userviewport.SetTop(cam->GetViewportTop()); // Don't do bars on user specified viewport RAS_FrameSettings settings = scene->GetFramingType(); if (settings.FrameType() == RAS_FrameSettings::e_frame_bars) settings.SetFrameType(RAS_FrameSettings::e_frame_extend); RAS_FramingManager::ComputeViewport( scene->GetFramingType(), userviewport, viewport ); area = userviewport; } else if ( !m_overrideCam || (scene->GetName() != m_overrideSceneName) || m_overrideCamUseOrtho ) { RAS_FramingManager::ComputeViewport( scene->GetFramingType(), m_canvas->GetDisplayArea(), viewport ); area = m_canvas->GetDisplayArea(); } else { viewport.SetLeft(0); viewport.SetBottom(0); viewport.SetRight(int(m_canvas->GetWidth())); viewport.SetTop(int(m_canvas->GetHeight())); area = m_canvas->GetDisplayArea(); } } void KX_KetsjiEngine::RenderShadowBuffers(KX_Scene *scene) { CListValue *lightlist = scene->GetLightList(); int i, drawmode; m_rendertools->SetAuxilaryClientInfo(scene); for (i=0; iGetCount(); i++) { KX_GameObject *gameobj = (KX_GameObject*)lightlist->GetValue(i); KX_LightObject *light = (KX_LightObject*)gameobj; light->Update(); if (m_drawingmode == RAS_IRasterizer::KX_TEXTURED && light->HasShadowBuffer()) { /* make temporary camera */ RAS_CameraData camdata = RAS_CameraData(); KX_Camera *cam = new KX_Camera(scene, scene->m_callbacks, camdata, true, true); cam->SetName("__shadow__cam__"); MT_Transform camtrans; /* switch drawmode for speed */ drawmode = m_rasterizer->GetDrawingMode(); m_rasterizer->SetDrawingMode(RAS_IRasterizer::KX_SHADOW); /* binds framebuffer object, sets up camera .. */ light->BindShadowBuffer(m_rasterizer, m_canvas, cam, camtrans); /* update scene */ scene->CalculateVisibleMeshes(m_rasterizer, cam, light->GetShadowLayer()); /* render */ m_rasterizer->ClearDepthBuffer(); m_rasterizer->ClearColorBuffer(); scene->RenderBuckets(camtrans, m_rasterizer, m_rendertools); /* unbind framebuffer object, restore drawmode, free camera */ light->UnbindShadowBuffer(m_rasterizer); m_rasterizer->SetDrawingMode(drawmode); cam->Release(); } } } // update graphics void KX_KetsjiEngine::RenderFrame(KX_Scene* scene, KX_Camera* cam) { bool override_camera; RAS_Rect viewport, area; float nearfrust, farfrust, focallength; // KX_Camera* cam = scene->GetActiveCamera(); if (!cam) return; GetSceneViewport(scene, cam, area, viewport); // store the computed viewport in the scene scene->SetSceneViewport(viewport); // set the viewport for this frame and scene m_canvas->SetViewPort(viewport.GetLeft(), viewport.GetBottom(), viewport.GetRight(), viewport.GetTop()); // see KX_BlenderMaterial::Activate //m_rasterizer->SetAmbient(); m_rasterizer->DisplayFog(); override_camera = m_overrideCam && (scene->GetName() == m_overrideSceneName); override_camera = override_camera && (cam->GetName() == "__default__cam__"); if (override_camera && m_overrideCamUseOrtho) { m_rasterizer->SetProjectionMatrix(m_overrideCamProjMat); if (!cam->hasValidProjectionMatrix()) { // needed to get frustrum planes for culling MT_Matrix4x4 projmat; projmat.setValue(m_overrideCamProjMat.getPointer()); cam->SetProjectionMatrix(projmat); } } else if (cam->hasValidProjectionMatrix()) { m_rasterizer->SetProjectionMatrix(cam->GetProjectionMatrix()); } else { RAS_FrameFrustum frustum; bool orthographic = !cam->GetCameraData()->m_perspective; nearfrust = cam->GetCameraNear(); farfrust = cam->GetCameraFar(); focallength = cam->GetFocalLength(); MT_Matrix4x4 projmat; if (override_camera) { nearfrust = m_overrideCamNear; farfrust = m_overrideCamFar; } if (orthographic) { RAS_FramingManager::ComputeOrtho( scene->GetFramingType(), area, viewport, cam->GetScale(), nearfrust, farfrust, cam->GetSensorFit(), frustum ); if (!cam->GetViewport()) { frustum.x1 *= m_cameraZoom; frustum.x2 *= m_cameraZoom; frustum.y1 *= m_cameraZoom; frustum.y2 *= m_cameraZoom; } projmat = m_rasterizer->GetOrthoMatrix( frustum.x1, frustum.x2, frustum.y1, frustum.y2, frustum.camnear, frustum.camfar); } else { RAS_FramingManager::ComputeFrustum( scene->GetFramingType(), area, viewport, cam->GetLens(), cam->GetSensorWidth(), cam->GetSensorHeight(), cam->GetSensorFit(), nearfrust, farfrust, frustum ); if (!cam->GetViewport()) { frustum.x1 *= m_cameraZoom; frustum.x2 *= m_cameraZoom; frustum.y1 *= m_cameraZoom; frustum.y2 *= m_cameraZoom; } projmat = m_rasterizer->GetFrustumMatrix( frustum.x1, frustum.x2, frustum.y1, frustum.y2, frustum.camnear, frustum.camfar, focallength); } cam->SetProjectionMatrix(projmat); // Otherwise the projection matrix for each eye will be the same... if (!orthographic && m_rasterizer->Stereo()) cam->InvalidateProjectionMatrix(); } MT_Transform camtrans(cam->GetWorldToCamera()); MT_Matrix4x4 viewmat(camtrans); m_rasterizer->SetViewMatrix(viewmat, cam->NodeGetWorldOrientation(), cam->NodeGetWorldPosition(), cam->GetCameraData()->m_perspective); cam->SetModelviewMatrix(viewmat); // The following actually reschedules all vertices to be // redrawn. There is a cache between the actual rescheduling // and this call though. Visibility is imparted when this call // runs through the individual objects. m_logger->StartLog(tc_scenegraph, m_kxsystem->GetTimeInSeconds(), true); SG_SetActiveStage(SG_STAGE_CULLING); scene->CalculateVisibleMeshes(m_rasterizer,cam); m_logger->StartLog(tc_rasterizer, m_kxsystem->GetTimeInSeconds(), true); SG_SetActiveStage(SG_STAGE_RENDER); #ifdef WITH_PYTHON // Run any pre-drawing python callbacks scene->RunDrawingCallbacks(scene->GetPreDrawCB()); #endif scene->RenderBuckets(camtrans, m_rasterizer, m_rendertools); //render all the font objects for this scene scene->RenderFonts(); if (scene->GetPhysicsEnvironment()) scene->GetPhysicsEnvironment()->debugDrawWorld(); } /* * To run once per scene */ void KX_KetsjiEngine::PostRenderScene(KX_Scene* scene) { // We need to first make sure our viewport is correct (enabling multiple viewports can mess this up) m_canvas->SetViewPort(0, 0, m_canvas->GetWidth(), m_canvas->GetHeight()); m_rasterizer->FlushDebugShapes(); scene->Render2DFilters(m_canvas); #ifdef WITH_PYTHON scene->RunDrawingCallbacks(scene->GetPostDrawCB()); #endif } void KX_KetsjiEngine::StopEngine() { if (m_bInitialized) { if (m_animation_record) { // printf("TestHandlesPhysicsObjectToAnimationIpo\n"); m_sceneconverter->TestHandlesPhysicsObjectToAnimationIpo(); } KX_SceneList::iterator sceneit; for (sceneit = m_scenes.begin();sceneit != m_scenes.end() ; sceneit++) { KX_Scene* scene = *sceneit; m_sceneconverter->RemoveScene(scene); } m_scenes.clear(); // cleanup all the stuff m_rasterizer->Exit(); } } // Scene Management is able to switch between scenes // and have several scene's running in parallel void KX_KetsjiEngine::AddScene(KX_Scene* scene) { m_scenes.push_back(scene); PostProcessScene(scene); } void KX_KetsjiEngine::PostProcessScene(KX_Scene* scene) { bool override_camera = (m_overrideCam && (scene->GetName() == m_overrideSceneName)); SG_SetActiveStage(SG_STAGE_SCENE); // if there is no activecamera, or the camera is being // overridden we need to construct a temporarily camera if (!scene->GetActiveCamera() || override_camera) { KX_Camera* activecam = NULL; RAS_CameraData camdata = RAS_CameraData(); if (override_camera) { camdata.m_lens = m_overrideCamLens; camdata.m_clipstart = m_overrideCamNear; camdata.m_clipend = m_overrideCamFar; camdata.m_perspective= !m_overrideCamUseOrtho; } activecam = new KX_Camera(scene,KX_Scene::m_callbacks,camdata); activecam->SetName("__default__cam__"); // set transformation if (override_camera) { const MT_CmMatrix4x4& cammatdata = m_overrideCamViewMat; MT_Transform trans = MT_Transform(cammatdata.getPointer()); MT_Transform camtrans; camtrans.invert(trans); activecam->NodeSetLocalPosition(camtrans.getOrigin()); activecam->NodeSetLocalOrientation(camtrans.getBasis()); activecam->NodeUpdateGS(0); } else { activecam->NodeSetLocalPosition(MT_Point3(0.0, 0.0, 0.0)); activecam->NodeSetLocalOrientation(MT_Vector3(0.0, 0.0, 0.0)); activecam->NodeUpdateGS(0); } scene->AddCamera(activecam); scene->SetActiveCamera(activecam); scene->GetObjectList()->Add(activecam->AddRef()); scene->GetRootParentList()->Add(activecam->AddRef()); //done with activecam activecam->Release(); } scene->UpdateParents(0.0); } void KX_KetsjiEngine::RenderDebugProperties() { STR_String debugtxt; int title_xmargin = -7; int title_y_top_margin = 4; int title_y_bottom_margin = 2; int const_xindent = 4; int const_ysize = 14; int xcoord = 12; // mmmm, these constants were taken from blender source int ycoord = 17; // to 'mimic' behavior int profile_indent = 72; float tottime = m_logger->GetAverage(); if (tottime < 1e-6f) { tottime = 1e-6f; } // Set viewport to entire canvas RAS_Rect viewport; m_canvas->SetViewPort(0, 0, int(m_canvas->GetWidth()), int(m_canvas->GetHeight())); if (m_show_framerate || m_show_profile) { /* Title for profiling("Profile") */ m_rendertools->RenderText2D(RAS_IRenderTools::RAS_TEXT_PADDED, "Profile", xcoord + const_xindent + title_xmargin, // Adds the constant x indent (0 for now) to the title x margin ycoord, m_canvas->GetWidth() /* RdV, TODO ?? */, m_canvas->GetHeight() /* RdV, TODO ?? */); // Increase the indent by default increase ycoord += const_ysize; // Add the title indent afterwards ycoord += title_y_bottom_margin; } /* Framerate display */ if (m_show_framerate) { m_rendertools->RenderText2D(RAS_IRenderTools::RAS_TEXT_PADDED, "Frametime :", xcoord + const_xindent, ycoord, m_canvas->GetWidth() /* RdV, TODO ?? */, m_canvas->GetHeight() /* RdV, TODO ?? */); debugtxt.Format("%5.1fms (%.1ffps)", tottime * 1000.f, 1.0/tottime); m_rendertools->RenderText2D(RAS_IRenderTools::RAS_TEXT_PADDED, debugtxt.ReadPtr(), xcoord + const_xindent + profile_indent, ycoord, m_canvas->GetWidth() /* RdV, TODO ?? */, m_canvas->GetHeight() /* RdV, TODO ?? */); // Increase the indent by default increase ycoord += const_ysize; } /* Profile display */ if (m_show_profile) { for (int j = tc_first; j < tc_numCategories; j++) { m_rendertools->RenderText2D(RAS_IRenderTools::RAS_TEXT_PADDED, m_profileLabels[j], xcoord + const_xindent, ycoord, m_canvas->GetWidth(), m_canvas->GetHeight()); double time = m_logger->GetAverage((KX_TimeCategory)j); debugtxt.Format("%5.2fms | %d%%", time*1000.f, (int)(time/tottime * 100.f)); m_rendertools->RenderText2D(RAS_IRenderTools::RAS_TEXT_PADDED, debugtxt.ReadPtr(), xcoord + const_xindent + profile_indent, ycoord, m_canvas->GetWidth(), m_canvas->GetHeight()); m_rendertools->RenderBox2D(xcoord + (int)(2.2 * profile_indent), ycoord, m_canvas->GetWidth(), m_canvas->GetHeight(), time/tottime); ycoord += const_ysize; } } // Add the ymargin for titles below the other section of debug info ycoord += title_y_top_margin; /* Property display*/ if (m_show_debug_properties) { /* Title for debugging("Debug properties") */ m_rendertools->RenderText2D(RAS_IRenderTools::RAS_TEXT_PADDED, "Debug Properties", xcoord + const_xindent + title_xmargin, // Adds the constant x indent (0 for now) to the title x margin ycoord, m_canvas->GetWidth() /* RdV, TODO ?? */, m_canvas->GetHeight() /* RdV, TODO ?? */); // Increase the indent by default increase ycoord += const_ysize; // Add the title indent afterwards ycoord += title_y_bottom_margin; /* Calculate amount of properties that can displayed. */ unsigned propsAct = 0; unsigned propsMax = (m_canvas->GetHeight() - ycoord) / const_ysize; KX_SceneList::iterator sceneit; for (sceneit = m_scenes.begin();sceneit != m_scenes.end(); sceneit++) { KX_Scene* scene = *sceneit; /* the 'normal' debug props */ vector& debugproplist = scene->GetDebugProperties(); for (unsigned i=0; i < debugproplist.size() && propsAct < propsMax; i++) { CValue *propobj = debugproplist[i]->m_obj; STR_String objname = propobj->GetName(); STR_String propname = debugproplist[i]->m_name; propsAct++; if (propname == "__state__") { // reserve name for object state KX_GameObject* gameobj = static_cast(propobj); unsigned int state = gameobj->GetState(); debugtxt = objname + "." + propname + " = "; bool first = true; for (int statenum=1;state;state >>= 1, statenum++) { if (state & 1) { if (!first) { debugtxt += ","; } debugtxt += STR_String(statenum); first = false; } } m_rendertools->RenderText2D(RAS_IRenderTools::RAS_TEXT_PADDED, debugtxt.ReadPtr(), xcoord + const_xindent, ycoord, m_canvas->GetWidth(), m_canvas->GetHeight()); ycoord += const_ysize; } else { CValue *propval = propobj->GetProperty(propname); if (propval) { STR_String text = propval->GetText(); debugtxt = objname + ": '" + propname + "' = " + text; m_rendertools->RenderText2D(RAS_IRenderTools::RAS_TEXT_PADDED, debugtxt.ReadPtr(), xcoord + const_xindent, ycoord, m_canvas->GetWidth(), m_canvas->GetHeight()); ycoord += const_ysize; } } } } } } KX_SceneList* KX_KetsjiEngine::CurrentScenes() { return &m_scenes; } KX_Scene* KX_KetsjiEngine::FindScene(const STR_String& scenename) { KX_SceneList::iterator sceneit = m_scenes.begin(); // bit risky :) better to split the second clause while ( (sceneit != m_scenes.end()) && ((*sceneit)->GetName() != scenename)) { sceneit++; } return ((sceneit == m_scenes.end()) ? NULL : *sceneit); } void KX_KetsjiEngine::ConvertAndAddScene(const STR_String& scenename,bool overlay) { // only add scene when it doesn't exist! if (FindScene(scenename)) { printf("warning: scene %s already exists, not added!\n",scenename.ReadPtr()); } else { if (overlay) { m_addingOverlayScenes.push_back(scenename); } else { m_addingBackgroundScenes.push_back(scenename); } } } void KX_KetsjiEngine::RemoveScene(const STR_String& scenename) { if (FindScene(scenename)) { m_removingScenes.push_back(scenename); } else { // STR_String tmpname = scenename; std::cout << "warning: scene " << scenename << " does not exist, not removed!" << std::endl; } } void KX_KetsjiEngine::RemoveScheduledScenes() { if (m_removingScenes.size()) { vector::iterator scenenameit; for (scenenameit=m_removingScenes.begin();scenenameit != m_removingScenes.end();scenenameit++) { STR_String scenename = *scenenameit; KX_SceneList::iterator sceneit; for (sceneit = m_scenes.begin();sceneit != m_scenes.end() ; sceneit++) { KX_Scene* scene = *sceneit; if (scene->GetName()==scenename) { m_sceneconverter->RemoveScene(scene); m_scenes.erase(sceneit); break; } } } m_removingScenes.clear(); } } KX_Scene* KX_KetsjiEngine::CreateScene(Scene *scene, bool libloading) { KX_Scene* tmpscene = new KX_Scene(m_keyboarddevice, m_mousedevice, m_networkdevice, scene->id.name+2, scene, m_canvas); m_sceneconverter->ConvertScene(tmpscene, m_rendertools, m_canvas, libloading); return tmpscene; } KX_Scene* KX_KetsjiEngine::CreateScene(const STR_String& scenename) { Scene *scene = m_sceneconverter->GetBlenderSceneForName(scenename); return CreateScene(scene); } void KX_KetsjiEngine::AddScheduledScenes() { vector::iterator scenenameit; if (m_addingOverlayScenes.size()) { for (scenenameit = m_addingOverlayScenes.begin(); scenenameit != m_addingOverlayScenes.end(); scenenameit++) { STR_String scenename = *scenenameit; KX_Scene* tmpscene = CreateScene(scenename); m_scenes.push_back(tmpscene); PostProcessScene(tmpscene); } m_addingOverlayScenes.clear(); } if (m_addingBackgroundScenes.size()) { for (scenenameit = m_addingBackgroundScenes.begin(); scenenameit != m_addingBackgroundScenes.end(); scenenameit++) { STR_String scenename = *scenenameit; KX_Scene* tmpscene = CreateScene(scenename); m_scenes.insert(m_scenes.begin(),tmpscene); PostProcessScene(tmpscene); } m_addingBackgroundScenes.clear(); } } void KX_KetsjiEngine::ReplaceScene(const STR_String& oldscene,const STR_String& newscene) { m_replace_scenes.push_back(std::make_pair(oldscene,newscene)); } // replace scene is not the same as removing and adding because the // scene must be in exact the same place (to maintain drawingorder) // (nzc) - should that not be done with a scene-display list? It seems // stupid to rely on the mem allocation order... void KX_KetsjiEngine::ReplaceScheduledScenes() { if (m_replace_scenes.size()) { vector >::iterator scenenameit; for (scenenameit = m_replace_scenes.begin(); scenenameit != m_replace_scenes.end(); scenenameit++) { STR_String oldscenename = (*scenenameit).first; STR_String newscenename = (*scenenameit).second; int i=0; /* Scenes are not supposed to be included twice... I think */ KX_SceneList::iterator sceneit; for (sceneit = m_scenes.begin();sceneit != m_scenes.end() ; sceneit++) { KX_Scene* scene = *sceneit; if (scene->GetName() == oldscenename) { m_sceneconverter->RemoveScene(scene); KX_Scene* tmpscene = CreateScene(newscenename); m_scenes[i]=tmpscene; PostProcessScene(tmpscene); } i++; } } m_replace_scenes.clear(); } } void KX_KetsjiEngine::SuspendScene(const STR_String& scenename) { KX_Scene* scene = FindScene(scenename); if (scene) scene->Suspend(); } void KX_KetsjiEngine::ResumeScene(const STR_String& scenename) { KX_Scene* scene = FindScene(scenename); if (scene) scene->Resume(); } void KX_KetsjiEngine::SetUseFixedTime(bool bUseFixedTime) { m_bFixedTime = bUseFixedTime; } void KX_KetsjiEngine::SetAnimRecordMode(bool animation_record, int startFrame) { m_animation_record = animation_record; if (animation_record) { //when recording physics keyframes, run at a variable (capped) frame rate (fixed time == full speed) m_bFixedTime = false; } m_currentFrame = startFrame; } bool KX_KetsjiEngine::GetUseFixedTime(void) const { return m_bFixedTime; } double KX_KetsjiEngine::GetSuspendedDelta() { return m_suspendeddelta; } double KX_KetsjiEngine::GetTicRate() { return m_ticrate; } void KX_KetsjiEngine::SetTicRate(double ticrate) { m_ticrate = ticrate; } int KX_KetsjiEngine::GetMaxLogicFrame() { return m_maxLogicFrame; } void KX_KetsjiEngine::SetMaxLogicFrame(int frame) { m_maxLogicFrame = frame; } int KX_KetsjiEngine::GetMaxPhysicsFrame() { return m_maxPhysicsFrame; } void KX_KetsjiEngine::SetMaxPhysicsFrame(int frame) { m_maxPhysicsFrame = frame; } bool KX_KetsjiEngine::GetRestrictAnimationFPS() { return m_restrict_anim_fps; } void KX_KetsjiEngine::SetRestrictAnimationFPS(bool bRestrictAnimFPS) { m_restrict_anim_fps = bRestrictAnimFPS; } double KX_KetsjiEngine::GetAnimFrameRate() { return m_anim_framerate; } double KX_KetsjiEngine::GetClockTime(void) const { return m_clockTime; } double KX_KetsjiEngine::GetFrameTime(void) const { return m_frameTime; } double KX_KetsjiEngine::GetRealTime(void) const { return m_kxsystem->GetTimeInSeconds(); } void KX_KetsjiEngine::SetAnimFrameRate(double framerate) { m_anim_framerate = framerate; } double KX_KetsjiEngine::GetAverageFrameRate() { return m_average_framerate; } void KX_KetsjiEngine::SetExitKey(short key) { m_exitkey = key; } short KX_KetsjiEngine::GetExitKey() { return m_exitkey; } void KX_KetsjiEngine::SetTimingDisplay(bool frameRate, bool profile, bool properties) { m_show_framerate = frameRate; m_show_profile = profile; m_show_debug_properties = properties; } void KX_KetsjiEngine::GetTimingDisplay(bool& frameRate, bool& profile, bool& properties) const { frameRate = m_show_framerate; profile = m_show_profile; properties = m_show_debug_properties; } void KX_KetsjiEngine::ProcessScheduledScenes(void) { // Check whether there will be changes to the list of scenes if (m_addingOverlayScenes.size() || m_addingBackgroundScenes.size() || m_replace_scenes.size() || m_removingScenes.size()) { // Change the scene list ReplaceScheduledScenes(); RemoveScheduledScenes(); AddScheduledScenes(); } } void KX_KetsjiEngine::SetHideCursor(bool hideCursor) { m_hideCursor = hideCursor; } bool KX_KetsjiEngine::GetHideCursor(void) const { return m_hideCursor; } void KX_KetsjiEngine::SetUseOverrideFrameColor(bool overrideFrameColor) { m_overrideFrameColor = overrideFrameColor; } bool KX_KetsjiEngine::GetUseOverrideFrameColor(void) const { return m_overrideFrameColor; } void KX_KetsjiEngine::SetOverrideFrameColor(float r, float g, float b) { m_overrideFrameColorR = r; m_overrideFrameColorG = g; m_overrideFrameColorB = b; } void KX_KetsjiEngine::GetOverrideFrameColor(float& r, float& g, float& b) const { r = m_overrideFrameColorR; g = m_overrideFrameColorG; b = m_overrideFrameColorB; } void KX_KetsjiEngine::Resize() { KX_SceneList::iterator sceneit; /* extended mode needs to recalculate camera frustrums when */ KX_Scene* firstscene = *m_scenes.begin(); const RAS_FrameSettings &framesettings = firstscene->GetFramingType(); if (framesettings.FrameType() == RAS_FrameSettings::e_frame_extend) { for (sceneit = m_scenes.begin();sceneit != m_scenes.end(); sceneit++) { KX_Camera* cam = ((KX_Scene *)*sceneit)->GetActiveCamera(); cam->InvalidateProjectionMatrix(); } } } void KX_KetsjiEngine::SetGlobalSettings(GlobalSettings* gs) { m_globalsettings.matmode = gs->matmode; m_globalsettings.glslflag = gs->glslflag; } GlobalSettings* KX_KetsjiEngine::GetGlobalSettings(void) { return &m_globalsettings; }