Created
January 8, 2026 21:26
-
-
Save stoobit/7899d64245bdfc86100f3cffc8b72069 to your computer and use it in GitHub Desktop.
A python script that converts models generated by SAM-3D-Body to the USDZ file format using blender.
This file contains hidden or 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
| import bpy | |
| import os | |
| import math | |
| import shutil | |
| # --- CONFIGURATION --- | |
| SOURCE_FOLDER = "/Users/name/.../models_original/" | |
| EXPORT_FOLDER = "/Users/name/.../usdz/" | |
| OBJECTS_TO_KEEP = ["HumanMesh"] | |
| MATCH_LOOSELY = True | |
| WRAPPER_ROTATION = (0, 180, 135) | |
| TARGET_COLOR = (0.92, 0.92, 0.92, 1.0) | |
| # ------------------------------ | |
| def clear_scene(): | |
| if bpy.context.active_object and bpy.context.active_object.mode == "EDIT": | |
| bpy.ops.object.mode_set(mode="OBJECT") | |
| bpy.ops.object.select_all(action="SELECT") | |
| bpy.ops.object.delete() | |
| for block in bpy.data.meshes: | |
| if block.users == 0: | |
| bpy.data.meshes.remove(block) | |
| for block in bpy.data.armatures: | |
| if block.users == 0: | |
| bpy.data.armatures.remove(block) | |
| for block in bpy.data.objects: | |
| if block.users == 0: | |
| bpy.data.objects.remove(block) | |
| for block in bpy.data.materials: | |
| if block.users == 0: | |
| bpy.data.materials.remove(block) | |
| def make_materials_solid_and_dark(obj): | |
| """Forces material to be Opaque and Darker""" | |
| for slot in obj.material_slots: | |
| mat = slot.material | |
| if not mat: | |
| continue | |
| mat.blend_method = "OPAQUE" | |
| if hasattr(mat, "shadow_method"): | |
| mat.shadow_method = "OPAQUE" | |
| mat.use_backface_culling = False | |
| if getattr(mat, "use_nodes", False): | |
| nodes = mat.node_tree.nodes | |
| principled = next((n for n in nodes if n.type == "BSDF_PRINCIPLED"), None) | |
| if principled: | |
| if "Alpha" in principled.inputs: | |
| principled.inputs["Alpha"].default_value = 1.0 | |
| if "Transmission" in principled.inputs: | |
| principled.inputs["Transmission"].default_value = 0.0 | |
| if "Transmission Weight" in principled.inputs: | |
| principled.inputs["Transmission Weight"].default_value = 0.0 | |
| color_input = None | |
| if "Base Color" in principled.inputs: | |
| color_input = principled.inputs["Base Color"] | |
| elif "Color" in principled.inputs: | |
| color_input = principled.inputs["Color"] | |
| if color_input: | |
| if not color_input.is_linked: | |
| color_input.default_value = TARGET_COLOR | |
| def process_files(): | |
| if os.path.exists(EXPORT_FOLDER): | |
| shutil.rmtree(EXPORT_FOLDER) | |
| os.makedirs(EXPORT_FOLDER) | |
| files = [ | |
| f | |
| for f in os.listdir(SOURCE_FOLDER) | |
| if f.lower().endswith((".fbx", ".obj", ".glb", ".gltf")) | |
| ] | |
| print(f"Found {len(files)} files to process...") | |
| for filename in files: | |
| filepath = os.path.join(SOURCE_FOLDER, filename) | |
| clear_scene() | |
| print(f"Processing: {filename}") | |
| try: | |
| if filename.lower().endswith(".fbx"): | |
| bpy.ops.import_scene.fbx(filepath=filepath) | |
| elif filename.lower().endswith(".obj"): | |
| bpy.ops.import_scene.obj(filepath=filepath) | |
| elif filename.lower().endswith((".glb", ".gltf")): | |
| bpy.ops.import_scene.gltf(filepath=filepath) | |
| except Exception as e: | |
| print(f"Error importing {filename}: {e}") | |
| continue | |
| kept_objects = [] | |
| for obj in bpy.data.objects: | |
| is_target = False | |
| for keep_name in OBJECTS_TO_KEEP: | |
| if MATCH_LOOSELY: | |
| if obj.name.startswith(keep_name): | |
| is_target = True | |
| else: | |
| if obj.name == keep_name: | |
| is_target = True | |
| if is_target: | |
| kept_objects.append(obj) | |
| bpy.ops.object.select_all(action="DESELECT") | |
| for obj in bpy.data.objects: | |
| if obj not in kept_objects: | |
| obj.select_set(True) | |
| bpy.ops.object.delete() | |
| for obj in kept_objects: | |
| if obj.type == "MESH": | |
| make_materials_solid_and_dark(obj) | |
| if kept_objects: | |
| bpy.ops.object.empty_add(type="PLAIN_AXES", location=(0, 0, 0)) | |
| wrapper = bpy.context.active_object | |
| for obj in kept_objects: | |
| obj.select_set(True) | |
| bpy.context.view_layer.objects.active = wrapper | |
| bpy.ops.object.parent_set(type="OBJECT", keep_transform=True) | |
| obj.select_set(False) | |
| wrapper.rotation_mode = "XYZ" | |
| wrapper.rotation_euler = ( | |
| math.radians(WRAPPER_ROTATION[0]), | |
| math.radians(WRAPPER_ROTATION[1]), | |
| math.radians(WRAPPER_ROTATION[2]), | |
| ) | |
| # 5. EXPORT AS USDZ | |
| file_root = os.path.splitext(filename)[0] | |
| out_name = file_root + ".usdz" | |
| out_path = os.path.join(EXPORT_FOLDER, out_name) | |
| bpy.ops.object.select_all(action="SELECT") | |
| bpy.ops.wm.usd_export( | |
| filepath=out_path, | |
| selected_objects_only=True, | |
| export_materials=True, | |
| relative_paths=True, | |
| ) | |
| print(f" Saved to: {out_path}") | |
| print("----- BATCH PROCESSING COMPLETE -----") | |
| if __name__ == "__main__": | |
| process_files() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment