Source code for meshes.chunk_mesh

"""
OpenGL geometry wrapper and VRAM manager for active world chunks.

This module links the generated Numba geometry data directly to the ModernGL VAO/VBO contexts.
Crucially, it handles the dynamic VBO object pooling architecture, safely tracking and recycling
massive GPU memory buffers on the fly to prevent strict VRAM memory leaking during chunk streaming.
"""

import math
from typing import Any, Tuple

from meshes.base_mesh import BaseMesh
from meshes.chunk_mesh_builder import build_chunk_mesh
from profiler import global_profiler


[docs] class ChunkMesh(BaseMesh): """ Manages the OpenGL geometry for a chunk, handling opaque and transparent meshes. It interfaces with the greedy meshing builder to generate vertex data and utilizes a VBO pool to manage GPU memory efficiently. Ensures chunks can be seamlessly uploaded and released from VRAM during rapid world streaming. Args: chunk (Any): The parent chunk instance this mesh is visually representing. """ @global_profiler.profile_func('ChunkMesh_Init') def __init__(self, chunk: Any) -> None: """ Initializes the chunk mesh, linking it to its parent chunk and the appropriate shader program. Sets up the vertex buffer format and prepares attributes for rendering. """ super().__init__() self.app: Any = chunk.app self.chunk: Any = chunk self.ctx: Any = self.app.ctx self.program: Any = self.app.shader_program.chunk self.vbo_format: str = '1u4 1u4' self.format_size: int = 2 self.attrs: Tuple[str, ...] = ('packed_data', 'light_data') self.vao: Any = None self.vbo: Any = None self.vertex_data: Any = None self.opaque_count: int = 0 self.water_count: int = 0
[docs] @global_profiler.profile_func('ChunkMesh_Render') def render(self) -> None: """ Issues the draw call for the opaque portion of the chunk's mesh, provided it has valid geometry to render. """ if self.vao and self.opaque_count > 0: self.vao.render(vertices=self.opaque_count)
[docs] @global_profiler.profile_func('ChunkMesh_RenderWater') def render_water(self) -> None: """ Issues the draw call for the transparent water portion of the chunk's mesh, starting from the end of the opaque vertex data. """ if self.vao and self.water_count > 0: self.vao.render(vertices=self.water_count, first=self.opaque_count)
[docs] @global_profiler.profile_func('ChunkMesh_GetVAO') def get_vao(self) -> Any: """ Retrieves or builds the Vertex Array Object (VAO) for the chunk. It first generates the raw vertex data, then attempts to recycle an appropriately sized VBO from the global pool to prevent memory leaks, allocating a new one if necessary. """ if self.vertex_data is None: result = self.get_vertex_data() self.vertex_data = result[0] self.opaque_count = result[1] self.water_count = result[2] if self.vertex_data.size == 0: return None byte_size: int = self.vertex_data.nbytes pool: Any = self.chunk.world.vbo_pool # Find the smallest VBO in the pool that can safely fit our new mesh data best_index: int = -1 best_size: float = float('inf') for index, (pool_vbo, pool_vao) in enumerate(pool): if pool_vbo.size >= byte_size and pool_vbo.size < best_size: best_index = index best_size = pool_vbo.size if best_index != -1: pool.rotate(-best_index) self.vbo, self.vao = pool.popleft() self.vbo.write(self.vertex_data) self.vertex_data = None return self.vao # Allocate a new VBO, but round the size up to the nearest power of 2 # This ensures the VBOs are generic sizes (e.g. 128KB, 256KB) and highly reusable! reserve_size: int = 2 ** math.ceil(math.log2(byte_size)) if byte_size > 0 else 0 self.vbo = self.ctx.buffer(reserve=reserve_size) self.vbo.write(self.vertex_data) vao: Any = self.ctx.vertex_array(self.program, [(self.vbo, self.vbo_format, *self.attrs)], skip_errors=True) self.vertex_data = None return vao
[docs] @global_profiler.profile_func('ChunkMesh_GetVertexData') def get_vertex_data(self) -> Tuple[Any, int, int]: """ Triggers the greedy meshing algorithm to construct the optimized vertex payload (including ambient occlusion and lighting) from the chunk's 3D voxel array. """ mesh: Tuple[Any, int, int] = build_chunk_mesh( chunk_voxels=self.chunk.voxels, chunk_lightmap=self.chunk.lightmap, format_size=self.format_size, chunk_pos=self.chunk.position, world_voxels=self.chunk.world.voxels, world_lightmaps=self.chunk.world.lightmaps, chunk_positions=self.chunk.world.chunk_positions, ) return mesh