Source code for morse.core.morse_time
"""
This module deals with the time management in Morse, providing several
possible implementations.
At the moment, it provides two implementations:
- best effort (i.e. try to simulate at real-time, by dropping
frame). The simulation is less acurate, because the physical steps
are not really constant.
- fixed simulation step. Compute all physical / logical step. The
simulation will be more precise, but the simulation time will
differ from computer clock time.
"""
import logging
logger = logging.getLogger("morse." + __name__)
import time
import copy
from morse.core import blenderapi
from morse.helpers.statistics import Stats
[docs]class BestEffortStrategy:
def __init__ (self, relative_time):
if relative_time:
self.time = 0.0
self._real_time_offset = time.time()
else:
self.time = time.time()
self._real_time_offset = 0.0
self._time_offset = copy.copy(self.time)
self._stat_jitter = Stats()
self._stat_nb_frame = Stats()
self._time_frame = 0.0
self._last_time = 0.0
self._nb_frame = 0
scene = blenderapi.scene()
for obj in scene.objects:
if obj.name == '__morse_dt_analyser':
self._morse_dt_analyser = obj
self._prepare_compute_dt()
logger.info('Morse configured in Best Effort Mode')
[docs] def update (self):
"""
The exact physical time elapsed between two logic call is hard
to guess. In the nominal case, it is easy, as long as you have
one logical step per render step. In other case, it depends on
logic_max_step, physics_max_step, and "complex" internal logic.
So, instead of guessing it, observe it. Assuming the physical engine
is perfect, put a solid at one meter by sec on x axis, and observe
its displacement between two frame. We have:
dx = vx * dt
where vw = 1.0. So we have dx = dt.
Modern version of Blender (>= 2.77) provides the method
bge.logic.getFrameTime() so if this information is available,
just use it.
"""
current_time = blenderapi.frame_time()
if current_time == -1:
self._dt = self._morse_dt_analyser.worldPosition[0] - self.px
self.time += self._dt
self._prepare_compute_dt()
else:
self.time = current_time + self._time_offset
self._update_statistics()
[docs] def name(self):
return 'Best Effort'
@property
def mean(self):
return self._stat_jitter.mean
def _prepare_compute_dt(self):
self.px = self._morse_dt_analyser.worldPosition[0]
self._morse_dt_analyser.setLinearVelocity([1.0, 0.0, 0.0], True)
[docs] def statistics(self):
return {
"mean_time" : self._stat_jitter.mean,
"variance_time": self._stat_jitter.variance,
"mean_frame_by_sec": self._stat_nb_frame.mean,
"variance_frame_by_sec": self._stat_nb_frame.variance
}
def _update_statistics(self):
if self._last_time == 0.0:
self._last_time = self.time
else:
ds = self.time - self._last_time
self._last_time = self.time
self._stat_jitter.update(ds)
if self._nb_frame == 0:
self._time_frame = self.time
self._nb_frame = 1
else:
if self.time - self._time_frame > 1.0:
self._stat_nb_frame.update(self._nb_frame)
self._nb_frame = 0
else:
self._nb_frame = self._nb_frame + 1
@property
def real_time(self):
return time.time() - self._real_time_offset
[docs]class FixedSimulationStepStrategy:
def __init__ (self, relative_time):
if relative_time:
self.time = 0.0
self._real_time_offset = time.time()
else:
self.time = time.time()
self._real_time_offset = 0.0
self._time_offset = copy.copy(self.time)
self._incr = 1.0 / blenderapi.getfrequency()
self._stat_jitter = Stats()
self._last_time = 0.0
logger.info('Morse configured in Fixed Simulation Step Mode with '
'time step of %f sec ( 1.0 / %d)' %
(self._incr, blenderapi.getfrequency()))
[docs] def update (self):
current_time = blenderapi.frame_time()
if current_time == -1:
self.time = self.time + self._incr
else:
self.time = current_time + self._time_offset
self._update_statistics()
[docs] def name (self):
return 'Fixed Simulation Step'
@property
def real_time(self):
return time.time() - self._real_time_offset
@property
def mean(self):
return self._incr
[docs] def statistics (self):
return {
"mean_time" : self._stat_jitter.mean,
"variance_time": self._stat_jitter.variance,
"diff_real_time": self.time - time.time()
}
def _update_statistics(self):
if self._last_time == 0.0:
self._last_time = time.time()
else:
ds = time.time() - self._last_time
self._last_time = time.time()
self._stat_jitter.update(ds)
[docs]class TimeStrategies:
(BestEffort, FixedSimulationStep) = range(2)
internal_mapping = {
BestEffort:
{ "impl": BestEffortStrategy,
"python_repr": b"TimeStrategies.BestEffort",
"human_repr" : "Best Effort"
},
FixedSimulationStep:
{ "impl": FixedSimulationStepStrategy,
"python_repr": b"TimeStrategies.FixedSimulationStep",
"human_repr": "Fixed Simulation Step"
}
}
[docs] @staticmethod
def make(strategy, use_relative_time):
try:
return TimeStrategies.internal_mapping[strategy]["impl"](use_relative_time)
except KeyError:
return None
[docs] @staticmethod
def python_repr(strategy):
try:
return TimeStrategies.internal_mapping[strategy]["python_repr"]
except KeyError:
return None
[docs] @staticmethod
def human_repr(strategy):
try:
return TimeStrategies.internal_mapping[strategy]["human_repr"]
except KeyError:
return None
[docs]def time_isafter(t1, t2):
""" Returns true if t1 > t2 in morse_time. Returns false otherwise """
return t2 - t1 < blenderapi.persistantstorage().time.mean / 2