Source code for moderngl_window.loaders.scene.wavefront

import logging
import os

import numpy

import pywavefront
from pywavefront import cache
from pywavefront.obj import ObjParser

import moderngl
from moderngl_window.loaders.base import BaseLoader
from moderngl_window.opengl.vao import VAO
from moderngl_window import resources
from moderngl_window.resources.decorators import texture_dirs
from moderngl_window.meta import SceneDescription, TextureDescription
from moderngl_window.scene import Material, MaterialTexture, Mesh, Node, Scene
from moderngl_window.exceptions import ImproperlyConfigured
from moderngl_window.geometry.attributes import AttributeNames


logger = logging.getLogger(__name__)


def translate_buffer_format(vertex_format: str, attr_names: AttributeNames):
    """Translate the buffer format"""
    buffer_format = []
    attributes = []
    mesh_attributes = []

    if "T2F" in vertex_format:
        buffer_format.append("2f")
        attributes.append(attr_names.TEXCOORD_0)
        mesh_attributes.append(("TEXCOORD_0", attr_names.TEXCOORD_0, 2))

    if "C3F" in vertex_format:
        buffer_format.append("3f")
        attributes.append(attr_names.COLOR_0)
        mesh_attributes.append(("COLOR_0", attr_names.COLOR_0, 3))

    if "N3F" in vertex_format:
        buffer_format.append("3f")
        attributes.append(attr_names.NORMAL)
        mesh_attributes.append(("NORMAL", attr_names.NORMAL, 3))

    buffer_format.append("3f")
    attributes.append(attr_names.POSITION)
    mesh_attributes.append(("POSITION", attr_names.POSITION, 3))

    return " ".join(buffer_format), attributes, mesh_attributes


class VAOCacheLoader(cache.CacheLoader):
    """Load geometry data directly into vaos"""
    attr_names = None

    def load_vertex_buffer(self, fd, material, length):
        buffer_format, attributes, mesh_attributes = translate_buffer_format(material.vertex_format,
                                                                             self.attr_names)

        vao = VAO(material.name, mode=moderngl.TRIANGLES)
        vao.buffer(fd.read(length), buffer_format, attributes)

        setattr(material, 'vao', vao)
        setattr(material, 'buffer_format', buffer_format)
        setattr(material, 'attributes', attributes)
        setattr(material, 'mesh_attributes', mesh_attributes)


ObjParser.cache_loader_cls = VAOCacheLoader


class Loader(BaseLoader):
    """Loade wavefront/obj files"""
    kind = 'wavefront'
    file_extensions = [
        ['.obj'],
        ['.obj', '.gz'],
        ['.bin'],
    ]

[docs] def __init__(self, meta: SceneDescription): super().__init__(meta)
[docs] 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