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
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
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