Creating mesh sequence from custom meshes using python

When you bake a fluid simulation down to a mesh sequence in blender you can play it back just fine and export it out using alembic and import into ue5 as a geometry cache and everything works great.

What I’m trying to do is take meshes I’ve created and convert them into a mesh sequence so they can be played back as if they were a normal baked simulation.

To clarify further, I have manually sculpted several “frames” of a wave simulation where each mesh is a frame of the animation. Each mesh is labeled “1”, “2”, “3”, and so on according to which frame of the animation it belongs to.

I know I can key frame visibility and toggle it on and off but thats not efficient nor is it how a mesh sequence is normally handled in alembic.

I’ve tried everything including trying to use ChatGPT to figure it out with no results. I will happily pay someone to create a script that does this. Can anyone help?

Edit: If this isn’t possible with python alone and requires c++ or requires making a custom addon for blender please let me know where I can go to pay someone to make that for me. I’m tired of messing around with it and just want to throw money at the problem to make it go away.

1 Like

This is something I have worked with a lot, so I’ll add a basic outline for how this can be done through Python scripting.

This is using Blender 3.4.1, but should also work across versions 2.80 - 3.5.

Blend file and script: mesh_sequence_script.blend (1.0 MB)

Exported Alembic file of the sequence: alembic_mesh_sequence.abc (108.1 KB)

Script with notes:

# Usage and notes: 
#    - Press the 'Run Script' play button and scrub through the timeline.
#      The script should only be run once per Blender session. Any any changes to the script
#      are made, save the Blend file, re-load, and run the script again.
#    - Open the Blender System Console window to see any error output.
#      On Windows OS, the Blender System Console can be opened with 'Window > Toggle System Console'
#    - The object can be exported as an Alembic animated mesh. However, there is an odd quirk of
#      Blender where it will not recognize this as an animated mesh by default. To get Blender to
#      recognize this as an animated mesh, a modifier needs to be added to the MeshSequence object.
#      For example, a smooth modifier with 0 smoothing can be used.   

import bpy

# This function will be set up to be run every time the frame changes
# At the end of the script, this function will be registered as a frame_change_pre handler
def update_mesh(scene):
    # Get the frame object according to the frame number
    frame_object_name = str(scene.frame_current)
    frame_object = bpy.data.objects.get(frame_object_name)
    
    if frame_object is None:
        # If an object does not exist for this frame, do nothing
        return
    
    # This object will be used to hold the updated geometry
    mesh_sequence_object = bpy.data.objects.get("MeshSequence")
    
    # Update the geometry of the mesh sequence object
    mesh_sequence_object.data = frame_object.data
    

# Register a function to be called every time the frame changes
bpy.app.handlers.frame_change_pre.append(update_mesh)

Hope this helps with your project!

3 Likes

When I import your sequence into UE5 it tries to import as a static mesh but when I set it as a geometry cache and correct the other settings it works as expected.

However when I run the script and export it myself in blender 3.4.1 the import settings recognize its a geometry cache but for some reason each object is its own track with a start and end frame of 1.

I’ve tried various export settings with the same result. Am I missing something?

Also I really want to thank you. This is the closest I’ve been to being free of this issue thats been plaguing me forever. I have spent hundreds of hours sculpting thousands of frames of various explosions, fires, and random 3d vfx just to have them sit there collecting digital dust because I couldn’t use them in my game. This brings me a step closer to finally implementing them so if I can send you some cash so you can buy yourself a drink or something as a way of saying thanks please let me know!

1 Like

Hey, I just tested the script and was able to reproduce the export issue. Looks like this was my mistake in the post.

I had originally posted a more complicated script and had it edited to be more simple - but it looks like this caused problems in the export.

Here is the original Blend file and script that I used for export:

mesh_sequence_script.blend (1.0 MB)

