euler314 commited on
Commit
af88c1b
·
verified ·
1 Parent(s): 0a4a925

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +150 -24
app.py CHANGED
@@ -6,6 +6,7 @@ import requests
6
  import base64
7
  import sys
8
  import shutil
 
9
  from streamlit_ace import st_ace
10
 
11
  st.set_page_config(page_title="Blender 3D Viewer", layout="wide")
@@ -34,6 +35,37 @@ def find_blender():
34
 
35
  return None
36
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
37
  # Check if Blender is available at startup
38
  blender_path = find_blender()
39
  if not blender_path:
@@ -50,7 +82,6 @@ DEFAULT_TEXTURE_URLS = [
50
  # Sidebar: Blender script editor with example script
51
  default_script = """
52
  import bpy
53
- import os
54
  import math
55
 
56
  # Clear the scene
@@ -126,17 +157,116 @@ light.data.energy = 2.0
126
  bpy.context.scene.render.engine = 'CYCLES'
127
  bpy.context.scene.cycles.device = 'CPU'
128
  bpy.context.scene.render.film_transparent = True
129
-
130
- # Save the .blend file using an absolute path
131
- output_dir = os.environ.get('BLENDER_OUTPUT_DIR', os.getcwd())
132
- blend_file_path = os.path.join(output_dir, "scene.blend")
133
- bpy.ops.wm.save_as_mainfile(filepath=blend_file_path)
134
  """
135
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
136
  # Sidebar: Blender script editor
137
  st.sidebar.header("Blender Python Script")
138
  script_text = st_ace(
139
- value=default_script,
140
  placeholder="Paste your Blender Python script here...",
141
  language="python",
142
  theme="monokai",
@@ -172,10 +302,11 @@ if st.sidebar.button("Run & Export GLB"):
172
  # Use a more robust temporary directory approach
173
  tmp_dir = tempfile.mkdtemp(prefix="blender_app_")
174
  try:
175
- # 1) Save user script
 
176
  script_path = os.path.join(tmp_dir, "user_script.py")
177
  with open(script_path, "w") as f:
178
- f.write(script_text)
179
 
180
  # 2) Collect textures
181
  texture_paths = []
@@ -367,14 +498,8 @@ with st.expander("About this app"):
367
  - Generate and visualize 3D models with animations
368
  - Download the results as GLB files for use in other applications
369
 
370
- The example script creates a rotating Earth with the uploaded texture.
371
-
372
- ### Troubleshooting
373
-
374
- - If you encounter errors, check the console output for details
375
- - Make sure your script correctly saves files using absolute paths
376
- - For complex models, allow more time for processing
377
- - If the app doesn't work at all, the Hugging Face Space might need proper Blender installation
378
  """)
379
 
380
  with st.expander("Script Tips"):
@@ -390,14 +515,15 @@ with st.expander("Script Tips"):
390
  image = bpy.data.images.load(texture_paths[0])
391
  ```
392
 
393
- 2. **Saving files**: Always use absolute paths with the environment variable
 
 
394
 
395
  ```python
396
- # Example code to save your blend file
397
- output_dir = os.environ.get('BLENDER_OUTPUT_DIR', os.getcwd())
398
- blend_file_path = os.path.join(output_dir, "scene.blend")
399
- bpy.ops.wm.save_as_mainfile(filepath=blend_file_path)
 
400
  ```
401
-
402
- 3. **Animations**: Make sure to set keyframes if you want your model to animate
403
  """)
 
6
  import base64
7
  import sys
8
  import shutil
9
+ import re
10
  from streamlit_ace import st_ace
11
 
12
  st.set_page_config(page_title="Blender 3D Viewer", layout="wide")
 
35
 
36
  return None
37
 
38
+ # Preprocess script to ensure it has necessary imports and path handling
39
+ def preprocess_script(script_text, tmp_dir):
40
+ # Check if 'import os' exists in the script
41
+ if 'import os' not in script_text:
42
+ script_text = 'import os\n' + script_text
43
+
44
+ # Check if 'import math' exists in the script (commonly needed)
45
+ if 'import math' not in script_text and 'math.' in script_text:
46
+ script_text = 'import math\n' + script_text
47
+
48
+ # Check if script already has file saving code
49
+ if 'bpy.ops.wm.save_as_mainfile' not in script_text:
50
+ # Add file saving code at the end
51
+ script_text += f'''
52
+
53
+ # Save the .blend file
54
+ output_dir = os.environ.get('BLENDER_OUTPUT_DIR', os.getcwd())
55
+ blend_file_path = os.path.join(output_dir, "scene.blend")
56
+ bpy.ops.wm.save_as_mainfile(filepath=blend_file_path)
57
+ print(f"Saved blend file to: {{blend_file_path}}")
58
+ '''
59
+ else:
60
+ # Make sure existing save code uses absolute paths correctly
61
+ script_text = re.sub(
62
+ r'bpy\.ops\.wm\.save_as_mainfile\s*\(\s*filepath\s*=\s*["\']([^"\']+)["\']',
63
+ 'bpy.ops.wm.save_as_mainfile(filepath=os.path.join(os.environ.get("BLENDER_OUTPUT_DIR", os.getcwd()), "scene.blend")',
64
+ script_text
65
+ )
66
+
67
+ return script_text
68
+
69
  # Check if Blender is available at startup
70
  blender_path = find_blender()
71
  if not blender_path:
 
82
  # Sidebar: Blender script editor with example script
83
  default_script = """
84
  import bpy
 
85
  import math
86
 
87
  # Clear the scene
 
157
  bpy.context.scene.render.engine = 'CYCLES'
158
  bpy.context.scene.cycles.device = 'CPU'
159
  bpy.context.scene.render.film_transparent = True
 
 
 
 
 
160
  """
161
 
162
+ # Sidebar: Blender script examples
163
+ script_examples = {
164
+ "Earth with Texture": default_script,
165
+ "Solar System": """import bpy
166
+ import math
167
+
168
+ # Clear existing objects
169
+ bpy.ops.object.select_all(action='SELECT')
170
+ bpy.ops.object.delete(use_global=False)
171
+
172
+ # Function to create a sphere (planet or sun)
173
+ def create_celestial_body(name, radius, location, color):
174
+ # Create mesh sphere
175
+ bpy.ops.mesh.primitive_uv_sphere_add(radius=radius, location=location)
176
+ obj = bpy.context.active_object
177
+ obj.name = name
178
+
179
+ # Create material
180
+ mat = bpy.data.materials.new(name + "_Material")
181
+ mat.use_nodes = True
182
+ bsdf = mat.node_tree.nodes.get('Principled BSDF')
183
+ bsdf.inputs['Base Color'].default_value = (*color, 1)
184
+ bsdf.inputs['Roughness'].default_value = 0.5
185
+
186
+ # Assign material
187
+ if obj.data.materials:
188
+ obj.data.materials[0] = mat
189
+ else:
190
+ obj.data.materials.append(mat)
191
+ return obj
192
+
193
+ # Create the Sun (yellow)
194
+ sun = create_celestial_body('Sun', radius=2, location=(0,0,0), color=(1.0, 1.0, 0.0))
195
+
196
+ # Create an empty at origin to parent planets for orbiting
197
+ def create_orbit_empty(name):
198
+ bpy.ops.object.empty_add(type='PLAIN_AXES', location=(0,0,0))
199
+ empty = bpy.context.active_object
200
+ empty.name = name
201
+ return empty
202
+
203
+ # Planet definitions: name, radius, distance from sun, color, orbital period in frames
204
+ planets = [
205
+ ('Mercury', 0.3, 4, (0.8, 0.5, 0.2), 88),
206
+ ('Venus', 0.5, 6, (1.0, 0.8, 0.0), 224),
207
+ ('Earth', 0.5, 8, (0.2, 0.4, 1.0), 365),
208
+ ('Mars', 0.4, 10,(1.0, 0.3, 0.2), 687)
209
+ ]
210
+
211
+ for name, radius, distance, color, period in planets:
212
+ # Create orbit controller
213
+ orbit_empty = create_orbit_empty(name + '_Orbit')
214
+
215
+ # Create planet as child of orbit empty
216
+ planet = create_celestial_body(name, radius, location=(distance, 0, 0), color=color)
217
+ planet.parent = orbit_empty
218
+
219
+ # Animate the orbit: rotate empty around Z
220
+ orbit_empty.rotation_euler = (0, 0, 0)
221
+ orbit_empty.keyframe_insert(data_path='rotation_euler', frame=1, index=2)
222
+ orbit_empty.rotation_euler = (0, 0, math.radians(360))
223
+ orbit_empty.keyframe_insert(data_path='rotation_euler', frame=period, index=2)
224
+
225
+ # Set linear interpolation for smooth constant motion
226
+ action = orbit_empty.animation_data.action
227
+ fcurve = action.fcurves.find('rotation_euler', index=2)
228
+ if fcurve:
229
+ for kp in fcurve.keyframe_points:
230
+ kp.interpolation = 'LINEAR'
231
+
232
+ # Set scene frames
233
+ bpy.context.scene.frame_start = 1
234
+ bpy.context.scene.frame_end = 687 # Use Mars' period as the end frame
235
+
236
+ # Add simple starfield as world background
237
+ world = bpy.context.scene.world
238
+ world.use_nodes = True
239
+ bg = world.node_tree.nodes['Background']
240
+ bg.inputs['Color'].default_value = (0, 0, 0, 1)
241
+
242
+ # Set up a better camera view
243
+ bpy.ops.object.camera_add(location=(0, -20, 15))
244
+ camera = bpy.context.active_object
245
+ camera.rotation_euler = (math.radians(55), 0, 0)
246
+ bpy.context.scene.camera = camera
247
+
248
+ # Add a sun light
249
+ bpy.ops.object.light_add(type='SUN', location=(10, -10, 10))
250
+ sun_light = bpy.context.active_object
251
+ sun_light.data.energy = 5.0
252
+
253
+ # Set render settings
254
+ bpy.context.scene.render.engine = 'CYCLES'
255
+ bpy.context.scene.cycles.device = 'CPU'
256
+
257
+ print('Solar system animation setup complete.')"""
258
+ }
259
+
260
+ # Add example selector
261
+ selected_example = st.sidebar.selectbox(
262
+ "Load example script:",
263
+ options=list(script_examples.keys())
264
+ )
265
+
266
  # Sidebar: Blender script editor
267
  st.sidebar.header("Blender Python Script")
268
  script_text = st_ace(
269
+ value=script_examples[selected_example],
270
  placeholder="Paste your Blender Python script here...",
271
  language="python",
272
  theme="monokai",
 
302
  # Use a more robust temporary directory approach
303
  tmp_dir = tempfile.mkdtemp(prefix="blender_app_")
304
  try:
305
+ # 1) Preprocess and save user script
306
+ processed_script = preprocess_script(script_text, tmp_dir)
307
  script_path = os.path.join(tmp_dir, "user_script.py")
308
  with open(script_path, "w") as f:
309
+ f.write(processed_script)
310
 
311
  # 2) Collect textures
312
  texture_paths = []
 
498
  - Generate and visualize 3D models with animations
499
  - Download the results as GLB files for use in other applications
500
 
501
+ The app automatically adds necessary imports (`import os`) and file saving code to your scripts,
502
+ so you can focus on creating 3D content without worrying about environment details.
 
 
 
 
 
 
503
  """)
504
 
505
  with st.expander("Script Tips"):
 
515
  image = bpy.data.images.load(texture_paths[0])
516
  ```
517
 
518
+ 2. **You don't need to add file saving code** - the app automatically adds it
519
+
520
+ 3. **Animations**: Make sure to set keyframes if you want your model to animate:
521
 
522
  ```python
523
+ # Example animation (rotate object 360 degrees over 250 frames)
524
+ obj.rotation_euler = (0, 0, 0)
525
+ obj.keyframe_insert(data_path="rotation_euler", frame=1)
526
+ obj.rotation_euler = (0, 0, math.radians(360))
527
+ obj.keyframe_insert(data_path="rotation_euler", frame=250)
528
  ```
 
 
529
  """)