123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160 |
- from typing import Any, List, Union, TYPE_CHECKING
- import numpy
- import os.path
- import trimesh
- from UM.Mesh.MeshData import MeshData, calculateNormalsFromIndexedVertices
- from UM.Mesh.MeshReader import MeshReader
- from UM.MimeTypeDatabase import MimeTypeDatabase, MimeType
- from UM.Scene.GroupDecorator import GroupDecorator
- from cura.CuraApplication import CuraApplication
- from cura.Scene.BuildPlateDecorator import BuildPlateDecorator
- from cura.Scene.ConvexHullDecorator import ConvexHullDecorator
- from cura.Scene.CuraSceneNode import CuraSceneNode
- from cura.Scene.SliceableObjectDecorator import SliceableObjectDecorator
- if TYPE_CHECKING:
- from UM.Scene.SceneNode import SceneNode
- class TrimeshReader(MeshReader):
- """Class that leverages Trimesh to import files."""
- def __init__(self) -> None:
- super().__init__()
- self._supported_extensions = [".dae", ".gltf", ".glb", ".ply", ".zae"]
- MimeTypeDatabase.addMimeType(
- MimeType(
- name = "model/vnd.collada+xml",
- comment = "COLLADA Digital Asset Exchange",
- suffixes = ["dae"]
- )
- )
- MimeTypeDatabase.addMimeType(
- MimeType(
- name = "model/gltf-binary",
- comment = "glTF Binary",
- suffixes = ["glb"]
- )
- )
- MimeTypeDatabase.addMimeType(
- MimeType(
- name = "model/gltf+json",
- comment = "glTF Embedded JSON",
- suffixes = ["gltf"]
- )
- )
-
-
-
-
-
-
-
-
- MimeTypeDatabase.addMimeType(
- MimeType(
- name = "application/x-ply",
- comment = "Stanford Triangle Format",
- suffixes = ["ply"]
- )
- )
- MimeTypeDatabase.addMimeType(
- MimeType(
- name = "model/vnd.collada+xml+zip",
- comment = "Compressed COLLADA Digital Asset Exchange",
- suffixes = ["zae"]
- )
- )
- def _read(self, file_name: str) -> Union["SceneNode", List["SceneNode"]]:
- """Reads a file using Trimesh.
- :param file_name: The file path. This is assumed to be one of the file
- types that Trimesh can read. It will not be checked again.
- :return: A scene node that contains the file's contents.
- """
-
-
-
-
-
- if file_name.lower().endswith(".gltf"):
- mesh_or_scene = trimesh.load(open(file_name, "r", encoding = "utf-8"), file_type = "gltf")
- else:
- mesh_or_scene = trimesh.load(file_name)
- meshes = []
- if isinstance(mesh_or_scene, trimesh.Trimesh):
- meshes = [mesh_or_scene]
- elif isinstance(mesh_or_scene, trimesh.Scene):
- meshes = [mesh for mesh in mesh_or_scene.geometry.values()]
- active_build_plate = CuraApplication.getInstance().getMultiBuildPlateModel().activeBuildPlate
- nodes = []
- for mesh in meshes:
- if not isinstance(mesh, trimesh.Trimesh):
- continue
- mesh.merge_vertices()
- mesh.remove_unreferenced_vertices()
- mesh.fix_normals()
- mesh_data = self._toMeshData(mesh, file_name)
- file_base_name = os.path.basename(file_name)
- new_node = CuraSceneNode()
- new_node.setMeshData(mesh_data)
- new_node.setSelectable(True)
- new_node.setName(file_base_name if len(meshes) == 1 else "{file_base_name} {counter}".format(file_base_name = file_base_name, counter = str(len(nodes) + 1)))
- new_node.addDecorator(BuildPlateDecorator(active_build_plate))
- new_node.addDecorator(SliceableObjectDecorator())
- nodes.append(new_node)
- if len(nodes) == 1:
- return nodes[0]
-
- group_node = CuraSceneNode()
- group_node.addDecorator(GroupDecorator())
- group_node.addDecorator(ConvexHullDecorator())
- group_node.addDecorator(BuildPlateDecorator(active_build_plate))
- for node in nodes:
- node.setParent(group_node)
- return group_node
- def _toMeshData(self, tri_node: trimesh.base.Trimesh, file_name: str = "") -> MeshData:
- """Converts a Trimesh to Uranium's MeshData.
- :param tri_node: A Trimesh containing the contents of a file that was just read.
- :param file_name: The full original filename used to watch for changes
- :return: Mesh data from the Trimesh in a way that Uranium can understand it.
- """
- tri_faces = tri_node.faces
- tri_vertices = tri_node.vertices
- indices_list = []
- vertices_list = []
- index_count = 0
- face_count = 0
- for tri_face in tri_faces:
- face = []
- for tri_index in tri_face:
- vertices_list.append(tri_vertices[tri_index])
- face.append(index_count)
- index_count += 1
- indices_list.append(face)
- face_count += 1
- vertices = numpy.asarray(vertices_list, dtype = numpy.float32)
- indices = numpy.asarray(indices_list, dtype = numpy.int32)
- normals = calculateNormalsFromIndexedVertices(vertices, indices, face_count)
- mesh_data = MeshData(vertices = vertices, indices = indices, normals = normals, file_name = file_name)
- return mesh_data
|