Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorArnaud Degroote <arnaud.degroote@isae-supaero.fr>2015-12-12 04:37:42 +0300
committerJorge Bernal <jbernalmartinez@gmail.com>2015-12-12 04:46:53 +0300
commitaae93ae4c6c13e2b30ba272e24354dd020880868 (patch)
tree2f3cea018e4e19258d562ae45ccfdef929ff12d0
parente089b1f08bdf72eb4441e62f9c99e01ace0ef5c4 (diff)
BGE: Improve clock management
This patch improves clock management in BGE, to be able to accelerate / slow the time, and also to finely synchronize clock with external engines. Several new python functions have been added and existence ones have been improved for that purpose. Now we have: - 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. - 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. - getRealTime(): Get the number of real (system-clock) seconds elapsed since the beginning of the simulation. - 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. - 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. - 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. - 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. - 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). Rationale are described more precisely in the thread http://lists.blender.org/pipermail/bf-gamedev/2013-November/000165.html. See also T37640 Reviewers: sybren, panzergame, #game_engine, lordloki, moguri Reviewed By: sybren, panzergame, #game_engine, lordloki, moguri Subscribers: moguri, hg1, sybren, panzergame, dfelinto, lordloki Projects: #game_engine Maniphest Tasks: T37640 Differential Revision: https://developer.blender.org/D728
-rw-r--r--doc/python_api/rst/bge.logic.rst70
-rw-r--r--source/gameengine/Ketsji/KX_KetsjiEngine.cpp77
-rw-r--r--source/gameengine/Ketsji/KX_KetsjiEngine.h47
-rw-r--r--source/gameengine/Ketsji/KX_PythonInit.cpp70
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"},