ginipick commited on
Commit
6a6eb1e
ยท
verified ยท
1 Parent(s): 28c3f88

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +206 -85
app.py CHANGED
@@ -75,16 +75,43 @@ print(f"Output directory: {LOG_PATH}")
75
 
76
  def normalize_mesh(mesh):
77
  """Normalize mesh to fit in a unit cube centered at origin"""
78
- vertices = mesh.vertices
79
- bounds = np.array([vertices.min(axis=0), vertices.max(axis=0)])
80
- center = (bounds[0] + bounds[1]) / 2
81
- scale = 1.0 / (bounds[1] - bounds[0]).max()
82
-
83
- # Create a copy to avoid modifying the original
84
- normalized_mesh = mesh.copy()
85
- normalized_mesh.vertices = (vertices - center) * scale
86
-
87
- return normalized_mesh, center, scale
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
88
 
89
  def create_rotation_animation(mesh, duration=3.0, fps=30):
90
  """Create a rotation animation around the Y axis"""
@@ -98,15 +125,33 @@ def create_rotation_animation(mesh, duration=3.0, fps=30):
98
  t = frame_idx / (num_frames - 1) # Normalized time [0, 1]
99
  angle = t * 2 * math.pi # Full rotation
100
 
101
- # Create a copy of the mesh to animate
102
- animated_mesh = mesh.copy()
103
-
104
- # Apply rotation around Y axis
105
- rotation_matrix = tf.rotation_matrix(angle, [0, 1, 0])
106
- animated_mesh.apply_transform(rotation_matrix)
107
-
108
- # Add to frames
109
- frames.append(animated_mesh)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
110
 
111
  return frames
112
 
@@ -120,17 +165,30 @@ def create_float_animation(mesh, duration=3.0, fps=30, amplitude=0.2):
120
 
121
  for frame_idx in range(num_frames):
122
  t = frame_idx / (num_frames - 1) # Normalized time [0, 1]
123
-
124
- # Create a copy of the mesh to animate
125
- animated_mesh = mesh.copy()
126
-
127
- # Apply floating motion (sinusoidal)
128
  y_offset = amplitude * math.sin(2 * math.pi * t)
129
- translation_matrix = tf.translation_matrix([0, y_offset, 0])
130
- animated_mesh.apply_transform(translation_matrix)
131
 
132
- # Add to frames
133
- frames.append(animated_mesh)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
134
 
135
  return frames
136
 
@@ -142,64 +200,98 @@ def create_explode_animation(mesh, duration=3.0, fps=30):
142
  # Normalize the mesh for consistent animation
143
  mesh, original_center, original_scale = normalize_mesh(mesh)
144
 
145
- # Split the mesh into components
146
- # If the mesh can't be split, we'll just move vertices outward
147
- try:
148
- components = mesh.split(only_watertight=False)
149
- if len(components) <= 1:
150
- raise ValueError("Mesh cannot be split into components")
151
- except:
152
- # If splitting fails, work with the original mesh
153
- components = None
154
-
155
- for frame_idx in range(num_frames):
156
- t = frame_idx / (num_frames - 1) # Normalized time [0, 1]
157
-
158
- if components:
159
- # Create a scene to hold all components
160
- scene = trimesh.Scene()
161
 
162
- # Move each component outward from center
163
- for component in components:
164
- # Create a copy of the component
165
- animated_component = component.copy()
166
 
167
- # Calculate direction from center to component centroid
168
- direction = animated_component.centroid
 
 
 
169
  if np.linalg.norm(direction) < 1e-10:
170
- # If component is at center, choose random direction
171
  direction = np.random.rand(3) - 0.5
172
 
173
  direction = direction / np.linalg.norm(direction)
174
 
175
- # Apply explosion movement
176
- translation = direction * t * 0.5 # Scale factor for explosion
177
  translation_matrix = tf.translation_matrix(translation)
