Пример #1
0
    def load(self) -> Scene:
        """Loads and stl scene/file

        Returns:
            Scene: The Scene instance
        """
        path = self.find_scene(self.meta.path)
        if not path:
            raise ImproperlyConfigured("Scene '{}' not found".format(
                self.meta.path))

        file_obj = str(path)
        if file_obj.endswith('.gz'):
            file_obj = gzip.GzipFile(file_obj)

        stl_mesh = trimesh.load(file_obj, file_type='stl')
        scene = Scene(self.meta.resolved_path)
        scene_mesh = Mesh("mesh")
        scene_mesh.material = Material("default")

        vao = VAO("mesh", mode=moderngl.TRIANGLES)
        vao.buffer(numpy.array(stl_mesh.vertices, dtype='f4'), '3f',
                   ['in_position'])
        vao.buffer(numpy.array(stl_mesh.vertex_normals, dtype='f4'), '3f',
                   ['in_normal'])
        vao.index_buffer(numpy.array(stl_mesh.faces, dtype='u4'))
        scene_mesh.vao = vao
        scene_mesh.add_attribute('POSITION', 'in_position', 3)
        scene_mesh.add_attribute('NORMAL', 'in_normal', 3)

        scene.meshes.append(scene_mesh)
        scene.root_nodes.append(Node(mesh=scene_mesh))
        scene.prepare()

        return scene
Пример #2
0
class Loader(BaseLoader):
    """Loader for GLTF 2.0 files"""

    kind = "gltf"
    file_extensions = [
        [".gltf"],
        [".glb"],
    ]
    #: Supported GLTF extensions
    #: https://github.com/KhronosGroup/glTF/tree/master/extensions
    supported_extensions = []

    def __init__(self, meta: SceneDescription):
        """Initialize loading GLTF 2 scene.

        Supported formats:

        - gltf json format with external resources
        - gltf embedded buffers
        - glb Binary format
        """
        super().__init__(meta)
        self.scenes = []
        self.nodes = []
        self.meshes = []
        self.materials = []
        self.images = []
        self.samplers = []
        self.textures = []

        self.path = None
        self.scene = None
        self.gltf = None

    def load(self) -> Scene:
        """Load a GLTF 2 scene including referenced textures.

        Returns:
            Scene: The scene instance
        """
        self.path = self.find_scene(self.meta.path)
        if not self.path:
            raise ImproperlyConfigured("Scene '{}' not found".format(
                self.meta.path))

        self.scene = Scene(self.path)

        # Load gltf json file
        if self.path.suffix == ".gltf":
            self.load_gltf()

        # Load binary gltf file
        if self.path.suffix == ".glb":
            self.load_glb()

        self.gltf.check_version()
        self.gltf.check_extensions(self.supported_extensions)
        self.load_images()
        self.load_samplers()
        self.load_textures()
        self.load_materials()
        self.load_meshes()
        self.load_nodes()

        self.scene.calc_scene_bbox()
        self.scene.prepare()

        return self.scene

    def load_gltf(self):
        """Loads a gltf json file parsing its contents"""
        with open(str(self.path)) as fd:
            self.gltf = GLTFMeta(self.path, json.load(fd), self.meta)

    def load_glb(self):
        """Loads a binary gltf file parsing its contents"""
        with open(str(self.path), "rb") as fd:
            # Check header
            magic = fd.read(4)
            if magic != GLTF_MAGIC_HEADER:
                raise ValueError("{} has incorrect header {} != {}".format(
                    self.path, magic, GLTF_MAGIC_HEADER))

            version = struct.unpack("<I", fd.read(4))[0]
            if version != 2:
                raise ValueError("{} has unsupported version {}".format(
                    self.path, version))

            # Total file size including headers
            _ = struct.unpack("<I", fd.read(4))[0]  # noqa

            # Chunk 0 - json
            chunk_0_length = struct.unpack("<I", fd.read(4))[0]
            chunk_0_type = fd.read(4)
            if chunk_0_type != b"JSON":
                raise ValueError(
                    "Expected JSON chunk, not {} in file {}".format(
                        chunk_0_type, self.path))

            json_meta = fd.read(chunk_0_length).decode()

            # chunk 1 - binary buffer
            chunk_1_length = struct.unpack("<I", fd.read(4))[0]
            chunk_1_type = fd.read(4)
            if chunk_1_type != b"BIN\x00":
                raise ValueError(
                    "Expected BIN chunk, not {} in file {}".format(
                        chunk_1_type, self.path))

            self.gltf = GLTFMeta(
                self.path,
                json.loads(json_meta),
                self.meta,
                binary_buffer=fd.read(chunk_1_length),
            )

    def load_images(self):
        """Load images referenced in gltf metadata"""
        for image in self.gltf.images:
            self.images.append(image.load(self.path.parent))

    def load_samplers(self):
        """Load samplers referenced in gltf metadata"""
        for sampler in self.gltf.samplers:
            # Use a sane default sampler if the sampler data is empty
            # Samplers can simply just be json data: "{}"
            if sampler.minFilter is sampler.magFilter is None:
                self.samplers.append(
                    self.ctx.sampler(
                        filter=(moderngl.LINEAR_MIPMAP_LINEAR,
                                moderngl.LINEAR),
                        repeat_x=False,
                        repeat_y=False,
                        anisotropy=16.0,
                    ))
            else:
                self.samplers.append(
                    self.ctx.sampler(
                        filter=(sampler.minFilter, sampler.magFilter),
                        repeat_x=sampler.wrapS in [REPEAT, MIRRORED_REPEAT],
                        repeat_y=sampler.wrapT in [REPEAT, MIRRORED_REPEAT],
                        anisotropy=16.0,
                    ))

    def load_textures(self):
        """Load textures referenced in gltf metadata"""
        for texture_meta in self.gltf.textures:
            texture = MaterialTexture()

            if texture_meta.source is not None:
                texture.texture = self.images[texture_meta.source]

            if texture_meta.sampler is not None:
                texture.sampler = self.samplers[texture_meta.sampler]

            self.textures.append(texture)

    def load_meshes(self):
        """Load meshes referenced in gltf metadata"""
        for meta_mesh in self.gltf.meshes:
            # Returns a list of meshes
            meshes = meta_mesh.load(self.materials)
            self.meshes.append(meshes)

            for mesh in meshes:
                self.scene.meshes.append(mesh)

    def load_materials(self):
        """Load materials referenced in gltf metadata"""
        # Create material objects
        for meta_mat in self.gltf.materials:
            mat = Material(meta_mat.name)
            mat.color = meta_mat.baseColorFactor or [1.0, 1.0, 1.0, 1.0]
            mat.double_sided = meta_mat.doubleSided

            if meta_mat.baseColorTexture is not None:
                mat.mat_texture = self.textures[
                    meta_mat.baseColorTexture["index"]]

            self.materials.append(mat)
            self.scene.materials.append(mat)

    def load_nodes(self):
        """Load nodes referenced in gltf metadata"""
        # Start with root nodes in the scene
        for node_id in self.gltf.scenes[0].nodes:
            node = self.load_node(self.gltf.nodes[node_id])
            self.scene.root_nodes.append(node)

    def load_node(self, meta, parent=None):
        """Load a single node"""
        # Create the node
        node = Node(name=meta.name)
        self.scene.nodes.append(node)

        if meta.matrix is not None:
            node.matrix = Matrix44(value=meta.matrix)

        if meta.mesh is not None:
            # Since we split up meshes with multiple primitives, this can be a list
            # If only one mesh we set it on the node as normal
            if len(self.meshes[meta.mesh]) == 1:
                node.mesh = self.meshes[meta.mesh][0]
            # If multiple meshes we add them as new child node
            elif len(self.meshes[meta.mesh]) > 1:
                for mesh in self.meshes[meta.mesh]:
                    node.add_child(Node(mesh=mesh))

        if meta.camera is not None:
            # FIXME: Use a proper camera class
            node.camera = self.gltf.cameras[meta.camera]

        if parent:
            parent.add_child(node)

        # Follow children
        if meta.has_children:
            for node_id in meta.children:
                self.load_node(self.gltf.nodes[node_id], parent=node)

        return node
