c844aa265a
Erwin Coumans: Abstract the physics engine Charlie C: Joystick fixes Me: Moved the ray cast (shadows, mouse sensor & ray sensor)
1296 lines
30 KiB
C++
1296 lines
30 KiB
C++
/*
|
|
* $Id$
|
|
*
|
|
* ***** BEGIN GPL/BL DUAL LICENSE BLOCK *****
|
|
*
|
|
* This program is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU General Public License
|
|
* as published by the Free Software Foundation; either version 2
|
|
* of the License, or (at your option) any later version. The Blender
|
|
* Foundation also sells licenses for use in proprietary software under
|
|
* the Blender License. See http://www.blender.org/BL/ for information
|
|
* about this.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software Foundation,
|
|
* Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
|
*
|
|
* The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
|
|
* All rights reserved.
|
|
*
|
|
* The Original Code is: all of this file.
|
|
*
|
|
* Contributor(s): none yet.
|
|
*
|
|
* ***** END GPL/BL DUAL LICENSE BLOCK *****
|
|
* The engine ties all game modules together.
|
|
*/
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
#include <config.h>
|
|
#endif
|
|
|
|
#ifdef WIN32
|
|
#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_PythonInit.h"
|
|
#include "KX_PyConstraintBinding.h"
|
|
#include "PHY_IPhysicsEnvironment.h"
|
|
#include "SumoPhysicsEnvironment.h"
|
|
|
|
#include "SND_Scene.h"
|
|
#include "SND_IAudioDevice.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"
|
|
|
|
// 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 30.0
|
|
#define DEFAULT_PHYSICS_TIC_RATE 90.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;
|
|
|
|
|
|
/**
|
|
* 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),
|
|
m_audiodevice(NULL),
|
|
m_pythondictionary(NULL),
|
|
m_keyboarddevice(NULL),
|
|
m_mousedevice(NULL),
|
|
|
|
m_propertiesPresent(false),
|
|
|
|
m_bInitialized(false),
|
|
m_activecam(0),
|
|
m_bFixedTime(false),
|
|
|
|
m_firstframe(true),
|
|
|
|
m_previoustime(0.0),
|
|
m_deltatime(0.0),
|
|
|
|
m_exitcode(KX_EXIT_REQUEST_NO_REQUEST),
|
|
m_exitstring(""),
|
|
|
|
m_drawingmode(5),
|
|
m_cameraZoom(1.0),
|
|
|
|
m_overrideCam(false),
|
|
m_overrideCamUseOrtho(false),
|
|
|
|
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),
|
|
|
|
// 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)
|
|
{
|
|
// 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;
|
|
}
|
|
|
|
|
|
|
|
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::SetAudioDevice(SND_IAudioDevice* audiodevice)
|
|
{
|
|
MT_assert(audiodevice);
|
|
m_audiodevice = audiodevice;
|
|
}
|
|
|
|
|
|
|
|
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;
|
|
}
|
|
|
|
|
|
|
|
void KX_KetsjiEngine::SetPythonDictionary(PyObject* pythondictionary)
|
|
{
|
|
MT_assert(pythondictionary);
|
|
m_pythondictionary = pythondictionary;
|
|
}
|
|
|
|
|
|
|
|
void KX_KetsjiEngine::SetSceneConverter(KX_ISceneConverter* sceneconverter)
|
|
{
|
|
MT_assert(sceneconverter);
|
|
m_sceneconverter = sceneconverter;
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
* Ketsji Init(), Initializes datastructures and converts data from
|
|
* Blender into Ketsji native (realtime) format also sets up the
|
|
* graphics context
|
|
*/
|
|
void KX_KetsjiEngine::StartEngine()
|
|
{
|
|
m_previoustime = m_kxsystem->GetTimeInSeconds();
|
|
m_firstframe = true;
|
|
m_bInitialized = true;
|
|
m_ticrate = DEFAULT_LOGIC_TIC_RATE;
|
|
}
|
|
|
|
bool KX_KetsjiEngine::BeginFrame()
|
|
{
|
|
bool result = false;
|
|
|
|
RAS_Rect vp;
|
|
KX_Scene* firstscene = *m_scenes.begin();
|
|
const RAS_FrameSettings &framesettings = firstscene->GetFramingType();
|
|
|
|
// set the area used for rendering
|
|
m_rasterizer->SetRenderArea();
|
|
|
|
RAS_FramingManager::ComputeViewport(framesettings, m_canvas->GetDisplayArea(), vp);
|
|
|
|
if (m_canvas->BeginDraw())
|
|
{
|
|
result = true;
|
|
|
|
m_canvas->SetViewPort(vp.GetLeft(), vp.GetBottom(), vp.GetRight(), vp.GetTop());
|
|
SetBackGround( firstscene->GetWorldInfo() );
|
|
m_rasterizer->BeginFrame( m_drawingmode , m_kxsystem->GetTimeInSeconds());
|
|
m_rendertools->BeginFrame( m_rasterizer);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
|
|
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();
|
|
}
|
|
// 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();
|
|
}
|
|
|
|
|
|
|
|
void KX_KetsjiEngine::NextFrame()
|
|
{
|
|
m_logger->StartLog(tc_services, m_kxsystem->GetTimeInSeconds(), true);
|
|
|
|
double curtime;
|
|
if (m_bFixedTime)
|
|
curtime = m_previoustime + 1.0/m_ticrate;
|
|
else
|
|
curtime = m_kxsystem->GetTimeInSeconds();
|
|
m_deltatime += curtime - m_previoustime;
|
|
float realDeltaTime = curtime - m_previoustime;
|
|
|
|
m_previoustime = curtime;
|
|
double localtime = curtime - m_deltatime;
|
|
|
|
// Compute the number of logic frames to do each update (fixed tic bricks)
|
|
int frames = (int) (m_deltatime*m_ticrate);
|
|
m_deltatime -= double(frames)/m_ticrate;
|
|
|
|
KX_SceneList::iterator sceneit;
|
|
|
|
while (frames)
|
|
{
|
|
localtime += 1.0/m_ticrate;
|
|
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);
|
|
scene->UpdateObjectActivity();
|
|
|
|
if (!scene->IsSuspended())
|
|
{
|
|
m_logger->StartLog(tc_physics, m_kxsystem->GetTimeInSeconds(), true);
|
|
m_logger->StartLog(tc_network, m_kxsystem->GetTimeInSeconds(), true);
|
|
scene->GetNetworkScene()->proceed(localtime);
|
|
|
|
// set Python hooks for each scene
|
|
PHY_SetActiveEnvironment(scene->GetPhysicsEnvironment());
|
|
PHY_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);
|
|
scene->UpdateParents(localtime);
|
|
|
|
// Process sensors, and controllers
|
|
m_logger->StartLog(tc_logic, m_kxsystem->GetTimeInSeconds(), true);
|
|
scene->LogicBeginFrame(localtime);
|
|
|
|
// Scenegraph needs to be updated again, because Logic Controllers
|
|
// can affect the local matrices.
|
|
m_logger->StartLog(tc_scenegraph, m_kxsystem->GetTimeInSeconds(), true);
|
|
scene->UpdateParents(localtime);
|
|
|
|
// Process actuators
|
|
|
|
// Do some cleanup work for this logic frame
|
|
m_logger->StartLog(tc_logic, m_kxsystem->GetTimeInSeconds(), true);
|
|
scene->LogicUpdateFrame(localtime, true);
|
|
scene->LogicEndFrame();
|
|
|
|
// Actuators can affect the scenegraph
|
|
m_logger->StartLog(tc_scenegraph, m_kxsystem->GetTimeInSeconds(), true);
|
|
scene->UpdateParents(localtime);
|
|
|
|
scene->GetPhysicsEnvironment()->beginFrame();
|
|
|
|
// 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(localtime,realDeltaTime);
|
|
} // suspended
|
|
|
|
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();
|
|
|
|
if (m_audiodevice)
|
|
m_audiodevice->NextFrame();
|
|
|
|
// scene management
|
|
ProcessScheduledScenes();
|
|
|
|
frames--;
|
|
}
|
|
|
|
// 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())
|
|
{
|
|
// set Python hooks for each scene
|
|
PHY_SetActiveEnvironment(scene->GetPhysicsEnvironment());
|
|
PHY_SetActiveScene(scene);
|
|
|
|
// 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(curtime,0.f);
|
|
// Update scenegraph after physics step. This maps physics calculations
|
|
// into node positions.
|
|
m_logger->StartLog(tc_scenegraph, m_kxsystem->GetTimeInSeconds(), true);
|
|
scene->UpdateParents(curtime);
|
|
|
|
// Do some cleanup work for this logic frame
|
|
m_logger->StartLog(tc_logic, m_kxsystem->GetTimeInSeconds(), true);
|
|
scene->LogicUpdateFrame(curtime, false);
|
|
|
|
// Actuators can affect the scenegraph
|
|
m_logger->StartLog(tc_scenegraph, m_kxsystem->GetTimeInSeconds(), true);
|
|
scene->UpdateParents(curtime);
|
|
} // suspended
|
|
|
|
DoSound(scene);
|
|
|
|
m_logger->StartLog(tc_services, m_kxsystem->GetTimeInSeconds(), true);
|
|
}
|
|
|
|
// Start logging time spend outside main loop
|
|
m_logger->StartLog(tc_outside, m_kxsystem->GetTimeInSeconds(), true);
|
|
}
|
|
|
|
|
|
|
|
void KX_KetsjiEngine::Render()
|
|
{
|
|
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();
|
|
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);
|
|
}
|
|
|
|
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;
|
|
|
|
// pass the scene's worldsettings to the rasterizer
|
|
SetWorldSettings(scene->GetWorldInfo());
|
|
|
|
if (scene->IsClearingZBuffer())
|
|
m_rasterizer->ClearDepthBuffer();
|
|
|
|
m_rendertools->SetAuxilaryClientInfo(scene);
|
|
|
|
//Initialize scene viewport.
|
|
SetupRenderFrame(scene);
|
|
|
|
// do the rendering
|
|
RenderFrame(scene);
|
|
}
|
|
|
|
// only one place that checks for stereo
|
|
if(m_rasterizer->Stereo())
|
|
{
|
|
m_rasterizer->SetEye(RAS_IRasterizer::RAS_STEREO_RIGHTEYE);
|
|
|
|
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;
|
|
|
|
// 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);
|
|
|
|
//Initialize scene viewport.
|
|
SetupRenderFrame(scene);
|
|
|
|
// do the rendering
|
|
RenderFrame(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;
|
|
}
|
|
|
|
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;
|
|
MT_Point3 listenerposition = cam->NodeGetWorldPosition();
|
|
MT_Vector3 listenervelocity = cam->GetLinearVelocity();
|
|
MT_Matrix3x3 listenerorientation = cam->NodeGetWorldOrientation();
|
|
|
|
SND_Scene* soundscene = scene->GetSoundScene();
|
|
soundscene->SetListenerTransform(
|
|
listenerposition,
|
|
listenervelocity,
|
|
listenerorientation);
|
|
|
|
soundscene->Proceed();
|
|
}
|
|
|
|
|
|
|
|
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())
|
|
{
|
|
if (m_drawingmode == RAS_IRasterizer::KX_TEXTURED)
|
|
{
|
|
if (wi->hasMist())
|
|
{
|
|
m_rasterizer->SetFog(
|
|
wi->getMistStart(),
|
|
wi->getMistDistance(),
|
|
wi->getMistColorRed(),
|
|
wi->getMistColorGreen(),
|
|
wi->getMistColorBlue()
|
|
);
|
|
}
|
|
else
|
|
{
|
|
m_rasterizer->DisableFog();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
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::SetupRenderFrame(KX_Scene *scene)
|
|
{
|
|
// 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.
|
|
|
|
RAS_Rect viewport;
|
|
|
|
if (
|
|
m_overrideCam ||
|
|
(scene->GetName() != m_overrideSceneName) ||
|
|
m_overrideCamUseOrtho
|
|
) {
|
|
RAS_FramingManager::ComputeViewport(
|
|
scene->GetFramingType(),
|
|
m_canvas->GetDisplayArea(),
|
|
viewport
|
|
);
|
|
} else {
|
|
viewport.SetLeft(0);
|
|
viewport.SetBottom(0);
|
|
viewport.SetRight(int(m_canvas->GetWidth()));
|
|
viewport.SetTop(int(m_canvas->GetHeight()));
|
|
}
|
|
// 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()
|
|
);
|
|
|
|
}
|
|
|
|
|
|
// update graphics
|
|
void KX_KetsjiEngine::RenderFrame(KX_Scene* scene)
|
|
{
|
|
float left, right, bottom, top, nearfrust, farfrust;
|
|
const float ortho = 100.0;
|
|
KX_Camera* cam = scene->GetActiveCamera();
|
|
|
|
if (!cam)
|
|
return;
|
|
|
|
m_rasterizer->DisplayFog();
|
|
|
|
if (m_overrideCam && (scene->GetName() == m_overrideSceneName) && m_overrideCamUseOrtho) {
|
|
MT_CmMatrix4x4 projmat = m_overrideCamProjMat;
|
|
m_rasterizer->SetProjectionMatrix(projmat);
|
|
} else if (cam->hasValidProjectionMatrix())
|
|
{
|
|
m_rasterizer->SetProjectionMatrix(cam->GetProjectionMatrix());
|
|
} else
|
|
{
|
|
RAS_FrameFrustum frustum;
|
|
float lens = cam->GetLens();
|
|
nearfrust = cam->GetCameraNear();
|
|
farfrust = cam->GetCameraFar();
|
|
|
|
if (!cam->GetCameraData()->m_perspective)
|
|
{
|
|
lens *= ortho;
|
|
nearfrust = (nearfrust + 1.0)*ortho;
|
|
farfrust *= ortho;
|
|
}
|
|
|
|
RAS_FramingManager::ComputeFrustum(
|
|
scene->GetFramingType(),
|
|
m_canvas->GetDisplayArea(),
|
|
scene->GetSceneViewport(),
|
|
lens,
|
|
nearfrust,
|
|
farfrust,
|
|
frustum
|
|
);
|
|
|
|
left = frustum.x1 * m_cameraZoom;
|
|
right = frustum.x2 * m_cameraZoom;
|
|
bottom = frustum.y1 * m_cameraZoom;
|
|
top = frustum.y2 * m_cameraZoom;
|
|
nearfrust = frustum.camnear;
|
|
farfrust = frustum.camfar;
|
|
|
|
MT_Matrix4x4 projmat = m_rasterizer->GetFrustumMatrix(
|
|
left, right, bottom, top, nearfrust, farfrust);
|
|
|
|
cam->SetProjectionMatrix(projmat);
|
|
|
|
// Otherwise the projection matrix for each eye will be the same...
|
|
if (m_rasterizer->Stereo())
|
|
cam->InvalidateProjectionMatrix();
|
|
}
|
|
|
|
MT_Transform camtrans(cam->GetWorldToCamera());
|
|
if (!cam->GetCameraData()->m_perspective)
|
|
camtrans.getOrigin()[2] *= ortho;
|
|
MT_Matrix4x4 viewmat(camtrans);
|
|
|
|
m_rasterizer->SetViewMatrix(viewmat, cam->NodeGetWorldPosition(),
|
|
cam->GetCameraLocation(), cam->GetCameraOrientation());
|
|
cam->SetModelviewMatrix(viewmat);
|
|
|
|
scene->UpdateMeshTransformations();
|
|
|
|
// 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.
|
|
scene->CalculateVisibleMeshes(m_rasterizer);
|
|
|
|
scene->RenderBuckets(camtrans, m_rasterizer, m_rendertools);
|
|
}
|
|
|
|
|
|
|
|
void KX_KetsjiEngine::StopEngine()
|
|
{
|
|
if (m_bInitialized)
|
|
{
|
|
KX_SceneList::iterator sceneit;
|
|
for (sceneit = m_scenes.begin();sceneit != m_scenes.end() ; sceneit++)
|
|
{
|
|
KX_Scene* scene = *sceneit;
|
|
delete 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));
|
|
|
|
// 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();
|
|
activecam = new KX_Camera(scene,KX_Scene::m_callbacks,camdata, false);
|
|
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,true);
|
|
} else {
|
|
activecam->NodeSetLocalPosition(MT_Point3(0.0, 0.0, 0.0));
|
|
activecam->NodeSetLocalOrientation(MT_Vector3(0.0, 0.0, 0.0));
|
|
activecam->NodeUpdateGS(0,true);
|
|
}
|
|
|
|
scene->AddCamera(activecam);
|
|
scene->SetActiveCamera(activecam);
|
|
scene->GetObjectList()->Add(activecam->AddRef());
|
|
scene->GetRootParentList()->Add(activecam->AddRef());
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
/* 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("%2.2f %%", 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;
|
|
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)
|
|
{
|
|
delete scene;
|
|
m_scenes.erase(sceneit);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
m_removingScenes.clear();
|
|
}
|
|
}
|
|
|
|
|
|
|
|
KX_Scene* KX_KetsjiEngine::CreateScene(const STR_String& scenename)
|
|
{
|
|
|
|
KX_Scene* tmpscene = new KX_Scene(m_keyboarddevice,
|
|
m_mousedevice,
|
|
m_networkdevice,
|
|
m_audiodevice,
|
|
scenename);
|
|
|
|
m_sceneconverter->ConvertScene(scenename,
|
|
tmpscene,
|
|
m_pythondictionary,
|
|
m_keyboarddevice,
|
|
m_rendertools,
|
|
m_canvas);
|
|
|
|
return tmpscene;
|
|
}
|
|
|
|
|
|
|
|
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)
|
|
{
|
|
delete 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;
|
|
}
|
|
|
|
|
|
|
|
bool KX_KetsjiEngine::GetUseFixedTime(void) const
|
|
{
|
|
return m_bFixedTime;
|
|
}
|
|
|
|
double KX_KetsjiEngine::GetTicRate()
|
|
{
|
|
return m_ticrate;
|
|
}
|
|
|
|
void KX_KetsjiEngine::SetTicRate(double ticrate)
|
|
{
|
|
m_ticrate = ticrate;
|
|
}
|
|
|
|
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;
|
|
}
|
|
|