seawolf2357 commited on
Commit
39ea68e
·
verified ·
1 Parent(s): 883b871

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +285 -435
app.py CHANGED
@@ -1,3 +1,8 @@
 
 
 
 
 
1
  import os
2
  import shutil
3
  import torch
@@ -5,99 +10,16 @@ import numpy as np
5
  from PIL import Image
6
  import tempfile
7
  import uuid
8
- from typing import *
9
  from datetime import datetime
10
- from pathlib import Path
11
-
12
- from typing import Iterable
13
- from gradio.themes import Soft
14
- from gradio.themes.utils import colors, fonts, sizes
15
 
16
  import rerun as rr
17
- # Attempt to import blueprint for advanced view configuration
18
  try:
19
  import rerun.blueprint as rrb
20
  except ImportError:
21
  rrb = None
22
-
23
- from gradio_rerun import Rerun
24
-
25
- # --- Theme Configuration ---
26
- colors.orange_red = colors.Color(
27
- name="orange_red",
28
- c50="#FFF0E5",
29
- c100="#FFE0CC",
30
- c200="#FFC299",
31
- c300="#FFA366",
32
- c400="#FF8533",
33
- c500="#FF4500",
34
- c600="#E63E00",
35
- c700="#CC3700",
36
- c800="#B33000",
37
- c900="#992900",
38
- c950="#802200",
39
- )
40
-
41
- class OrangeRedTheme(Soft):
42
- def __init__(
43
- self,
44
- *,
45
- primary_hue: colors.Color | str = colors.gray,
46
- secondary_hue: colors.Color | str = colors.orange_red,
47
- neutral_hue: colors.Color | str = colors.slate,
48
- text_size: sizes.Size | str = sizes.text_lg,
49
- font: fonts.Font | str | Iterable[fonts.Font | str] = (
50
- fonts.GoogleFont("Outfit"), "Arial", "sans-serif",
51
- ),
52
- font_mono: fonts.Font | str | Iterable[fonts.Font | str] = (
53
- fonts.GoogleFont("IBM Plex Mono"), "ui-monospace", "monospace",
54
- ),
55
- ):
56
- super().__init__(
57
- primary_hue=primary_hue,
58
- secondary_hue=secondary_hue,
59
- neutral_hue=neutral_hue,
60
- text_size=text_size,
61
- font=font,
62
- font_mono=font_mono,
63
- )
64
- super().set(
65
- background_fill_primary="*primary_50",
66
- background_fill_primary_dark="*primary_900",
67
- body_background_fill="linear-gradient(135deg, *primary_200, *primary_100)",
68
- body_background_fill_dark="linear-gradient(135deg, *primary_900, *primary_800)",
69
- button_primary_text_color="white",
70
- button_primary_text_color_hover="white",
71
- button_primary_background_fill="linear-gradient(90deg, *secondary_500, *secondary_600)",
72
- button_primary_background_fill_hover="linear-gradient(90deg, *secondary_600, *secondary_700)",
73
- button_primary_background_fill_dark="linear-gradient(90deg, *secondary_600, *secondary_700)",
74
- button_primary_background_fill_hover_dark="linear-gradient(90deg, *secondary_500, *secondary_600)",
75
- button_secondary_text_color="black",
76
- button_secondary_text_color_hover="white",
77
- button_secondary_background_fill="linear-gradient(90deg, *primary_300, *primary_300)",
78
- button_secondary_background_fill_hover="linear-gradient(90deg, *primary_400, *primary_400)",
79
- button_secondary_background_fill_dark="linear-gradient(90deg, *primary_500, *primary_600)",
80
- button_secondary_background_fill_hover_dark="linear-gradient(90deg, *primary_500, *primary_500)",
81
- slider_color="*secondary_500",
82
- slider_color_dark="*secondary_600",
83
- block_title_text_weight="600",
84
- block_border_width="3px",
85
- block_shadow="*shadow_drop_lg",
86
- button_primary_shadow="*shadow_drop_lg",
87
- button_large_padding="11px",
88
- color_accent_soft="*primary_100",
89
- block_label_background_fill="*primary_200",
90
- )
91
-
92
- orange_red_theme = OrangeRedTheme()
93
-
94
- # --- Environment Setup ---
95
- os.environ["OPENCV_IO_ENABLE_OPENEXR"] = '1'
96
- os.environ["PYTORCH_CUDA_ALLOC_CONF"] = "expandable_segments:True"
97
- os.environ["ATTN_BACKEND"] = "flash_attn_3"
98
- os.environ["FLEX_GEMM_AUTOTUNE_CACHE_PATH"] = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'autotune_cache.json')
99
- os.environ["FLEX_GEMM_AUTOTUNER_VERBOSE"] = '1'
100
 
 
101
  import gradio as gr
102
  from gradio_client import Client, handle_file
103
  import spaces
@@ -105,21 +27,20 @@ from diffusers import ZImagePipeline
105
  from trellis2.pipelines import Trellis2ImageTo3DPipeline
106
  import o_voxel
107
 
 
 
 
 
 
 
108
  MAX_SEED = np.iinfo(np.int32).max
109
  TMP_DIR = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'tmp')
110
 
111
- print("Initializing models...")
112
-
113
  print("Loading Z-Image-Turbo...")
114
  try:
115
- z_pipe = ZImagePipeline.from_pretrained(
116
- "Tongyi-MAI/Z-Image-Turbo",
117
- torch_dtype=torch.bfloat16,
118
- low_cpu_mem_usage=False,
119
- )
120
  device = "cuda" if torch.cuda.is_available() else "cpu"
121
  z_pipe.to(device)
122
- print("Z-Image-Turbo loaded.")
123
  except Exception as e:
124
  print(f"Failed to load Z-Image-Turbo: {e}")
125
  z_pipe = None
@@ -130,22 +51,24 @@ try:
130
  trellis_pipeline.rembg_model = None
131
  trellis_pipeline.low_vram = False
132
  trellis_pipeline.cuda()
133
- print("TRELLIS.2 loaded.")
134
  except Exception as e:
135
  print(f"Failed to load TRELLIS.2: {e}")
136
  trellis_pipeline = None
137
 
138
  rmbg_client = Client("briaai/BRIA-RMBG-2.0")
139
 
 
140
  def start_session(req: gr.Request):
141
  user_dir = os.path.join(TMP_DIR, str(req.session_hash))
142
  os.makedirs(user_dir, exist_ok=True)
