示例#1
0
    def resolve_loader(self, meta: ResourceDescription) -> None:
        """
        Attempts to assign a loader class to a ResourceDecription.

        Args:
            meta (:py:class:`~moderngl_window.meta.base.ResourceDescription`): The resource description instance
        """
        # Get loader using kind if specified
        if meta.kind:
            for loader_cls in self.loaders:
                if loader_cls.kind == meta.kind:
                    meta.loader_cls = loader_cls
                    return

            raise ImproperlyConfigured(
                "Resource has invalid loader kind '{}': {}\nAvailiable loaders: {}"
                .format(meta.kind, meta,
                        [loader.kind for loader in self.loaders]))

        # Get loader based on file extension
        for loader_cls in self.loaders:
            if loader_cls.supports_file(meta):
                meta.loader_cls = loader_cls
                return

        raise ImproperlyConfigured(
            "Could not find a loader for: {}".format(meta))
示例#2
0
    def _validate(self, faces):
        """Validates each face ensuring components and size it the same"""
        components = faces[0].components
        data_size = len(faces[0].data)
        for face in faces:
            if face.components != components:
                raise ImproperlyConfigured(
                    "Cubemap face textures have different number of components"
                )
            if len(face.data) != data_size:
                raise ImproperlyConfigured(
                    "Cubemap face textures must all have the same size")

        return components
示例#3
0
    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 _append_unique_path(path: Union[Path, str], dest: list):
    path = Path(path)
    if not path.is_absolute():
        raise ImproperlyConfigured("Search path must be absolute: {}".format(path))

    if not path.is_dir():
        raise ImproperlyConfigured("Search path is not a directory: {}".format(path))

    if not path.exists():
        raise ImproperlyConfigured("Search path do not exist: {}".format(path))

    for resource_path in dest:
        if Path(resource_path).samefile(path):
            break
    else:
        dest.append(Path(path).absolute())
示例#5
0
    def _open_image(self):
        if self.meta.image:
            self.image = self.meta.image
        else:
            self.meta.resolved_path = self.find_texture(self.meta.path)
            logger.info("loading %s", self.meta.resolved_path)
            if not self.meta.resolved_path:
                raise ImproperlyConfigured("Cannot find texture: {}".format(
                    self.meta.path))

            self.image = Image.open(self.meta.resolved_path)

            # If the image is animated (like a gif anim) we convert it into a vertical strip
            if hasattr(self.image, "is_animated") and self.image.is_animated:
                self.layers = self.image.n_frames
                anim = Image.new(
                    self.image.palette.mode,
                    (self.image.width,
                     self.image.height * self.image.n_frames),
                )
                anim.putalpha(0)

                for frame_number in range(self.image.n_frames):
                    self.image.seek(frame_number)
                    frame = self._palette_to_raw(self.image, mode="RGBA")
                    anim.paste(frame, (0, frame_number * self.image.height))

                self.image = anim

        self.image = self._apply_modifiers(self.image)
        return self.image
示例#6
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
示例#7
0
    def __init__(self, meta):
        super().__init__(meta)
        self.layers = self.meta.layers

        if self.layers is None:
            raise ImproperlyConfigured(
                "TextureArray requires layers parameter")
示例#8
0
 def __init__(self):
     """Initialize finder class by looking up the paths referenced in ``settings_attr``.
     """
     if not hasattr(settings, self.settings_attr):
         raise ImproperlyConfigured(
             "Settings doesn't define {}. "
             "This is required when using a FileSystemFinder.".format(
                 self.settings_attr))
     self.paths = getattr(settings, self.settings_attr)
示例#9
0
    def _load_texture(self, path):
        """Find and load separate texture. Useful when multiple textue files needs to be loaded"""
        resolved_path = self.find_texture(path)
        logger.info("loading %s", resolved_path)
        if not resolved_path:
            raise ImproperlyConfigured("Cannot find texture: {}".format(path))

        image = Image.open(resolved_path)
        return self._apply_modifiers(image)
示例#10
0
    def load_shader(self, shader_type: str, path: str):
        """Load a single shader"""
        if path:
            resolved_path = self.find_program(path)
            if not resolved_path:
                raise ImproperlyConfigured("Cannot find {} shader '{}'".format(
                    shader_type, path))

            logger.info("Loading: %s", resolved_path)

            with open(str(resolved_path), 'r') as fd:
                return fd.read()
