2014-12-12 20:50:35 +00:00
|
|
|
# updateloop.nas - Generic Nasal update loop for implementing sytems,
|
2014-11-29 10:53:26 +00:00
|
|
|
# instruments or physics simulation.
|
|
|
|
#
|
|
|
|
# Copyright (C) 2014 Anton Gomez Alvedro
|
|
|
|
#
|
|
|
|
# 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.
|
|
|
|
|
2014-12-12 20:50:35 +00:00
|
|
|
|
|
|
|
##
|
|
|
|
# The UpdateLoop accepts a vector of Updatable components that it will call
|
|
|
|
# at regular intervals. In order to define custom objects to be controlled by
|
|
|
|
# the loop, the Updatable interface must be implemented:
|
2014-11-29 10:53:26 +00:00
|
|
|
#
|
2014-12-12 20:50:35 +00:00
|
|
|
# var MyComponent = {
|
2014-11-30 09:46:26 +00:00
|
|
|
# parents: [Updatable],
|
2014-12-12 20:50:35 +00:00
|
|
|
#
|
|
|
|
# reset: func {
|
|
|
|
# ...
|
|
|
|
# },
|
|
|
|
# update: func(dt) {
|
|
|
|
# ...
|
|
|
|
# },
|
2014-11-29 10:53:26 +00:00
|
|
|
# ...
|
|
|
|
# };
|
|
|
|
|
2014-11-30 09:46:26 +00:00
|
|
|
var Updatable = {
|
2014-12-12 20:50:35 +00:00
|
|
|
|
|
|
|
##
|
|
|
|
# When reset() is called, the component shall clean all internal state and
|
|
|
|
# reinitialize itself as if starting from scratch.
|
|
|
|
#
|
|
|
|
# Reset will be called automatically after a teleport, and then on demand
|
|
|
|
# when UpdateLoop.reset() is explicitly called.
|
|
|
|
|
2014-11-29 10:53:26 +00:00
|
|
|
reset: func { },
|
|
|
|
|
2014-12-12 20:50:35 +00:00
|
|
|
##
|
|
|
|
# Called at regular intervals from the UdateLoop.
|
|
|
|
# dt: Elapsed time in seconds since the last call.
|
2014-11-29 10:53:26 +00:00
|
|
|
|
2014-12-12 20:50:35 +00:00
|
|
|
update: func(dt) { },
|
2014-11-29 10:53:26 +00:00
|
|
|
};
|
|
|
|
|
2014-12-12 20:50:35 +00:00
|
|
|
##
|
|
|
|
# Wraps a set of user provided Updatable objects and calls them periodically.
|
2014-11-29 10:53:26 +00:00
|
|
|
# Takes care of critical sim signals (reinit, fdm-initialized, speed-up).
|
|
|
|
|
|
|
|
var UpdateLoop = {
|
|
|
|
|
|
|
|
# UpdateLoop.new(components, [update_period], [enable])
|
|
|
|
#
|
|
|
|
# components: Vector of components to update on every iteration.
|
2014-12-12 20:50:35 +00:00
|
|
|
# update_period: Time in seconds between updates.
|
|
|
|
# enable: Enable the loop immediately after creation.
|
|
|
|
# ignore_fdm: Do not wait for the fdm to be ready for starting the loop.
|
2014-11-29 10:53:26 +00:00
|
|
|
|
2014-12-12 20:50:35 +00:00
|
|
|
new: func(components, update_period = 0, enable = 1, ignore_fdm = 0) {
|
2014-11-29 10:53:26 +00:00
|
|
|
|
|
|
|
var m = { parents: [UpdateLoop] };
|
|
|
|
m.initialized = 0;
|
|
|
|
m.enabled = enable;
|
2014-12-12 20:50:35 +00:00
|
|
|
m.ignore_fdm = ignore_fdm;
|
2014-11-29 10:53:26 +00:00
|
|
|
m.update_period = update_period;
|
|
|
|
m.time_last = 0;
|
|
|
|
m.sim_speed = 1;
|
|
|
|
m.components = (components != nil)? components : [];
|
|
|
|
|
|
|
|
m.timer = maketimer(update_period,
|
|
|
|
func { call(me._update, [], m) });
|
|
|
|
|
|
|
|
m.lst = [];
|
|
|
|
|
|
|
|
append(m.lst, setlistener("/sim/speed-up",
|
|
|
|
func(n) { m.sim_speed = n.getValue() }, 1, 0));
|
|
|
|
|
2014-12-12 20:50:35 +00:00
|
|
|
append(m.lst, setlistener("sim/signals/reinit",
|
|
|
|
func(n) { m._on_teleport(n) }));
|
2014-11-29 10:53:26 +00:00
|
|
|
|
2014-12-12 20:50:35 +00:00
|
|
|
if (ignore_fdm or getprop("sim/signals/fdm-initialized")) {
|
|
|
|
m.reset();
|
|
|
|
enable and m.timer.start();
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!ignore_fdm) {
|
|
|
|
append(m.lst, setlistener("sim/signals/fdm-initialized",
|
|
|
|
func(n) { m._on_teleport(n) }));
|
|
|
|
}
|
2014-11-29 10:53:26 +00:00
|
|
|
|
|
|
|
return m;
|
|
|
|
},
|
|
|
|
|
|
|
|
del: func {
|
2014-11-30 09:46:26 +00:00
|
|
|
me.disable();
|
2014-11-29 10:53:26 +00:00
|
|
|
foreach (var l; me.lst) removelistener(l);
|
|
|
|
},
|
|
|
|
|
|
|
|
##
|
|
|
|
# Resets internal state for all components controlled by the loop.
|
|
|
|
# It is of course responsibility of every component to clean their internal
|
|
|
|
# state appropriately.
|
|
|
|
|
|
|
|
reset: func {
|
|
|
|
me.time_last = getprop("/sim/time/elapsed-sec");
|
|
|
|
|
|
|
|
foreach (var component; me.components)
|
|
|
|
component.reset();
|
|
|
|
|
|
|
|
me.initialized = 1;
|
|
|
|
},
|
|
|
|
|
|
|
|
##
|
2014-12-12 20:50:35 +00:00
|
|
|
# The loop will start updating the components under its control.
|
2014-11-29 10:53:26 +00:00
|
|
|
|
|
|
|
enable: func {
|
|
|
|
if (me.initialized) me.timer.start();
|
|
|
|
me.enabled = 1;
|
|
|
|
},
|
|
|
|
|
|
|
|
##
|
2014-12-12 20:50:35 +00:00
|
|
|
# The loop will freeze and its components will not get updates until
|
|
|
|
# enabled again.
|
2014-11-29 10:53:26 +00:00
|
|
|
|
|
|
|
disable: func {
|
|
|
|
me.timer.stop();
|
|
|
|
me.enabled = 0;
|
|
|
|
},
|
|
|
|
|
|
|
|
_update: func {
|
|
|
|
var time_now = getprop("/sim/time/elapsed-sec");
|
|
|
|
var dt = (time_now - me.time_last) * me.sim_speed;
|
|
|
|
if (dt == 0) return;
|
|
|
|
|
|
|
|
me.time_last = time_now;
|
|
|
|
|
|
|
|
foreach (var component; me.components)
|
|
|
|
component.update(dt);
|
|
|
|
},
|
2014-12-12 20:50:35 +00:00
|
|
|
|
|
|
|
_on_teleport: func(n) {
|
|
|
|
var signal = n.getName();
|
|
|
|
|
|
|
|
if (signal == "reinit" and n.getValue()) {
|
|
|
|
me.timer.stop();
|
|
|
|
me.initialized = 0;
|
|
|
|
}
|
|
|
|
elsif (me.ignore_fdm or signal == "fdm-initialized") {
|
|
|
|
me.timer.isRunning and me.timer.stop();
|
|
|
|
me.reset();
|
|
|
|
me.enabled and me.timer.start();
|
|
|
|
}
|
|
|
|
}
|
2014-11-29 10:53:26 +00:00
|
|
|
};
|