Move web/ to javascript/ and declare it via [tool.comfy] web.
Rename the seven third-party bundles (playcanvas, spark, three.js, gsplat,
OrbitControls, TrackballControls) to .mjs. Every reference to them is inside the
HTML viewers, but ComfyUI auto-imports **/*.js under the web dir into the shared
browser realm, so ~8MB of vendor code was being evaluated in the main page on
every startup -- and with it 36 collision errors: document.body injection,
document-level listeners, unguarded message listeners and self.onmessage writes.
The importmap values, script srcs and three.module's internal ./three.core ref
are rewritten to match.
DisplayName becomes "GaussianPack": the namespace enforced on
registerExtension names is derived from it, and "ComfyUI-GaussianPack" made
that prefix invalid. The analysis extension is renamed to match.
js/gaussian_preview.js hooked "PlyPreviewLoadGaussianPLYEnhance", a node id
that appears nowhere in this repo, so the PLY dropdown auto-refresh had never
run; it now targets LoadPLY and LoadPLYOutput, the two nodes that actually carry
the ply_file widget. Guard its message listener on event.source.
Asset seeding resolves the input directory through
folder_paths.get_input_directory(), so it follows ComfyUI Desktop and
--input-directory instead of writing into the code tree.
comfy-test.toml moves to the current schema: [test.workflows] cuda (there is
no "gpu" key -- the accelerator is named by backend, and the old spelling parsed
to an empty list, so CUDA runners silently ran the full unfiltered set), an
explicit [test.platforms] platforms allowlist (per-platform booleans are no
longer supported), and the javascript level.
User asked for the test config to mirror TRELLIS2's. Changes:
- levels: ["syntax", "install", "registration", "instantiation"]
-> ["syntax", "install", "execution"]
Drops the two intermediate levels (execution implies them) and
adds `execution` so the actual workflow runs. Without `execution`
the test was passing vacuously without ever loading the workflow
JSON.
- publish: true -> 'true' (string, matching TRELLIS2's spelling)
- [test.platforms]: dropped macos_desktop / windows_desktop keys
to match TRELLIS2's key set. Kept CPU rows enabled and GPU rows
disabled (inverse of TRELLIS2 since GaussianPack is pure-Python).
[test.workflows] unchanged: cpu = ["merge_splats"], gpu = [].
The prior commit (6908dbe) was lost when the local repo was re-cloned
before its push could land. Redoing the full GaussianPack-side work
in one pass, plus two new asks from this session:
1. Display name: "Preview Gaussians Turntable" -> "Preview Gaussians".
2. Camera: OrbitControls -> TrackballControls. ParaView-style free
rotation — no fixed up vector, no gimbal lock at the poles, can
roll the camera. Replaces the "bound at azimuth and bottom" feel.
## Rename (mechanical sed pass)
ComfyUI-GSViewer -> ComfyUI-GaussianPack
GSViewer -> GaussianPack (log tags, extension name)
gsviewer -> gaussianpack (route path, logger name)
GSVIEWER -> GAUSSIANPACK
Hit __init__.py, spz_route.py, pyproject.toml, comfyui_extension.json,
README.md, web/js/gaussian_preview.js.
## LiTo-style project structure
- comfy-test.toml — test matrix; workflow-level testing
happens downstream in ComfyUI-LiTo.
- comfy-env-root.toml — empty [node_reqs]; GaussianPack has no
downstream custom-node deps.
- comfy-env.toml — numpy + plyfile + NanoGS via pypi-git.
- install.py — two-liner mirroring LiTo's.
- requirements.txt — DELETED, superseded by comfy-env.toml.
## GaussianMerge node
merge_gaussians.py: takes a 3DGS .ply + target_count, writes a slimmer
.ply via NanoGS pairwise merging (MPMM — same algorithm
@playcanvas/splat-transform's `--decimate` uses). Pure Python, CPU only;
the upstream paper notes a CUDA port as future work. ~10-30s for 1M ->
200K splats.
Lazy-imports nanogs.simplification + nanogs.utils.params inside the
merge method to keep import cost off ComfyUI startup. Caches via
mtime + (target_count, opacity_threshold, k) sidecar — re-running with
the same inputs is a no-op.
Pass-through if target >= input. ratio is clamped to NanoGS's required
open (0, 1) interval.
Node parameters:
ply_path STRING, forceInput
target_count INT, default 200_000
output_filename STRING, default "merged"
opacity_threshold FLOAT, default 0.1
k INT, default 16
## Camera: TrackballControls
Vendored web/js/TrackballControls.js (three@0.169.0, ~18 KB) +
importmap entry alongside OrbitControls. The viewer now constructs
TrackballControls with rotateSpeed=3.5, zoomSpeed=1.2, panSpeed=0.8,
staticMoving=true (no inertia drift). ResizeObserver now calls
controls.handleResize() so mouse-to-NDC mapping stays correct after
canvas resize. .target / .update() / .enabled API surface is identical
between OrbitControls and TrackballControls — no callsite changes
needed in gaussian_preview.js.
OrbitControls.js stays vendored (still in importmap) for any future
node that wants the constrained "preserve up" behavior.
Workflow chain (LiTo apple):
LoadImage -> LiToImageTo3D -> LiToExportPLY (~225 MB raw)
|
GaussianMerge(target=200_000)
|
PreviewGaussians (~50 MB)