diff options
-rw-r--r-- | doc/python_api/rst/bge.logic.rst | 70 | ||||
-rw-r--r-- | source/gameengine/Ketsji/KX_KetsjiEngine.cpp | 77 | ||||
-rw-r--r-- | source/gameengine/Ketsji/KX_KetsjiEngine.h | 47 | ||||
-rw-r--r-- | source/gameengine/Ketsji/KX_PythonInit.cpp | 70 |
4 files changed, 248 insertions, 16 deletions
diff --git a/doc/python_api/rst/bge.logic.rst b/doc/python_api/rst/bge.logic.rst index b119bdd1ba1..3f35901234a 100644 --- a/doc/python_api/rst/bge.logic.rst +++ b/doc/python_api/rst/bge.logic.rst @@ -378,6 +378,76 @@ General functions Render next frame (if Python has control) +********************** +Time related functions +********************** + +.. function:: getClockTime() + + Get the current BGE render time, in seconds. The BGE render time is the + simulation time corresponding to the next scene that will be rendered. + + :rtype: double + +.. function:: getFrameTime() + + Get the current BGE frame time, in seconds. The BGE frame time is the + simulation time corresponding to the current call of the logic system. + Generally speaking, it is what the user is interested in. + + :rtype: double + +.. function:: getRealTime() + + Get the number of real (system-clock) seconds elapsed since the beginning + of the simulation. + + :rtype: double + +.. function:: getTimeScale() + + Get the time multiplier between real-time and simulation time. The default + value is 1.0. A value greater than 1.0 means that the simulation is going + faster than real-time, a value lower than 1.0 means that the simulation is + going slower than real-time. + + :rtype: double + +.. function:: setTimeScale(time_scale) + + Set the time multiplier between real-time and simulation time. A value + greater than 1.0 means that the simulation is going faster than real-time, + a value lower than 1.0 means that the simulation is going slower than + real-time. Note that a too large value may lead to some physics + instabilities. + + :arg time_scale: The new time multiplier. + +.. function:: getUseExternalClock() + + Get if the BGE use the inner BGE clock, or rely or on an external + clock. The default is to use the inner BGE clock. + + :rtype: bool + +.. function:: setUseExternalClock(use_external_clock) + + Set if the BGE use the inner BGE clock, or rely or on an external + clock. If the user selects the use of an external clock, he should call + regularly the setClockTime method. + + :arg use_external_clock: the new setting + +.. function:: setClockTime(new_time) + + Set the next value of the simulation clock. It is preferable to use this + method from a custom main function in python, as calling it in the logic + block can easily lead to a blocked system (if the time does not advance + enough to run at least the next logic step). + + :arg new_time: the next value of the BGE clock (in second). + + ***************** Utility functions ***************** diff --git a/source/gameengine/Ketsji/KX_KetsjiEngine.cpp b/source/gameengine/Ketsji/KX_KetsjiEngine.cpp index 5f36a980a53..fa924f74290 100644 --- a/source/gameengine/Ketsji/KX_KetsjiEngine.cpp +++ b/source/gameengine/Ketsji/KX_KetsjiEngine.cpp @@ -128,6 +128,7 @@ KX_KetsjiEngine::KX_KetsjiEngine(KX_ISystem* system) m_bInitialized(false), m_activecam(0), m_bFixedTime(false), + m_useExternalClock(false), m_firstframe(true), @@ -135,6 +136,8 @@ KX_KetsjiEngine::KX_KetsjiEngine(KX_ISystem* system) m_clockTime(0.f), m_previousClockTime(0.f), m_previousAnimTime(0.f), + m_timescale(1.0f), + m_previousRealTime(0.0f), m_exitcode(KX_EXIT_REQUEST_NO_REQUEST), @@ -411,6 +414,7 @@ void KX_KetsjiEngine::StartEngine(bool clearIpo) m_clockTime = m_kxsystem->GetTimeInSeconds(); m_frameTime = m_kxsystem->GetTimeInSeconds(); m_previousClockTime = m_kxsystem->GetTimeInSeconds(); + m_previousRealTime = m_kxsystem->GetTimeInSeconds(); m_firstframe = true; m_bInitialized = true; @@ -554,7 +558,7 @@ void KX_KetsjiEngine::EndFrame() bool KX_KetsjiEngine::NextFrame() { - double timestep = 1.0/m_ticrate; + double timestep = m_timescale / m_ticrate; double framestep = timestep; // static hidden::Clock sClock; @@ -563,12 +567,43 @@ bool KX_KetsjiEngine::NextFrame() //float dt = sClock.getTimeMicroseconds() * 0.000001f; //sClock.reset(); - if (m_bFixedTime) { - m_clockTime += timestep; - } - else { - // m_clockTime += dt; - m_clockTime = m_kxsystem->GetTimeInSeconds(); + /* + * Clock advancement. There is basically three case: + * - m_useExternalClock is true, the user is responsible to advance the time + * manually using setClockTime, so here, we do not do anything. + * - m_useExternalClock is false, m_bFixedTime is true, we advance for one + * timestep, which already handle the time scaling parameter + * - m_useExternalClock is false, m_bFixedTime is false, we consider how much + * time has elapsed since last call and we scale this time by the time + * scaling parameter. If m_timescale is 1.0 (default value), the clock + * corresponds to the computer clock. + * + * Once clockTime has been computed, we will compute how many logic frames + * will be executed before the next rendering phase (which will occur at "clockTime"). + * The game time elapsing between two logic frames (called framestep) + * depends on several variables: + * - ticrate + * - max_physic_frame + * - max_logic_frame + * XXX The logic over computation framestep is definitively not clear (and + * I'm not even sure it is correct). If needed frame is strictly greater + * than max_physics_frame, we are doing a jump in game time, but keeping + * framestep = 1 / ticrate, while if frames is greater than + * max_logic_frame, we increase framestep. + * + * XXX render.fps is not considred anywhere. + */ + if (!m_useExternalClock) { + if (m_bFixedTime) { + m_clockTime += timestep; + } + else { + double current_time = m_kxsystem->GetTimeInSeconds(); + double dt = current_time - m_previousRealTime; + m_previousRealTime = current_time; + // m_clockTime += dt; + m_clockTime += dt * m_timescale; + } } double deltatime = m_clockTime - m_frameTime; @@ -579,16 +614,14 @@ bool KX_KetsjiEngine::NextFrame() return false; } - // Compute the number of logic frames to do each update (fixed tic bricks) - int frames =int(deltatime*m_ticrate+1e-6); + int frames = int(deltatime * m_ticrate / m_timescale + 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) @@ -1756,6 +1789,10 @@ void KX_KetsjiEngine::SetUseFixedTime(bool bUseFixedTime) m_bFixedTime = bUseFixedTime; } +void KX_KetsjiEngine::SetUseExternalClock(bool useExternalClock) +{ + m_useExternalClock = useExternalClock; +} void KX_KetsjiEngine::SetAnimRecordMode(bool animation_record, int startFrame) { @@ -1783,6 +1820,11 @@ bool KX_KetsjiEngine::GetUseFixedTime(void) const return m_bFixedTime; } +bool KX_KetsjiEngine::GetUseExternalClock(void) const +{ + return m_useExternalClock; +} + double KX_KetsjiEngine::GetSuspendedDelta() { return m_suspendeddelta; @@ -1798,6 +1840,16 @@ void KX_KetsjiEngine::SetTicRate(double ticrate) m_ticrate = ticrate; } +double KX_KetsjiEngine::GetTimeScale() const +{ + return m_timescale; +} + +void KX_KetsjiEngine::SetTimeScale(double timescale) +{ + m_timescale = timescale; +} + int KX_KetsjiEngine::GetMaxLogicFrame() { return m_maxLogicFrame; @@ -1838,6 +1890,11 @@ double KX_KetsjiEngine::GetClockTime(void) const return m_clockTime; } +void KX_KetsjiEngine::SetClockTime(double externalClockTime) +{ + m_clockTime = externalClockTime; +} + double KX_KetsjiEngine::GetFrameTime(void) const { return m_frameTime; diff --git a/source/gameengine/Ketsji/KX_KetsjiEngine.h b/source/gameengine/Ketsji/KX_KetsjiEngine.h index ec855be3212..3b8cec2ac82 100644 --- a/source/gameengine/Ketsji/KX_KetsjiEngine.h +++ b/source/gameengine/Ketsji/KX_KetsjiEngine.h @@ -103,16 +103,19 @@ private: bool m_bInitialized; int m_activecam; bool m_bFixedTime; + bool m_useExternalClock; bool m_firstframe; int m_currentFrame; - double m_frameTime;//discrete timestamp of the 'game logic frame' - double m_clockTime;//current time - double m_previousClockTime;//previous clock time - double m_previousAnimTime; //the last time animations were updated + double m_frameTime; // current logic game time + double m_clockTime; // game time for the next rendering step + double m_previousClockTime; // game time of the previous rendering step + double m_previousAnimTime; //game time when the animations were last updated double m_remainingTime; + double m_timescale; // time scaling parameter. if > 1.0, time goes faster than real-time. If < 1.0, times goes slower than real-time. + double m_previousRealTime; static int m_maxLogicFrame; /* maximum number of consecutive logic frame */ static int m_maxPhysicsFrame; /* maximum number of consecutive physics frame */ @@ -297,15 +300,37 @@ public: bool GetUseFixedTime(void) const; /** - * Returns current render frame clock time + * Sets if the BGE relies on a external clock or its own internal clock + */ + void SetUseExternalClock(bool bUseExternalClock); + + /** + * Returns if we rely on an external clock + * \return Current setting + */ + bool GetUseExternalClock(void) const; + + /** + * Returns next render frame game time */ double GetClockTime(void) const; + + /** + * Set the next render frame game time. It will impact also frame time, as + * this one is derived from clocktime + */ + void SetClockTime(double externalClockTime); + /** - * Returns current logic frame clock time + * Returns current logic frame game time */ double GetFrameTime(void) const; + /** + * Returns the real (system) time + */ double GetRealTime(void) const; + /** * Returns the difference between the local time of the scene (when it * was running and not suspended) and the "curtime" @@ -361,6 +386,16 @@ public: */ static double GetAverageFrameRate(); + /** + * Gets the time scale multiplier + */ + double GetTimeScale() const; + + /** + * Sets the time scale multiplier + */ + void SetTimeScale(double scale); + static void SetExitKey(short key); static short GetExitKey(); diff --git a/source/gameengine/Ketsji/KX_PythonInit.cpp b/source/gameengine/Ketsji/KX_PythonInit.cpp index d35f09dd820..8a010726bba 100644 --- a/source/gameengine/Ketsji/KX_PythonInit.cpp +++ b/source/gameengine/Ketsji/KX_PythonInit.cpp @@ -548,6 +548,64 @@ static PyObject *gPyGetAverageFrameRate(PyObject *) return PyFloat_FromDouble(KX_KetsjiEngine::GetAverageFrameRate()); } +static PyObject *gPyGetUseExternalClock(PyObject *) +{ + return PyBool_FromLong(gp_KetsjiEngine->GetUseExternalClock()); +} + +static PyObject *gPySetUseExternalClock(PyObject *, PyObject *args) +{ + bool bUseExternalClock; + + if (!PyArg_ParseTuple(args, "p:setUseExternalClock", &bUseExternalClock)) + return NULL; + + gp_KetsjiEngine->SetUseExternalClock(bUseExternalClock); + Py_RETURN_NONE; +} + +static PyObject *gPyGetClockTime(PyObject *) +{ + return PyFloat_FromDouble(gp_KetsjiEngine->GetClockTime()); +} + +static PyObject *gPySetClockTime(PyObject *, PyObject *args) +{ + double externalClockTime; + + if (!PyArg_ParseTuple(args, "d:setClockTime", &externalClockTime)) + return NULL; + + gp_KetsjiEngine->SetClockTime(externalClockTime); + Py_RETURN_NONE; +} + +static PyObject *gPyGetFrameTime(PyObject *) +{ + return PyFloat_FromDouble(gp_KetsjiEngine->GetFrameTime()); +} + +static PyObject *gPyGetRealTime(PyObject *) +{ + return PyFloat_FromDouble(gp_KetsjiEngine->GetRealTime()); +} + +static PyObject *gPyGetTimeScale(PyObject *) +{ + return PyFloat_FromDouble(gp_KetsjiEngine->GetTimeScale()); +} + +static PyObject *gPySetTimeScale(PyObject *, PyObject *args) +{ + double time_scale; + + if (!PyArg_ParseTuple(args, "d:setTimeScale", &time_scale)) + return NULL; + + gp_KetsjiEngine->SetTimeScale(time_scale); + Py_RETURN_NONE; +} + static PyObject *gPyGetBlendFileList(PyObject *, PyObject *args) { char cpath[sizeof(gp_GamePythonPath)]; @@ -847,7 +905,19 @@ static struct PyMethodDef game_methods[] = { {"setAnimRecordFrame", (PyCFunction) gPySetAnimRecordFrame, METH_VARARGS, (const char *)"Sets the current frame number used for animation recording"}, {"getExitKey", (PyCFunction) gPyGetExitKey, METH_NOARGS, (const char *)"Gets the key used to exit the game engine"}, {"setExitKey", (PyCFunction) gPySetExitKey, METH_VARARGS, (const char *)"Sets the key used to exit the game engine"}, + {"getUseExternalClock", (PyCFunction) gPyGetUseExternalClock, METH_NOARGS, (const char *)"Get if we use the time provided by an external clock"}, + {"setUseExternalClock", (PyCFunction) gPySetUseExternalClock, METH_VARARGS, (const char *)"Set if we use the time provided by an external clock"}, + {"getClockTime", (PyCFunction) gPyGetClockTime, METH_NOARGS, (const char *)"Get the last BGE render time. " + "The BGE render time is the simulated time corresponding to the next scene that will be renderered"}, + {"setClockTime", (PyCFunction) gPySetClockTime, METH_VARARGS, (const char *)"Set the BGE render time. " + "The BGE render time is the simulated time corresponding to the next scene that will be rendered"}, + {"getFrameTime", (PyCFunction) gPyGetFrameTime, METH_NOARGS, (const char *)"Get the BGE last frametime. " + "The BGE frame time is the simulated time corresponding to the last call of the logic system"}, + {"getRealTime", (PyCFunction) gPyGetRealTime, METH_NOARGS, (const char *)"Get the real system time. " + "The real-time corresponds to the system time" }, {"getAverageFrameRate", (PyCFunction) gPyGetAverageFrameRate, METH_NOARGS, (const char *)"Gets the estimated average frame rate"}, + {"getTimeScale", (PyCFunction) gPyGetTimeScale, METH_NOARGS, (const char *)"Get the time multiplier"}, + {"setTimeScale", (PyCFunction) gPySetTimeScale, METH_VARARGS, (const char *)"Set the time multiplier"}, {"getBlendFileList", (PyCFunction)gPyGetBlendFileList, METH_VARARGS, (const char *)"Gets a list of blend files in the same directory as the current blend file"}, {"PrintGLInfo", (PyCFunction)pyPrintExt, METH_NOARGS, (const char *)"Prints GL Extension Info"}, {"PrintMemInfo", (PyCFunction)pyPrintStats, METH_NOARGS, (const char *)"Print engine statistics"}, |