Get a Deformed Point Position Over Time

Hello,
I am trying to get a deformed point position at a different frame. Is this possible?

I know about doing this for CCurves from this post, CCurve.GetValue() with Track Before & After Functions using CTrack.Remap(), but can it be done with a BaseObject.GetDeformCache()?

Thank you!

I found this post on CGSociety where @m_adam shares a link to this file: basedocument_read_animated_mesh.py. Awesome stuff!

I've got it working, but I'm curious: do I need to use doc.SetTime() to get all the values over time at once?

This seems like a resource-expensive process for my project because I'm using it in a tag.

Hello @blastframe,

thank you for reaching out to us. To read the vertex positions of a deformed point object, one must use the deform cache as pointed out by yourself.

The deform cache will however only reflect the current document state, e.g., the current frame of the document. To evaluate a document at a frame/time X which is different from its current one, one must use BaseDocument.ExecutePasses() as shown in the script posted by @m_adam. And yes, ExecutePasses() is a expensive call since it will rebuild the whole document. Depending on your document setup, it might even be necessary to not just execute the frame X, but instead crawl towards frame X when the document contains objects that depend on their previous frame state, e.g., particles. Point deformations usually do not exhibit that behavior, but there is nothing which would prevent someone from writing such a plugin and your user using it.

It depends on your exact scenario what to do. Do you want to look into the 'past' or the 'future'? Do you want to support timeline scrubbing? These self-dependent things are usually a pain in the *** to build, but one cheap trick is to simply cache yourself what is needed (when you only want to look into the past, i.e., frames that already have been played by the user).

Cheers,
Ferdinand

@ferdinand Thank you for the prompt reply.

In my project I need to get the entire timeline and be able to scrub it. Can you please explain the "crawl" method more?

I was able to remove the EventAdd from my expression tag. I tried removing ExecutePasses() and it the deformation animation froze.

Hi,
with crawl towards frame X I meant the following (as sort of pseudo code):

# Execute the passes up to frame X
for f in range(x):
    time = FrameToBaseTime(f, doc)
    document.SetTime(time)
    document.ExecutePasses(time, *myData)

# Evaluate the point object.
...

So, we are not only executing the pass for frame x, but also for all frames before it. However, as previously stated, this is only necessary when you expect the scene state to be dependent on its prior states. When you have only position animations in the document, you can jump to frame X right away. But when you have for example a particle system in it, you might have to chew trough all previous frames first, unless these particles are cached.

Cheers,
Ferdinand

@ferdinand Hi, thank you for the reply. I won't need particles. This is a polygon object with a deformer whose point positions I need to get over the Preview Range.

Isn't the crawl you're describing the same as the range loop in the basedocument_read_animated_mesh.py? My question is: is there a more performant way if this needs to be calculated on each frame?

If this is the only way, since the animation keyframes would be on another object (the deformer, joints, or a controller) and not a point level animation, I believe the position will depend upon all of the ExecutePasses (animation, expressions, and caches) as in the example. Unless the deformed cache stores these points between playback?

Thank you.

Hello @blastframe,

yes, there is a loop in that script which is like my pseudo-code one. If they are doing the same thing is a bit a point of view. The script evaluates each frame to do something each frame, while my pseudo code evaluates all frames to do something after that. So, for clarity again as pseudo code:

# Evaluate all frames and do something for each frame, i.e., what Maxime's
# script does.
for frame in FRAMES:
    for item in document:
        item = evaluate(item, frame)
        DoSomthing(item)

# Evaluate all frames and after that do something, i.e., what I am proposing.
# From a more abstract point of view, both are doing the same thing. The only
# difference here is that we are not interested in the intermediate results,
# only in updating an item with is prior state: `item = evaluate(item, frame)`
for frame in FRAMES:
    for item in document:
        item = evaluate(item, frame)

DoSomthing(someItem)

If you say, you are fine with not respecting this than this will simplify things. And most things that impact (deform) caches, won't be impacted by that. But there are direct cases which are affected, e.g. cloth simulations, some deformers like for example the collision the deformer and more. And indirect cases like when you have a particle system whose state over three corners will impact some deformation.

And yes, this is very taxing to do, because you must evaluate a lot of stuff each frame. And you will have likely to execute all passes (animation, expressions, and caches); in the end it depends on how a specific scene is setup. The performant way to do this is cache the required data yourself. Like for example the collision deformer can do it; i.e., you click a button and then all data is calculated which then later is only played back.

I would not say that it is impossible to do this in Python, but you will lack some important types to store such data in Python that are exposed in our C++ API. Python is also in general not well suited for such task, due to its inherent slowness of iteration. But that does not mean that with some smart thinking you could not make it work in Python in a performant way. This all depends on what you are exactly trying to do and is more of a general complexity of an algorithm or project design question (and therefore out of scope of support).

The bottom line is that your initial question of Get a Deformed Point Position Over Time might contain more complexity than anticipated by you, because it implies something is the recursive function itself and time. Which is always a pain in the *** to handle.

Cheers,
Ferdinand