Pinning and moving vertices of cloth at arbitrary frame sequences, with updated physics

Hi all, I am currently using Blender 2.82a and am trying to simulate actions on a cloth, where each action is parameterized by a cloth, a set of vertex indices to pull upwards, and a set of frames. The broader context is to enable learning from an image to an appropriate action. Here’s an example of an action sequence:

  • Frames 0-49: let cloth settle. Get image at frame 49.
  • Frames 50-99, given image at frame 49, apply action by pinning vertices on frame 50, moving them with armatures, then releasing the vertices.
  • Rinse and repeat, to get a sequence of actions and simulator updates, where actions are determined based on Blender’s images of updated cloth and scene items.

There is a related question that I recently posted on StackExchange:

I managed to partially resolve it [and will update that question], but am now running into unexpected physics. I have reduced the code to a minimal working example which you can find in this pastebin (https://pastebin.com/9Gd9WKhU) of around 175 lines, with boilerplate code at the top, and the key stuff in define_action() and advance_simulator(). I just run blender -P script-name.py.

Code summary: I initialize the cloth at a height of 0.200m, and let the cloth settle for the first 50 frames. Then for the next 50 frames, I define keyframes that move the lower left corner (with respect to the camera view) up by 0.300m. This almost works, except that at frame 51, the lower left vertex immediately jumps from the height at rest (which is 0.034m) to 0.200m, the original height. Then, over the next 49 frames, it correctly goes up by 0.300m, so that it reaches 0.500m. But, this is undesirable: the height should go from 0.034m to 0.334m over frames 51 to 100. The code saves the images at each frame, and I’ve printed out the positions. The debug prints say:

after frame 50, vertex idx 0: <Vector (-1.0000, -1.0000, 0.2000)>, <Vector (-1.0000, -1.0000, 0.0340)>

after frame 51, vertex idx 0: <Vector (-1.0000, -1.0000, 0.2000)>, <Vector (-1.0000, -1.0000, 0.2000)>

after frame 100, vertex idx 0: <Vector (-1.0000, -1.0000, 0.2000)>, <Vector (-1.0000, -1.0000, 0.5000)>

where the second Vector is of interest, because that’s directly from this code in my pastebin:

depsgraph = bpy.context.evaluated_depsgraph_get()
cloth_upd = cloth.evaluated_get(depsgraph)
v_c  = [cloth.matrix_world @ v.co for v in list(cloth.data.vertices)]
v_cd = [cloth_upd.matrix_world @ v.co for v in list(cloth_upd.data.vertices)]
print('after frame {}, vertex idx 0: {}, {}'.format(f, v_c[0], v_cd[0]))

which queries the updated cloth in cloth_upd. I wonder if this is related to the issue I’m seeing?

You can also see the GIF that I’ve made of the full 100 frames, showing that the immediate jump halfway through is causing bad physics:

TL;DR: physics of pinning the cloth appears to use the cloth vertices from the very beginning at frame 0, rather than the current frame, causing unexpected physics.

Does anyone have any advice, either specific or at a high level? Also, please let me know if there is other information I need to provide, or if this isn’t the right place to ask the question. This topic of using Blender as a simulator has frequently come up in my collaborations so I feel like this may be of wider interest. Thanks, and I hope you are safe.

2 Likes

I don’t know if you’re still struggling with this, but I tried your pastebin script in blender 2.90.0 (Linux) and it seems to work perfectly.

Edit: Well, the animation in the viewport works perfectly, but the images the script generates show the bug, so I’m not sure what’s happening there.

Edit2: Sorry for the misunderstanding on my part, while the jump isn’t there in the viewport I now realize that it’s also the pinning that’s the problem, which is still there. Turns out you can disregard the whole post :stuck_out_tongue:

Despite my confused start, I did some digging and found out what’s happening and a potential fix for it.
I hope this helps.

It’s not a bug with physics at all. The jumping behavior is happening because the vertex pinning works from the original state of the mesh, so when you let the cloth settle and then apply the pinning it immediately returns the vertex to it’s original position in 1 keyframe (hence the jump). In order to avoid the jump you have to offset the bone’s starting pose location by the updated position of the cloth vertex. You can do that by adding something like this before the initial keyframe:

# Keyframe inserts need to use `b.[...]` attributes, like `b.location`.
depsgraph = bpy.context.evaluated_depsgraph_get()
cloth_upd = cloth.evaluated_get(depsgraph)
v_c  = [cloth.matrix_world @ v.co for v in list(cloth.data.vertices)]
v_cd = [cloth_upd.matrix_world @ v.co for v in list(cloth_upd.data.vertices)]
arm.pose.bones["Bone"].location = Vector((0, (v_cd[vertex_idxs[0]][2] - v_c[vertex_idxs[0]][2]), 0))

This doesn’t work for the viewport playback, but the rendered images are much improved.

1 Like