Any way to evaluate with depsgraph for a given (*not* current) frame non-destructively?

In short: I want to make a function that, given a frame (not the current frame, just any frame) float and an object/posebone, can return the world matrix of said thing without affecting the scene in any way. The depsgraph seems like it should be the solution, however I don’t see how I can use that without changing the frame.
Is this even possible?

I’ve decided to update the old motion trail addon by Bart Crouch that was in master, see https://github.com/a-One-Fan/Blender-Motion-Trail-Update

Sample code
def get_matrix_any_depsgraph(frame, target, context):
	oldframe = context.scene.frame_float
	context.scene.frame_float = frame

	dg = context.evaluated_depsgraph_get()
	
	isBone = type(target) is PoseBone
	ob = target.id_data if isBone else target
		
	evalledOb = ob.evaluated_get(dg)

	if isBone:
		resMat = evalledOb.matrix_world @ evalledOb.pose.bones[target.name].matrix
	else:
		resMat = evalledOb.matrix_world

	context.scene.frame_float = oldframe

	return resMat

So far I’m making good progress (feel free to use the addon if you want, it’s in a good state), however this issue has stumped me. The world matrices are needed to calculate the motion trail, and calculating it should work without affecting the scene in any way.
The docs page on the depsgraph mentions animation, but after reading it I didn’t see anything for using it for a different frame than the current, and ctrl+f for frame in fact yields no results (other than the extra links at the bottom).

So far I have tried the following:

  • Ignore depsgraph and cobble world matrices together from evaluating keyframes and calculating parent-child relations in python manually, basically writing my own depsgraph.
    This works for the cases that I’ve made code for, but it seems redundant (the DG already does this?), it would be a huge amount of work to support more things, and I’m already having headaches with bone hierarchies!
  • scene.frame_set() - updates too many things, too slow, worse than the variant below.
  • scene.frame_float = frame (or just scene.frame = frame) - this performs much better than frame_set, however setting the frame to something then back to the original still triggers an update to the scene. Is this a bug? Generally I would assume changing this then setting it back shortly after should not cause much of an update (else why have frame_set?). It kills unkeyed changes, and worse, if this is done while an animation is playing it will freeze the current frame, either causing stutters or completely stopping the animation from progressing depending on how often it’s called.
  • Make context override with different frame, get DG from that - can’t get DG from context override :frowning:

Iceythe on the Blender artists forums was kind enough to look into this, but told me some not very encouraging things. If the internal motion trail doesn’t have this quite solved AND also relies on some internal C/++ methods not available in bpy, is there even anything I can do? I guess I’m asking for a second opinion on what the person said…

In a debug build, I noticed that even passing the render velocity vector takes a step back when the render starts. So it seems like no way (but I’m not directly connected with this, so this is not the opinion of the developer)

1 Like