import pathlib import sys import bpy CATALOGS = ( ("11111111-1111-4111-8111-111111111111", "Characters", "Characters"), ("22222222-2222-4222-8222-222222222222", "Characters/Heroes", "Heroes"), ("33333333-3333-4333-8333-333333333333", "Materials/Metal", "Metal"), ) def set_metadata(asset, *, catalog_id, author, description, copyright_text, license_text, tags, active_tag, preferred_import_method, properties): metadata = asset.asset_data metadata.catalog_id = catalog_id metadata.author = author metadata.description = description metadata.copyright = copyright_text metadata.license = license_text for tag in tags: metadata.tags.new(tag, skip_if_exists=False) metadata.active_tag = active_tag metadata.use_preferred_import_method = preferred_import_method is not None if preferred_import_method is not None: metadata.preferred_import_method = preferred_import_method for name, value in properties.items(): metadata[name] = value def write_catalog_definition(path): lines = [ "# This is an Asset Catalog Definition file for Blender.", "#", "# Generated by M12-01B from a locked Blender 5.2 runtime.", "", "VERSION 1", "", ] lines.extend(f"{catalog_id}:{catalog_path}:{simple_name}" for catalog_id, catalog_path, simple_name in CATALOGS) path.write_text("\n".join(lines) + "\n", encoding="utf-8", newline="\n") def main(output_directory): output = pathlib.Path(output_directory).resolve() output.mkdir(parents=True, exist_ok=True) bpy.ops.wm.read_factory_settings(use_empty=True) mesh = bpy.data.meshes.new("M12 Hero Mesh") mesh.from_pydata(((-1.0, -1.0, 0.0), (1.0, -1.0, 0.0), (0.0, 1.0, 0.0)), (), ((0, 1, 2),)) hero = bpy.data.objects.new("M12 Hero", mesh) bpy.context.scene.collection.objects.link(hero) hero.asset_mark() set_metadata( hero, catalog_id=CATALOGS[1][0], author="M12 Artist", description="Desktop catalog v1 object fixture", copyright_text="Copyright 2026 M12 Fixture Authors", license_text="CC0-1.0", tags=("character", "hero", "rig-ready"), active_tag=1, preferred_import_method="APPEND", properties={"approved": True, "rating": 5, "source": "M12-01B"}, ) material = bpy.data.materials.new("M12 Brushed Metal") material.diffuse_color = (0.25, 0.3, 0.35, 1.0) material.asset_mark() set_metadata( material, catalog_id=CATALOGS[2][0], author="", description="", copyright_text="", license_text="", tags=("metal",), active_tag=0, preferred_import_method=None, properties={"roughness": 0.35}, ) world = bpy.data.worlds.new("M12 Uncataloged World") world.color = (0.02, 0.03, 0.04) world.asset_mark() set_metadata( world, catalog_id="", author="M12 Artist", description="Asset without a catalog assignment", copyright_text="", license_text="CC0-1.0", tags=(), active_tag=0, preferred_import_method="LINK", properties={}, ) catalog_path = output / "blender_assets.cats.txt" fixture_path = output / "m12_asset_catalog_v1.blend" write_catalog_definition(catalog_path) bpy.ops.wm.save_as_mainfile(filepath=str(fixture_path), compress=True) print(f"m12-asset-catalog-v1 fixture={fixture_path} catalogs={len(CATALOGS)} assets=3") if __name__ == "__main__": arguments = sys.argv[sys.argv.index("--") + 1:] if "--" in sys.argv else [] if len(arguments) != 1: raise SystemExit("usage: blender --background --factory-startup --python generate-asset-catalog-v1.py -- OUTPUT_DIRECTORY") main(arguments[0])