1
$\begingroup$

I'm writing a custom exporter that ultimately exports a FBX file and a set of render images.

Before that happens, the exporter traverses through the scene and modifies specific objects. For example, it applies rotations on the camera, moves meshes to world origin, modifies meshes. After that, it exports the FBX and calls to render stills.

After exporting and rendering, it undoes all the changes in the scene.

The problem is duplicating and effectively undoing all the changes has been difficult to maintain. The second problem is that the names of the exported objects must be the same as the objects in the scene; not "Cube.001".

I have so far:

with DuplicateScene(root_obj) as dup_root_obj:
    modify_cameras(dup_root_obj)
    modify_meshes(dup_root_obj)
    export_model(dup_root_obj)
    export_stills(dup_root_obj)

The problem, again, is that the exported FBX doesn't contain the original name as in the scene.

$\endgroup$
  • $\begingroup$ Each object in blender has a unique name (mostly). Duplicating scene, object etc will give you the .nnn extension. Suggest a script that appends the scene into a new empty file, make changes and then export. Or save file, make changes, export original scene, revert. $\endgroup$ – batFINGER Jan 11 '17 at 6:21
  • $\begingroup$ @batFINGER, suggest a script? I like the idea of appending the scene into a new empty file. $\endgroup$ – mrkotfw Jan 11 '17 at 8:01
  • $\begingroup$ This would be ideal for a batch script with blender in background mode. As long as the save file op isn't run it wont save your pre render changes. $\endgroup$ – batFINGER Jan 11 '17 at 12:36
  • $\begingroup$ How would this be easier running Blender in background mode, as opposed to exporting directly within Blender itself? $\endgroup$ – mrkotfw Jan 11 '17 at 19:32
  • $\begingroup$ @batFINGER, it looks like the solution to load files causes the context to be lost. I've looked over other answers and it seems difficult to reload the context. Is this true? $\endgroup$ – mrkotfw Jan 12 '17 at 6:49
0
$\begingroup$

I thought a little about what @batFINGER mentioned in the comments, and it got me thinking:

class DuplicateScene2():
    _file_stack = []

    def __init__(self):
        if not bpy.data.is_saved:
            raise ValueError("Save .blend file before exporting")
        # Push filepath onto stack
        if len(DuplicateScene2._file_stack) == 0:
            DuplicateScene2._file_stack.append(bpy.data.filepath)
            bpy.ops.wm.save_mainfile(filepath = bpy.data.filepath,
                                     check_existing = False)
        else:
            filepath, fileext = os.path.splitext(bpy.data.filepath)
            DuplicateScene2._file_stack.append("%s.copy.%02i.blend" % (filepath, DuplicateScene2._unique_id))
            DuplicateScene2._unique_id += 1
            # Save file
            filepath = DuplicateScene2._file_stack[-1]
            bpy.ops.wm.save_as_mainfile(filepath = filepath,
                                        check_existing = False,
                                        copy = True)

    def __enter__(self):
        # Return new context override
        context = bpy.context
        window = context.window_manager.windows[0]
        screen = window.screen
        scene = context.scene
        area_view3d = [area for area in screen.areas if area.type == 'VIEW_3D'][0]
        space_view3d = [space for space in area_view3d.spaces if space.type == 'VIEW_3D'][0]
        return {'window': window,
                'screen': screen,
                'area': area_view3d,
                'space': space_view3d,
                'context': context,
                'scene': scene}

    def __exit__(self, type, value, traceback):
        filepath = DuplicateScene2._file_stack.pop()
        bpy.ops.wm.open_mainfile(filepath = filepath,
                                 load_ui = True,
                                 use_scripts = False)
        if len(DuplicateScene2._file_stack) > 0:
            os.remove(filepath)

Using it like so:

try:
    with DuplicateScene2 as override_context:
        ...
except ValueError as e:
    self.report({'ERROR'}, "%s" % (e))
    return {'CANCELLED'}

With this, checking if the file has been saved or not will warn the user about needing to save before exporting. Any exceptions thrown will still allow the file to be reverted.

If there are "cleaner" methods, or suggestions, please do post!

Edit: This doesn't work nested, which is what I realized that I also need.

Edit: Added supported with nesting.

$\endgroup$

Your Answer

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

Not the answer you're looking for? Browse other questions tagged or ask your own question.