143
 
 
144
  def end_session(req: gr.Request):
145
  user_dir = os.path.join(TMP_DIR, str(req.session_hash))
146
  if os.path.exists(user_dir):
147
  shutil.rmtree(user_dir)
148
 
 
149
  def remove_background(input: Image.Image) -> Image.Image:
150
  with tempfile.NamedTemporaryFile(suffix='.png') as f:
151
  input = input.convert('RGB')
@@ -154,11 +77,10 @@ def remove_background(input: Image.Image) -> Image.Image:
154
  output = Image.open(output)
155
  return output
156
 
 
157
  def preprocess_image(input: Image.Image) -> Image.Image:
158
- """Preprocess the input image: remove bg, crop, resize."""
159
  if input is None:
160
  return None
161
-
162
  has_alpha = False
163
  if input.mode == 'RGBA':
164
  alpha = np.array(input)[:, :, 3]
@@ -172,12 +94,11 @@ def preprocess_image(input: Image.Image) -> Image.Image:
172
  output = input
173
  else:
174
  output = remove_background(input)
175
-
176
  output_np = np.array(output)
177
  alpha = output_np[:, :, 3]
178
  bbox = np.argwhere(alpha > 0.8 * 255)
179
  if bbox.size == 0:
180
- return output
181
  bbox = np.min(bbox[:, 1]), np.min(bbox[:, 0]), np.max(bbox[:, 1]), np.max(bbox[:, 0])
182
  center = (bbox[0] + bbox[2]) / 2, (bbox[1] + bbox[3]) / 2
183
  size = max(bbox[2] - bbox[0], bbox[3] - bbox[1])
@@ -189,21 +110,20 @@ def preprocess_image(input: Image.Image) -> Image.Image:
189
  output = Image.fromarray((output * 255).astype(np.uint8))
190
  return output
191
 
 
192
  def get_seed(randomize_seed: bool, seed: int) -> int:
193
  return np.random.randint(0, MAX_SEED) if randomize_seed else seed
194
 
 
195
  @spaces.GPU
196
  def generate_txt2img(prompt, progress=gr.Progress(track_tqdm=True)):
197
- """Generate Image using Z-Image Turbo"""
198
  if z_pipe is None:
199
  raise gr.Error("Z-Image-Turbo model failed to load.")
200
  if not prompt.strip():
201
  raise gr.Error("Please enter a prompt.")
202
-
203
  device = "cuda" if torch.cuda.is_available() else "cpu"
204
  generator = torch.Generator(device).manual_seed(42)
205
-
206
- progress(0.1, desc="Generating Text-to-Image...")
207
  try:
208
  result = z_pipe(
209
  prompt=prompt,
@@ -216,176 +136,85 @@ def generate_txt2img(prompt, progress=gr.Progress(track_tqdm=True)):
216
  )
217
  return result.images[0]
218
  except Exception as e:
219
- raise gr.Error(f"Z-Image Generation failed: {str(e)}")
 
220
 
221
  @spaces.GPU(duration=120)
222
  def generate_3d(
223
- image: Image.Image,
224
- seed: int,
225
- resolution: str,
226
- decimation_target: int,
227
- texture_size: int,
228
- ss_guidance_strength: float,
229
- ss_guidance_rescale: float,
230
- ss_sampling_steps: int,
231
- ss_rescale_t: float,
232
- shape_guidance: float,
233
- shape_rescale: float,
234
- shape_steps: int,
235
- shape_rescale_t: float,
236
- tex_guidance: float,
237
- tex_rescale: float,
238
- tex_steps: int,
239
- tex_rescale_t: float,
240
- req: gr.Request,
241
- progress=gr.Progress(track_tqdm=True),
242
  ) -> Tuple[str, str]:
243
-
244
  if image is None:
245
  raise gr.Error("Please provide an input image.")
246
-
247
  if trellis_pipeline is None:
248
  raise gr.Error("TRELLIS model is not loaded.")
249
-
250
  user_dir = os.path.join(TMP_DIR, str(req.session_hash))
251
  os.makedirs(user_dir, exist_ok=True)
252
 
253
- progress(0.1, desc="Generating 3D Geometry...")
254
  try:
255
  outputs, latents = trellis_pipeline.run(
256
- image,
257
- seed=seed,
258
- preprocess_image=False,
259
- sparse_structure_sampler_params={
260
- "steps": ss_sampling_steps,
261
- "guidance_strength": ss_guidance_strength,
262
- "guidance_rescale": ss_guidance_rescale,
263
- "rescale_t": ss_rescale_t,
264
- },
265
- shape_slat_sampler_params={
266
- "steps": shape_steps,
267
- "guidance_strength": shape_guidance,
268
- "guidance_rescale": shape_rescale,
269
- "rescale_t": shape_rescale_t,
270
- },
271
- tex_slat_sampler_params={
272
- "steps": tex_steps,
273
- "guidance_strength": tex_guidance,
274
- "guidance_rescale": tex_rescale,
275
- "rescale_t": tex_rescale_t,
276
- },
277
  pipeline_type={"512": "512", "1024": "1024_cascade", "1536": "1536_cascade"}[resolution],
278
  return_latent=True,
279
  )
280
 
281
- # 2. Process Mesh
282
  progress(0.7, desc="Processing Mesh...")
283
  mesh = outputs[0]
284
- # FIX: Aggressive simplification to prevent CUDA errors during post-processing
285
- mesh.simplify(1000000)
286
-
287
- # 3. Export to GLB
288
- progress(0.9, desc="Baking Texture & Exporting GLB...")
289
 
290
- grid_size = latents[2]
 
291
 
292
- # Safe export with fallback if high-quality hole filling fails
293
  try:
294
  glb = o_voxel.postprocess.to_glb(
295
- vertices=mesh.vertices,
296
- faces=mesh.faces,
297
- attr_volume=mesh.attrs,
298
- coords=mesh.coords,
299
- attr_layout=trellis_pipeline.pbr_attr_layout,
300
- grid_size=grid_size,
301
- aabb=[[-0.5, -0.5, -0.5], [0.5, 0.5, 0.5]],
302
- decimation_target=decimation_target,
303
- texture_size=texture_size,
304
- remesh=True,
305
- remesh_band=1,
306
- remesh_project=0,
307
- use_tqdm=True,
308
  )