# Usage and notes: 
#    - Press the 'Run Script' play button and scrub through the timeline.
#      The script should only be run once per Blender session. Any any changes to the script
#      are made, save the Blend file, re-load, and run the script again.
#    - Open the Blender System Console window to see print output.
#      On Windows OS, the Blender System Console can be opened with 'Window > Toggle System Console'
#    - The object can be exported as an Alembic animated mesh. However, there is an odd quirk of
#      Blender where it will not recognize this as an animated mesh by default. To get Blender to
#      recognize this as an animated mesh, a modifier needs to be added to the MeshSequence object.
#      For example, a smooth modifier with 0 smoothing can be used.   

import bpy

# This function will be set up to be run every time the frame changes
# At the end of the script, this function will be registered as a frame_change_pre handler
def update_mesh(scene):
    # Get the frame object according to the frame number
    frame_object_name = str(scene.frame_current)
    frame_object = bpy.data.objects.get(frame_object_name)
    
    if frame_object is None:
        # If an object does not exist for this frame, do nothing
        return
    
    # Extract the geometry (vertices and polygons)
    vertex_coordinate_list = [(frame_object.matrix_world @ v.co) for v in frame_object.data.vertices]
    polygon_index_list = []
    for polygon in frame_object.data.polygons:
        face_indices = []
        for vertex_index in polygon.vertices:
            face_indices.append(vertex_index)
        polygon_index_list.append(face_indices)
        
    print("Updating frame", scene.frame_current)
    print("The vertex list is:")
    print(vertex_coordinate_list)
    print("The polygon list is:")
    print(polygon_index_list)
    print()
        
    # This object will be used to hold the updated geometry
    mesh_sequence_object = bpy.data.objects.get("MeshSequence")
    
    # Update the geometry of the mesh sequence object
    mesh_sequence_object.data.clear_geometry()
    mesh_sequence_object.data.from_pydata(vertex_coordinate_list, [], polygon_index_list)
    
# Register a function to be called every time the frame changes
bpy.app.handlers.frame_change_pre.append(update_mesh)

These are the settings I used for export (just the Only Selected Objects option enabled):

Hope this works out now. No cash needed, just glad to help!

2 Likes

I just tried it with a quick 20 frame flame animation I made. As far as I can tell it works flawlessly. It looks exactly like the old version I did using UE4’s material mesh flipbook nonsense but way more efficient and now I have complete control over it through blueprints so I can start stacking these geo caches to make true 3D vfx.

Thank you again. Your solution of manually scrubbing through the timeline to update the geometry was really smart. It never even occurred to me that was a possible solution.

Do you do commissions? I’d love to hire you to add some extra functionality to this script in the near future. If you’d be interested in that feel free to add me on discord (Tech Tree#4058)

1 Like

Glad to help, that looks awesome. I’m sorry to say that I’m not available for work at the moment.

Something else to look into outside of scripting could be to use Blender’s Geometry Nodes features. I’m not very knowledgeable of this area, but I assume this type of problem would be good in a node setup. For example, I think the node setup would be something like this

Get current frame > Convert frame to string (object name “1”, “2”, “3”, …) > Get object from name > output geometry

It may help to ask about this with the geometry-nodes tag.

2 Likes

I looked into geometry nodes when they first came out but it wasn’t particularly useful for what I needed back then but they got a lot of updates since then so its definitely on my list of things to look at.

I think for right now I’m going to export out all my animations and get everything working on Unreal Engine’s side then figure out how to modify this script to pack UV and vertex color information into the alembic file, if it isn’t already there. I think geometry nodes might actually be very helpful in the creation process of these pseudo-simulations. I’m just excited I can finally resume work on this part of my game.

Thanks again for your help!

I’ve been looking into this myself. There’s a Geo Nodes method on YouTube, it works incredibly well, I’ve tested with uv and textures and it’s all working as intended.

Unfortunately for me, geo nodes do not handle normal data. The Blender developers haven’t decided how it should be handled. So the ‘realize instances’ node just throws normal data away. I’m attempting to modify this script to retain normals, but I’m not very proficient at scripting in Blender.

1 Like