-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathShapeGen.py
More file actions
161 lines (133 loc) · 5.77 KB
/
ShapeGen.py
File metadata and controls
161 lines (133 loc) · 5.77 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
import bpy
import math
import random
from mathutils import Matrix, Vector, Euler
import os
# -----------------------------------------------------------------------------
# CONFIGURATION
# -----------------------------------------------------------------------------
rotation_increment = math.radians(45)
loop = 7
filepath_name = "/Users/albert/Documents/GitHub/Human_AI_Benchmark"
output_path = os.path.join(filepath_name, "ShapeGen_Sequence")
os.makedirs(output_path, exist_ok=True)
# -----------------------------------------------------------------------------
# HELPER FUNCTIONS
# -----------------------------------------------------------------------------
def clear_scene():
for collection in bpy.data.collections:
if collection:
for obj in collection.objects:
bpy.data.objects.remove(obj, do_unlink=True)
bpy.data.collections.remove(collection)
bpy.ops.object.select_all(action='SELECT')
bpy.ops.object.delete()
for block in bpy.data.meshes: bpy.data.meshes.remove(block)
for block in bpy.data.materials: bpy.data.materials.remove(block)
for block in bpy.data.textures: bpy.data.textures.remove(block)
for block in bpy.data.images: bpy.data.images.remove(block)
def rotate_pivot_world_axis(pivot_obj, angle_rad, axis_name):
# Standard World Axes (Fixed)
axis_map = {
'X': Vector((1, 0, 0)),
'Y': Vector((0, 1, 0)),
'Z': Vector((0, 0, 1))
}
vec = axis_map[axis_name]
rot_mat = Matrix.Rotation(angle_rad, 4, vec)
# GLOBAL ROTATION: Put rot_mat FIRST
# This applies the rotation along the fixed World Axis
parent_empty.matrix_world = rot_mat @ parent_empty.matrix_world
# Ensure it stays at center
parent_empty.location = Vector((0,0,0))
# -----------------------------------------------------------------------------
# MAIN EXECUTION
# -----------------------------------------------------------------------------
clear_scene()
# 1. Generate & Bake (Standard Setup)
bpy.ops.mesh.shape_generator()
gen_collection = bpy.data.collections.get("Generated Shape Collection")
if gen_collection:
bpy.ops.object.select_all(action='DESELECT')
for obj in gen_collection.objects:
obj.select_set(True)
bpy.context.view_layer.objects.active = obj
try:
if gen_collection:
props = gen_collection.shape_generator_properties
props.random_seed = 97
props.amount = 3
props.mirror_x = False; props.mirror_y = False; props.mirror_z = False
# Smooth Modifiers
props.is_bevel = True; props.bevel_segments = 10
props.is_subsurf = True; props.subsurf_segments = 2
# Apply standard modifiers
shapeGenObj = bpy.context.view_layer.objects.active
if shapeGenObj:
for mod in ["Bevel", "Mirror", "Subdivision"]:
if mod in shapeGenObj.modifiers:
bpy.ops.object.modifier_apply(modifier=mod)
# Bake Setup
for obj in gen_collection.objects: obj.select_set(True)
props.join_objects = True
props.bake_smooth_result = True
props.bake_object = True
# Apply Bake Modifiers & Shade Smooth
if shapeGenObj:
bpy.context.view_layer.objects.active = shapeGenObj
shapeGenObj.select_set(True)
if "Shape Generator Remesh" in shapeGenObj.modifiers:
bpy.ops.object.modifier_apply(modifier="Shape Generator Remesh")
if "Shape Generator Smooth" in shapeGenObj.modifiers:
bpy.ops.object.modifier_apply(modifier="Shape Generator Smooth")
bpy.ops.object.shade_smooth()
bpy.ops.object.origin_set(type='ORIGIN_GEOMETRY', center='BOUNDS')
bpy.ops.object.transform_apply(location=False, rotation=False, scale=True)
except Exception as e:
print(f"Error: {e}")
# 2. Parenting to Pivot
shapeGenObj = bpy.data.objects.get('Generated Shape')
world_origin = Vector((0, 0, 0))
bpy.ops.object.empty_add(type='PLAIN_AXES', location=world_origin)
parent_empty = bpy.context.object
parent_empty.name = "RotationPivot"
if shapeGenObj:
shapeGenObj.location = Vector((0, 0, 0))
shapeGenObj.rotation_euler = (0, 0, 0)
shapeGenObj.parent = parent_empty
# 3. Camera Setup (Fixed looking at -Y)
scene = bpy.context.scene
bpy.ops.object.camera_add()
camera = bpy.context.object
scene.camera = camera
camera.location = Vector((0, -10.0, 0))
camera.rotation_euler = Euler((math.radians(90), 0, 0), 'XYZ')
# 4. Render Settings
scene.render.engine = 'BLENDER_WORKBENCH'
scene.render.resolution_x = 512
scene.render.resolution_y = 512
scene.display.shading.light = 'STUDIO'
scene.display.shading.color_type = 'MATERIAL'
scene.render.film_transparent = True
# -----------------------------------------------------------------------------
# RENDER LOOP (WORLD AXIS ROTATION)
# -----------------------------------------------------------------------------
rotation_order_str = "base"
# Render Base
scene.render.filepath = os.path.join(output_path, f"{rotation_order_str}.png")
bpy.ops.render.render(write_still=True)
print(f"Rendered: {rotation_order_str}")
for i in range(1, loop):
axis = random.choice(['X', 'Y', 'Z'])
direction = random.choice([-1, 1])
angle = direction * rotation_increment
# Append to filename to record the step taken
rot_symbol = f"{'-' if direction == -1 else ''}{axis}"
rotation_order_str += f"_{rot_symbol}"
# Perform WORLD rotation
# This guarantees the visual change corresponds exactly to the World Axis
rotate_pivot_world_axis(parent_empty, angle, axis)
scene.render.filepath = os.path.join(output_path, f"{rotation_order_str}.png")
bpy.ops.render.render(write_still=True)
print(f"Rendered step {i}: {rotation_order_str} (World Axis Rotation)")
print("Complete.")