309
- except RuntimeError as e:
310
- print(f"Warning: Post-processing failed with remesh=True. Error: {e}")
311
- print("Retrying with remesh=False (Standard mesh generation)...")
312
  glb = o_voxel.postprocess.to_glb(
313
- vertices=mesh.vertices,
314
- faces=mesh.faces,
315
- attr_volume=mesh.attrs,
316
- coords=mesh.coords,
317
- attr_layout=trellis_pipeline.pbr_attr_layout,
318
- grid_size=grid_size,
319
- aabb=[[-0.5, -0.5, -0.5], [0.5, 0.5, 0.5]],
320
- decimation_target=decimation_target,
321
- texture_size=texture_size,
322
- remesh=False,
323
- remesh_band=1,
324
- remesh_project=0,
325
- use_tqdm=True,
326
  )
327
 
328
- now = datetime.now()
329
- timestamp = now.strftime("%Y-%m-%dT%H%M%S")
330
- glb_path = os.path.join(user_dir, f'trellis_output_{timestamp}.glb')
331
-
332
- # FIX: extension_webp=False ensures compatibility with Rerun/Standard Viewers
333
  glb.export(glb_path, extension_webp=False)
334
 
335
- # --- Rerun Visualization Logic ---
336
- progress(0.95, desc="Creating Rerun Visualization...")
337
-
338
  run_id = str(uuid.uuid4())
 
339
 
340
- # Robustly handle different Rerun SDK versions
341
- rec = None
342
- if hasattr(rr, "new_recording"):
343
- rec = rr.new_recording(application_id="TRELLIS-3D-Viewer", recording_id=run_id)
344
- elif hasattr(rr, "RecordingStream"):
345
- rec = rr.RecordingStream(application_id="TRELLIS-3D-Viewer", recording_id=run_id)
346
- else:
347
- rr.init("TRELLIS-3D-Viewer", recording_id=run_id, spawn=False)
348
- rec = rr
349
-
350
- # 1. Clear State
351
  rec.log("world", rr.Clear(recursive=True), static=True)
352
-
353
- # 2. Set View Coordinates: RIGHT_HAND_Y_UP
354
- # This defines +Y as Up, +X as Right, and +Z as "Back" (towards the viewer).
355
- # This effectively places the default camera in front of the object (at +Z).
356
  rec.log("world", rr.ViewCoordinates.RIGHT_HAND_Y_UP, static=True)
357
-
358
- # 3. Add Axes Helpers (Red=X, Green=Y, Blue=Z)
359
- try:
360
- rec.log("world/axes/x", rr.Arrows3D(vectors=[[0.5, 0, 0]], colors=[[255, 0, 0]]), static=True)
361
- rec.log("world/axes/y", rr.Arrows3D(vectors=[[0, 0.5, 0]], colors=[[0, 255, 0]]), static=True)
362
- rec.log("world/axes/z", rr.Arrows3D(vectors=[[0, 0, 0.5]], colors=[[0, 0, 255]]), static=True)
363
- except Exception:
364
- pass
365
-
366
- # 4. Log the 3D Model
367
  rec.log("world/model", rr.Asset3D(path=glb_path), static=True)
368
 
369
- # 5. Send Blueprint (if supported) to force a clean 3D view
370
  if rrb is not None:
371
  try:
372
- # Create a simple blueprint with a 3D view of "world"
373
- blueprint = rrb.Blueprint(
374
- rrb.Spatial3DView(
375
- origin="/world",
376
- name="3D View",
377
- ),
378
- collapse_panels=True,
379
- )
380
  rec.send_blueprint(blueprint)
381
- except Exception as e:
382
- print(f"Blueprint creation failed (non-fatal): {e}")
383
-
384
- # Save the Rerun recording (.rrd)
385
- rrd_path = os.path.join(user_dir, f'trellis_output_{timestamp}.rrd')
386
  rec.save(rrd_path)
387
-
388
- # Clean up
389
  torch.cuda.empty_cache()
390
  return rrd_path, glb_path
391
 
