blender/source/gameengine/Ketsji/KX_KetsjiEngine.cpp
Dalai Felinto 0890b80ed9 Patch:[#25163] BGE support for Blender Font objects - unicode support
Problem/Bug:
------------
There were no way to have proper unicode characters (e.g. Japanese) in Blender Game Engine. Now we can :)
You can see a sample here: http://blog.mikepan.com/multi-language-support-in-blender/

Functionality Explanation:
--------------------------
This patch converts the Blender Font Objects to a new BGE type: KX_FontObject
This object inherits KX_GameObject.cpp and has the following properties:
- text (the text of the object)
- size (taken from the Blender object, usually is 1.0)
- resolution (1.0 by default, maybe not really needed, but at least for debugging/the time being it's nice to have) 

The way we deal with linked objects is different than Blender. In Blender the text and size are a property of the Text databock. Therefore linked objects necessarily share the same text (and size, although the size of the object datablock affects that too). In BGE they are stored and accessed per object. Without that it would be problematic to have addObject adding texts that don't share the same data.

Known problems/limitations/ToDo:
--------------------------------
1) support for packed font and the <builtin>
2) figure why some fonts are displayed in a different size in 3DView/BGE (BLF)
3) investigate some glitches I see some times
4) support for multiline
5) support for more Blender Font Object options (text aligment, text boxes, ...)

[1] Diego (bdiego) evantually will help on that. For the time being we are using the "default" (ui) font to replace the <builtin>.
[2] but not all of them. I need to cross check who is calculating the size/dpi in/correctly - Blender or BLF. (e.g. fonts that work well - MS Gothic)
[3] I think this may be related to the resolution we are drawing the font
[4] It can't/will not be handled inside BFL. So the way I see it is to implement a mini text library/api that works as a middlelayer between the drawing step and BLF. 
    So instead of:
      BLF_draw(fontid, (char *)text, strlen(text));
    We would do:
      MAGIC_ROUTINE_IM_NOT_BLF_draw(fontir, (char *)text, styleflag, width, height);
[5] don't hold your breath ... but if someone wants to have fun in the holidays the (4) and (5) are part of the same problem.

Code Explanation:
-----------------
The patch should be simple to read. They are three may parts:
1) BL_BlenderDataConversion.cpp:: converts the OB_FONT object into a KX_FontObject.cpp and store it in the KX_Scene->m_fonts
2) KetsjiEngine.cpp::RenderFonts:: loop through the texts and call their internal drawing routine.
3) KX_FontObject.cpp::
  a) constructor: load the font of the object, and store other values.
  b) DrawText: calculate the aspect for the given size (sounds hacky but this is how blf works) and call the render routine in RenderTools
4) KX_BlenderGL.cpp (called from rendertools) ::BL_print_game_line:: Draws the text. Using the BLF API

*) In order to handle visibility of the object added with AddObject I'm adding to the m_scene.m_fonts list only the Fonts in a visible layer - unlike Cameras and Lamps where all the objects are added.

Acknowledgements:
----------------
Thanks Benoit for the review and adjustment suggestions.
Thanks Diego for the BFL expertise, patches and support (Latin community ftw)
Thanks my boss for letting me do part of this patch during work time. Good thing we are starting a project in a partnership with a Japanese Foundation and eventual will need unicode in BGE :) for more details on that - www.nereusprogram.org - let's call it the main sponsor of this "bug feature" ;)
2010-12-16 10:25:41 +00:00

1917 lines
47 KiB
C++

