π Multi-Model Viewer 3D
Your GLB, but you can finally take it apart
- model_file
- camera_info
- bg_image
- image
- mask
- camera_info
- model_info
ComfyUI's stock 3D preview treats a GLB like a single blob: load it, orbit it, done. Fine when a file is one object. Useless when it's a character plus armor, or a room full of furniture. MultiModelViewer (π Multi-Model Viewer 3D) is the browser viewer for the other case - a scene built from named parts, where you want to flip one part's visibility, snap the camera to it, or blow the whole thing apart like a parts diagram.
It's the display side of the small ComfyUI-MultiModel3D pack, and it pairs naturally with the pack's MergeGLB node, which produces exactly the prefixed, per-part GLBs this viewer understands. It also accepts any FILE_3D - wire it straight after core ComfyUI's Load3D node and you're good. 3D in ComfyUI is still mostly a "generate one object, export one GLB" affair (think Hunyuan3D or Trellis output), so being able to actually inspect and slice a multi-part scene is more unusual than it should be.
How it works. The backend half is almost boring: it resolves the GLB path, walks the scene with trimesh to pull out each node's name, group, center and size, then hands the frontend a filename to load. All the real work happens in the browser. A Three.js 0.170.0 GLTFLoader parses the full scene graph, and a custom DOM widget gives you an π toggle per sub-model, a π― focus button that keeps your current viewing angle, and a π₯ explode slider that scatters parts outward with a scale-aware offset (so tiny parts don't fly into orbit). Mouse events are isolated from the ComfyUI canvas via stopPropagation(), so orbiting doesn't drag your graph around.
Where people get burned: the IMAGE and MASK outputs are 1Γ64Γ64 placeholder tensors - a tiny blank white frame and a black mask. That's not a bug; the node is an output node and the real preview lives in the widget on the canvas. Don't try to route that "render" into anything; it isn't one.
The inputs and outputs that matter:
model_file(required) - your GLB, from MergeGLB, Load3D, or even a plain path string; the node resolves relative paths against ComfyUI's output/input/temp folders.camera_info(LOAD3D_CAMERA) - a pass-through. Feed it a camera from Load3D and thecamera_infooutput echoes the same dict back, keeping camera state consistent across the graph.bg_image- accepted, but the current frontend doesn't actually use it yet. Don't count on it.model_infooutput - the sub-model list as JSON (node_name, group, display_name, center, size, visible) if you want to do something programmatic with the parts.
One real grouping gotcha: the viewer splits names like 0_body_0 into group 0 + display body_0. That works because MergeGLB's index prefix mode names parts 0_, 1_β¦ If you merge in filename mode instead, the prefixes aren't numeric, every part collapses into group 0, and your tidy list turns into a wall of full names. For per-part control, merge with prefix_mode set to index.
Install. ComfyUI Manager (search "ComfyUI-MultiModel3D") or clone it yourself:
cd ComfyUI/custom_nodes
git clone https://github.com/xuxiao305/ComfyUI-MultiModel3D
pip install -r requirements.txt
Dependencies are light - trimesh and numpy - plus Three.js files that auto-download on first startup (an install script pulls them from unpkg into web/lib/three/). If that download fails, the viewer falls back to a CDN, which needs internet. Once the local files are there, the viewer works fully offline.
If the viewer shows nothing, open the browser console (F12) and look for Three.js load errors. The widget loads from /extensions/ComfyUI-MultiModel3D/lib/three/... - which is why the install folder must keep that exact name; rename it and the local files won't be served. Beyond that, this is a young pack with a tiny footprint, so expect rough edges and lean on the GitHub issues when you find one.
Inputs (3)
| Name | Type | Default | Description |
|---|---|---|---|
| model_file | FILE_3D,FILE_3D_GLB | β | |
| camera_infoopt | LOAD3D_CAMERA | β | |
| bg_imageopt | IMAGE | β |
Outputs (4)
| Name | Type | Description |
|---|---|---|
| image | IMAGE | β |
| mask | MASK | β |
| camera_info | LOAD3D_CAMERA | β |
| model_info | STRING | β |