Dealing with Time¶
How do I determine the current time?¶
Actually this is a fairly complex question. As a user the answer is simple – the current time is shown on the time slider and you scrub it to change time. But when writing plug-ins you have to realize that nexus takes a much more holistic view of time and simple linear thinking can cause problems.
For commands that perform edits, or items that draw themselves in 3D, the current global time can generally be used. This is maintained as part of the selection system along with all the other user-controlled state that affects UI interaction.
C++
CLxUser_SelectionService sel_svc;
double time = 0.0;
time = sel_svc.GetTime ();
Python
lx.service.Selection ().GetTime ()
As part of modifier evaluation however, time is not universal. Modifiers can be evaluated at arbitrary times and should not rely on the time provided by the selection system. Instead, they should allocate time as an input to the modifier, allowing the evaluation time to be determined.
C++
LxResult
eval_Alloc (
ILxUnknownID item_obj,
unsigned index,
ILxUnknownID eval_obj,
void **ppvObj)
{
CLxUser_Evaluation eval (eval_obj);
// CLxUser_Attributes
_attr.set (eval_obj);
_time_idx = eval.AddTime ();
return LXe_OK;
}
void
mod_Evaluate ()
{
double time = 0.0;
time = _attr.Float (_time_idx);
}
Python
def eval_Alloc (self, item_obj, index, eval_obj):
eval = lx.object.Evaluation (eval_obj)
self.attr = lx.object.Attributes (eval_obj)
self.time_idx = eval.AddTime ()
def mod_Evaluate (self):
time = self._attr.GetFlt (self.time_idx)
How do I know when the current time changes?¶
Getting notified of global state changes is done though a Global Listener Object. This is an object that you create and export to modo, and your methods will be called when events happen. The easiest way to create a one-off object of this type is to use a Singleton Polymorph. The selevent_Time() method will be called with the new time as the user scrubs the timeline.
C++
class CTimeChangeTracker :
public CLxImpl_[[SelectionListener Interface|SelectionListener]],
public CLxSingletonPolymorph
{
public:
LXxSINGLETON_METHOD;
CTimeChangeTracker ()
{
AddInterface (new CLxIfc_[[SelectionListener Interface|SelectionListener]]<CTimeChangeTracker>);
}
void
selevent_Time (
double time) LXx_OVERRIDE
{
current_time = time;
}
};
Since this is a singleton you’d store it as global state in your plug-in.
C++
static CTimeChangeTracker *time_tracker = 0;
The first time you need to start tracking time you create the object and register it with the ListenerService. Do not do this in your initialize() function since that may be too soon.
C++
When you are done tracking time changes you should unregister your listener.
C++
CLxUser_ListenerService ls;
ls.RemoveListener (*time_tracker);
delete time_tracker;
How can my channel modifier read inputs at different times?¶
This is done by using methods on the Evaluation Interface. You need to cache this interface as part of your member data in your Allocate() method.
C++
LxResult
CTimeOffset::cmod_Allocate (
ILxUnknownID cmod,
ILxUnknownID eval,
ILxUnknownID item,
void **ppvData)
{
m_eval.set (eval);
...
}
How can I make sure modifier is evaluated for every time change?¶
Time can be allocated as an input for the modifier. As changes to input channels invalidates modifiers and causes them to be re-evaluated, allocating time as an input to the modifier will cause the modifier to be invalidated and re-evaluated whenever time changes.
Allocate time as an input channel:
C++
CLxUser_Evaluation ev (eval);
idx_currentTime = ev.AddTime();
In the modifier Evaluate function, read the current time.
C++
CLxUser_Attributes at (attr);
double currentTime = at.Float(idx_currentTime);