SOLVED Get parameter value at given time.

Dear community,

What is a good way to evaluate a parameter value at a given frame from a threaded context?

I need to get the value of a parameter at a given time within the ModifyObject() function of a class derived from ObjectData. I need to do this when using bucket rendering. The required time is stored in a BaseTime member. The solution I've found so far is to use ExecutePasses(bt, true, true, true, BUILDFLAGS::EXTERNALRENDERER). However, this causes issues because the ModifyObject() function is called again. Additionally, I would like to avoid calculating the entire scene again. Is there a better way to do this?

On a related note, I get a debugbreak when calling ExecutePasses, is this due to the way I'm calling it?

Thank you for your attention,
danniccs

Hello @danniccs,

thank you for reaching out to us. Your question is a bit ambiguous. You do not mention if the parameter you want to retrieve the value for is animated or not.

  1. You can interpolate a curve with CCurve::GetValue(). The function returns a float, which means that you must deal with things as Vectors on a sub-channel (in the case of vectors: component) level. Cinema also expresses integers as floats on the level of CKey values. Additionally, multiple track types cannot be interpolated, for example, tracks for Boolean values.
  2. When you have no tracks, curves, and keys, and instead are talking about parameters that are driven by simulation procedures, then use must indeed use ExecutePasses, in some cases you might even have to simulate multiple frames. I would recommend cloning the document you are executing the passes on, to avoid feedback loops.

To get a better answer, I would recommend posting executable code and providing more information on the scope of parameter values you want to evaluate.

Cheers,
Ferdinand

Hi @ferdinand, thanks a lot for the answer.

The parameter I am trying to evaluate is actually a BaseLink to a BaseObject representing a mesh. As such, I cannot use the CCurve::GetValue approach directly on the parameter. However, if possible I want to avoid running ExecutePass, since this would probably be quite slow.

I realized I can solve my issue if I can get access to the world matrix of the mesh at the given time. Is there a way to get the interpolated world matrix at a specific time from a BaseObject? If not, can I get the CTrack associated with that world matrix? I could then interpolate the values between keys and use the resulting matrix. If neither of those is possible I will probably end up using ExecutePasses.

Thanks again for the reply,
Daniel

@danniccs said in Get parameter value at given time.:

The parameter I am trying to evaluate is actually a BaseLink to a BaseObject representing a mesh. As such, I cannot use the CCurve::GetValue approach directly on the parameter.

BaseLink is a discrete parameter, as you cannot interpolate between two links. Nevertheless, you should be able to find out what link is represented by a CCurve "curve", as you still have keys.

  1. Use CCurve.FindKey() to determine the key which is holding the data for the current frame. This would be the key to the left, except if there is none, in which case it's the key to the right.
  2. Use CKey.GetGeData() to read the data. This is not a float or long value - in this case, it's a BaseLink.

Here is some Python script that reads the currently linked camera from a (selected) stage object:

import c4d
from c4d import gui


def PrintLinkKey (currentTime, obj, parameterDesc):
    track = obj.FindCTrack(parameterDesc)

    if track == None: return
    curve = track.GetCurve()
    cat = track.GetTrackCategory()
    if cat != c4d.CTRACK_CATEGORY_DATA: return

    key = None
    currentKey = curve.FindKey(currentTime, c4d.FINDANIM_LEFT)
    if currentKey:
        key = currentKey['key']
    else:
        currentKey = curve.FindKey(currentTime, c4d.FINDANIM_RIGHT)
        if currentKey:
            key = currentKey['key']
            
    if key != None:
        data = key.GetGeData()
        print (data)


def main():
    if op == None : return

    descCamLink = c4d.DescID(c4d.DescLevel(c4d.STAGEOBJECT_CLINK, c4d.DTYPE_BASELISTLINK, 0))
    PrintLinkKey (doc.GetTime(), op, descCamLink)


if __name__=='__main__':
    main()

(Yes, I know you tagged C++ but you can use the same calls and logic - not going to create a full plugin for that...)

I might not have been clear in my previous post. The parameter is a BaseLink to a mesh, and that parameter is not animated. It should always be a link to the same BaseObject. The mesh itself has animated parameters, and I need to access the mesh object at a specified time t. At the very least, I need to know its position, rotation and scale values at t.

I have tried using CCurve.FindKey() and CKey.GetGeData()/CKey.GetValue() to get the mesh position at t, but the parameters in obase.h don't have tracks themselves. What I was wondering is if there is a way to get the world matrix of an object at time t without using ExecutePasses().

@danniccs said in Get parameter value at given time.:

I might not have been clear in my previous post. The parameter is a BaseLink to a mesh, and that parameter is not animated. It should always be a link to the same BaseObject. The mesh itself has animated parameters, and I need to access the mesh object at a specified time t. At the very least, I need to know its position, rotation and scale values at t.

Okay, you are right, that wasn't totally clear 😇
But anyway, in my understanding now: you have some undefined things that drive a value that does not have a track and therefore neither a curve nor keys, and you want the value itself.
In my experience C4D will evaluate such a situation dynamically (unless cached) since there may be any kind of stuff used as driver: Python tags, XPresso tags, dynamics, particles, cloners, etc etc. So, unless you replicate the evaluation in your own code, you cannot access the result without actually allowing C4D to execute the evaluation.

Maybe Ferdinand will have a better idea once you provide the details he asked for, I am not yet seeing quite what you are going for.

Hi @danniccs,

So, effectively you do want to evaluate the global transform T of an object O at some arbitrary time t? I am afraid ExecutePasses is indeed the only viable solution here, as the transform of an object can be influenced indirectly by many things, as for example ancestor nodes, constraints, or simulations. There is no meaningful way to resolve this other than executing the passes.

This is also a case where you should not execute the passes for frame n, but all frames up to n, when you want to support simulations. In order to avoid extreme overhead by doing this over and over in ModifyObject, you should cache such information. In the simplest form this could be a button 'Build Time Cache' in the GUI of the object.

All in all, this also sounds very much like a plugin that is in violation of design principles for ObjectData plugins. An object that can look omniscient into the past and future for any object in the scene, is in principle a quite expensive idea; it does not really matter that it is only the transform you want to know. This also could open a whole can of worms of feedback loops, depending on what you intend to do with that transform. When the vertices of object P rely on the transform T of object Q, and T relies in turn in some form on the vertices of P, you are going to have a problem, especially when you make this also time dependent.

I would also first check if there are simpler ways to achieve a similar effect. We cannot provide support on designing plugins, and we also do not known the greater context here, but I am sure that there is a simpler or at least more performant solution.

Cheers,
Ferdinand

Hi @ferdinand and @Cairyn, thanks for the answers.

There may be a simpler way to achieve an effect similar to what we want (for example, by caching the world matrix at the time t) but I'm still thinking of alternate ways to be able to access the full mesh information at time t. Caching the full mesh is probably not a good option since we would have to update that cache every time the mesh is changed at any time before t (since this might change the mesh at t). A possible option is to restrict the usability a bit and always evaluate the mesh at time 0, I'm checking if this is something we can do.

Feedback loops could be an issue, but it should never happen that (using the example @ferdinand wrote) P relies on T and T also relies on P in this specific plugin. Also, t should always be a time before the current time, although that might not alleviate this particular problem.

In any case, I'm going to go ahead and mark this as solved, I think I got all the information I need to find a solution.

Thank you both very much for helping me out,
Daniel