-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathShapeNet_gizmo.py
More file actions
224 lines (181 loc) · 7.44 KB
/
ShapeNet_gizmo.py
File metadata and controls
224 lines (181 loc) · 7.44 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
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
import bpy
import math
import random
from mathutils import Matrix, Vector, Euler
import os
# -----------------------------------------------------------------------------
# CONFIGURATION
# -----------------------------------------------------------------------------
collection_name = "ImportedMeshes"
rotation_increment = math.radians(45)
loop = 7
# Paths
filepath_name = '/Users/albert'
#relative_path = '02747177/60b743e913182e9ad5067eac75a07f7'
# relative_path = '03325088/28568aa9e333a421c36fb70296e45483'
# relative_path = '03636649/b474613907b293682f8b82e61bb347fa'
relative_path = '03325088/a851047aeb3793403ca0c4be71e7b721'
obj_path = f'{filepath_name}/.cache/huggingface/hub/datasets--ShapeNet--ShapeNetCore/blobs/{relative_path}/models/model_normalized.obj'
current_dir = os.getcwd()
output_path = os.path.join(current_dir, "test","3")
os.makedirs(output_path, exist_ok=True)
# -----------------------------------------------------------------------------
# HELPER FUNCTIONS
# -----------------------------------------------------------------------------
def clear_scene():
"""Clears everything: Meshes, Lights, Cameras."""
bpy.ops.object.select_all(action='SELECT')
bpy.ops.object.delete()
# Purge orphan data
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 create_color_material(name, color):
mat = bpy.data.materials.new(name=name)
mat.use_nodes = False
mat.diffuse_color = color
return mat
def create_visual_aids(scale=1.0):
"""Creates renderable Axes (RGB) and a Center Sphere (Yellow)."""
# Materials
mat_x = create_color_material("Mat_Axis_X", (1.0, 0.0, 0.0, 1.0)) # Red
mat_y = create_color_material("Mat_Axis_Y", (0.0, 1.0, 0.0, 1.0)) # Green
mat_z = create_color_material("Mat_Axis_Z", (0.0, 0.0, 1.0, 1.0)) # Blue
mat_center = create_color_material("Mat_Center", (1.0, 1.0, 0.0, 1.0)) # Yellow
radius = 0.001 * scale
length = 1.0 * scale # Long enough to go THROUGH (-1 to +1)
# 1. Center Sphere
bpy.ops.mesh.primitive_uv_sphere_add(radius=radius*3, location=(0,0,0))
center_sphere = bpy.context.object
center_sphere.name = "Visual_Center"
center_sphere.active_material = mat_center
center_sphere.show_in_front = True # <--- X-RAY EFFECT
# 2. X Axis (Red)
bpy.ops.mesh.primitive_cylinder_add(radius=radius, depth=length, location=(0, 0, 0))
x_axis = bpy.context.object
x_axis.rotation_euler = (0, math.radians(90), 0)
x_axis.active_material = mat_x
# 3. Y Axis (Green)
bpy.ops.mesh.primitive_cylinder_add(radius=radius, depth=length, location=(0, 0, 0))
y_axis = bpy.context.object
y_axis.rotation_euler = (math.radians(90), 0, 0)
y_axis.active_material = mat_y
# 4. Z Axis (Blue)
bpy.ops.mesh.primitive_cylinder_add(radius=radius, depth=length, location=(0, 0, 0))
z_axis = bpy.context.object
z_axis.active_material = mat_z
# Group Axes
bpy.ops.object.select_all(action='DESELECT')
x_axis.select_set(True)
y_axis.select_set(True)
z_axis.select_set(True)
bpy.context.view_layer.objects.active = x_axis
bpy.ops.object.join()
x_axis.name = "Visual_Axes"
# Enable X-Ray ("In Front") for the joined axes
x_axis.show_in_front = True
return center_sphere, x_axis
def get_collection_center(objects):
"""Calculates the center of mass (Average of all Vertices)."""
total_center = Vector((0, 0, 0))
total_vertices = 0
for obj in objects:
if obj.type == 'MESH':
world_matrix = obj.matrix_world
for v in obj.data.vertices:
world_v = world_matrix @ v.co
total_center += world_v
total_vertices += 1
if total_vertices > 0:
return total_center / total_vertices
return Vector((0, 0, 0))
# -----------------------------------------------------------------------------
# MAIN EXECUTION
# -----------------------------------------------------------------------------
clear_scene()
# 1. Create Visuals (Gizmo)
print("Creating Visual Aids...")
center_sphere, axes_obj = create_visual_aids(scale=1.5)
# 2. Import
print(f"Loading OBJ: {obj_path}")
bpy.ops.wm.obj_import(
filepath=bpy.path.abspath(obj_path),
use_split_objects=True,
use_split_groups=True,
validate_meshes=False
)
visual_names = [center_sphere.name, axes_obj.name]
imported_objects = [o for o in bpy.context.selected_objects if o.type == 'MESH' and o.name not in visual_names]
# 3. Organize Collection
new_collection = bpy.data.collections.new(collection_name)
bpy.context.scene.collection.children.link(new_collection)
for obj in imported_objects:
if obj.users_collection:
for c in obj.users_collection:
c.objects.unlink(obj)
new_collection.objects.link(obj)
# 4. CENTER ONLY (No Flip)
print("Normalizing Object Position...")
c1 = get_collection_center(imported_objects)
for obj in imported_objects:
obj.matrix_world = Matrix.Translation(-c1) @ obj.matrix_world
print("Object Centered.")
# 5. PARENTING
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"
# Parent the OBJECTS to the empty
for obj in imported_objects:
obj.parent = parent_empty
# Parent the GIZMO to the empty (It now rotates WITH the object)
axes_obj.parent = parent_empty
# 6. CAMERA SETUP (Strictly -Y)
bpy.ops.object.camera_add()
camera = bpy.context.object
scene = bpy.context.scene
scene.camera = camera
camera.location = Vector((0, -3.0, 0))
camera.rotation_euler = Euler((math.radians(90), 0, 0), 'XYZ')
# 7. RENDER SETTINGS
scene.render.engine = 'BLENDER_WORKBENCH'
scene.render.resolution_x = 1080
scene.render.resolution_y = 1080
scene.display.shading.light = 'STUDIO'
scene.display.shading.color_type = 'MATERIAL'
scene.display.shading.show_backface_culling = False
scene.render.film_transparent = True
# -----------------------------------------------------------------------------
# RENDER LOOP
# -----------------------------------------------------------------------------
rotation_order_str = "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}")
def rotate_pivot_locally(angle_rad, axis_name):
"""
Rotates the Pivot (and thus the Object + Axes) around its OWN current local axis.
"""
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)
# Local Rotation: Old @ Rotation
parent_empty.matrix_world = parent_empty.matrix_world @ rot_mat
# Force Pivot back to 0,0,0 (Keep center constant)
parent_empty.location = Vector((0,0,0))
for i in range(1, loop):
axis = random.choice(['X', 'Y', 'Z'])
direction = random.choice([-1, 1])
angle = direction * rotation_increment
rot_symbol = f"{'-' if direction == -1 else ''}{axis}"
rotation_order_str += f"_{rot_symbol}"
rotate_pivot_locally(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}")
print("Complete.")