Source code for moderngl_window.scene.mesh

from __future__ import annotations
from typing import TYPE_CHECKING, Any, Optional

import glm
import moderngl

from moderngl_window.opengl.vao import VAO

from .material import Material

if TYPE_CHECKING:
    from .programs import MeshProgram


[docs] class Mesh: """Mesh info and geometry""" def __init__( self, name: str, vao: Optional[VAO] = None, material: Optional[Material] = None, attributes: Optional[dict[str, Any]] = None, bbox_min: glm.vec3 = glm.vec3(), bbox_max: glm.vec3 = glm.vec3(), ) -> None: """Initialize mesh. Args: name (str): name of the mesh Keyword Args: vao (VAO): geometry material (Material): material for the mesh attributes (dict): Details info about each mesh attribute (dict) bbox_min: xyz min values bbox_max: xyz max values Attributes example:: { "NORMAL": {"name": "in_normal", "components": 3, "type": GL_FLOAT}, "POSITION": {"name": "in_position", "components": 3, "type": GL_FLOAT} } """ self.name = name self.vao = vao self.material = material self.attributes = attributes or {} self.bbox_min = bbox_min self.bbox_max = bbox_max self.mesh_program: Optional["MeshProgram"] = None
[docs] def draw( self, projection_matrix: glm.mat4, model_matrix: glm.mat4, camera_matrix: glm.mat4, time: float = 0.0, ) -> None: """Draw the mesh using the assigned mesh program Keyword Args: projection_matrix (bytes): projection_matrix view_matrix (bytes): view_matrix camera_matrix (bytes): camera_matrix """ if self.mesh_program is not None: self.mesh_program.draw( self, projection_matrix=projection_matrix, model_matrix=model_matrix, camera_matrix=camera_matrix, time=time, )
[docs] def draw_bbox( self, proj_matrix: glm.mat4, model_matrix: glm.mat4, cam_matrix: glm.mat4, program: moderngl.Program, vao: VAO, ) -> None: """Renders the bounding box for this mesh. Args: proj_matrix: Projection matrix model_matrix: View/model matrix cam_matrix: Camera matrix program: The moderngl.Program rendering the bounding box vao: The vao mesh for the bounding box """ program["m_proj"].write(proj_matrix.to_bytes()) program["m_model"].write(model_matrix.to_bytes()) program["m_cam"].write(cam_matrix.to_bytes()) program["bb_min"].write(self.bbox_min.to_bytes()) program["bb_max"].write(self.bbox_max.to_bytes()) vao.render(program)
[docs] def draw_wireframe( self, proj_matrix: glm.mat4, model_matrix: glm.mat4, program: moderngl.Program ) -> None: """Render the mesh as wireframe. proj_matrix: Projection matrix model_matrix: View/model matrix program: The moderngl.Program rendering the wireframe """ assert self.vao is not None, "Can not draw the wireframe, vao is empty" program["m_proj"].write(proj_matrix.to_bytes()) program["m_model"].write(model_matrix.to_bytes()) self.vao.render(program)
[docs] def add_attribute(self, attr_type: str, name: str, components: int) -> None: """ Add metadata about the mesh :param attr_type: POSITION, NORMAL etc :param name: The attribute name used in the program :param components: Number of floats """ self.attributes[attr_type] = {"name": name, "components": components}
[docs] def calc_global_bbox( self, view_matrix: glm.mat4, bbox_min: glm.vec3 | None, bbox_max: glm.vec3 | None ) -> tuple[glm.vec3, glm.vec3]: """Calculates the global bounding. Args: view_matrix: View matrix bbox_min: xyz min bbox_max: xyz max Returns: bbox_min, bbox_max: Combined bbox """ # Copy and extend to vec4 bb1 = glm.vec4(self.bbox_min, 1.0) bb2 = glm.vec4(self.bbox_max, 1.0) # Transform the bbox values bmin = view_matrix * bb1 bmax = view_matrix * bb2 # If a rotation happened there is an axis change and we have to ensure max-min is positive for i in range(3): if bmax[i] - bmin[i] < 0: bmin[i], bmax[i] = bmax[i], bmin[i] if bbox_min is None or bbox_max is None: return (glm.vec3(bmin.x, bmin.y, bmin.z), glm.vec3(bmax.x, bmax.y, bmax.z)) for i in range(3): bbox_min[i] = min(bbox_min[i], bmin[i]) for i in range(3): bbox_max[i] = max(bbox_max[i], bmax[i]) return bbox_min, bbox_max
[docs] def has_normals(self) -> bool: """ Returns: bool: Does the mesh have a normals? """ return "NORMAL" in self.attributes
[docs] def has_uvs(self, layer: int = 0) -> bool: """ Returns: bool: Does the mesh have texture coordinates? """ return "TEXCOORD_{}".format(layer) in self.attributes