Spaces:
Running on Zero
Running on Zero
File size: 2,461 Bytes
bc4c433 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 | """Load OBJ triangle meshes for the NPZ ↔ mesh join.
This module only returns ``vertices (V, 3)`` and ``faces (T, 3)``.
It does **not** sample the envelope.
"""
from __future__ import annotations
from pathlib import Path
import numpy as np
import trimesh
from numpy.typing import NDArray
from scatteringnet.geometry.trimesh_util import as_trimesh
VerticesArray = NDArray[np.float32]
FacesArray = NDArray[np.int32]
# Same resolved OBJ can back several NPZs (``max_files_per_shape``). Cache
# the triangle arrays so catalog load does not re-parse the file.
_TRIANGLE_CACHE: dict[str, tuple[VerticesArray, FacesArray]] = {}
def clear_triangle_cache() -> None:
"""Drop cached OBJ arrays (tests / long-lived notebooks)."""
_TRIANGLE_CACHE.clear()
def load_obj_triangles(
path: Path | str,
*,
cache: bool = True,
) -> tuple[VerticesArray, FacesArray]:
"""
Read one OBJ as triangle vertices and face indices.
Parameters
----------
path:
Existing ``.obj`` file.
cache:
Reuse arrays for the same resolved path (catalog load).
Returns
-------
vertices:
``float32`` array of shape ``(V, 3)``.
faces:
``int32`` array of shape ``(T, 3)`` (0-based vertex indices).
"""
obj_path = Path(path)
if not obj_path.is_file():
raise FileNotFoundError(f"OBJ not found: {obj_path}")
if obj_path.suffix.lower() != ".obj":
raise ValueError(f"expected .obj, got {obj_path.suffix!r} ({obj_path})")
cache_key = str(obj_path.resolve())
if cache and cache_key in _TRIANGLE_CACHE:
return _TRIANGLE_CACHE[cache_key]
# process=False keeps the authored vertices; we only need the join.
loaded = trimesh.load(obj_path, force=None, process=False)
mesh = as_trimesh(loaded)
vertices = np.asarray(mesh.vertices, dtype=np.float32)
faces = np.asarray(mesh.faces, dtype=np.int32)
if vertices.ndim != 2 or vertices.shape[1] != 3:
raise ValueError(
f"vertices must have shape (V, 3), got {tuple(vertices.shape)} in {obj_path}"
)
if faces.ndim != 2 or faces.shape[1] != 3:
raise ValueError(
f"faces must have shape (T, 3), got {tuple(faces.shape)} in {obj_path}"
)
if int(faces.shape[0]) < 1:
raise ValueError(f"OBJ has no triangles: {obj_path}")
arrays = (vertices, faces)
if cache:
_TRIANGLE_CACHE[cache_key] = arrays
return arrays
|