|
from typing import * |
|
from pathlib import Path |
|
|
|
import numpy as np |
|
|
|
|
|
def write_glb(path: Union[str, Path], vertices: np.ndarray, faces: np.ndarray, vertex_colors: np.ndarray = None, uv: np.ndarray = None): |
|
import pygltflib |
|
|
|
has_colors = vertex_colors is not None |
|
has_uv = uv is not None |
|
|
|
triangles_bytes = faces.astype(np.uint32).flatten().tobytes() |
|
vertices_bytes = vertices.astype(np.float32).tobytes() |
|
vertex_colors_bytes = vertex_colors.astype(np.float32).tobytes() if has_colors else None |
|
uv_bytes = uv.astype(np.float32).tobytes() if has_uv else None |
|
|
|
|
|
gltf = pygltflib.GLTF2( |
|
scene=0, |
|
scenes=[pygltflib.Scene(nodes=[0])], |
|
nodes=[pygltflib.Node(mesh=0)], |
|
meshes=[ |
|
pygltflib.Mesh( |
|
primitives=[ |
|
pygltflib.Primitive( |
|
attributes=pygltflib.Attributes( |
|
POSITION=1, |
|
COLOR_0=2 if has_colors else None, |
|
TEXCOORD_0=2 + has_colors if has_uv else None |
|
), |
|
indices=0 |
|
) |
|
] |
|
) |
|
], |
|
accessors=list(filter(None, [ |
|
pygltflib.Accessor( |
|
bufferView=0, |
|
componentType=pygltflib.UNSIGNED_INT, |
|
count=faces.size, |
|
type=pygltflib.SCALAR, |
|
max=[int(faces.max())], |
|
min=[int(faces.min())], |
|
), |
|
pygltflib.Accessor( |
|
bufferView=1, |
|
componentType=pygltflib.FLOAT, |
|
count=len(vertices), |
|
type=pygltflib.VEC3, |
|
max=vertices.max(axis=0).tolist(), |
|
min=vertices.min(axis=0).tolist(), |
|
), |
|
pygltflib.Accessor( |
|
bufferView=2, |
|
componentType=pygltflib.FLOAT, |
|
count=len(vertices), |
|
type=pygltflib.VEC3, |
|
max=vertex_colors.max(axis=0).tolist(), |
|
min=vertex_colors.min(axis=0).tolist(), |
|
) if has_colors else None, |
|
pygltflib.Accessor( |
|
bufferView=3, |
|
componentType=pygltflib.FLOAT, |
|
count=len(uv), |
|
type=pygltflib.VEC2, |
|
max=uv.max(axis=0).tolist(), |
|
min=uv.min(axis=0).tolist(), |
|
) if has_uv else None, |
|
])), |
|
bufferViews=list(filter(None, [ |
|
pygltflib.BufferView( |
|
buffer=0, |
|
byteLength=len(triangles_bytes), |
|
target=pygltflib.ELEMENT_ARRAY_BUFFER, |
|
), |
|
pygltflib.BufferView( |
|
buffer=0, |
|
byteOffset=len(triangles_bytes), |
|
byteLength=len(vertices_bytes), |
|
target=pygltflib.ARRAY_BUFFER, |
|
), |
|
pygltflib.BufferView( |
|
buffer=0, |
|
byteOffset=len(triangles_bytes) + len(vertices_bytes), |
|
byteLength=len(vertex_colors_bytes), |
|
target=pygltflib.ARRAY_BUFFER, |
|
) if has_colors else None, |
|
pygltflib.BufferView( |
|
buffer=0, |
|
byteOffset=len(triangles_bytes) + len(vertices_bytes) + (len(vertex_colors_bytes) if has_colors else 0), |
|
byteLength=len(uv_bytes), |
|
target=pygltflib.ARRAY_BUFFER, |
|
) if has_uv else None, |
|
])), |
|
buffers=[ |
|
pygltflib.Buffer( |
|
byteLength=len(triangles_bytes) + len(vertices_bytes) + (len(vertex_colors_bytes) if has_colors else 0) + (len(uv_bytes) if has_uv else 0), |
|
) |
|
] |
|
) |
|
gltf.set_binary_blob(triangles_bytes + vertices_bytes + (vertex_colors_bytes or b'') + (uv_bytes or b'')) |
|
with open(path, 'wb') as f: |
|
for chunk in gltf.save_to_bytes(): |
|
f.write(chunk) |