import json import math import os import sys import bpy def mesh_object(name, vertices, edges, faces, location): mesh = bpy.data.meshes.new(f"{name}Mesh") mesh.from_pydata(vertices, edges, faces) mesh.update() obj = bpy.data.objects.new(name, mesh) obj.location = location bpy.context.scene.collection.objects.link(obj) return obj def modifier_type_code(modifier): items = bpy.types.Modifier.bl_rna.properties["type"].enum_items return items[modifier.type].value def evaluated_mesh(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, "vertexCount": len(mesh.vertices), "triangleCount": len(mesh.loop_triangles), "positions": [value for vertex in mesh.vertices for value in vertex.co], "indices": [index for triangle in mesh.loop_triangles for index in triangle.vertices], } finally: evaluated.to_mesh_clear() def main(output_dir): bpy.ops.wm.read_factory_settings(use_empty=True) edge_split = mesh_object( "ExtendedEdgeSplit", [(0, 0, 0), (2, 0, 0), (0, 2, 0), (0, 0, 2)], [], [(0, 1, 2), (0, 3, 1)], (-3, 0, 0), ) edge_modifier = edge_split.modifiers.new("Native Edge Split", "EDGE_SPLIT") edge_modifier.use_edge_angle = True edge_modifier.use_edge_sharp = False edge_modifier.split_angle = math.radians(30) screw = mesh_object( "ExtendedScrew", [(1, 0, -1), (1, 0, 1)], [(0, 1)], [], (3, 0, 0), ) screw_modifier = screw.modifiers.new("Native Screw", "SCREW") screw_modifier.axis = "Z" screw_modifier.angle = math.tau screw_modifier.steps = 8 screw_modifier.render_steps = 12 screw_modifier.iterations = 1 screw_modifier.use_merge_vertices = False disabled = screw.modifiers.new("Disabled Edge Split", "EDGE_SPLIT") disabled.show_viewport = False disabled.show_render = True disabled.show_in_editmode = True displace = mesh_object( "ExtendedDisplace", [(-1, -1, -1), (1, -1, -1), (1, 1, -1), (-1, 1, -1), (-1, -1, 1), (1, -1, 1), (1, 1, 1), (-1, 1, 1)], [], [(0, 1, 2, 3), (4, 7, 6, 5), (0, 4, 5, 1), (1, 5, 6, 2), (2, 6, 7, 3), (4, 0, 3, 7)], (7, 0, 0), ) displace_modifier = displace.modifiers.new("Native Constant Displace", "DISPLACE") displace_modifier.direction = "Z" displace_modifier.space = "LOCAL" displace_modifier.strength = 0.75 displace_modifier.mid_level = 0.25 blocked_displace = mesh_object( "ExtendedDisplaceBlocked", [(-1, -1, 0), (1, -1, 0), (1, 1, 0), (-1, 1, 0)], [], [(0, 1, 2, 3)], (11, 0, 0), ) blocked_modifier = blocked_displace.modifiers.new("Blocked RGB Displace", "DISPLACE") blocked_modifier.direction = "RGB_TO_XYZ" mesh_object( "ExtendedDisplaceBlockedBaseline", [(-1, -1, 0), (1, -1, 0), (1, 1, 0), (-1, 1, 0)], [], [(0, 1, 2, 3)], (15, 0, 0), ) scene = bpy.context.scene scene.frame_set(1) depsgraph = bpy.context.evaluated_depsgraph_get() objects = [edge_split, screw, displace, blocked_displace] golden = { "schemaVersion": 1, "taskId": "N-002", "blenderVersion": bpy.app.version_string, "fixture": "modifier_extended_native.blend", "frame": scene.frame_current, "objects": [ { "name": obj.name, "modifiers": [ { "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, } for modifier in obj.modifiers ], } for obj in objects ], "meshes": [evaluated_mesh(obj, depsgraph) for obj in objects], "tolerance": {"maxPositionError": 1e-5, "rmsPositionError": 1e-6}, } os.makedirs(output_dir, exist_ok=True) output_path = os.path.join(output_dir, golden["fixture"]) bpy.ops.wm.save_as_mainfile(filepath=output_path) golden_dir = os.path.abspath(os.path.join(output_dir, "..", "..", "golden", "N-002")) os.makedirs(golden_dir, exist_ok=True) with open(os.path.join(golden_dir, "modifier_extended_native.json"), "w", encoding="utf-8") as handle: json.dump(golden, handle, indent=2, sort_keys=True) handle.write("\n") print(f"modifier-extended-generated fixture={output_path}") if __name__ == "__main__": args = sys.argv[sys.argv.index("--") + 1:] main(os.path.abspath(args[0]))