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);