Last active
June 8, 2024 20:06
-
-
Save jupiterbjy/09820be0064579d1c4db2ea6df59bc1f to your computer and use it in GitHub Desktop.
Export blender file's all objects while retaining collection structure as directories. Also selects armature and it's parented objects for proper exporting
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
""" | |
Blender batch export script with subdir / auto centering / output redirection. | |
Exports all selected objects into individual files specified in EXPORT_TYPE. | |
All files will be saved in script's named subdir. | |
No need to center every object into world center - script will handle that. | |
Written by jupiterbjy@gmail.com | |
""" | |
import os | |
import pathlib | |
import json | |
import bpy | |
# CONFIG -------------- | |
EXPORT_TYPE = "gltf" | |
SCALE = 1.0 | |
IGNORE_PREFIX = "_" | |
# --------------------- | |
# select extension and exporter | |
EXTENSION = { | |
"gltf": ".glb", | |
"fbx": ".fbx", | |
"stl": ".stl", | |
}[EXPORT_TYPE] | |
EXPORTER = { | |
"gltf": getattr(bpy.ops.export_scene, "gltf"), | |
"fbx": getattr(bpy.ops.export_mesh, "fbx"), | |
"stl": getattr(bpy.ops.export_mesh, "stl"), | |
}[EXPORT_TYPE] | |
# check if file is saved | |
if not bpy.data.filepath: | |
raise Exception("Blend file is not saved") | |
# init basedir | |
_file = pathlib.Path(bpy.data.filepath) | |
basedir = _file.parent / _file.stem | |
basedir.mkdir(exist_ok=True) | |
# cache view layer | |
view_layer = bpy.context.view_layer | |
# shadow print() for output redirection to UI --------- | |
_overrides = [] | |
for window in bpy.context.window_manager.windows: | |
_overrides.extend( | |
{"window": window, "screen": window.screen, "area": area} for area in window.screen.areas if | |
area.type == "CONSOLE" | |
) | |
def print(*args, sep=""): | |
for override in _overrides: | |
with bpy.context.temp_override(**override): | |
bpy.ops.console.scrollback_append(text=sep.join(map(str, args)), type="OUTPUT") | |
def _visit(collection, root): | |
if collection.name[0] == "_": | |
print(f"Skipping {root}/{collection.name}") | |
return | |
for obj in collection.objects: | |
if obj.name[0] == "_": | |
print(f"Skipping {root}/{collection.name}/{obj.name}") | |
continue | |
yield (obj, "/".join((root, collection.name))) | |
print(collection.name) | |
for child_collection in collection.children: | |
yield from _visit(child_collection, "/".join((root, collection.name))) | |
def visit(): | |
"""Visit collection recursively and yield (object, str_collection_path) pairs""" | |
for collection in bpy.context.scene.collection.children: | |
yield from _visit(collection, "") | |
def save_obj(obj, col_path): | |
"""Save given object at collection path""" | |
bpy.ops.object.select_all(action='DESELECT') | |
# prep path | |
base_path = basedir / col_path | |
base_path.mkdir(exist_ok=True, parents=True) | |
# build clean path without illegal characters | |
fn = base_path / (bpy.path.clean_name(obj.name) + EXTENSION) | |
# some exporters only use the active object - make sure it's selected and active | |
obj.select_set(True) | |
view_layer.objects.active = obj | |
# if armature, select all meshes parented to the armature | |
if obj.type == "ARMATURE": | |
for child_obj in obj.children: | |
child_obj.select_set(True) | |
# move object to world center - or some exporter like gltf keeps offset when saving | |
# need to cache old position explicitly | |
old_pos = obj.location[:] | |
obj.location = (0, 0, 0) | |
old_scale = obj.scale[:] | |
obj.scale = (SCALE, SCALE, SCALE) | |
# export | |
EXPORTER(filepath=fn.as_posix(), export_apply=True, use_selection=True) | |
print("written: ", fn) | |
# reset selection and position | |
obj.location = old_pos | |
obj.scale = old_scale | |
obj.select_set(False) | |
# Driver --- | |
def main(): | |
# for each cached selection export | |
for obj, col_path in visit(): | |
# strip preceeding slash | |
save_obj(obj, col_path[1:]) | |
main() | |
bpy.ops.object.select_all(action='DESELECT') |
Author
jupiterbjy
commented
Jun 8, 2024
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment