import json import os import sys import bpy FIXTURES = [ "modifier_generate_scene.blend", "modifier_deform_scene.blend", "modifier_deform_curve_scene.blend", "modifier_physics_scene.blend", "modifier_geometry_nodes_scene.blend", "modifier_grease_pencil_scene.blend", ] def modifier_type_code(modifier): items = bpy.types.Modifier.bl_rna.properties["type"].enum_items item = items.get(modifier.type) return item.value if item is not None else -1 def modifier_record(modifier): record = { "name": modifier.name, "type": modifier.type, "typeCode": modifier_type_code(modifier), "showViewport": modifier.show_viewport, "showRender": modifier.show_render, "showEditmode": modifier.show_in_editmode, "showOnCage": modifier.show_on_cage, } targets = [] for field in ("object", "target", "auxiliary_target"): if hasattr(modifier, field): target = getattr(modifier, field) if target is not None and target.name not in targets: targets.append(target.name) if targets: record["targetObjects"] = targets if modifier.type == "NODES" and modifier.node_group is not None: record["nodeGroup"] = modifier.node_group.name return record def evaluated_mesh_record(obj, depsgraph): evaluated = obj.evaluated_get(depsgraph) mesh = evaluated.to_mesh(preserve_all_data_layers=True, depsgraph=depsgraph) try: mesh.calc_loop_triangles() return { "object": obj.name, "sourceMesh": obj.data.name, "vertexCount": len(mesh.vertices), "triangleCount": len(mesh.loop_triangles), "positions": [component for vertex in mesh.vertices for component in vertex.co], "indices": [index for triangle in mesh.loop_triangles for index in triangle.vertices], "worldMatrix": [value for row in evaluated.matrix_world for value in row], } finally: evaluated.to_mesh_clear() def grease_pencil_record(obj): layers = [] for layer in obj.data.layers: frames = [] for frame in layer.frames: drawing = frame.drawing frames.append({ "frame": frame.frame_number, "strokeCount": len(drawing.strokes), "pointCount": sum(len(stroke.points) for stroke in drawing.strokes), "positions": [component for stroke in drawing.strokes for point in stroke.points for component in point.position], }) layers.append({"name": layer.name, "frames": frames}) return {"object": obj.name, "data": obj.data.name, "layers": layers} def node_group_records(): return [{ "name": group.name, "type": group.bl_idname, "nodes": [{"name": node.name, "type": node.bl_idname} for node in group.nodes], "links": len(group.links), } for group in bpy.data.node_groups] def golden_for_fixture(path): bpy.ops.wm.open_mainfile(filepath=path) scene = bpy.context.scene scene.frame_set(scene.frame_current) depsgraph = bpy.context.evaluated_depsgraph_get() objects = [] meshes = [] grease_pencils = [] for obj in sorted(bpy.data.objects, key=lambda item: item.name): objects.append({ "name": obj.name, "type": obj.type, "modifiers": [modifier_record(modifier) for modifier in obj.modifiers], }) if obj.type == "MESH": meshes.append(evaluated_mesh_record(obj, depsgraph)) elif obj.type == "GREASEPENCIL": grease_pencils.append(grease_pencil_record(obj)) return { "schemaVersion": 1, "blenderVersion": bpy.app.version_string, "fixture": os.path.basename(path), "frame": scene.frame_current, "objects": objects, "meshes": meshes, "nodeGroups": node_group_records(), "greasePencils": grease_pencils, "tolerance": {"maxPositionError": 1e-5, "rmsPositionError": 1e-6}, } def main(): if "--" not in sys.argv or len(sys.argv[sys.argv.index("--") + 1:]) < 2: raise SystemExit("usage: blender -b --python generate-modifier-goldens.py -- fixture-directory output-directory") args = sys.argv[sys.argv.index("--") + 1:] fixture_directory, output_directory = args[:2] fixture_directory = os.path.abspath(fixture_directory) output_directory = os.path.abspath(output_directory) os.makedirs(output_directory, exist_ok=True) requested = args[2:] or FIXTURES for fixture in requested: if fixture not in FIXTURES: raise SystemExit(f"unknown modifier fixture: {fixture}") source = os.path.join(fixture_directory, fixture) golden = golden_for_fixture(source) output = os.path.join(output_directory, fixture.replace(".blend", ".json")) with open(output, "w", encoding="utf-8") as handle: json.dump(golden, handle, indent=2, sort_keys=True) handle.write("\n") print(f"modifier-golden-generated fixture={fixture} output={output}") if __name__ == "__main__": main()