@@ -393,47 +222,235 @@ def generate_3d(
393
  torch.cuda.empty_cache()
394
  raise gr.Error(f"Generation failed: {str(e)}")
395
 
396
- css="""
397
- #col-container {
398
- margin: 0 auto;
399
- max-width: 960px;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
400
  }
401
- #main-title h1 {font-size: 2.4em !important;}
402
  """
403
 
 
 
 
 
 
 
 
 
 
 
 
 
404
  if __name__ == "__main__":
405
  os.makedirs(TMP_DIR, exist_ok=True)
406
 
407
- with gr.Blocks(delete_cache=(300, 300)) as demo:
408
- gr.Markdown("# **TRELLIS.2 (Text-to-3D)**", elem_id="main-title")
409
- gr.Markdown("""
410
- **Workflow:**
411
- Generate a 3D asset directly by converting Text-to-Image → 3D or Image-to-3D, powered by [TRELLIS.2](https://huggingface.co/microsoft/TRELLIS.2-4B) and [Z-Image-Turbo](https://huggingface.co/Tongyi-MAI/Z-Image-Turbo).
 
 
 
 
 
412
  """)
 
 
 
413
 
414
  with gr.Row():
415
  with gr.Column(scale=1, min_width=360):
416
-
417
  with gr.Tabs():
418
- with gr.Tab("Text-to-Image-3D"):
419
- txt_prompt = gr.Textbox(label="Prompt", placeholder="eg. A Plane 3D", lines=2)
420
- btn_gen_img = gr.Button("1.Generate Image", variant="primary")
421
- with gr.Tab("Image-to-3D"):
422
- gr.Markdown("Upload an image directly if you have one.")
423
 
424
- image_prompt = gr.Image(label="Input Image", format="png", image_mode="RGBA", type="pil", height=350)
425
 
426
- with gr.Accordion(label="3D Settings", open=False):
427
- resolution = gr.Radio(["512", "1024", "1536"], label="Generation Resolution", value="1024")
428
  seed = gr.Slider(0, MAX_SEED, label="Seed", value=0, step=1)
429
- randomize_seed = gr.Checkbox(label="Randomize Seed", value=True)
430
-
431
  decimation_target = gr.Slider(50000, 500000, label="Target Faces", value=150000, step=10000)
432
  texture_size = gr.Slider(512, 4096, label="Texture Size", value=1024, step=512)
433
 
434
- btn_gen_3d = gr.Button("2.Generate 3D", variant="primary", scale=2)
435
 
436
- with gr.Accordion(label="Advanced Sampler Settings", open=False):
437
  gr.Markdown("**Stage 1: Sparse Structure**")
438
  ss_guidance_strength = gr.Slider(1.0, 10.0, value=7.5, label="Guidance")
439
  ss_guidance_rescale = gr.Slider(0.0, 1.0, value=0.7, label="Rescale")
@@ -453,193 +470,26 @@ if __name__ == "__main__":
453
  tex_rescale_t = gr.Slider(1.0, 6.0, value=3.0, label="Rescale T")
454
 
455
  with gr.Column(scale=2):
456
- gr.Markdown("### 3D Output")
457
-
458
- rerun_output = Rerun(
459
- label="Rerun 3D Viewer",
460
- height=600
461
- )
462
-
463
- download_btn = gr.DownloadButton(label="3.Download GLB File", variant="primary")
464
-
465
- gr.Examples(
466
- examples=[
467
- ["example-images/A (1).webp"],
468
- ["example-images/A (2).webp"],
469
- ["example-images/A (3).webp"],
470
- ["example-images/A (4).webp"],
471
- ["example-images/A (5).webp"],
472
- ["example-images/A (6).webp"],
473
- ["example-images/A (7).webp"],
474
- ["example-images/A (8).webp"],
475
- ["example-images/A (9).webp"],
476
- ["example-images/A (10).webp"],
477
- ["example-images/A (11).webp"],
478
- ["example-images/A (12).webp"],
479
- ["example-images/A (13).webp"],
480
- ["example-images/A (14).webp"],
481
- ["example-images/A (15).webp"],
482
- ["example-images/A (16).webp"],
483
- ["example-images/A (17).webp"],
484
- ["example-images/A (18).webp"],
485
- ["example-images/A (19).webp"],
486
- ["example-images/A (20).webp"],
487
- ["example-images/A (21).webp"],
488
- ["example-images/A (22).webp"],
489
- ["example-images/A (23).webp"],
490
- ["example-images/A (24).webp"],
491
- ["example-images/A (25).webp"],
492
- ["example-images/A (26).webp"],
493
- ["example-images/A (27).webp"],
494
- ["example-images/A (28).webp"],
495
- ["example-images/A (29).webp"],
496
- ["example-images/A (30).webp"],
497
- ["example-images/A (31).webp"],
498
- ["example-images/A (32).webp"],
499
- ["example-images/A (33).webp"],
500
- ["example-images/A (34).webp"],
501
- ["example-images/A (35).webp"],
502
- ["example-images/A (36).webp"],
503
- ["example-images/A (37).webp"],
504
- ["example-images/A (38).webp"],
505
- ["example-images/A (39).webp"],
506
- ["example-images/A (40).webp"],
507
- ["example-images/A (41).webp"],
508
- ["example-images/A (42).webp"],
509
- ["example-images/A (43).webp"],
510
- ["example-images/A (44).webp"],
511
- ["example-images/A (45).webp"],
512
- ["example-images/A (46).webp"],
513
- ["example-images/A (47).webp"],
514
- ["example-images/A (48).webp"],
515
- ["example-images/A (49).webp"],
516
- ["example-images/A (50).webp"],
517
- ["example-images/A (51).webp"],
518
- ["example-images/A (52).webp"],
519
- ["example-images/A (53).webp"],
520
- ["example-images/A (54).webp"],
521
- ["example-images/A (55).webp"],
522
- ["example-images/A (56).webp"],
523
- ["example-images/A (57).webp"],
524
- ["example-images/A (58).webp"],
525
- ["example-images/A (59).webp"],
526
- ["example-images/A (60).webp"],
527
- ["example-images/A (61).webp"],
528
- ["example-images/A (62).webp"],
529
- ["example-images/A (63).webp"],
530
- ["example-images/A (64).webp"],
531
- ["example-images/A (65).webp"],
532
- ["example-images/A (66).webp"],
533
- ["example-images/A (67).webp"],
534
- ["example-images/A (68).webp"],
535
- ["example-images/A (69).webp"],
536
- ["example-images/A (70).webp"],
537
- ["example-images/A (71).webp"],
538
- ],
539
- inputs=[image_prompt],
540
- label="Image Examples [image-to-3d]"
541
- )
542
-
543
- gr.Examples(
544
- examples=[
545
- ["A Cat 3D model"],
546
- ["A realistic Cat 3D model"],
547
- ["A cartoon Cat 3D model"],
548
- ["A low poly Cat 3D"],
549
- ["A cyberpunk Cat 3D"],
550
- ["A robotic Cat 3D"],
551
- ["A fluffy Cat 3D"],
552
- ["A fantasy Cat 3D creature"],
553
- ["A stylized Cat 3D"],
554
- ["A Cat 3D sculpture"],
555
-
556
- ["A Plane 3D model"],
557
- ["A commercial Plane 3D"],
558
- ["A fighter jet Plane 3D"],
559
- ["A low poly Plane 3D"],
560
- ["A vintage Plane 3D"],
561
- ["A futuristic Plane 3D"],
562
- ["A cargo Plane 3D"],
563
- ["A private jet Plane 3D"],
564
- ["A toy Plane 3D"],
565
- ["A realistic Plane 3D"],
566
-
567
- ["A Car 3D model"],
568
- ["A sports Car 3D"],
569
- ["A luxury Car 3D"],
570
- ["A low poly Car 3D"],
571
- ["A racing Car 3D"],
572
- ["A cyberpunk Car 3D"],
573
- ["A vintage Car 3D"],
574
- ["A futuristic Car 3D"],
575
- ["A SUV Car 3D"],
576
- ["A electric Car 3D"],
577
-
578
- ["A Shoe 3D model"],
579
- ["A sneaker Shoe 3D"],
580
- ["A running Shoe 3D"],
581
- ["A leather Shoe 3D"],
582
- ["A high heel Shoe 3D"],
583
- ["A boot Shoe 3D"],
584
- ["A low poly Shoe 3D"],
585
- ["A futuristic Shoe 3D"],
586
- ["A sports Shoe 3D"],
587
- ["A casual Shoe 3D"],
588
-
589
- ["A Chair 3D model"],
590
- ["A Table 3D model"],
591
- ["A Sofa 3D model"],
592
- ["A Lamp 3D model"],
593
- ["A Watch 3D model"],
594
- ["A Backpack 3D model"],
595
- ["A Drone 3D model"],
596
- ["A Robot 3D model"],
597
- ["A Smartphone 3D model"],
598
- ["A Headphones 3D model"],
599
-
600
- ["A House 3D model"],
601
- ["A Skyscraper 3D model"],
602
- ["A Bridge 3D model"],
603
- ["A Castle 3D model"],
604
- ["A Spaceship 3D model"],
605
- ["A Rocket 3D model"],
606
- ["A Satellite 3D model"],
607
- ["A Tank 3D model"],
608
- ["A Motorcycle 3D model"],
609
- ["A Bicycle 3D model"]
610
- ],
611
- inputs=[txt_prompt],
612
- label="3D Prompt Examples [text-to-3d]"
613
- )
614
 
 
 
 
615
  demo.load(start_session)
616
  demo.unload(end_session)
617
 
618
- btn_gen_img.click(
619
- generate_txt2img,
620
- inputs=[txt_prompt],
621
- outputs=[image_prompt]
622
- ).then(
623
- preprocess_image,
624
- inputs=[image_prompt],
625
- outputs=[image_prompt]
626
  )
627
 
628
- image_prompt.upload(
629
- preprocess_image,
630
- inputs=[image_prompt],
631
- outputs=[image_prompt],
632
- )
633
 
634
- btn_gen_3d.click(
635
- get_seed,
636
- inputs=[randomize_seed, seed],
637
- outputs=[seed],
638
- ).then(
639
  generate_3d,
640
  inputs=[
641
- image_prompt, seed, resolution,
642
- decimation_target, texture_size,
643
  ss_guidance_strength, ss_guidance_rescale, ss_sampling_steps, ss_rescale_t,
644
  shape_guidance, shape_rescale, shape_steps, shape_rescale_t,
645
  tex_guidance, tex_rescale, tex_steps, tex_rescale_t,
@@ -647,4 +497,4 @@ if __name__ == "__main__":
647
  outputs=[rerun_output, download_btn],
648
  )
649
 
650
- demo.launch(theme=orange_red_theme, css=css, mcp_server=True, ssr_mode=False, show_error=True)
 
1
+ """
2
+ TRELLIS.2 Text-to-3D Generator
3
+ 🎨 Comic Classic Theme
4
+ """
5
+
6
  import os
7
  import shutil
8
  import torch
 
10
  from PIL import Image
11
  import tempfile
12
  import uuid
13
+ from typing import Tuple
14
  from datetime import datetime
 
 
 
 
 
15
 
16
  import rerun as rr
 
17
  try:
18
  import rerun.blueprint as rrb
19
  except ImportError:
20
  rrb = None
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
21
 
22
+ from gradio_rerun import Rerun
23
  import gradio as gr
24
  from gradio_client import Client, handle_file
25
  import spaces
 
27
  from trellis2.pipelines import Trellis2ImageTo3DPipeline
28
  import o_voxel
29
 
30
+ os.environ["OPENCV_IO_ENABLE_OPENEXR"] = '1'
31
+ os.environ["PYTORCH_CUDA_ALLOC_CONF"] = "expandable_segments:True"
32
+ os.environ["ATTN_BACKEND"] = "flash_attn_3"
33
+ os.environ["FLEX_GEMM_AUTOTUNE_CACHE_PATH"] = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'autotune_cache.json')
34
+ os.environ["FLEX_GEMM_AUTOTUNER_VERBOSE"] = '1'
35
+
36
  MAX_SEED = np.iinfo(np.int32).max
37
  TMP_DIR = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'tmp')