示例#11
0
    def _check_meta(self, meta: Any):
        """Check is the instance is a resource description
        Raises:
            ImproperlyConfigured if not a ResourceDescription instance
        """
        if inspect.isclass(type(meta)):
            if issubclass(meta.__class__, ResourceDescription):
                return

        raise ImproperlyConfigured(
            "Resource loader got type {}, not a resource description".format(
                type(meta)))
示例#12
0
    def _load_texture(self, path):
        """Find and load separate texture. Useful when multiple textue files needs to be loaded"""
        resolved_path = self.find_texture(path)
        logger.info("loading %s", resolved_path)
        if not resolved_path:
            raise ImproperlyConfigured("Cannot find texture: {}".format(path))

        image = Image.open(resolved_path)
        if self.meta.flip:
            image = image.transpose(Image.FLIP_TOP_BOTTOM)

        image = self._palette_to_raw(image)
        return image
示例#13
0
    def _open_image(self):
        if self.meta.image:
            self.image = self.meta.image
        else:
            self.meta.resolved_path = self.find_texture(self.meta.path)
            logger.info("loading %s", self.meta.resolved_path)
            if not self.meta.resolved_path:
                raise ImproperlyConfigured("Cannot find texture: {}".format(
                    self.meta.path))

            self.image = Image.open(self.meta.resolved_path)

        if self.meta.flip:
            self.image = self.image.transpose(Image.FLIP_TOP_BOTTOM)
示例#14
0
    def load(
        self,
    ) -> Union[moderngl.Program, moderngl.ComputeShader,
               program.ReloadableProgram]:
        """Loads a shader program were each shader is a separate file.

        This detected and dictated by the ``kind`` in the ``ProgramDescription``.

        Returns:
            moderngl.Program: The Program instance
        """
        prog = None

        vs_source = self._load_shader("vertex", self.meta.vertex_shader)
        geo_source = self._load_shader("geometry", self.meta.geometry_shader)
        fs_source = self._load_shader("fragment", self.meta.fragment_shader)
        tc_source = self._load_shader("tess_control",
                                      self.meta.tess_control_shader)
        te_source = self._load_shader("tess_evaluation",
                                      self.meta.tess_evaluation_shader)
        cs_source = self._load_shader("compute", self.meta.compute_shader)

        if vs_source:
            shaders = program.ProgramShaders.from_separate(
                self.meta,
                vs_source,
                geometry_source=geo_source,
                fragment_source=fs_source,
                tess_control_source=tc_source,
                tess_evaluation_source=te_source,
            )
            shaders.handle_includes(self._load_source)
            prog = shaders.create()

            # Wrap the program if reloadable is set
            if self.meta.reloadable:
                # Disable reload flag so reloads will return Program instances
                self.meta.reloadable = False
                # Wrap it ..
                prog = program.ReloadableProgram(self.meta, prog)
        elif cs_source:
            shaders = program.ProgramShaders.compute_shader(
                self.meta, cs_source)
            shaders.handle_includes(self._load_source)
            prog = shaders.create_compute_shader()
        else:
            raise ImproperlyConfigured("Cannot find a shader source to load")

        return prog
    def load(self) -> str:
        """Load a file in text mode.

        Returns:
            str: The string contents of the file
        """
        self.meta.resolved_path = self.find_data(self.meta.path)

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

        logger.info("Loading: %s", self.meta.path)

        with open(str(self.meta.resolved_path), "r") as fd:
            return fd.read()
示例#16
0
    def _load_face(self, path: str, face_name: str = None):
        """Obtain raw byte data for a face

        Returns:
            Tuple[int, bytes]: number of components, byte data
        """
        if not path:
            raise ImproperlyConfigured(
                "{} texture face not supplied".format(face_name))

        image = self._load_texture(path)
        components, data = image_data(image)
        return FaceInfo(width=image.size[0],
                        height=image.size[1],
                        data=data,
                        components=components)
示例#17
0
    def load(self) -> dict:
        """Load a file as json

        Returns:
            dict: The json contents
        """
        self.meta.resolved_path = self.find_data(self.meta.path)

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

        logger.info("Loading: %s", self.meta.path)

        with open(str(self.meta.resolved_path), "r") as fd:
            return json.loads(fd.read())
