Commit
·
3a77493
1
Parent(s):
4bc1108
Refactor GLTF export in Mesh class to streamline buffer views and accessors. Enhance texture handling and ensure proper integration of vertex attributes, improving overall GLB file generation.
Browse files
mesh.py
CHANGED
@@ -595,13 +595,12 @@ class Mesh:
|
|
595 |
Args:
|
596 |
path (str): path to write.
|
597 |
"""
|
|
|
598 |
|
599 |
# assert self.v.shape[0] == self.vn.shape[0] and self.v.shape[0] == self.vt.shape[0]
|
600 |
if self.vt is not None and self.v.shape[0] != self.vt.shape[0]:
|
601 |
self.align_v_to_vt()
|
602 |
|
603 |
-
import pygltflib
|
604 |
-
|
605 |
f_np = self.f.detach().cpu().numpy().astype(np.uint32)
|
606 |
f_np_blob = f_np.flatten().tobytes()
|
607 |
|
@@ -615,13 +614,53 @@ class Mesh:
|
|
615 |
attributes = pygltflib.Attributes(POSITION=1)
|
616 |
accessor_count = 2 # Start after position (0) and indices (1)
|
617 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
618 |
# Add vertex colors if they exist
|
619 |
if self.vc is not None:
|
620 |
vc_np = self.vc.detach().cpu().numpy().astype(np.float32)
|
621 |
vc_np_blob = vc_np.tobytes()
|
622 |
|
623 |
# Add vertex color buffer view
|
624 |
-
|
625 |
pygltflib.BufferView(
|
626 |
buffer=0,
|
627 |
byteOffset=byteOffset,
|
@@ -632,7 +671,7 @@ class Mesh:
|
|
632 |
)
|
633 |
|
634 |
# Add vertex color accessor
|
635 |
-
|
636 |
pygltflib.Accessor(
|
637 |
bufferView=accessor_count,
|
638 |
componentType=pygltflib.FLOAT,
|
@@ -650,7 +689,7 @@ class Mesh:
|
|
650 |
blob += vc_np_blob
|
651 |
byteOffset += len(vc_np_blob)
|
652 |
|
653 |
-
#
|
654 |
gltf = pygltflib.GLTF2(
|
655 |
scene=0,
|
656 |
scenes=[pygltflib.Scene(nodes=[0])],
|
@@ -659,46 +698,9 @@ class Mesh:
|
|
659 |
attributes=attributes,
|
660 |
indices=0,
|
661 |
)])],
|
662 |
-
buffers=[
|
663 |
-
|
664 |
-
|
665 |
-
# buffer view (based on dtype)
|
666 |
-
bufferViews=[
|
667 |
-
# triangles; as flatten (element) array
|
668 |
-
pygltflib.BufferView(
|
669 |
-
buffer=0,
|
670 |
-
byteLength=len(f_np_blob),
|
671 |
-
target=pygltflib.ELEMENT_ARRAY_BUFFER,
|
672 |
-
),
|
673 |
-
# positions; as vec3 array
|
674 |
-
pygltflib.BufferView(
|
675 |
-
buffer=0,
|
676 |
-
byteOffset=len(f_np_blob),
|
677 |
-
byteLength=len(v_np_blob),
|
678 |
-
byteStride=12, # vec3
|
679 |
-
target=pygltflib.ARRAY_BUFFER,
|
680 |
-
),
|
681 |
-
],
|
682 |
-
accessors=[
|
683 |
-
# 0 = triangles
|
684 |
-
pygltflib.Accessor(
|
685 |
-
bufferView=0,
|
686 |
-
componentType=pygltflib.UNSIGNED_INT,
|
687 |
-
count=f_np.size,
|
688 |
-
type=pygltflib.SCALAR,
|
689 |
-
max=[int(f_np.max())],
|
690 |
-
min=[int(f_np.min())],
|
691 |
-
),
|
692 |
-
# 1 = positions
|
693 |
-
pygltflib.Accessor(
|
694 |
-
bufferView=1,
|
695 |
-
componentType=pygltflib.FLOAT,
|
696 |
-
count=len(v_np),
|
697 |
-
type=pygltflib.VEC3,
|
698 |
-
max=v_np.max(axis=0).tolist(),
|
699 |
-
min=v_np.min(axis=0).tolist(),
|
700 |
-
),
|
701 |
-
],
|
702 |
)
|
703 |
|
704 |
# Add material for vertex colors
|
@@ -715,53 +717,26 @@ class Mesh:
|
|
715 |
))
|
716 |
gltf.meshes[0].primitives[0].material = 0
|
717 |
|
718 |
-
#
|
719 |
if self.vt is not None:
|
720 |
-
|
721 |
vt_np = self.vt.detach().cpu().numpy().astype(np.float32)
|
722 |
vt_np_blob = vt_np.tobytes()
|
723 |
|
724 |
-
|
725 |
-
albedo = (albedo * 255).astype(np.uint8)
|
726 |
-
albedo = cv2.cvtColor(albedo, cv2.COLOR_RGB2BGR)
|
727 |
-
albedo_blob = cv2.imencode('.png', albedo)[1].tobytes()
|
728 |
-
|
729 |
-
# update primitive
|
730 |
-
gltf.meshes[0].primitives[0].attributes.TEXCOORD_0 = 2
|
731 |
-
gltf.meshes[0].primitives[0].material = 0
|
732 |
-
|
733 |
-
# update materials
|
734 |
-
gltf.materials.append(pygltflib.Material(
|
735 |
-
pbrMetallicRoughness=pygltflib.PbrMetallicRoughness(
|
736 |
-
baseColorTexture=pygltflib.TextureInfo(index=0, texCoord=0),
|
737 |
-
metallicFactor=0.0,
|
738 |
-
roughnessFactor=1.0,
|
739 |
-
),
|
740 |
-
alphaMode=pygltflib.OPAQUE,
|
741 |
-
alphaCutoff=None,
|
742 |
-
doubleSided=True,
|
743 |
-
))
|
744 |
-
|
745 |
-
gltf.textures.append(pygltflib.Texture(sampler=0, source=0))
|
746 |
-
gltf.samplers.append(pygltflib.Sampler(magFilter=pygltflib.LINEAR, minFilter=pygltflib.LINEAR_MIPMAP_LINEAR, wrapS=pygltflib.REPEAT, wrapT=pygltflib.REPEAT))
|
747 |
-
gltf.images.append(pygltflib.Image(bufferView=3, mimeType="image/png"))
|
748 |
-
|
749 |
-
# update buffers
|
750 |
gltf.bufferViews.append(
|
751 |
-
# index = 2, texcoords; as vec2 array
|
752 |
pygltflib.BufferView(
|
753 |
buffer=0,
|
754 |
byteOffset=byteOffset,
|
755 |
byteLength=len(vt_np_blob),
|
756 |
-
byteStride=8,
|
757 |
target=pygltflib.ARRAY_BUFFER,
|
758 |
)
|
759 |
)
|
760 |
|
|
|
761 |
gltf.accessors.append(
|
762 |
-
# 2 = texcoords
|
763 |
pygltflib.Accessor(
|
764 |
-
bufferView=
|
765 |
componentType=pygltflib.FLOAT,
|
766 |
count=len(vt_np),
|
767 |
type=pygltflib.VEC2,
|
@@ -770,59 +745,19 @@ class Mesh:
|
|
770 |
)
|
771 |
)
|
772 |
|
773 |
-
|
774 |
-
|
775 |
-
|
776 |
-
gltf.bufferViews.append(
|
777 |
-
# index = 3, albedo texture; as none target
|
778 |
-
pygltflib.BufferView(
|
779 |
-
buffer=0,
|
780 |
-
byteOffset=byteOffset,
|
781 |
-
byteLength=len(albedo_blob),
|
782 |
-
)
|
783 |
-
)
|
784 |
|
785 |
-
blob +=
|
786 |
-
byteOffset += len(
|
787 |
|
|
|
788 |
gltf.buffers[0].byteLength = byteOffset
|
789 |
|
790 |
-
|
791 |
-
if self.metallicRoughness is not None:
|
792 |
-
metallicRoughness = self.metallicRoughness.detach().cpu().numpy()
|
793 |
-
metallicRoughness = (metallicRoughness * 255).astype(np.uint8)
|
794 |
-
metallicRoughness = cv2.cvtColor(metallicRoughness, cv2.COLOR_RGB2BGR)
|
795 |
-
metallicRoughness_blob = cv2.imencode('.png', metallicRoughness)[1].tobytes()
|
796 |
-
|
797 |
-
# update texture definition
|
798 |
-
gltf.materials[0].pbrMetallicRoughness.metallicFactor = 1.0
|
799 |
-
gltf.materials[0].pbrMetallicRoughness.roughnessFactor = 1.0
|
800 |
-
gltf.materials[0].pbrMetallicRoughness.metallicRoughnessTexture = pygltflib.TextureInfo(index=1, texCoord=0)
|
801 |
-
|
802 |
-
gltf.textures.append(pygltflib.Texture(sampler=1, source=1))
|
803 |
-
gltf.samplers.append(pygltflib.Sampler(magFilter=pygltflib.LINEAR, minFilter=pygltflib.LINEAR_MIPMAP_LINEAR, wrapS=pygltflib.REPEAT, wrapT=pygltflib.REPEAT))
|
804 |
-
gltf.images.append(pygltflib.Image(bufferView=4, mimeType="image/png"))
|
805 |
-
|
806 |
-
# update buffers
|
807 |
-
gltf.bufferViews.append(
|
808 |
-
# index = 4, metallicRoughness texture; as none target
|
809 |
-
pygltflib.BufferView(
|
810 |
-
buffer=0,
|
811 |
-
byteOffset=byteOffset,
|
812 |
-
byteLength=len(metallicRoughness_blob),
|
813 |
-
)
|
814 |
-
)
|
815 |
-
|
816 |
-
blob += metallicRoughness_blob
|
817 |
-
byteOffset += len(metallicRoughness_blob)
|
818 |
-
|
819 |
-
gltf.buffers[0].byteLength = byteOffset
|
820 |
-
|
821 |
-
|
822 |
-
# set actual data
|
823 |
gltf.set_binary_blob(blob)
|
824 |
|
825 |
-
#
|
826 |
gltf.save(path)
|
827 |
|
828 |
|
|
|
595 |
Args:
|
596 |
path (str): path to write.
|
597 |
"""
|
598 |
+
import pygltflib
|
599 |
|
600 |
# assert self.v.shape[0] == self.vn.shape[0] and self.v.shape[0] == self.vt.shape[0]
|
601 |
if self.vt is not None and self.v.shape[0] != self.vt.shape[0]:
|
602 |
self.align_v_to_vt()
|
603 |
|
|
|
|
|
604 |
f_np = self.f.detach().cpu().numpy().astype(np.uint32)
|
605 |
f_np_blob = f_np.flatten().tobytes()
|
606 |
|
|
|
614 |
attributes = pygltflib.Attributes(POSITION=1)
|
615 |
accessor_count = 2 # Start after position (0) and indices (1)
|
616 |
|
617 |
+
# Initialize buffer views list
|
618 |
+
buffer_views = [
|
619 |
+
# triangles; as flatten (element) array
|
620 |
+
pygltflib.BufferView(
|
621 |
+
buffer=0,
|
622 |
+
byteLength=len(f_np_blob),
|
623 |
+
target=pygltflib.ELEMENT_ARRAY_BUFFER,
|
624 |
+
),
|
625 |
+
# positions; as vec3 array
|
626 |
+
pygltflib.BufferView(
|
627 |
+
buffer=0,
|
628 |
+
byteOffset=len(f_np_blob),
|
629 |
+
byteLength=len(v_np_blob),
|
630 |
+
byteStride=12, # vec3
|
631 |
+
target=pygltflib.ARRAY_BUFFER,
|
632 |
+
),
|
633 |
+
]
|
634 |
+
|
635 |
+
# Initialize accessors list
|
636 |
+
accessors = [
|
637 |
+
# 0 = triangles
|
638 |
+
pygltflib.Accessor(
|
639 |
+
bufferView=0,
|
640 |
+
componentType=pygltflib.UNSIGNED_INT,
|
641 |
+
count=f_np.size,
|
642 |
+
type=pygltflib.SCALAR,
|
643 |
+
max=[int(f_np.max())],
|
644 |
+
min=[int(f_np.min())],
|
645 |
+
),
|
646 |
+
# 1 = positions
|
647 |
+
pygltflib.Accessor(
|
648 |
+
bufferView=1,
|
649 |
+
componentType=pygltflib.FLOAT,
|
650 |
+
count=len(v_np),
|
651 |
+
type=pygltflib.VEC3,
|
652 |
+
max=v_np.max(axis=0).tolist(),
|
653 |
+
min=v_np.min(axis=0).tolist(),
|
654 |
+
),
|
655 |
+
]
|
656 |
+
|
657 |
# Add vertex colors if they exist
|
658 |
if self.vc is not None:
|
659 |
vc_np = self.vc.detach().cpu().numpy().astype(np.float32)
|
660 |
vc_np_blob = vc_np.tobytes()
|
661 |
|
662 |
# Add vertex color buffer view
|
663 |
+
buffer_views.append(
|
664 |
pygltflib.BufferView(
|
665 |
buffer=0,
|
666 |
byteOffset=byteOffset,
|
|
|
671 |
)
|
672 |
|
673 |
# Add vertex color accessor
|
674 |
+
accessors.append(
|
675 |
pygltflib.Accessor(
|
676 |
bufferView=accessor_count,
|
677 |
componentType=pygltflib.FLOAT,
|
|
|
689 |
blob += vc_np_blob
|
690 |
byteOffset += len(vc_np_blob)
|
691 |
|
692 |
+
# Create the GLTF object with all components
|
693 |
gltf = pygltflib.GLTF2(
|
694 |
scene=0,
|
695 |
scenes=[pygltflib.Scene(nodes=[0])],
|
|
|
698 |
attributes=attributes,
|
699 |
indices=0,
|
700 |
)])],
|
701 |
+
buffers=[pygltflib.Buffer(byteLength=byteOffset)],
|
702 |
+
bufferViews=buffer_views,
|
703 |
+
accessors=accessors,
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
704 |
)
|
705 |
|
706 |
# Add material for vertex colors
|
|
|
717 |
))
|
718 |
gltf.meshes[0].primitives[0].material = 0
|
719 |
|
720 |
+
# Handle textures if they exist
|
721 |
if self.vt is not None:
|
|
|
722 |
vt_np = self.vt.detach().cpu().numpy().astype(np.float32)
|
723 |
vt_np_blob = vt_np.tobytes()
|
724 |
|
725 |
+
# Add texture coordinates buffer view
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
726 |
gltf.bufferViews.append(
|
|
|
727 |
pygltflib.BufferView(
|
728 |
buffer=0,
|
729 |
byteOffset=byteOffset,
|
730 |
byteLength=len(vt_np_blob),
|
731 |
+
byteStride=8, # vec2
|
732 |
target=pygltflib.ARRAY_BUFFER,
|
733 |
)
|
734 |
)
|
735 |
|
736 |
+
# Add texture coordinates accessor
|
737 |
gltf.accessors.append(
|
|
|
738 |
pygltflib.Accessor(
|
739 |
+
bufferView=len(gltf.bufferViews) - 1,
|
740 |
componentType=pygltflib.FLOAT,
|
741 |
count=len(vt_np),
|
742 |
type=pygltflib.VEC2,
|
|
|
745 |
)
|
746 |
)
|
747 |
|
748 |
+
# Add texture coordinates to attributes
|
749 |
+
gltf.meshes[0].primitives[0].attributes.TEXCOORD_0 = len(gltf.accessors) - 1
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
750 |
|
751 |
+
blob += vt_np_blob
|
752 |
+
byteOffset += len(vt_np_blob)
|
753 |
|
754 |
+
# Update buffer size
|
755 |
gltf.buffers[0].byteLength = byteOffset
|
756 |
|
757 |
+
# Set the binary blob
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
758 |
gltf.set_binary_blob(blob)
|
759 |
|
760 |
+
# Save the GLB file
|
761 |
gltf.save(path)
|
762 |
|
763 |
|