38
 
 
 
39
  print("Loading Z-Image-Turbo...")
40
  try:
41
+ z_pipe = ZImagePipeline.from_pretrained("Tongyi-MAI/Z-Image-Turbo", torch_dtype=torch.bfloat16, low_cpu_mem_usage=False)
 
 
 
 
42
  device = "cuda" if torch.cuda.is_available() else "cpu"
43
  z_pipe.to(device)
 
44
  except Exception as e:
45
  print(f"Failed to load Z-Image-Turbo: {e}")
46
  z_pipe = None
 
51
  trellis_pipeline.rembg_model = None
52
  trellis_pipeline.low_vram = False
53
  trellis_pipeline.cuda()
 
54
  except Exception as e:
55
  print(f"Failed to load TRELLIS.2: {e}")
56
  trellis_pipeline = None
57
 
58
  rmbg_client = Client("briaai/BRIA-RMBG-2.0")
59
 
60
+
61
  def start_session(req: gr.Request):
62
  user_dir = os.path.join(TMP_DIR, str(req.session_hash))
63
  os.makedirs(user_dir, exist_ok=True)
64
 
65
+
66
  def end_session(req: gr.Request):
67
  user_dir = os.path.join(TMP_DIR, str(req.session_hash))
68
  if os.path.exists(user_dir):
69
  shutil.rmtree(user_dir)
70
 
71
+
72
  def remove_background(input: Image.Image) -> Image.Image:
73
  with tempfile.NamedTemporaryFile(suffix='.png') as f:
74
  input = input.convert('RGB')
 
77
  output = Image.open(output)
78
  return output
79
 
80
+
81
  def preprocess_image(input: Image.Image) -> Image.Image:
 
82
  if input is None:
83
  return None
 
84
  has_alpha = False
85
  if input.mode == 'RGBA':
86
  alpha = np.array(input)[:, :, 3]
 
94
  output = input
95
  else:
96
  output = remove_background(input)
 
97
  output_np = np.array(output)
98
  alpha = output_np[:, :, 3]
99
  bbox = np.argwhere(alpha > 0.8 * 255)
100
  if bbox.size == 0:
101
+ return output
102
  bbox = np.min(bbox[:, 1]), np.min(bbox[:, 0]), np.max(bbox[:, 1]), np.max(bbox[:, 0])
103
  center = (bbox[0] + bbox[2]) / 2, (bbox[1] + bbox[3]) / 2
104
  size = max(bbox[2] - bbox[0], bbox[3] - bbox[1])
 
110
  output = Image.fromarray((output * 255).astype(np.uint8))
111
  return output
112
 
113
+
114
  def get_seed(randomize_seed: bool, seed: int) -> int:
115
  return np.random.randint(0, MAX_SEED) if randomize_seed else seed
116
 
117
+
118
  @spaces.GPU
119
  def generate_txt2img(prompt, progress=gr.Progress(track_tqdm=True)):
 
120
  if z_pipe is None:
121
  raise gr.Error("Z-Image-Turbo model failed to load.")
