Motion-Rig / preflight.py
STATICAI's picture
Restore preflight.py from Make-It-Animatable
1fc2cc9 verified
Raw History Blame Contribute Delete
3.89 kB
"""Bounded GLB metadata preflight; does not load textures or allocate mesh arrays."""
import json
import struct
from pathlib import Path
MAX_FACES = 500_000
MAX_VERTICES = 500_000
MAX_JSON = 16 * 1024 * 1024
def inspect_glb(path):
path = Path(path)
size = path.stat().st_size
with path.open("rb") as stream:
header = stream.read(20)
if len(header) != 20:
raise ValueError("Truncated GLB header")
magic, version, length, json_length, chunk = struct.unpack("<4sIIII", header)
if magic != b"glTF" or version != 2 or length != size or chunk != 0x4E4F534A:
raise ValueError("Invalid GLB 2.0 header or file length")
if json_length > MAX_JSON or json_length > size - 20:
raise ValueError("GLB metadata is too large or truncated")
doc = json.loads(stream.read(json_length))
meshes = doc.get("meshes", [])
accessors = doc.get("accessors", [])
vertices = faces = 0
unsupported = False
for mesh in meshes:
for primitive in mesh.get("primitives", []):
if primitive.get("mode", 4) != 4:
unsupported = True
continue
position = primitive.get("attributes", {}).get("POSITION")
if position is None:
raise ValueError("Mesh primitive has no POSITION accessor")
count = int(accessors[position]["count"])
index = primitive.get("indices")
indices = int(accessors[index]["count"]) if index is not None else count
if count < 0 or indices < 0 or indices % 3:
raise ValueError("Invalid vertex or triangle count")
vertices += count
faces += indices // 3
# Count node instances because upstream merges transformed scene geometry.
nodes = doc.get("nodes", [])
instances = {}
for node in nodes:
if "mesh" in node:
instances[node["mesh"]] = instances.get(node["mesh"], 0) + 1
instantiated_faces = 0
instantiated_vertices = 0
for i, mesh in enumerate(meshes):
multiplicity = max(1, instances.get(i, 0))
for primitive in mesh.get("primitives", []):
pos = primitive.get("attributes", {}).get("POSITION")
if pos is None:
continue
count = int(accessors[pos]["count"])
index = primitive.get("indices")
n = int(accessors[index]["count"]) if index is not None else count
instantiated_vertices += count * multiplicity
if primitive.get("mode", 4) == 4:
instantiated_faces += n // 3 * multiplicity
required_extensions = doc.get("extensionsRequired", [])
unsupported = unsupported or any(x in required_extensions for x in
("KHR_draco_mesh_compression", "EXT_meshopt_compression"))
dense = instantiated_faces > MAX_FACES or instantiated_vertices > MAX_VERTICES or size > 512 * 1024**2
warning = ("Preprocessing/decimation required: this input exceeds the conservative "
"500,000-face or 500,000-vertex limit. The original is preserved; "
"inference has not started.") if dense else ""
if unsupported:
warning = "Preprocessing required: export uncompressed triangle geometry before inference."
return dict(file_size_bytes=size, mesh_count=len(meshes), vertices=vertices,
triangles=faces, instantiated_vertices=instantiated_vertices,
instantiated_triangles=instantiated_faces,
materials=len(doc.get("materials", [])),
textures_present=bool(doc.get("textures") or doc.get("images")),
density_risk="high" if dense else "within conservative limit",
inference_allowed=bool(meshes) and faces > 0 and not dense and not unsupported,
warning=warning)