Files
workinf_Blender_Wasm/tools/web/generate-modifier-fixtures.py
2026-08-12 04:47:48 -04:00

367 lines
14 KiB
Python

import math
import os
import sys
import bpy
def reset_scene():
bpy.ops.wm.read_factory_settings(use_empty=True)
scene = bpy.context.scene
scene.frame_start = 1
scene.frame_end = 24
scene.render.fps = 24
return scene
def mesh_object(name, vertices, faces, location=(0.0, 0.0, 0.0)):
mesh = bpy.data.meshes.new(f"{name}Mesh")
mesh.from_pydata(vertices, [], faces)
mesh.update()
obj = bpy.data.objects.new(name, mesh)
bpy.context.collection.objects.link(obj)
obj.location = location
return obj
def cube_object(name, location=(0.0, 0.0, 0.0), scale=1.0):
vertices = [
(-1, -1, -1), (1, -1, -1), (1, 1, -1), (-1, 1, -1),
(-1, -1, 1), (1, -1, 1), (1, 1, 1), (-1, 1, 1),
]
faces = [
(0, 1, 2, 3), (4, 7, 6, 5), (0, 4, 5, 1),
(1, 5, 6, 2), (2, 6, 7, 3), (4, 0, 3, 7),
]
obj = mesh_object(name, vertices, faces, location)
obj.scale = (scale, scale, scale)
return obj
def plane_grid(name, size=2.0, z=0.0, location=(0.0, 0.0, 0.0)):
vertices = [(-size, -size, z), (0, -size, z), (size, -size, z),
(-size, 0, z), (0, 0, z), (size, 0, z),
(-size, size, z), (0, size, z), (size, size, z)]
faces = [(0, 1, 4, 3), (1, 2, 5, 4), (3, 4, 7, 6), (4, 5, 8, 7)]
return mesh_object(name, vertices, faces, location)
def select_active(obj):
bpy.ops.object.select_all(action="DESELECT")
obj.select_set(True)
bpy.context.view_layer.objects.active = obj
def generate_fixture(output_path):
reset_scene()
x = -12.0
subsurf = cube_object("GenerateSubsurf", (x, 0, 0))
mod = subsurf.modifiers.new("Subdivision", "SUBSURF")
mod.levels = 1
mod.render_levels = 1
x += 4
mirror = mesh_object("GenerateMirror", [(0.2, -1, -1), (1.8, -1, -1), (1.8, 1, -1),
(0.2, 1, -1), (0.2, -1, 1), (1.8, -1, 1),
(1.8, 1, 1), (0.2, 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)], (x, 0, 0))
mod = mirror.modifiers.new("Mirror X", "MIRROR")
mod.use_axis[0] = True
mod.use_clip = True
x += 4
array = cube_object("GenerateArray", (x, 0, 0), 0.6)
mod = array.modifiers.new("Array Three", "ARRAY")
mod.count = 3
mod.relative_offset_displace = (1.5, 0.0, 0.0)
x += 4
bevel = cube_object("GenerateBevel", (x, 0, 0))
mod = bevel.modifiers.new("Bevel", "BEVEL")
mod.width = 0.2
mod.segments = 2
x += 4
solidify = plane_grid("GenerateSolidify", 1.0, location=(x, 0, 0))
mod = solidify.modifiers.new("Solidify", "SOLIDIFY")
mod.thickness = 0.25
x += 4
triangulate = cube_object("GenerateTriangulate", (x, 0, 0))
triangulate.modifiers.new("Triangulate", "TRIANGULATE")
x += 4
weld = mesh_object("GenerateWeld", [(-1, -1, 0), (1, -1, 0), (1, 1, 0),
(-1, -1, 0), (1, 1, 0), (-1, 1, 0)],
[(0, 1, 2), (3, 4, 5)], (x, 0, 0))
mod = weld.modifiers.new("Weld", "WELD")
mod.merge_threshold = 0.001
x += 4
boolean_source = cube_object("GenerateBoolean", (x, 0, 0))
boolean_target = cube_object("GenerateBooleanTarget", (x + 0.75, 0, 0), 0.75)
boolean_target.display_type = "WIRE"
mod = boolean_source.modifiers.new("Boolean Difference", "BOOLEAN")
mod.operation = "DIFFERENCE"
mod.solver = "EXACT"
mod.object = boolean_target
wireframe = cube_object("GenerateWireframe", (x + 4, 0, 0))
mod = wireframe.modifiers.new("Wireframe", "WIREFRAME")
mod.thickness = 0.15
mod.use_replace = True
mod.use_boundary = True
bpy.context.scene.frame_set(1)
bpy.ops.wm.save_as_mainfile(filepath=output_path, compress=True)
def deform_fixture(output_path):
reset_scene()
lattice_data = bpy.data.lattices.new("DeformLatticeTargetData")
lattice_data.points_u = 2
lattice_data.points_v = 2
lattice_data.points_w = 2
lattice_target = bpy.data.objects.new("DeformLatticeTarget", lattice_data)
bpy.context.collection.objects.link(lattice_target)
lattice_target.location = (-8, 0, 0)
lattice_target.scale = (1.5, 1.5, 1.5)
lattice_data.points[-1].co_deform.z += 0.75
lattice_source = cube_object("DeformLattice", (-8, 0, 0))
mod = lattice_source.modifiers.new("Lattice", "LATTICE")
mod.object = lattice_target
mod.strength = 0.8
hook_source = plane_grid("DeformHook", 1.0, location=(-3, 0, 0))
hook_target = bpy.data.objects.new("DeformHookTarget", None)
bpy.context.collection.objects.link(hook_target)
hook_target.location = (-3, 0, 1.25)
mod = hook_source.modifiers.new("Hook", "HOOK")
mod.object = hook_target
mod.vertex_indices_set([4, 5, 7, 8])
mod.strength = 0.7
shrink_target = plane_grid("DeformShrinkwrapTarget", 1.5, z=0.0, location=(2, 0, 0))
shrink_source = plane_grid("DeformShrinkwrap", 1.0, z=0.8, location=(2, 0, 0))
shrink_source.data.vertices[4].co.z = 1.4
mod = shrink_source.modifiers.new("Shrinkwrap", "SHRINKWRAP")
mod.target = shrink_target
mod.wrap_method = "NEAREST_SURFACEPOINT"
mod.offset = 0.1
simple = cube_object("DeformSimple", (7, 0, 0))
mod = simple.modifiers.new("Simple Twist", "SIMPLE_DEFORM")
mod.deform_method = "TWIST"
mod.deform_axis = "Z"
mod.angle = math.radians(35.0)
mesh_deform = cube_object("DeformMeshDeform", (12, 0, 0))
mesh_cage = cube_object("DeformMeshCage", (12, 0, 0), 1.4)
for polygon in mesh_cage.data.polygons:
polygon.flip()
mesh_cage.data.update()
mesh_cage.display_type = "WIRE"
mod = mesh_deform.modifiers.new("Mesh Deform", "MESH_DEFORM")
mod.object = mesh_cage
select_active(mesh_deform)
bpy.ops.object.meshdeform_bind(modifier=mod.name)
if not mod.is_bound:
raise RuntimeError("Mesh Deform fixture failed to bind")
for vertex in mesh_cage.data.vertices:
if vertex.co.z > 0.0:
vertex.co.z += 0.75
mesh_cage.data.update()
surface_deform = plane_grid("DeformSurface", 1.0, z=0.5, location=(17, 0, 0))
surface_target = plane_grid("DeformSurfaceTarget", 1.5, location=(17, 0, 0))
mod = surface_deform.modifiers.new("Surface Deform", "SURFACE_DEFORM")
mod.target = surface_target
select_active(surface_deform)
bpy.ops.object.surfacedeform_bind(modifier=mod.name)
if not mod.is_bound:
raise RuntimeError("Surface Deform fixture failed to bind")
surface_target.data.vertices[4].co.z += 0.75
surface_target.data.update()
cast = cube_object("DeformCast", (22, 0, 0))
mod = cast.modifiers.new("Cast Sphere", "CAST")
mod.cast_type = "SPHERE"
mod.factor = 0.75
mod.use_radius_as_size = False
mod.size = 1.25
smooth = plane_grid("DeformSmooth", 1.5, location=(27, 0, 0))
smooth.data.vertices[4].co.z = 1.5
mod = smooth.modifiers.new("Smooth", "SMOOTH")
mod.factor = 0.5
mod.iterations = 2
bpy.context.scene.frame_set(1)
bpy.ops.wm.save_as_mainfile(filepath=output_path, compress=True)
def deform_curve_fixture(output_path):
reset_scene()
curve_data = bpy.data.curves.new("DeformCurveTargetData", "CURVE")
curve_data.dimensions = "3D"
spline = curve_data.splines.new("BEZIER")
spline.bezier_points.add(2)
for point, coordinate in zip(spline.bezier_points, [(-2, 0, 0), (0, 0.75, 0), (2, 0, 1)]):
point.co = coordinate
point.handle_left_type = "AUTO"
point.handle_right_type = "AUTO"
curve_target = bpy.data.objects.new("DeformCurveTarget", curve_data)
bpy.context.collection.objects.link(curve_target)
curve_source = plane_grid("DeformCurve", 1.5)
modifier = curve_source.modifiers.new("Curve", "CURVE")
modifier.object = curve_target
modifier.deform_axis = "POS_X"
bpy.context.scene.frame_set(1)
bpy.ops.wm.save_as_mainfile(filepath=output_path, compress=True)
def physics_fixture(output_path):
scene = reset_scene()
scene.frame_end = 12
build = cube_object("PhysicsBuild", (-4, 0, 0))
mod = build.modifiers.new("Build", "BUILD")
mod.frame_start = 1
mod.frame_duration = 10
mod.use_random_order = False
wave = plane_grid("PhysicsWave", 2.0, location=(2, 0, 0))
mod = wave.modifiers.new("Wave", "WAVE")
mod.height = 0.4
mod.width = 1.2
mod.speed = 0.25
mod.start_position_x = 0.0
mod.start_position_y = 0.0
cloth = plane_grid("PhysicsClothDisabled", 1.0, location=(7, 0, 1))
mod = cloth.modifiers.new("Cloth Disabled", "CLOTH")
mod.show_viewport = False
soft = plane_grid("PhysicsSoftBodyDisabled", 1.0, location=(11, 0, 1))
select_active(soft)
bpy.ops.object.modifier_add(type="SOFT_BODY")
soft.modifiers[-1].name = "Soft Body Disabled"
soft.modifiers[-1].show_viewport = False
collision = cube_object("PhysicsCollisionDisabled", (15, 0, 0))
select_active(collision)
bpy.ops.object.modifier_add(type="COLLISION")
collision.modifiers[-1].name = "Collision Disabled"
collision.modifiers[-1].show_viewport = False
scene.frame_set(6)
bpy.ops.wm.save_as_mainfile(filepath=output_path, compress=True)
def geometry_nodes_fixture(output_path):
reset_scene()
obj = cube_object("GeometryNodesObject")
group = bpy.data.node_groups.new("WebGeometryNodes", "GeometryNodeTree")
group.interface.new_socket(name="Geometry", in_out="INPUT", socket_type="NodeSocketGeometry")
group.interface.new_socket(name="Geometry", in_out="OUTPUT", socket_type="NodeSocketGeometry")
input_node = group.nodes.new("NodeGroupInput")
output_node = group.nodes.new("NodeGroupOutput")
transform = group.nodes.new("GeometryNodeTransform")
transform.inputs["Translation"].default_value = (0.25, 0.5, 1.0)
transform.inputs["Rotation"].default_value = (0.0, 0.0, math.radians(15.0))
group.links.new(input_node.outputs["Geometry"], transform.inputs["Geometry"])
group.links.new(transform.outputs["Geometry"], output_node.inputs["Geometry"])
modifier = obj.modifiers.new("Geometry Nodes Transform", "NODES")
modifier.node_group = group
set_position_object = cube_object("GeometryNodesSetPosition", (4, 0, 0))
set_position_group = bpy.data.node_groups.new("WebGeometryNodesSetPosition", "GeometryNodeTree")
set_position_group.interface.new_socket(name="Geometry", in_out="INPUT", socket_type="NodeSocketGeometry")
set_position_group.interface.new_socket(name="Geometry", in_out="OUTPUT", socket_type="NodeSocketGeometry")
set_position_input = set_position_group.nodes.new("NodeGroupInput")
set_position_output = set_position_group.nodes.new("NodeGroupOutput")
set_position = set_position_group.nodes.new("GeometryNodeSetPosition")
set_position.inputs["Offset"].default_value = (0.0, 0.0, 1.0)
set_position_group.links.new(set_position_input.outputs["Geometry"], set_position.inputs["Geometry"])
set_position_group.links.new(set_position.outputs["Geometry"], set_position_output.inputs["Geometry"])
modifier = set_position_object.modifiers.new("Geometry Nodes Set Position", "NODES")
modifier.node_group = set_position_group
simulation = cube_object("GeometryNodesSimulation", (8, 0, 0))
simulation_group = bpy.data.node_groups.new("WebGeometryNodesSimulation", "GeometryNodeTree")
simulation_group.interface.new_socket(name="Geometry", in_out="INPUT", socket_type="NodeSocketGeometry")
simulation_group.interface.new_socket(name="Geometry", in_out="OUTPUT", socket_type="NodeSocketGeometry")
simulation_input = simulation_group.nodes.new("NodeGroupInput")
simulation_output = simulation_group.nodes.new("NodeGroupOutput")
zone_input = simulation_group.nodes.new("GeometryNodeSimulationInput")
zone_output = simulation_group.nodes.new("GeometryNodeSimulationOutput")
zone_input.pair_with_output(zone_output)
simulation_group.links.new(simulation_input.outputs["Geometry"], zone_input.inputs["Geometry"])
simulation_group.links.new(zone_input.outputs["Geometry"], zone_output.inputs["Geometry"])
simulation_group.links.new(zone_output.outputs["Geometry"], simulation_output.inputs["Geometry"])
modifier = simulation.modifiers.new("Geometry Nodes Simulation", "NODES")
modifier.node_group = simulation_group
cube_object("GeometryNodesSimulationBaseline", (12, 0, 0))
bpy.context.scene.frame_set(1)
bpy.ops.wm.save_as_mainfile(filepath=output_path, compress=True)
def grease_pencil_fixture(output_path):
reset_scene()
grease_pencil = bpy.data.grease_pencils.new("GreasePencilData")
layer = grease_pencil.layers.new("Lines", set_active=True)
drawing = layer.frames.new(1).drawing
drawing.add_strokes([4])
for point, coordinate in zip(drawing.strokes[0].points,
[(-1.5, 0, 0), (-0.5, 0.5, 0), (0.5, -0.25, 0), (1.5, 0.25, 0)]):
point.position = coordinate
point.radius = 0.05
point.opacity = 0.9
obj = bpy.data.objects.new("GreasePencilObject", grease_pencil)
bpy.context.collection.objects.link(obj)
modifier_types = sorted(item.identifier for item in bpy.types.Modifier.bl_rna.properties["type"].enum_items
if item.identifier.startswith("GREASE_PENCIL_") or item.identifier == "LINEART")
for modifier_type in modifier_types:
modifier = obj.modifiers.new(modifier_type.replace("GREASE_PENCIL_", "GP ").replace("_", " ").title(),
modifier_type)
modifier.show_viewport = False
bpy.context.scene.frame_set(1)
bpy.ops.wm.save_as_mainfile(filepath=output_path, compress=True)
GENERATORS = {
"generate": generate_fixture,
"deform": deform_fixture,
"deform_curve": deform_curve_fixture,
"physics": physics_fixture,
"geometry_nodes": geometry_nodes_fixture,
"grease_pencil": grease_pencil_fixture,
}
def main():
if "--" not in sys.argv:
raise SystemExit("usage: blender -b --python generate-modifier-fixtures.py -- output-directory")
args = sys.argv[sys.argv.index("--") + 1:]
if not args:
raise SystemExit("missing output directory")
output_directory = os.path.abspath(args[0])
os.makedirs(output_directory, exist_ok=True)
requested = args[1:] or list(GENERATORS)
for category in requested:
if category not in GENERATORS:
raise SystemExit(f"unknown fixture category: {category}")
output_path = os.path.join(output_directory, f"modifier_{category}_scene.blend")
GENERATORS[category](output_path)
print(f"fixture-generated category={category} path={output_path}")
if __name__ == "__main__":
main()