122
  if not prompt.strip():
123
  raise gr.Error("Please enter a prompt.")
 
124
  device = "cuda" if torch.cuda.is_available() else "cpu"
125
  generator = torch.Generator(device).manual_seed(42)
126
+ progress(0.1, desc="Generating Image...")
 
127
  try:
128
  result = z_pipe(
129
  prompt=prompt,
 
136
  )
137
  return result.images[0]
138
  except Exception as e:
139
+ raise gr.Error(f"Generation failed: {str(e)}")
140
+
141
 
142
  @spaces.GPU(duration=120)
143
  def generate_3d(
144
+ image: Image.Image, seed: int, resolution: str,
145
+ decimation_target: int, texture_size: int,
146
+ ss_guidance_strength: float, ss_guidance_rescale: float,
147
+ ss_sampling_steps: int, ss_rescale_t: float,
148
+ shape_guidance: float, shape_rescale: float,
149
+ shape_steps: int, shape_rescale_t: float,
150
+ tex_guidance: float, tex_rescale: float,
151
+ tex_steps: int, tex_rescale_t: float,
152
+ req: gr.Request, progress=gr.Progress(track_tqdm=True)
 
 
 
 
 
 
 
 
 
 
153
  ) -> Tuple[str, str]:
 
154
  if image is None:
155
  raise gr.Error("Please provide an input image.")
 
156
  if trellis_pipeline is None:
157
  raise gr.Error("TRELLIS model is not loaded.")
158
+
159
  user_dir = os.path.join(TMP_DIR, str(req.session_hash))
160
  os.makedirs(user_dir, exist_ok=True)
161
 
162
+ progress(0.1, desc="Generating 3D...")
163
  try:
164
  outputs, latents = trellis_pipeline.run(
165
+ image, seed=seed, preprocess_image=False,
166
+ sparse_structure_sampler_params={"steps": ss_sampling_steps, "guidance_strength": ss_guidance_strength, "guidance_rescale": ss_guidance_rescale, "rescale_t": ss_rescale_t},
167
+ shape_slat_sampler_params={"steps": shape_steps, "guidance_strength": shape_guidance, "guidance_rescale": shape_rescale, "rescale_t": shape_rescale_t},
168
+ tex_slat_sampler_params={"steps": tex_steps, "guidance_strength": tex_guidance, "guidance_rescale": tex_rescale, "rescale_t": tex_rescale_t},
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
169
  pipeline_type={"512": "512", "1024": "1024_cascade", "1536": "1536_cascade"}[resolution],
170
  return_latent=True,
171
  )
172
 
 
173
  progress(0.7, desc="Processing Mesh...")
174
  mesh = outputs[0]
175
+ mesh.simplify(1000000)
 
 
 
 
176
 
177
+ progress(0.9, desc="Exporting GLB...")
178
+ grid_size = latents[2]
179
 
 
180
  try:
181
  glb = o_voxel.postprocess.to_glb(
182
+ vertices=mesh.vertices, faces=mesh.faces, attr_volume=mesh.attrs,
183
+ coords=mesh.coords, attr_layout=trellis_pipeline.pbr_attr_layout,
184
+ grid_size=grid_size, aabb=[[-0.5, -0.5, -0.5], [0.5, 0.5, 0.5]],
185
+ decimation_target=decimation_target, texture_size=texture_size,
186
+ remesh=True, remesh_band=1, remesh_project=0, use_tqdm=True,
 
 
 
 
 
 
 
 
187
  )
188
+ except RuntimeError:
 
 
189
  glb = o_voxel.postprocess.to_glb(
190
+ vertices=mesh.vertices, faces=mesh.faces, attr_volume=mesh.attrs,
191
+ coords=mesh.coords, attr_layout=trellis_pipeline.pbr_attr_layout,
192
+ grid_size=grid_size, aabb=[[-0.5, -0.5, -0.5], [0.5, 0.5, 0.5]],
193
+ decimation_target=decimation_target, texture_size=texture_size,
194
+ remesh=False, remesh_band=1, remesh_project=0, use_tqdm=True,
 
 
 
 
 
 
 
 
195
  )
196
 
197
+ timestamp = datetime.now().strftime("%Y-%m-%dT%H%M%S")
198
+ glb_path = os.path.join(user_dir, f'output_{timestamp}.glb')
 
 
 
199
  glb.export(glb_path, extension_webp=False)
200
 
201
+ progress(0.95, desc="Creating Viewer...")
 
 
202
  run_id = str(uuid.uuid4())
203
+ rec = rr.new_recording(application_id="TRELLIS-3D-Viewer", recording_id=run_id) if hasattr(rr, "new_recording") else rr.RecordingStream(application_id="TRELLIS-3D-Viewer", recording_id=run_id) if hasattr(rr, "RecordingStream") else rr
204
 
 
 
 
 
 
 
 
 
 
 
 
205
  rec.log("world", rr.Clear(recursive=True), static=True)
 
 
 
 
206
  rec.log("world", rr.ViewCoordinates.RIGHT_HAND_Y_UP, static=True)
 
 
 
 
 
 
 
 
 
 
207
  rec.log("world/model", rr.Asset3D(path=glb_path), static=True)
208
 
 
209
  if rrb is not None:
210
  try:
211
+ blueprint = rrb.Blueprint(rrb.Spatial3DView(origin="/world", name="3D View"), collapse_panels=True)
 
 
 
 
 
 
 
212
  rec.send_blueprint(blueprint)
213
+ except:
214
+ pass
215
+
216
+ rrd_path = os.path.join(user_dir, f'output_{timestamp}.rrd')
 
217
  rec.save(rrd_path)
 
 
218
  torch.cuda.empty_cache()
219
  return rrd_path, glb_path
220
 
 
222
  torch.cuda.empty_cache()
223
  raise gr.Error(f"Generation failed: {str(e)}")
224
 
