88 lines
3.1 KiB
Python
88 lines
3.1 KiB
Python
import bpy
|
|
import os
|
|
import pathlib
|
|
|
|
# Ensure blend file is saved
|
|
blend_filepath = bpy.data.filepath
|
|
if not blend_filepath:
|
|
raise Exception("Blend file is not saved")
|
|
|
|
assert bpy.context.scene is not None
|
|
|
|
def clean_blend_path(blend_path: str) -> str:
|
|
return os.path.join(os.path.dirname(blend_path), bpy.path.clean_name(os.path.basename(blend_path)))
|
|
|
|
# Directory setup
|
|
abs_filepath = bpy.path.abspath(blend_filepath)
|
|
src_assets_path = os.path.dirname(abs_filepath)
|
|
project_path = os.path.dirname(src_assets_path)
|
|
assets_path = os.path.join(project_path, "assets")
|
|
rel_blend_path = os.path.relpath(abs_filepath, src_assets_path)
|
|
clean_rel_path = clean_blend_path(rel_blend_path)
|
|
scene_name = bpy.context.scene.name
|
|
instance_output_path = os.path.join(assets_path, "blender", clean_rel_path, f"{scene_name}.scn")
|
|
|
|
# Utility: returns path to store the glb
|
|
def get_model_export_path(obj: bpy.types.Object) -> str:
|
|
if obj.instance_type == 'COLLECTION' and obj.instance_collection:
|
|
lib_path = obj.instance_collection.library.filepath
|
|
collection_name = bpy.path.clean_name(obj.instance_collection.name)
|
|
rel_lib_path = os.path.relpath(bpy.path.abspath(lib_path), src_assets_path)
|
|
rel_lib_path = clean_blend_path(rel_lib_path)
|
|
return os.path.join("blender", rel_lib_path, f"{collection_name}.glb")
|
|
else:
|
|
obj_name = bpy.path.clean_name(obj.name)
|
|
return os.path.join("blender", clean_rel_path, f"{obj_name}.glb")
|
|
|
|
# Utility: writes an object to glb
|
|
def export_object_as_glb(obj: bpy.types.Object, export_path: str):
|
|
full_export_path = os.path.join(assets_path, export_path)
|
|
pathlib.Path(os.path.dirname(full_export_path)).mkdir(parents=True, exist_ok=True)
|
|
|
|
bpy.ops.object.select_all(action='DESELECT')
|
|
obj.select_set(True)
|
|
|
|
assert bpy.context.view_layer is not None
|
|
bpy.context.view_layer.objects.active = obj
|
|
|
|
bpy.ops.export_scene.gltf(filepath=full_export_path, use_selection=True, export_apply=True)
|
|
print(f"Exported {obj.name} -> {export_path}")
|
|
|
|
# Collect all visible, non-hidden objects in the scene
|
|
visible_objects = [
|
|
obj for obj in bpy.context.scene.objects
|
|
if obj.visible_get()
|
|
]
|
|
|
|
exported = set()
|
|
instances = []
|
|
|
|
for obj in visible_objects:
|
|
model_path = get_model_export_path(obj)
|
|
|
|
if model_path not in exported:
|
|
export_object_as_glb(obj, model_path)
|
|
exported.add(model_path)
|
|
|
|
loc = obj.location
|
|
rot = obj.rotation_quaternion
|
|
scale = obj.scale
|
|
instance_sexpr = (
|
|
'(inst'
|
|
f'\n\t:model "assets/{model_path}"'
|
|
f'\n\t:pos ({loc.x:.6f} {loc.z:.6f} {loc.y:.6f})'
|
|
f'\n\t:rot ({rot.x:.6f} {rot.y:.6f} {rot.z:.6f} {rot.w:.6f})'
|
|
f'\n\t:scale ({scale.x:.6f} {scale.y:.6f} {scale.z:.6f}))'
|
|
)
|
|
instances.append(instance_sexpr)
|
|
|
|
# Save instances to scene file
|
|
instance_output_abs = os.path.join(project_path, instance_output_path)
|
|
pathlib.Path(os.path.dirname(instance_output_abs)).mkdir(parents=True, exist_ok=True)
|
|
|
|
with open(instance_output_abs, "w", encoding="utf-8") as f:
|
|
for line in instances:
|
|
f.write(line + "\n")
|
|
|
|
print(f"Written instances to {instance_output_abs}")
|