178
- animated_component.apply_transform(translation_matrix)
179
 
180
- # Add to scene
181
- scene.add_geometry(animated_component)
182
 
183
- # Convert scene to mesh (approximation)
184
- animated_mesh = trimesh.util.concatenate(scene.dump())
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
185
  else:
186
- # Work with vertices directly if components approach failed
187
- animated_mesh = mesh.copy()
188
- vertices = animated_mesh.vertices.copy()
189
-
190
- # Calculate directions from center (0,0,0) to each vertex
191
- directions = vertices.copy()
192
- norms = np.linalg.norm(directions, axis=1, keepdims=True)
193
- mask = norms > 1e-10
194
- directions[mask] = directions[mask] / norms[mask]
195
- directions[~mask] = np.random.rand(np.sum(~mask), 3) - 0.5
196
-
197
- # Apply explosion factor
198
- vertices += directions * t * 0.3
199
- animated_mesh.vertices = vertices
200
-
201
- # Add to frames
202
- frames.append(animated_mesh)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
203
 
204
  return frames
205
 
@@ -263,23 +355,29 @@ def generate_gif_from_frames(frames, output_path, fps=30, resolution=(640, 480),
263
 
264
  for frame in frames:
265
  # Create a scene with the frame
266
- scene = trimesh.Scene(frame)
 
 
 
267
 
268
- # Set camera and rendering parameters
269
  try:
270
- # Try to get a good view of the object
271
- scene.camera_transform = scene.camera_transform
272
  except:
273
- # If that fails, use a default camera position
274
- scene.camera_transform = tf.translation_matrix([0, 0, 2])
275
 
 
 
 
276
  # Render the frame
277
  try:
278
- img = scene.save_image(resolution=resolution, background=background_color)
279
- gif_frames.append(Image.open(img))
 
280
  except Exception as e:
281
  print(f"Error rendering frame: {str(e)}")
282
- # Create a blank image if rendering fails
283
  gif_frames.append(Image.new('RGB', resolution, (255, 255, 255)))
284
 
285
  # Save as GIF
@@ -300,7 +398,30 @@ def create_animation_mesh(input_mesh_path, animation_type='rotate', duration=3.0
300
  """Create animation from input mesh based on animation type"""
301
  # Load the mesh
302
  try:
303
- mesh = trimesh.load(input_mesh_path)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
304
  except Exception as e:
305
  print(f"Error loading mesh: {str(e)}")
306
  return None, None
 
75
 
76
  def normalize_mesh(mesh):
77
  """Normalize mesh to fit in a unit cube centered at origin"""
78
+ if isinstance(mesh, trimesh.Scene):
79
+ # Scene ๊ฐ์ฒด ์ฒ˜๋ฆฌ
80
+ # ์”ฌ์—์„œ ๋ชจ๋“  ๋ฉ”์‹œ ์ถ”์ถœ
81
+ meshes = []
82
+ for geometry in mesh.geometry.values():
83
+ if isinstance(geometry, trimesh.Trimesh):
84
+ meshes.append(geometry)
85
+
86
+ if not meshes:
87
+ raise ValueError("No meshes found in scene")
88
+
89
+ # ๋ชจ๋“  ๋ฉ”์‹œ์˜ ์ •์ ์„ ๊ฒฐํ•ฉํ•˜์—ฌ ๊ฒฝ๊ณ„ ์ƒ์ž ๊ณ„์‚ฐ
90
+ all_vertices = np.vstack([m.vertices for m in meshes])
91
+ bounds = np.array([all_vertices.min(axis=0), all_vertices.max(axis=0)])
92
+ center = (bounds[0] + bounds[1]) / 2
93
+ scale = 1.0 / (bounds[1] - bounds[0]).max()
94
+
95
+ # ๊ฐ ๋ฉ”์‹œ๋ฅผ ์ •๊ทœํ™”ํ•˜์—ฌ ์ƒˆ ์”ฌ ์ƒ์„ฑ
96
+ normalized_scene = trimesh.Scene()
97
+ for mesh_obj in meshes:
98
+ normalized_mesh = mesh_obj.copy()
99
+ normalized_mesh.vertices = (normalized_mesh.vertices - center) * scale
100
+ normalized_scene.add_geometry(normalized_mesh)
101
+
102
+ return normalized_scene, center, scale
103
+ else:
104
+ # ์ผ๋ฐ˜ Trimesh ๊ฐ์ฒด ์ฒ˜๋ฆฌ
105
+ vertices = mesh.vertices
106
+ bounds = np.array([vertices.min(axis=0), vertices.max(axis=0)])
107
+ center = (bounds[0] + bounds[1]) / 2
108
+ scale = 1.0 / (bounds[1] - bounds[0]).max()
109
+
110
+ # ๋ณต์‚ฌ๋ณธ ์ƒ์„ฑํ•˜์—ฌ ์›๋ณธ ๋ณ€๊ฒฝ ๋ฐฉ์ง€
111
+ normalized_mesh = mesh.copy()
112
+ normalized_mesh.vertices = (vertices - center) * scale
113
+
114
+ return normalized_mesh, center, scale
115
 
116
  def create_rotation_animation(mesh, duration=3.0, fps=30):
117
  """Create a rotation animation around the Y axis"""
 
125
  t = frame_idx / (num_frames - 1) # Normalized time [0, 1]
126
  angle = t * 2 * math.pi # Full rotation
127
 
128
+ if isinstance(mesh, trimesh.Scene):
129
+ # Scene ๊ฐ์ฒด์ธ ๊ฒฝ์šฐ ๊ฐ ๋ฉ”์‹œ์— ํšŒ์ „ ์ ์šฉ
130
+ frame_scene = trimesh.Scene()
131
+
132
+ for node_name, transform, geometry_name in mesh.graph.nodes_geometry:
133
+ # ๋ฉ”์‹œ๋ฅผ ๋ณต์‚ฌํ•˜๊ณ  ํšŒ์ „ ์ ์šฉ
134
+ mesh_copy = mesh.geometry[geometry_name].copy()
135
+
136
+ # ๋ฉ”์‹œ์˜ ์ค‘์‹ฌ์  ๊ณ„์‚ฐ
137
+ center_point = mesh_copy.centroid if hasattr(mesh_copy, 'centroid') else np.zeros(3)
138
+
139
+ # ํšŒ์ „ ํ–‰๋ ฌ ์ƒ์„ฑ
140
+ rotation_matrix = tf.rotation_matrix(angle, [0, 1, 0], center_point)
141
+
142
+ # ๋ณ€ํ™˜ ์ ์šฉ
143
+ mesh_copy.apply_transform(rotation_matrix)
144
+
145
+ # ์”ฌ์— ์ถ”๊ฐ€
146
+ frame_scene.add_geometry(mesh_copy, node_name=node_name)
147
+
148
+ frames.append(frame_scene)
149
+ else:
150
+ # ์ผ๋ฐ˜ Trimesh ๊ฐ์ฒด์ธ ๊ฒฝ์šฐ ์ง์ ‘ ํšŒ์ „ ์ ์šฉ
151
+ animated_mesh = mesh.copy()
152
+ rotation_matrix = tf.rotation_matrix(angle, [0, 1, 0])
153
+ animated_mesh.apply_transform(rotation_matrix)
154
+ frames.append(animated_mesh)
155
 
156
  return frames
157
 
 
165
 
166
  for frame_idx in range(num_frames):
167
  t = frame_idx / (num_frames - 1) # Normalized time [0, 1]
 
 
 
 
 
168
  y_offset = amplitude * math.sin(2 * math.pi * t)
 
 
169
 
170
+ if isinstance(mesh, trimesh.Scene):
171
+ # Scene ๊ฐ์ฒด์ธ ๊ฒฝ์šฐ
172
+ frame_scene = trimesh.Scene()
173
+
174
+ for node_name, transform, geometry_name in mesh.graph.nodes_geometry:
175
+ # ๋ฉ”์‹œ๋ฅผ ๋ณต์‚ฌ
176
+ mesh_copy = mesh.geometry[geometry_name].copy()
177
+
178
+ # ๋ณ€ํ™˜ ์ ์šฉ
179
+ translation_matrix = tf.translation_matrix([0, y_offset, 0])
180
+ mesh_copy.apply_transform(translation_matrix)
181
+
182
+ # ์”ฌ์— ์ถ”๊ฐ€
183
+ frame_scene.add_geometry(mesh_copy, node_name=node_name)
184
+
185
+ frames.append(frame_scene)
186
+ else:
187
+ # ์ผ๋ฐ˜ Trimesh ๊ฐ์ฒด์ธ ๊ฒฝ์šฐ
188
+ animated_mesh = mesh.copy()
189
+ translation_matrix = tf.translation_matrix([0, y_offset, 0])
190
+ animated_mesh.apply_transform(translation_matrix)
191
+ frames.append(animated_mesh)
192
 
193
  return frames
194
 
 
200
  # Normalize the mesh for consistent animation
201
  mesh, original_center, original_scale = normalize_mesh(mesh)
202
 
203
+ # ์”ฌ์ธ ๊ฒฝ์šฐ ๋ถ€๋ถ„๋ณ„ ์ฒ˜๋ฆฌ
204
+ if isinstance(mesh, trimesh.Scene):
205
+ for frame_idx in range(num_frames):
206
+ t = frame_idx / (num_frames - 1) # Normalized time [0, 1]
207
+
208
+ # ์ƒˆ ์”ฌ ์ƒ์„ฑ
209
+ frame_scene = trimesh.Scene()
 
 
 
 
 
 
 
 
 
210
 
211
+ # ๊ฐ ๋…ธ๋“œ๋ณ„ ํญ๋ฐœ ์• ๋‹ˆ๋ฉ”์ด์…˜ ์ ์šฉ
212
+ for node_name, transform, geometry_name in mesh.graph.nodes_geometry:
213
+ mesh_copy = mesh.geometry[geometry_name].copy()
 
214
 
215
+ # ๋…ธ๋“œ์˜ ์ค‘์‹ฌ์  ๊ณ„์‚ฐ
216
+ center_point = mesh_copy.centroid if hasattr(mesh_copy, 'centroid') else np.zeros(3)
217
+
218
+ # ๋ฐฉํ–ฅ ๋ฒกํ„ฐ (์›์ ์—์„œ ๊ฐ์ฒด ์ค‘์‹ฌ๊นŒ์ง€)
219
+ direction = center_point
220
  if np.linalg.norm(direction) < 1e-10:
221
+ # ์ค‘์‹ฌ์ ์ด ์›์ ๊ณผ ๋„ˆ๋ฌด ๊ฐ€๊นŒ์šฐ๋ฉด ๋žœ๋ค ๋ฐฉํ–ฅ ์„ ํƒ
222
  direction = np.random.rand(3) - 0.5
223
 
224
  direction = direction / np.linalg.norm(direction)
225
 
226
+ # ํญ๋ฐœ ์ด๋™ ์ ์šฉ
227
+ translation = direction * t * 0.5 # ํญ๋ฐœ ๊ฐ•๋„ ์กฐ์ ˆ
228
  translation_matrix = tf.translation_matrix(translation)
229
+ mesh_copy.apply_transform(translation_matrix)
230
 
231
+ # ์”ฌ์— ์ถ”๊ฐ€
232
+ frame_scene.add_geometry(mesh_copy, node_name=f"{node_name}_{frame_idx}")
233
 
234
+ frames.append(frame_scene)
235
+ else:
236
+ # ๋ฉ”์‹œ๋ฅผ ์—ฌ๋Ÿฌ ๋ถ€๋ถ„์œผ๋กœ ๋ถ„ํ• 
237
+ try:
238
+ components = mesh.split(only_watertight=False)
239
+ if len(components) <= 1:
240
+ # ์ปดํฌ๋„ŒํŠธ๊ฐ€ ํ•˜๋‚˜๋ฟ์ด๋ฉด ์ •์  ๊ธฐ๋ฐ˜ ํญ๋ฐœ ์• ๋‹ˆ๋ฉ”์ด์…˜ ์‚ฌ์šฉ
241
+ raise ValueError("Mesh cannot be split into components")
242
+ except:
243
+ # ๋ถ„ํ• ์ด ์‹คํŒจํ•˜๋ฉด ์ •์  ๊ธฐ๋ฐ˜ ํญ๋ฐœ ์• ๋‹ˆ๋ฉ”์ด์…˜ ์‚ฌ์šฉ
244
+ for frame_idx in range(num_frames):
245
+ t = frame_idx / (num_frames - 1) # Normalized time [0, 1]
246
+
247
+ # ๋ฉ”์‹œ ๋ณต์‚ฌ
248
+ animated_mesh = mesh.copy()
249
+ vertices = animated_mesh.vertices.copy()
250
+
251
+ # ๊ฐ ์ •์ ์„ ์ค‘์‹ฌ์—์„œ ๋ฐ”๊นฅ์ชฝ์œผ๋กœ ์ด๋™
252
+ directions = vertices.copy()
253
+ norms = np.linalg.norm(directions, axis=1, keepdims=True)
254
+ mask = norms > 1e-10
255
+ directions[mask] = directions[mask] / norms[mask]
256
+ directions[~mask] = np.random.rand(np.sum(~mask), 3) - 0.5
257
+
258
+ # ํญ๋ฐœ ๊ณ„์ˆ˜ ์ ์šฉ
259
+ vertices += directions * t * 0.3
260
+ animated_mesh.vertices = vertices
261
+
262
+ frames.append(animated_mesh)
263
  else:
264
+ # ์ปดํฌ๋„ŒํŠธ ๊ธฐ๋ฐ˜ ํญ๋ฐœ ์• ๋‹ˆ๋ฉ”์ด์…˜
265
+ for frame_idx in range(num_frames):
266
+ t = frame_idx / (num_frames - 1) # Normalized time [0, 1]
267
+
268
+ # ์ƒˆ ์”ฌ ์ƒ์„ฑ
269
+ scene = trimesh.Scene()
270
+
271
+ # ๊ฐ ์ปดํฌ๋„ŒํŠธ๋ฅผ ์ค‘์‹ฌ์—์„œ ๋ฐ”๊นฅ์ชฝ์œผ๋กœ ์ด๋™
272
+ for i, component in enumerate(components):
273
+ # ์ปดํฌ๋„ŒํŠธ ๋ณต์‚ฌ
274
+ animated_component = component.copy()
275
+
276
+ # ์ปดํฌ๋„ŒํŠธ ์ค‘์‹ฌ์ ์—์„œ ๋ฐฉํ–ฅ ๊ณ„์‚ฐ
277
+ direction = animated_component.centroid
278
+ if np.linalg.norm(direction) < 1e-10:
279
+ # ์ค‘์‹ฌ์ ์ด ์›์ ๊ณผ ๋„ˆ๋ฌด ๊ฐ€๊นŒ์šฐ๋ฉด ๋žœ๋ค ๋ฐฉํ–ฅ ์„ ํƒ
280
+ direction = np.random.rand(3) - 0.5
281
+
282
+ direction = direction / np.linalg.norm(direction)
283
+
284
+ # ํญ๋ฐœ ์ด๋™ ์ ์šฉ
285
+ translation = direction * t * 0.5
286
+ translation_matrix = tf.translation_matrix(translation)
287
+ animated_component.apply_transform(translation_matrix)
288
+
289
+ # ์”ฌ์— ์ถ”๊ฐ€
290
+ scene.add_geometry(animated_component, node_name=f"component_{i}")
291
+
292
+ # ์”ฌ์„ ๋‹จ์ผ ๋ฉ”์‹œ๋กœ ๋ณ€ํ™˜ (๊ทผ์‚ฌ์น˜)
293
+ animated_mesh = trimesh.util.concatenate(scene.dump())
294
+ frames.append(animated_mesh)
295
 
296
  return frames
297
 
 
355
 
356
  for frame in frames:
357
  # Create a scene with the frame
358
+ if not isinstance(frame, trimesh.Scene):
359
+ scene = trimesh.Scene(frame)
360
+ else:
361
+ scene = frame
362
 
363
+ # ์ž๋™ ์นด๋ฉ”๋ผ ์„ค์ • (์ „์ฒด ์žฅ๋ฉด์ด ๋ณด์ด๋„๋ก)
364
  try:
365
+ scene.camera
 
366
  except:
367
+ # ๊ธฐ๋ณธ ์นด๋ฉ”๋ผ๊ฐ€ ์—†์œผ๋ฉด ์ƒ์„ฑ
368
+ scene.set_camera()
369
 
370
+ # ๋ชจ๋“  ๊ฐ์ฒด๊ฐ€ ๋ณด์ด๋„๋ก ์นด๋ฉ”๋ผ ์œ„์น˜ ์กฐ์ •
371
+ scene.camera_transform = scene.camera_transform
372
+
373
  # Render the frame
374
  try:
375
+ rendered_img = scene.save_image(resolution=resolution, background=background_color)
376
+ pil_img = Image.open(rendered_img)
377
+ gif_frames.append(pil_img)
378
  except Exception as e:
379
  print(f"Error rendering frame: {str(e)}")
380
+ # ๋ Œ๋”๋ง ์‹คํŒจ ์‹œ ๋นˆ ์ด๋ฏธ์ง€ ์ƒ์„ฑ
381
  gif_frames.append(Image.new('RGB', resolution, (255, 255, 255)))
382
 
383
  # Save as GIF
 
398
  """Create animation from input mesh based on animation type"""
399
  # Load the mesh
400
  try:
401
+ # ๋จผ์ € Scene์œผ๋กœ ๋กœ๋“œ ์‹œ๋„
402
+ loaded_obj = trimesh.load(input_mesh_path)
403
+
404
+ # Scene์ธ ๊ฒฝ์šฐ ๋‹จ์ผ ๋ฉ”์‹œ๋กœ ๋ณ€ํ™˜ ์‹œ๋„
405
+ if isinstance(loaded_obj, trimesh.Scene):
406
+ print("Loaded a scene, extracting meshes...")
407
+ # ์”ฌ์—์„œ ๋ชจ๋“  ๋ฉ”์‹œ ์ถ”์ถœ
408
+ meshes = []
409
+ for geometry in loaded_obj.geometry.values():
410
+ if isinstance(geometry, trimesh.Trimesh):
411
+ meshes.append(geometry)
412
+
413
+ if not meshes:
414
+ print("No meshes found in scene")
415
+ return None, None
416
+
417
+ # ๋ชจ๋“  ๋ฉ”์‹œ๋ฅผ ํ•˜๋‚˜๋กœ ๊ฒฐํ•ฉ
418
+ mesh = trimesh.util.concatenate(meshes)
419
+ elif isinstance(loaded_obj, trimesh.Trimesh):
420
+ mesh = loaded_obj
421
+ else:
422
+ print(f"Unsupported object type: {type(loaded_obj)}")
423
+ return None, None
424
+
425
  except Exception as e:
426
  print(f"Error loading mesh: {str(e)}")
427
  return None, None