225
+
226
+ css = """
227
+ @import url('https://fonts.googleapis.com/css2?family=Bangers&family=Comic+Neue:wght@400;700&display=swap');
228
+
229
+ .gradio-container {
230
+ background-color: #FEF9C3 !important;
231
+ background-image: radial-gradient(#1F2937 1px, transparent 1px) !important;
232
+ background-size: 20px 20px !important;
233
+ min-height: 100vh !important;
234
+ font-family: 'Comic Neue', cursive, sans-serif !important;
235
+ }
236
+
237
+ .huggingface-space-header, #space-header, .space-header,
238
+ [class*="space-header"], .svelte-1ed2p3z, .space-header-badge,
239
+ .header-badge, [data-testid="space-header"], .svelte-kqij2n,
240
+ .svelte-1ax1toq, .embed-container > div:first-child {
241
+ display: none !important;
242
+ visibility: hidden !important;
243
+ height: 0 !important;
244
+ width: 0 !important;
245
+ overflow: hidden !important;
246
+ opacity: 0 !important;
247
+ pointer-events: none !important;
248
+ }
249
+
250
+ footer, .footer, .gradio-container footer, .built-with,
251
+ [class*="footer"], .gradio-footer, .main-footer,
252
+ div[class*="footer"], .show-api, .built-with-gradio,
253
+ a[href*="gradio.app"], a[href*="huggingface.co/spaces"] {
254
+ display: none !important;
255
+ visibility: hidden !important;
256
+ height: 0 !important;
257
+ padding: 0 !important;
258
+ margin: 0 !important;
259
+ }
260
+
261
+ #col-container { max-width: 960px; margin: 0 auto; }
262
+
263
+ .header-text h1 {
264
+ font-family: 'Bangers', cursive !important;
265
+ color: #1F2937 !important;
266
+ font-size: 3.5rem !important;
267
+ font-weight: 400 !important;
268
+ text-align: center !important;
269
+ margin-bottom: 0.5rem !important;
270
+ text-shadow: 4px 4px 0px #FACC15, 6px 6px 0px #1F2937 !important;
271
+ letter-spacing: 3px !important;
272
+ -webkit-text-stroke: 2px #1F2937 !important;
273
+ }
274
+
275
+ .subtitle {
276
+ text-align: center !important;
277
+ font-family: 'Comic Neue', cursive !important;
278
+ font-size: 1.2rem !important;
279
+ color: #1F2937 !important;
280
+ margin-bottom: 1.5rem !important;
281
+ font-weight: 700 !important;
282
+ }
283
+
284
+ .gr-panel, .gr-box, .gr-form, .block, .gr-group {
285
+ background: #FFFFFF !important;
286
+ border: 3px solid #1F2937 !important;
287
+ border-radius: 8px !important;
288
+ box-shadow: 6px 6px 0px #1F2937 !important;
289
+ transition: all 0.2s ease !important;
290
+ }
291
+
292
+ .gr-panel:hover, .block:hover {
293
+ transform: translate(-2px, -2px) !important;
294
+ box-shadow: 8px 8px 0px #1F2937 !important;
295
+ }
296
+
297
+ textarea, input[type="text"], input[type="number"] {
298
+ background: #FFFFFF !important;
299
+ border: 3px solid #1F2937 !important;
300
+ border-radius: 8px !important;
301
+ color: #1F2937 !important;
302
+ font-family: 'Comic Neue', cursive !important;
303
+ font-size: 1rem !important;
304
+ font-weight: 700 !important;
305
+ transition: all 0.2s ease !important;
306
+ }
307
+
308
+ textarea:focus, input[type="text"]:focus, input[type="number"]:focus {
309
+ border-color: #3B82F6 !important;
310
+ box-shadow: 4px 4px 0px #3B82F6 !important;
311
+ outline: none !important;
312
+ }
313
+
314
+ .gr-button-primary, button.primary, .gr-button.primary {
315
+ background: #3B82F6 !important;
316
+ border: 3px solid #1F2937 !important;
317
+ border-radius: 8px !important;
318
+ color: #FFFFFF !important;
319
+ font-family: 'Bangers', cursive !important;
320
+ font-weight: 400 !important;
321
+ font-size: 1.3rem !important;
322
+ letter-spacing: 2px !important;
323
+ padding: 14px 28px !important;
324
+ box-shadow: 5px 5px 0px #1F2937 !important;
325
+ transition: all 0.1s ease !important;
326
+ text-shadow: 1px 1px 0px #1F2937 !important;
327
+ }
328
+
329
+ .gr-button-primary:hover, button.primary:hover, .gr-button.primary:hover {
330
+ background: #2563EB !important;
331
+ transform: translate(-2px, -2px) !important;
332
+ box-shadow: 7px 7px 0px #1F2937 !important;
333
+ }
334
+
335
+ .gr-button-primary:active, button.primary:active, .gr-button.primary:active {
336
+ transform: translate(3px, 3px) !important;
337
+ box-shadow: 2px 2px 0px #1F2937 !important;
338
+ }
339
+
340
+ .gr-button-secondary, button.secondary {
341
+ background: #EF4444 !important;
342
+ border: 3px solid #1F2937 !important;
343
+ border-radius: 8px !important;
344
+ color: #FFFFFF !important;
345
+ font-family: 'Bangers', cursive !important;
346
+ font-weight: 400 !important;
347
+ font-size: 1.1rem !important;
348
+ letter-spacing: 1px !important;
349
+ box-shadow: 4px 4px 0px #1F2937 !important;
350
+ transition: all 0.1s ease !important;
351
+ text-shadow: 1px 1px 0px #1F2937 !important;
352
+ }
353
+
354
+ .gr-button-secondary:hover, button.secondary:hover {
355
+ background: #DC2626 !important;
356
+ transform: translate(-2px, -2px) !important;
357
+ box-shadow: 6px 6px 0px #1F2937 !important;
358
+ }
359
+
360
+ label, .gr-input-label, .gr-block-label {
361
+ color: #1F2937 !important;
362
+ font-family: 'Comic Neue', cursive !important;
363
+ font-weight: 700 !important;
364
+ font-size: 1rem !important;
365
+ }
366
+
367
+ .gr-file-upload {
368
+ border: 3px dashed #1F2937 !important;
369
+ border-radius: 8px !important;
370
+ background: #FEF9C3 !important;
371
+ }
372
+
373
+ .gr-file-upload:hover {
374
+ border-color: #3B82F6 !important;
375
+ background: #EFF6FF !important;
376
+ }
377
+
378
+ ::-webkit-scrollbar { width: 12px; height: 12px; }
379
+ ::-webkit-scrollbar-track { background: #FEF9C3; border: 2px solid #1F2937; }
380
+ ::-webkit-scrollbar-thumb { background: #3B82F6; border: 2px solid #1F2937; border-radius: 0px; }
381
+ ::-webkit-scrollbar-thumb:hover { background: #EF4444; }
382
+
383
+ ::selection { background: #FACC15; color: #1F2937; }
384
+
385
+ a { color: #3B82F6 !important; text-decoration: none !important; font-weight: 700 !important; }
386
+ a:hover { color: #EF4444 !important; }
387
+
388
+ @media (max-width: 768px) {
389
+ .header-text h1 {
390
+ font-size: 2.2rem !important;
391
+ text-shadow: 3px 3px 0px #FACC15, 4px 4px 0px #1F2937 !important;
392
+ }
393
+ .gr-button-primary, button.primary { padding: 12px 20px !important; font-size: 1.1rem !important; }
394
+ .gr-panel, .block { box-shadow: 4px 4px 0px #1F2937 !important; }
395
+ }
396
+
397
+ @media (prefers-color-scheme: dark) {
398
+ .gradio-container { background-color: #FEF9C3 !important; }
399
  }
 
400
  """