/*
* $Id$
*
* ***** 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.
*/
#if defined(WIN32) && !defined(FREE_WINDOWS)
#pragma warning (disable : 4786)
#endif //WIN32
#include <iostream>
#include "KX_KetsjiEngine.h"
#include "ListValue.h"
#include "IntValue.h"
#include "VectorValue.h"
#include "BoolValue.h"
#include "FloatValue.h"
#define KX_NUM_ITERATIONS 4
#include "RAS_BucketManager.h"
#include "RAS_Rect.h"
#include "RAS_IRasterizer.h"
#include "RAS_IRenderTools.h"
#include "RAS_ICanvas.h"
#include "STR_String.h"
#include "MT_Vector3.h"
#include "MT_Transform.h"
#include "SCA_IInputDevice.h"
#include "KX_Scene.h"
#include "MT_CmMatrix4x4.h"
#include "KX_Camera.h"
#include "KX_FontObject.h"
#include "KX_Dome.h"
#include "KX_Light.h"
#include "KX_PythonInit.h"
#include "KX_PyConstraintBinding.h"
#include "PHY_IPhysicsEnvironment.h"
#include "AUD_C-API.h"
#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 "stdio.h"
#include "DNA_world_types.h"
#include "DNA_scene_types.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
const char KX_KetsjiEngine::m_profileLabels[tc_numCategories][15] = {
"Physics:", // tc_physics
"Logic", // tc_logic
"Network:", // tc_network
"Scenegraph:", // tc_scenegraph
"Sound:", // tc_sound
"Rasterizer:", // tc_rasterizer
"Services:", // tc_services
"Overhead:", // tc_overhead
"Outside:" // tc_outside
};
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;
/**
* 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_propertiesPresent(false),
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_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);
}
/**
* Destructor of the Ketsji Engine, release all memory
*/
KX_KetsjiEngine::~KX_KetsjiEngine()
{
delete m_logger;
if(m_usedome)
delete m_dome;
}
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;
}
#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()
{
GLuint viewport[4]={0};
glGetIntegerv(GL_VIEWPORT,(GLint *)viewport);
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;
int n_renders=m_dome->GetNumberRenders();// usually 4 or 6
for (int i=0;i<n_renders;i++){
m_canvas->ClearBuffer(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);
}
list<class KX_Camera*>* cameras = scene->GetCameras();
// Draw the scene once for each camera with an enabled viewport
list<KX_Camera*>::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);
}
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 datastructures 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<class KX_Camera*>* cameras = scene->GetCameras();
list<KX_Camera*>::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()
{
// Show profiling info
m_logger->StartLog(tc_overhead, m_kxsystem->GetTimeInSeconds(), true);
if (m_show_framerate || m_show_profile || (m_show_debug_properties && m_propertiesPresent))
{
RenderDebugProperties();
}
m_average_framerate = m_logger->GetAverage();
if (m_average_framerate < 1e-6)
m_average_framerate = 1e-6;
m_average_framerate = 1.0/m_average_framerate;
// 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_rasterizer->SwapBuffers();
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)
{
printf("problem with clock\n");
deltatime = 0.f;
m_clockTime = 0.f;
m_frameTime = 0.f;
}
// 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;
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 preceed 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);
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);
DoSound(scene);
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);
DoSound(scene);
m_logger->StartLog(tc_services, m_kxsystem->GetTimeInSeconds(), true);
}
}
m_previousClockTime = m_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<class KX_Camera*>* cameras = scene->GetCameras();
// Draw the scene once for each camera with an enabled viewport
list<KX_Camera*>::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<class KX_Camera*>* cameras = scene->GetCameras();
// Draw the scene once for each camera with an enabled viewport
list<KX_Camera*>::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::DoSound(KX_Scene* scene)
{
m_logger->StartLog(tc_sound, m_kxsystem->GetTimeInSeconds(), true);
KX_Camera* cam = scene->GetActiveCamera();
if (!cam)
return;
float f[4];
cam->NodeGetWorldPosition().getValue(f);
AUD_setListenerLocation(f);
cam->GetLinearVelocity().getValue(f);
AUD_setListenerVelocity(f);
cam->NodeGetWorldOrientation().getRotation().getValue(f);
AUD_setListenerOrientation(f);
}
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 near, float far)
{
m_overrideCamNear = near;
m_overrideCamFar = far;
}
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; i<lightlist->GetCount(); 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, cam, camtrans);
/* update scene */
scene->CalculateVisibleMeshes(m_rasterizer, cam, light->GetShadowLayer());
/* render */
m_rasterizer->ClearDepthBuffer();
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() && !cam->GetViewport() )
{
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,
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(),
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
RenderFonts(scene);
if (scene->GetPhysicsEnvironment())
scene->GetPhysicsEnvironment()->debugDrawWorld();
}
void KX_KetsjiEngine::RenderFonts(KX_Scene* scene)
{
list<class KX_FontObject*>* fonts = scene->GetFonts();
list<KX_FontObject*>::iterator it = fonts->begin();
while(it != fonts->end())
{
(*it)->DrawText();
++it;
}
}
/*
To run once per scene
*/
void KX_KetsjiEngine::PostRenderScene(KX_Scene* scene)
{
m_rendertools->MotionBlur(m_rasterizer);
scene->Render2DFilters(m_canvas);
#ifdef WITH_PYTHON
scene->RunDrawingCallbacks(scene->GetPostDrawCB());
#endif
m_rasterizer->FlushDebugLines();
}
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);
SceneListsChanged();
}
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;
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 xcoord = 10; // mmmm, these constants were taken from blender source
int ycoord = 14; // to 'mimic' behaviour
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()));
/* Framerate display */
if (m_show_framerate) {
debugtxt.Format("swap : %.3f (%.3f frames per second)", tottime, 1.0/tottime);
m_rendertools->RenderText2D(RAS_IRenderTools::RAS_TEXT_PADDED,
debugtxt.Ptr(),
xcoord,
ycoord,
m_canvas->GetWidth() /* RdV, TODO ?? */,
m_canvas->GetHeight() /* RdV, TODO ?? */);
ycoord += 14;
}
/* Profile and framerate display */
if (m_show_profile)
{
for (int j = tc_first; j < tc_numCategories; j++)
{
debugtxt.Format(m_profileLabels[j]);
m_rendertools->RenderText2D(RAS_IRenderTools::RAS_TEXT_PADDED,
debugtxt.Ptr(),
xcoord,ycoord,
m_canvas->GetWidth(),
m_canvas->GetHeight());
double time = m_logger->GetAverage((KX_TimeCategory)j);
debugtxt.Format("%.3fms (%2.2f %%)", time*1000.f, time/tottime * 100.f);
m_rendertools->RenderText2D(RAS_IRenderTools::RAS_TEXT_PADDED,
debugtxt.Ptr(),
xcoord + 60 ,ycoord,
m_canvas->GetWidth(),
m_canvas->GetHeight());
ycoord += 14;
}
}
/* Property display*/
if (m_show_debug_properties && m_propertiesPresent)
{
KX_SceneList::iterator sceneit;
for (sceneit = m_scenes.begin();sceneit != m_scenes.end() ; sceneit++)
{
KX_Scene* scene = *sceneit;
/* the 'normal' debug props */
vector<SCA_DebugProp*>& debugproplist = scene->GetDebugProperties();
for (vector<SCA_DebugProp*>::iterator it = debugproplist.begin();
!(it==debugproplist.end());it++)
{
CValue* propobj = (*it)->m_obj;
STR_String objname = propobj->GetName();
STR_String propname = (*it)->m_name;
if (propname == "__state__")
{
// reserve name for object state
KX_GameObject* gameobj = static_cast<KX_GameObject*>(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.Ptr(),
xcoord,
ycoord,
m_canvas->GetWidth(),
m_canvas->GetHeight());
ycoord += 14;
}
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.Ptr(),
xcoord,
ycoord,
m_canvas->GetWidth(),
m_canvas->GetHeight());
ycoord += 14;
}
}
}
}
}
}
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))
{
STR_String tmpname = scenename;
printf("warning: scene %s already exists, not added!\n",tmpname.Ptr());
}
else
{
if (overlay)
{
m_addingOverlayScenes.insert(scenename);
}
else
{
m_addingBackgroundScenes.insert(scenename);
}
}
}
void KX_KetsjiEngine::RemoveScene(const STR_String& scenename)
{
if (FindScene(scenename))
{
m_removingScenes.insert(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())
{
set<STR_String>::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)
{
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);
return tmpscene;
}
KX_Scene* KX_KetsjiEngine::CreateScene(const STR_String& scenename)
{
Scene *scene = m_sceneconverter->GetBlenderSceneForName(scenename);
return CreateScene(scene);
}
void KX_KetsjiEngine::AddScheduledScenes()
{
set<STR_String>::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.insert(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())
{
set<pair<STR_String,STR_String> >::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, always run at a fixed framerate
m_bFixedTime = true;
}
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;
}
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::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();
// Notify
SceneListsChanged();
}
}
void KX_KetsjiEngine::SceneListsChanged(void)
{
m_propertiesPresent = false;
KX_SceneList::iterator sceneit = m_scenes.begin();
while ((sceneit != m_scenes.end()) && (!m_propertiesPresent))
{
KX_Scene* scene = *sceneit;
vector<SCA_DebugProp*>& debugproplist = scene->GetDebugProperties();
m_propertiesPresent = !debugproplist.empty();
sceneit++;
}
}
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;
}