from pathlib import Path
from OpenGL import GL as gl
from picogl.backend.modern.core.shader.compile import compile_shader
from picogl.backend.modern.core.shader.helpers import log_gl_error, read_shader_source
from picogl.backend.modern.core.uniform.location_value import set_uniform_location_value
from picogl.logger import Logger as log
from picogl.shaders.uniform import get_uniform_location
[docs]
class ShaderProgram:
"""OpenGL Shader program manager for vertex and fragment shaders."""
[docs]
def __init__(
self,
shader_name: str = None,
vertex_source_file: str = None,
fragment_source_file: str = None,
glsl_dir: str | Path | None = None,
):
"""constructor"""
[docs]
self.shader_name = shader_name
[docs]
self.vertex_source_file = vertex_source_file
[docs]
self.fragment_source_file = fragment_source_file
[docs]
self.base_dir = glsl_dir
[docs]
self.vertex_shader = None
[docs]
self.fragment_shader = None
if vertex_source_file is not None and vertex_source_file is not None:
self.init_shader_from_glsl_files(
vertex_source_file=vertex_source_file,
fragment_source_file=fragment_source_file,
glsl_dir=glsl_dir,
)
[docs]
def __str__(self):
return f"PicoGLShader(name={self.shader_name}, program={self.program})"
[docs]
def __enter__(self):
self.bind()
[docs]
def __exit__(self, exc_type, exc_val, exc_tb):
self.unbind()
[docs]
def program_id(self):
return self.program
[docs]
def init_shader_from_glsl_files(
self,
vertex_source_file: str,
fragment_source_file: str,
glsl_dir: str | Path | None = None,
) -> None:
"""
init_shader_from_glsl_files
:param glsl_dir: directory containing vertex shaders
:param vertex_source_file: list of paths to vertex shaders
:param fragment_source_file: list of paths to fragment shaders
:return: None
"""
vertex_sources = read_shader_source(vertex_source_file, glsl_dir=glsl_dir)
fragment_sources = read_shader_source(fragment_source_file, glsl_dir=glsl_dir)
self.init_shader_from_glsl(vertex_sources, fragment_sources)
[docs]
def init_shader_from_glsl(self, vertex_source: str, fragment_source: str) -> None:
"""
init_shader_from_glsl
:param vertex_source: list of paths to vertex shaders
:param fragment_source: list of paths to fragment shaders
:return: None
"""
self.init_shader(vertex_source, fragment_source)
[docs]
def init_shader(self, vertex_source: str, fragment_source: str):
"""
init_shader
:param vertex_source: list of paths to vertex shaders
:param fragment_source: list of paths to fragment shaders
:return: None
Create, compile, and link shaders into a program.
"""
self.create_shader_program()
log.parameter("self.program", self.program, silent=True)
log.parameter("vertex_source", vertex_source, silent=True)
log.parameter("fragment_source", fragment_source, silent=True)
self.vertex_shader = compile_shader(
self.program, gl.GL_VERTEX_SHADER, vertex_source
)
self.fragment_shader = compile_shader(
self.program, gl.GL_FRAGMENT_SHADER, fragment_source
)
self.link_shader_program()
[docs]
def create_shader_program(self):
"""
create_shader_program
"""
self.program = gl.glCreateProgram()
log.message(f"Created shader program {self.program}", silent=True)
log_gl_error()
[docs]
def link_shader_program(self):
"""
link_shader_program
"""
log.message("Linking shader program...", silent=True)
gl.glLinkProgram(self.program)
if gl.GL_TRUE != gl.glGetProgramiv(self.program, gl.GL_LINK_STATUS):
err = gl.glGetProgramInfoLog(self.program)
raise RuntimeError(f"Shader link failed: {err}")
log_gl_error()
[docs]
def begin(self):
"""begin"""
gl.glUseProgram(self.program)
log_gl_error()
[docs]
def end(self):
"""end"""
gl.glUseProgram(0)
[docs]
def bind(self):
"""begin"""
gl.glUseProgram(self.program)
log_gl_error()
[docs]
def unbind(self):
gl.glUseProgram(0)
[docs]
def release(self):
gl.glUseProgram(0)
[docs]
def delete(self):
self.release()