401
 
402
+ EXAMPLES_IMAGE = [f"example-images/A ({i}).webp" for i in range(1, 72)]
403
+
404
+ EXAMPLES_TEXT = [
405
+ "A Cat 3D model", "A realistic Cat 3D model", "A cartoon Cat 3D model",
406
+ "A low poly Cat 3D", "A cyberpunk Cat 3D", "A robotic Cat 3D",
407
+ "A Plane 3D model", "A fighter jet Plane 3D", "A vintage Plane 3D",
408
+ "A Car 3D model", "A sports Car 3D", "A cyberpunk Car 3D",
409
+ "A Shoe 3D model", "A sneaker Shoe 3D", "A boot Shoe 3D",
410
+ "A Chair 3D model", "A Table 3D model", "A Robot 3D model",
411
+ "A House 3D model", "A Spaceship 3D model", "A Motorcycle 3D model",
412
+ ]
413
+
414
  if __name__ == "__main__":
415
  os.makedirs(TMP_DIR, exist_ok=True)
416
 
417
+ with gr.Blocks(title="TRELLIS.2 Text-to-3D", delete_cache=(300, 300)) as demo:
418
+
419
+ gr.HTML(f"<style>{css}</style>")
420
+
421
+ gr.HTML("""
422
+ <div style="text-align: center; margin: 20px 0 10px 0;">
423
+ <a href="https://www.humangen.ai" target="_blank" style="text-decoration: none;">
424
+ <img src="https://img.shields.io/static/v1?label=🏠 HOME&message=HUMANGEN.AI&color=0000ff&labelColor=ffcc00&style=for-the-badge" alt="HOME">
425
+ </a>
426
+ </div>
427
  """)
428
+
429
+ gr.Markdown("# 🎮 TRELLIS.2 TEXT-TO-3D 🎮", elem_classes="header-text")
430
+ gr.Markdown('<p class="subtitle">✨ Generate 3D models from text or images! 🚀</p>')
431
 
432
  with gr.Row():
433
  with gr.Column(scale=1, min_width=360):
 
434
  with gr.Tabs():
435
+ with gr.Tab("📝 Text-to-3D"):
436
+ txt_prompt = gr.Textbox(label="💬 Prompt", placeholder="e.g. A Cat 3D model", lines=2)
437
+ btn_gen_img = gr.Button("1️⃣ Generate Image", variant="primary")
438
+ with gr.Tab("🖼️ Image-to-3D"):
439
+ gr.Markdown("Upload an image directly.")
440
 
441
+ image_prompt = gr.Image(label="📷 Input Image", format="png", image_mode="RGBA", type="pil", height=350)
442
 
443
+ with gr.Accordion(label="⚙️ 3D Settings", open=False):
444
+ resolution = gr.Radio(["512", "1024", "1536"], label="Resolution", value="1024")
445
  seed = gr.Slider(0, MAX_SEED, label="Seed", value=0, step=1)
446
+ randomize_seed = gr.Checkbox(label="🎲 Randomize Seed", value=True)
447
+
448
  decimation_target = gr.Slider(50000, 500000, label="Target Faces", value=150000, step=10000)
449
  texture_size = gr.Slider(512, 4096, label="Texture Size", value=1024, step=512)
450
 
451
+ btn_gen_3d = gr.Button("2️⃣ Generate 3D", variant="primary")
452
 
453
+ with gr.Accordion(label="🔧 Advanced Sampler", open=False):
454
  gr.Markdown("**Stage 1: Sparse Structure**")
455
  ss_guidance_strength = gr.Slider(1.0, 10.0, value=7.5, label="Guidance")
456
  ss_guidance_rescale = gr.Slider(0.0, 1.0, value=0.7, label="Rescale")
 
470
  tex_rescale_t = gr.Slider(1.0, 6.0, value=3.0, label="Rescale T")
471
 
472
  with gr.Column(scale=2):
473
+ gr.Markdown("### 🎯 3D Output")
474
+ rerun_output = Rerun(label="3D Viewer", height=600)
475
+ download_btn = gr.DownloadButton(label="3️⃣ Download GLB", variant="primary")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
476
 
477
+ gr.Examples(examples=[[img] for img in EXAMPLES_IMAGE], inputs=[image_prompt], label="🖼️ Image Examples")
478
+ gr.Examples(examples=[[txt] for txt in EXAMPLES_TEXT], inputs=[txt_prompt], label="📝 Text Examples")
479
+
480
  demo.load(start_session)
481
  demo.unload(end_session)
482
 
483
+ btn_gen_img.click(generate_txt2img, inputs=[txt_prompt], outputs=[image_prompt]).then(
484
+ preprocess_image, inputs=[image_prompt], outputs=[image_prompt]
 
 
 
 
 
 
485
  )
486
 
487
+ image_prompt.upload(preprocess_image, inputs=[image_prompt], outputs=[image_prompt])
 
 
 
 
488
 
489
+ btn_gen_3d.click(get_seed, inputs=[randomize_seed, seed], outputs=[seed]).then(
 
 
 
 
490
  generate_3d,
491
  inputs=[
492
+ image_prompt, seed, resolution, decimation_target, texture_size,
 
493
  ss_guidance_strength, ss_guidance_rescale, ss_sampling_steps, ss_rescale_t,
494
  shape_guidance, shape_rescale, shape_steps, shape_rescale_t,
495
  tex_guidance, tex_rescale, tex_steps, tex_rescale_t,
 
497
  outputs=[rerun_output, download_btn],
498
  )
499
 
500
+ demo.launch()