示例#18
0
    def _load_source(self, path):
        """Finds and loads a single source file.

        Args:
            path: Path to resource
        Returns:
            Tuple[resolved_path, source]: The resolved path and the source
        """
        resolved_path = self.find_program(path)
        if not resolved_path:
            raise ImproperlyConfigured("Cannot find program '{}'".format(path))

        logger.info("Loading: %s", path)

        with open(str(resolved_path), "r") as fd:
            return resolved_path, fd.read()
示例#19
0
def get_finder(import_path: str):
    """
    Get a finder class from an import path.
    This function uses an lru cache.

    Args:
        import_path: string representing an import path
    Return:
        An instance of the finder
    Raises:
        ImproperlyConfigured is the finder is not found
    """
    Finder = import_string(import_path)
    if not issubclass(Finder, BaseFilesystemFinder):
        raise ImproperlyConfigured('Finder {} is not a subclass of .finders.FileSystemFinder'.format(import_path))

    return Finder()
示例#20
0
    def find(self, path: Path) -> Path:
        """Finds a file in the configured paths returning its absolute path.

        Args:
            path (pathlib.Path): The path to find
        Returns:
            The absolute path to the file or None if not found
        """
        # Update paths from settings to make them editable runtime
        if getattr(self, "settings_attr", None):
            self.paths = getattr(settings, self.settings_attr)

        if not isinstance(path, Path):
            raise ValueError(
                "FilesystemFinders only take Path instances, not {}".format(
                    type(path)))

        logger.debug("find %s", path)

        # Ignore absolute paths so other finder types can pick them up.
        if path.is_absolute():
            logger.debug("Ignoring absolute path: %s", path)
            return None

        logger.debug("paths: %s", self.paths)

        for search_path in self.paths:
            search_path = Path(search_path)

            # Keep ensuring all search paths are absolute
            if not search_path.is_absolute():
                raise ImproperlyConfigured(
                    "Search search path '{}' is not an absolute path")

            abspath = search_path / path
            logger.debug("abspath %s", abspath)

            if abspath.exists():
                logger.debug("found %s", abspath)
                return abspath

        return None
示例#21
0
    def apply_from_module_name(self, settings_module_name: str) -> None:
        """
        Apply settings from a python module by supplying the full
        pythonpath to the module.

        Args:
            settings_module_name (str): Full python path to the module

        Raises:
            ImproperlyConfigured if the module was not found
        """
        try:
            module = importlib.import_module(settings_module_name)
        except ImportError as ex:
            raise ImproperlyConfigured(
                "Settings module '{}' not found. From importlib: {}".format(
                    settings_module_name,
                    ex,
                ))

        self.apply_from_module(module)
示例#22
0
    def load(self) -> moderngl.Program:
        """Loads a shader program from a single glsl file.

        Each shader type is separated by preprocessors

        - VERTEX_SHADER
        - FRAGMENT_SHADER
        - GEOMETRY_SHADER
        - TESS_CONTROL_SHADER
        - TESS_EVALUATION_SHADER

        Example:

        .. code:: glsl

            #version 330

            #if defined VERTEX_SHADER

            in vec3 in_position;
            in vec2 in_texcoord_0;
            out vec2 uv0;

            void main() {
                gl_Position = vec4(in_position, 1);
                uv0 = in_texcoord_0;
            }

            #elif defined FRAGMENT_SHADER

            out vec4 fragColor;
            uniform sampler2D texture0;
            in vec2 uv0;

            void main() {
                fragColor = texture(texture0, uv0);
            }
            #endif

        Returns:
            moderngl.Program: The Program instance
        """
        self.meta.resolved_path = self.find_program(self.meta.path)
        if not self.meta.resolved_path:
            raise ImproperlyConfigured("Cannot find program '{}'".format(
                self.meta.path))

        logger.info("Loading: %s", self.meta.path)

        with open(str(self.meta.resolved_path), 'r') as fd:
            shaders = program.ProgramShaders.from_single(self.meta, fd.read())

        prog = shaders.create()

        # Wrap the program if reloadable is set
        if self.meta.reloadable:
            # Disable reload flag so reloads will return Program instances
            self.meta.reloadable = False
            # Wrap it ..
            prog = program.ReloadableProgram(self.meta, prog)

        return prog
示例#23
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