1
$\begingroup$

I aim to do the following using Blender 2.82a and python: (a) create a mesh in a scene with modifiers and save it, then (b) in another python script, load that same mesh with the same modifiers applied, so that I don't need to re-call the same modifier code [while beyond the scope of this question, I've had trouble with bad physics from this approach].

After an online search, I found documentation for export scene and import scene, along with related StackExchange questions on exporting and importing to .obj files. Here is my minimal working example. I'm using a cloth modifier, but this is not specific to cloth. The TL;DR of the code: (1) clear the scene, (2) create plane and sub-divide it, (3) apply modifiers, (4) save the .obj file. Then, if I run it again with step = 2 in the main method, load the .obj file.

import bpy
import sys

def setup_scene():
    for block in bpy.data.meshes:
        if block.users == 0:
            bpy.data.meshes.remove(block)
    bpy.ops.object.mode_set(mode='OBJECT')
    bpy.ops.object.select_all(action='SELECT')
    bpy.ops.object.delete()

def make_cloth():
    """Make a plane and sub-divide, then add modifiers."""
    bpy.ops.mesh.primitive_plane_add(location=(0, 0, 0))
    bpy.ops.object.editmode_toggle()
    bpy.ops.mesh.subdivide(number_cuts=20)
    bpy.ops.object.editmode_toggle()

    # Problem: modifiers are not preserved when saving and loading.
    bpy.ops.object.modifier_add(type='CLOTH')
    bpy.ops.object.modifier_add(type='SUBSURF')
    bpy.ops.object.modifier_add(type='SOLIDIFY')
    return bpy.context.object

def save_cloth(cloth, path):
    """Save cloth state (it's the selected object) for loading later."""
    bpy.ops.export_scene.obj(filepath=path, use_selection=True, use_mesh_modifiers=True, keep_vertex_order=True)

def load_cloth(path):
    """Load the cloth state from a prior call."""
    bpy.ops.import_scene.obj(filepath=path)
    cloth = bpy.data.objects['Plane']
    print('selected objects: {}'.format(bpy.context.selected_objects))

    # We need to re-apply modifiers. But subsurf / solidify will mean bad physics,
    # arising from higher vertex counts even with these lines commented out.
    #bpy.ops.object.select_all(action='DESELECT')
    #bpy.context.view_layer.objects.active = cloth
    #cloth.select_set(True)
    #bpy.ops.object.modifier_add(type='CLOTH')
    #bpy.ops.object.modifier_add(type='SUBSURF')
    #bpy.ops.object.modifier_add(type='SOLIDIFY')
    return cloth

if __name__ == '__main__':
    # First run script with step = 1, then re-run the script again with step = 2:
    #   blender -P test-save-load-obj.py -- 1
    #   blender -P test-save-load-obj.py -- 2
    argv = sys.argv
    argv = argv[argv.index("--") + 1:]
    step = int(argv[0])
    setup_scene()
    path = 'cloth_to_save.obj'
    if step == 1:
        cloth = make_cloth()
        save_cloth(cloth, path)
    elif step == 2:
        cloth = load_cloth(path)

As mentioned in the comments in the main method, running the above with the step argument as "1" results in the modifiers correctly applied:

enter image description here

The code additionally saves two files: cloth_to_save.mtl and cloth_to_save.obj.

I then run blender -P test-save-load-obj.py -- 2. This will load the meshes. But it will not apply the modifiers:

enter image description here

The above way of saving and loading will correctly preserve the positions of the vertices. So that's good -- I just wonder about the modifiers.


Update 1 (05 June 2020): as batFINGER explains in the comments, use_mesh_modifiers=True is like clicking on apply in the Blender GUI, and it doesn't involve saving the modifier to the .obj file. Therefore, it's unlikely to be part of my hypothetical solution.

One reason for my confusion is that the documentation for the export method that I'm using:

enter image description here

says that setting use_mesh_modifiers=True will result in: "Apply Modifiers, Apply modifiers." I'm a little confused about the documentation. My thought after reading it is that the modifiers (such as the cloth, subsurf, and solidify ones I used in my minimum working example) will be applied or preserved in some way. However, since that's not the case with my code, is there something I'm missing? Feel free to also suggest other ways of saving and loading if those are better than my .obj method. For now, I was thinking of just doing the above but explicitly calling the modifiers again (i.e., just repeating the lines of code in make_cloth that make modifiers).

Related question: this one about exporting to non-Blender programs.


Update 2 (08 June 2020): I modified the script above to take command line arguments. In addition, I further investigated that the vertex count of the cloth will increase dramatically when we load it back in, even without re-applying the cloth or subsurf modifiers. After running blender -P test-save-load-obj.py -- 1 and going to edit mode, I see 484 vertices:

enter image description here

Yet, running blender -P test-save-load-obj.py -- 2 and going to edit mode, I see far more vertices by default even without re-applying modifiers (presumably because subsurf and solidify both increase vertex count implicitly?):

enter image description here

In practice, this results in different physics.

$\endgroup$
6
  • $\begingroup$ Apply modifiers does just what clicking apply up the modifier stack does. AFAIK modifiers do not get eported as "matching" obj file modifiers to be imported back as blender modifiers. Akin to using to_mesh or bmesh from_object to create a mesh with modifiers applied. $\endgroup$ – batFINGER Jun 3 '20 at 19:57
  • $\begingroup$ Consider using command line arguments if using CLI blender.stackexchange.com/questions/6817/… $\endgroup$ – batFINGER Jun 3 '20 at 20:11
  • $\begingroup$ Thanks for the pointers @batFINGER :) I use command line arguments for my normal usage. Just wanted to keep things simple here, but probably it would have been better to use command line arguments. Regarding modifiers, thanks for the clarifications about clicking the modifiers. I haven't used to_mesh or from_object before, I think. I can check those out. $\endgroup$ – ComputerScientist Jun 3 '20 at 20:39
  • $\begingroup$ Fixed script to take command line arguments. $\endgroup$ – ComputerScientist Jun 8 '20 at 12:31
  • $\begingroup$ On latest edit are your subsurf levels same for preview and render? Will account for difference if obj exporter uses render subsurf level, and preview levels is less. $\endgroup$ – batFINGER Jun 8 '20 at 12:42

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.