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 fromObjectData
. I need to do this when using bucket rendering. The required time is stored in aBaseTime
member. The solution I've found so far is to useExecutePasses(bt, true, true, true, BUILDFLAGS::EXTERNALRENDERER)
. However, this causes issues because theModifyObject()
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.
- 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. - 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 - 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
-
Hi @ferdinand, thanks a lot for the answer.
The parameter I am trying to evaluate is actually a
BaseLink
to aBaseObject
representing a mesh. As such, I cannot use theCCurve::GetValue
approach directly on the parameter. However, if possible I want to avoid runningExecutePass
, 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 theCTrack
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 usingExecutePasses
.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.
- 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.
- 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 att
.I have tried using
CCurve.FindKey()
andCKey.GetGeData()
/CKey.GetValue()
to get the mesh position att
, 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 timet
without usingExecutePasses()
. -
@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 timet
. 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 beforet
(since this might change the mesh att
). 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