ui: remove PBR GLB export from online demo
Browse files- Remove generate_glb callback, btn_glb, glb_view, simplify/tex_size sliders.
- Move vid_hdri into the top row next to mesh_view.
- Add note: PBR GLB export is disabled on the online demo; use local
inference (example.py) for full material export.
- Pipeline code under trellis/ is untouched.
app.py
CHANGED
|
@@ -599,90 +599,6 @@ def generate_videos(
|
|
| 599 |
return str(p_cam), str(p_hdri), str(p_roll), msg
|
| 600 |
|
| 601 |
|
| 602 |
-
@GPU
|
| 603 |
-
def generate_glb(
|
| 604 |
-
asset_state: Dict[str, Any],
|
| 605 |
-
hdri_file_obj: Any,
|
| 606 |
-
hdri_rot: float,
|
| 607 |
-
simplify: float,
|
| 608 |
-
texture_size: int,
|
| 609 |
-
req: gr.Request,
|
| 610 |
-
progress: gr.Progress = gr.Progress(track_tqdm=True),
|
| 611 |
-
) -> tuple[str, str]:
|
| 612 |
-
"""β£ Export PBR GLB (baked texture + simplified mesh).
|
| 613 |
-
|
| 614 |
-
Separate @GPU callback so the heavy mesh simplification does not
|
| 615 |
-
blow the 120 s budget shared with video rendering.
|
| 616 |
-
Returns: (pbr_glb, status_msg)
|
| 617 |
-
"""
|
| 618 |
-
slat_path = _require_slat(asset_state)
|
| 619 |
-
hdri_path = _require_hdri(hdri_file_obj)
|
| 620 |
-
session_dir = CACHE_DIR / str(req.session_hash)
|
| 621 |
-
|
| 622 |
-
progress(0.05, desc="Loading NeAR on GPU")
|
| 623 |
-
pipe = _ensure_near_on_cuda()
|
| 624 |
-
|
| 625 |
-
progress(0.15, desc="Loading SLaT / HDRI / mesh")
|
| 626 |
-
slat = pipe.load_slat(slat_path)
|
| 627 |
-
hdri_np = _load_hdri_resized(pipe, hdri_path)
|
| 628 |
-
|
| 629 |
-
mesh_path = asset_state.get("mesh_path")
|
| 630 |
-
base_mesh: Optional[trimesh.Trimesh] = None
|
| 631 |
-
if mesh_path and os.path.isfile(mesh_path):
|
| 632 |
-
print(f"[NeAR] loading Hunyuan mesh from {mesh_path}β¦", flush=True)
|
| 633 |
-
raw = trimesh.load(mesh_path, force="mesh")
|
| 634 |
-
print(f"[NeAR] raw mesh bounds: {raw.bounds[0].round(3)} β {raw.bounds[1].round(3)}", flush=True)
|
| 635 |
-
base_mesh = _hunyuan_mesh_to_renderer_space(raw)
|
| 636 |
-
del raw
|
| 637 |
-
else:
|
| 638 |
-
print("[NeAR] no mesh_path β will use SLaT decoder mesh for GLB", flush=True)
|
| 639 |
-
|
| 640 |
-
progress(0.30, desc="Baking PBR GLBβ¦")
|
| 641 |
-
glb = pipe.export_glb_from_slat(
|
| 642 |
-
slat, hdri_np,
|
| 643 |
-
hdri_rot_deg=float(hdri_rot),
|
| 644 |
-
base_mesh=base_mesh,
|
| 645 |
-
simplify=float(simplify),
|
| 646 |
-
texture_size=int(texture_size),
|
| 647 |
-
fill_holes=True,
|
| 648 |
-
)
|
| 649 |
-
del slat, hdri_np, base_mesh
|
| 650 |
-
_free_cuda()
|
| 651 |
-
|
| 652 |
-
out_path = session_dir / "near_pbr.glb"
|
| 653 |
-
glb.export(out_path)
|
| 654 |
-
del glb
|
| 655 |
-
|
| 656 |
-
_teardown_near()
|
| 657 |
-
|
| 658 |
-
msg = f"**β£ PBR GLB ready** β `{Path(out_path).name}`"
|
| 659 |
-
return str(out_path), msg
|
| 660 |
-
|
| 661 |
-
|
| 662 |
-
def _hunyuan_mesh_to_renderer_space(mesh: trimesh.Trimesh) -> trimesh.Trimesh:
|
| 663 |
-
"""Transform a Hunyuan3D output mesh into the coordinate space expected by
|
| 664 |
-
NeAR's renderer / shape_to_coords: Y-up β Z-up, scale to [-0.5, 0.5]Β³.
|
| 665 |
-
|
| 666 |
-
Hunyuan outputs: Y-up, vertices in ~[-1.01, 1.01]Β³.
|
| 667 |
-
NeAR renderer: Z-up, vertices in [-0.5, 0.5]Β³.
|
| 668 |
-
"""
|
| 669 |
-
mesh = mesh.copy()
|
| 670 |
-
# Y-up β Z-up
|
| 671 |
-
R = trimesh.transformations.rotation_matrix(np.pi / 2, [1, 0, 0])
|
| 672 |
-
mesh.apply_transform(R)
|
| 673 |
-
# [-1.01,1.01] β [-0.5,0.5] (multiply by 0.5, same as shape_to_coords)
|
| 674 |
-
mesh.vertices = mesh.vertices * 0.5
|
| 675 |
-
|
| 676 |
-
bounds = mesh.bounds # [[xmin,ymin,zmin],[xmax,ymax,zmax]]
|
| 677 |
-
print(
|
| 678 |
-
f"[NeAR] base_mesh after transform: "
|
| 679 |
-
f"bounds={bounds[0].round(3)} β {bounds[1].round(3)}, "
|
| 680 |
-
f"vertices={len(mesh.vertices)}, faces={len(mesh.faces)}",
|
| 681 |
-
flush=True,
|
| 682 |
-
)
|
| 683 |
-
return mesh
|
| 684 |
-
|
| 685 |
-
|
| 686 |
def clear_cache(req: gr.Request) -> str:
|
| 687 |
"""Clear session cache and free GPU memory."""
|
| 688 |
session_dir = CACHE_DIR / str(req.session_hash)
|
|
@@ -794,10 +710,12 @@ def build_app() -> gr.Blocks:
|
|
| 794 |
|
| 795 |
gr.Markdown(
|
| 796 |
"## NeAR β Image-to-Relightable 3D Gaussian Splatting\n"
|
| 797 |
-
"**β Geometry** β **β‘ SLaT** β **β’ Videos**
|
| 798 |
-
"Upload an image and walk through the
|
| 799 |
"Geometry and appearance are decoupled β after step β you can swap the input image "
|
| 800 |
-
"before step β‘ to retexture the same mesh with a different style."
|
|
|
|
|
|
|
| 801 |
)
|
| 802 |
|
| 803 |
with gr.Row(equal_height=False):
|
|
@@ -837,8 +755,7 @@ def build_app() -> gr.Blocks:
|
|
| 837 |
|
| 838 |
gr.HTML('<p class="section-kicker" style="margin:2px 0 2px;padding:0">Actions</p>')
|
| 839 |
btn_videos = gr.Button("β’ Generate Videos", variant="primary")
|
| 840 |
-
|
| 841 |
-
with gr.Accordion("Video / export settings", open=False):
|
| 842 |
fps = gr.Slider(8, 48, value=24, step=1, label="FPS")
|
| 843 |
num_cam = gr.Slider(8, 96, value=48, step=2, label="Camera-orbit frames")
|
| 844 |
num_hdri = gr.Slider(8, 96, value=48, step=2, label="HDRI-rotation frames")
|
|
@@ -848,8 +765,6 @@ def build_app() -> gr.Blocks:
|
|
| 848 |
with gr.Row():
|
| 849 |
fov = gr.Slider(10, 70, value=40, step=1, label="FoV")
|
| 850 |
radius = gr.Slider(1.0, 4.0, value=2.0, step=0.05, label="Radius")
|
| 851 |
-
simplify = gr.Slider(0.80, 0.99, value=0.85, step=0.01, label="GLB simplify ratio")
|
| 852 |
-
tex_size = gr.Slider(512, 2048, value=1024, step=512, label="Texture resolution")
|
| 853 |
btn_clear = gr.Button("Clear session cache", variant="secondary")
|
| 854 |
|
| 855 |
# ββ CENTER: linear output area ββββββββββββββββββββββββββββββββββββ
|
|
@@ -861,12 +776,11 @@ def build_app() -> gr.Blocks:
|
|
| 861 |
|
| 862 |
with gr.Row():
|
| 863 |
mesh_view = gr.Model3D(label="β Geometry", height=250)
|
| 864 |
-
|
|
|
|
| 865 |
|
| 866 |
vid_cam = gr.Video(label="β’ Camera orbit",
|
| 867 |
autoplay=True, loop=True, height=250)
|
| 868 |
-
vid_hdri = gr.Video(label="β’ HDRI rotation",
|
| 869 |
-
autoplay=True, loop=True, height=250)
|
| 870 |
|
| 871 |
with gr.Accordion("HDRI env roll", open=False):
|
| 872 |
vid_roll = gr.Video(label="β’ HDRI env roll",
|
|
@@ -906,9 +820,6 @@ def build_app() -> gr.Blocks:
|
|
| 906 |
btn_videos.click(generate_videos,
|
| 907 |
[asset_state, hdri_file, hdri_rot, fps, num_cam, num_hdri, yaw, pitch, fov, radius],
|
| 908 |
[vid_cam, vid_hdri, vid_roll, status])
|
| 909 |
-
btn_glb.click(generate_glb,
|
| 910 |
-
[asset_state, hdri_file, hdri_rot, simplify, tex_size],
|
| 911 |
-
[glb_view, status])
|
| 912 |
btn_clear.click(clear_cache, [], [status])
|
| 913 |
|
| 914 |
return demo
|
|
|
|
| 599 |
return str(p_cam), str(p_hdri), str(p_roll), msg
|
| 600 |
|
| 601 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 602 |
def clear_cache(req: gr.Request) -> str:
|
| 603 |
"""Clear session cache and free GPU memory."""
|
| 604 |
session_dir = CACHE_DIR / str(req.session_hash)
|
|
|
|
| 710 |
|
| 711 |
gr.Markdown(
|
| 712 |
"## NeAR β Image-to-Relightable 3D Gaussian Splatting\n"
|
| 713 |
+
"**β Geometry** β **β‘ SLaT** β **β’ Videos**\n\n"
|
| 714 |
+
"Upload an image and walk through the three steps. "
|
| 715 |
"Geometry and appearance are decoupled β after step β you can swap the input image "
|
| 716 |
+
"before step β‘ to retexture the same mesh with a different style.\n\n"
|
| 717 |
+
"> π‘ **PBR GLB export** is disabled on this online demo due to bake-time limits. "
|
| 718 |
+
"Run the local inference script (`example.py`) for full PBR material export."
|
| 719 |
)
|
| 720 |
|
| 721 |
with gr.Row(equal_height=False):
|
|
|
|
| 755 |
|
| 756 |
gr.HTML('<p class="section-kicker" style="margin:2px 0 2px;padding:0">Actions</p>')
|
| 757 |
btn_videos = gr.Button("β’ Generate Videos", variant="primary")
|
| 758 |
+
with gr.Accordion("Video settings", open=False):
|
|
|
|
| 759 |
fps = gr.Slider(8, 48, value=24, step=1, label="FPS")
|
| 760 |
num_cam = gr.Slider(8, 96, value=48, step=2, label="Camera-orbit frames")
|
| 761 |
num_hdri = gr.Slider(8, 96, value=48, step=2, label="HDRI-rotation frames")
|
|
|
|
| 765 |
with gr.Row():
|
| 766 |
fov = gr.Slider(10, 70, value=40, step=1, label="FoV")
|
| 767 |
radius = gr.Slider(1.0, 4.0, value=2.0, step=0.05, label="Radius")
|
|
|
|
|
|
|
| 768 |
btn_clear = gr.Button("Clear session cache", variant="secondary")
|
| 769 |
|
| 770 |
# ββ CENTER: linear output area ββββββββββββββββββββββββββββββββββββ
|
|
|
|
| 776 |
|
| 777 |
with gr.Row():
|
| 778 |
mesh_view = gr.Model3D(label="β Geometry", height=250)
|
| 779 |
+
vid_hdri = gr.Video(label="β’ HDRI rotation",
|
| 780 |
+
autoplay=True, loop=True, height=250)
|
| 781 |
|
| 782 |
vid_cam = gr.Video(label="β’ Camera orbit",
|
| 783 |
autoplay=True, loop=True, height=250)
|
|
|
|
|
|
|
| 784 |
|
| 785 |
with gr.Accordion("HDRI env roll", open=False):
|
| 786 |
vid_roll = gr.Video(label="β’ HDRI env roll",
|
|
|
|
| 820 |
btn_videos.click(generate_videos,
|
| 821 |
[asset_state, hdri_file, hdri_rot, fps, num_cam, num_hdri, yaw, pitch, fov, radius],
|
| 822 |
[vid_cam, vid_hdri, vid_roll, status])
|
|
|
|
|
|
|
|
|
|
| 823 |
btn_clear.click(clear_cache, [], [status])
|
| 824 |
|
| 825 |
return demo
|