Пример #3
0
    def load(self):
        """Loads a wavefront/obj file including materials and textures

        Returns:
            Scene: The Scene instance
        """
        path = self.find_scene(self.meta.path)
        logger.info("loading %s", path)

        if not path:
            raise ImproperlyConfigured("Scene '{}' not found".format(
                self.meta.path))

        if path.suffix == '.bin':
            path = path.parent / path.stem

        VAOCacheLoader.attr_names = self.meta.attr_names

        data = pywavefront.Wavefront(str(path),
                                     create_materials=True,
                                     cache=self.meta.cache)
        scene = Scene(self.meta.resolved_path)
        texture_cache = {}

        for _, mat in data.materials.items():
            mesh = Mesh(mat.name)

            # Traditional loader
            if mat.vertices:
                buffer_format, attributes, mesh_attributes = translate_buffer_format(
                    mat.vertex_format, self.meta.attr_names)
                vbo = numpy.array(mat.vertices, dtype='f4')

                vao = VAO(mat.name, mode=moderngl.TRIANGLES)
                vao.buffer(vbo, buffer_format, attributes)
                mesh.vao = vao

                for attrs in mesh_attributes:
                    mesh.add_attribute(*attrs)

            # Binary cache loader
            elif hasattr(mat, 'vao'):
                mesh = Mesh(mat.name)
                mesh.vao = mat.vao
                for attrs in mat.mesh_attributes:
                    mesh.add_attribute(*attrs)
            else:
                # Empty
                continue

            scene.meshes.append(mesh)

            mesh.material = Material(mat.name)
            scene.materials.append(mesh.material)
            mesh.material.color = mat.diffuse

            if mat.texture:
                # A texture can be referenced multiple times, so we need to cache loaded ones
                texture = texture_cache.get(mat.texture.path)
                if not texture:
                    # HACK: pywavefront only give us an absolute path
                    rel_path = os.path.relpath(mat.texture.find(),
                                               str(path.parent))
                    logger.info("Loading: %s", rel_path)
                    with texture_dirs([path.parent]):
                        texture = resources.textures.load(
                            TextureDescription(
                                label=rel_path,
                                path=rel_path,
                                mipmap=True,
                                anisotropy=16.0,
                            ))
                    texture_cache[rel_path] = texture

                mesh.material.mat_texture = MaterialTexture(
                    texture=texture,
                    sampler=None,
                )

            node = Node(mesh=mesh)
            scene.root_nodes.append(node)

        # Not supported yet for obj
        # self.calc_scene_bbox()
        scene.prepare()

        return scene