import sys import types import numpy as np import trimesh from PIL import Image def _export_textured_glb(path): mesh = trimesh.creation.box() img = Image.new("RGB", (8, 8), (10, 20, 30)) uv = np.zeros((len(mesh.vertices), 2), dtype=np.float64) mesh.visual = trimesh.visual.TextureVisuals(uv=uv, image=img) trimesh.Scene(mesh).export(str(path), file_type="glb") def test_loadglb_node_end_to_end(tmp_path, monkeypatch): input_dir = tmp_path / "input" (input_dir / "3d").mkdir(parents=True) glb = input_dir / "3d" / "shoe.glb" _export_textured_glb(glb) fake = types.ModuleType("folder_paths") fake.get_input_directory = lambda: str(input_dir) fake.get_annotated_filepath = lambda rel: str(input_dir / rel) monkeypatch.setitem(sys.modules, "folder_paths", fake) # Evict any previously cached import so the stub is picked up at import time. monkeypatch.delitem(sys.modules, "cxn.load_glb", raising=False) from cxn.load_glb import LoadGLB from cxn.types import Model3D (model,) = LoadGLB().load(model_file="3d/shoe.glb", toe_axis="+X", up_axis="+Z") assert isinstance(model, Model3D) assert len(model.scene.geometry) >= 1 assert model.source_path.endswith("shoe.glb") assert len(model.textures) >= 1 assert all(isinstance(t, Image.Image) for t in model.textures) def test_loadglb_node_sentinel_raises(tmp_path, monkeypatch): import pytest input_dir = tmp_path / "input" (input_dir / "3d").mkdir(parents=True) fake = types.ModuleType("folder_paths") fake.get_input_directory = lambda: str(input_dir) fake.get_annotated_filepath = lambda rel: str(input_dir / rel) monkeypatch.setitem(sys.modules, "folder_paths", fake) # Evict any previously cached import so the stub is picked up at import time. monkeypatch.delitem(sys.modules, "cxn.load_glb", raising=False) from cxn.load_glb import LoadGLB, _NO_FILES_SENTINEL with pytest.raises(RuntimeError, match="No .glb"): LoadGLB().load(model_file=_NO_FILES_SENTINEL) def test_loadglb_node_applies_axis_remap(tmp_path, monkeypatch): input_dir = tmp_path / "input" (input_dir / "3d").mkdir(parents=True) glb = input_dir / "3d" / "shoe.glb" _export_textured_glb(glb) fake = types.ModuleType("folder_paths") fake.get_input_directory = lambda: str(input_dir) fake.get_annotated_filepath = lambda rel: str(input_dir / rel) monkeypatch.setitem(sys.modules, "folder_paths", fake) monkeypatch.delitem(sys.modules, "cxn.load_glb", raising=False) from cxn.glb_loader import load_glb_as_scene from cxn.load_glb import LoadGLB from cxn.loader_common import apply_axis_remap, build_axis_remap # Vertices as the node will load them (pre-remap), for the same file. raw_scene = load_glb_as_scene(str(glb)) raw_geom = list(raw_scene.geometry.values())[0] expected = ( np.asarray(raw_geom.vertices, dtype=np.float64) @ build_axis_remap("+Y", "+Z").T ) (model,) = LoadGLB().load(model_file="3d/shoe.glb", toe_axis="+Y", up_axis="+Z") out_geom = list(model.scene.geometry.values())[0] assert np.allclose(np.asarray(out_geom.vertices, dtype=np.float64), expected)