diff options
-rw-r--r-- | txtgameengine/__main__.py | 9 | ||||
-rw-r--r-- | txtgameengine/app.py | 2 | ||||
-rw-r--r-- | txtgameengine/platform.py | 10 | ||||
-rw-r--r-- | txtgameengine/shaders/__init__.py | 2 | ||||
-rw-r--r-- | txtgameengine/shaders/integrated.py | 16 | ||||
-rw-r--r-- | txtgameengine/shaders/shader.py | 50 |
6 files changed, 81 insertions, 8 deletions
diff --git a/txtgameengine/__main__.py b/txtgameengine/__main__.py index 1ebc309..9aeeec3 100644 --- a/txtgameengine/__main__.py +++ b/txtgameengine/__main__.py @@ -2,8 +2,8 @@ import numpy as np from PIL import Image from .scenes import SceneTxtGameApp, Scene -from OpenGL.GL import * from pathlib import Path +from .shaders import TextureShader shader_path = Path(__file__).parent / 'builtin_shaders' @@ -46,9 +46,7 @@ class EvilTriangleScene(TriangleScene): class TextureScene(Scene): def on_enter(self): - self.texture_shaders = self.app.shaders.load_shaders(str(shader_path / 'texture/vertex.glsl'), - str(shader_path / 'texture/fragment.glsl')) - self.sampler_location = self.app.shaders.get_uniform_location(self.texture_shaders, 'textureSampler') + self.texture_shaders = TextureShader(self.app) self.texture = self.app.render.setup_texture_from_pil(Image.open('test_image.png')) self.triangle = self.app.render.setup_buffer( np.array([ @@ -65,7 +63,8 @@ class TextureScene(Scene): def update(self, delta: float): with self.texture_shaders: - self.app.render.textured_triangle(self.sampler_location, self.texture, self.triangle, self.uvs) + self.app.render.textured_triangle(self.texture_shaders.textureSampler, self.texture, self.triangle, + self.uvs) class TestApp(SceneTxtGameApp): diff --git a/txtgameengine/app.py b/txtgameengine/app.py index f3cb711..e7121da 100644 --- a/txtgameengine/app.py +++ b/txtgameengine/app.py @@ -1,8 +1,10 @@ import time +from pathlib import Path from .platform import PlatformComponent, RenderComponent, ShaderComponent EPSILON = 1.e-10 +base_path = Path(__file__).parent class TxtGameApp: diff --git a/txtgameengine/platform.py b/txtgameengine/platform.py index 17d85ab..f4b3b5b 100644 --- a/txtgameengine/platform.py +++ b/txtgameengine/platform.py @@ -90,7 +90,7 @@ class ShaderComponent: self.app = app @staticmethod - def load_shaders(vertex_file, fragment_file): + def load_shaders(vertex_file: str, fragment_file: str): with open(vertex_file) as fp: vertex_source = fp.read() with open(fragment_file) as fp: @@ -101,8 +101,12 @@ class ShaderComponent: return shaders.compileProgram(vertex_shader, fragment_shader) @staticmethod - def get_uniform_location(shader, name): - return glGetUniformLocation(shader, name) + def bind_shader(prog_id: int): + glUseProgram(prog_id) + + @staticmethod + def get_uniform_location(prog_id: int, name: str): + return glGetUniformLocation(prog_id, name) class RenderComponent: diff --git a/txtgameengine/shaders/__init__.py b/txtgameengine/shaders/__init__.py new file mode 100644 index 0000000..64b0cc8 --- /dev/null +++ b/txtgameengine/shaders/__init__.py @@ -0,0 +1,2 @@ +from .shader import Shader +from .integrated import TextureShader, BasicShader diff --git a/txtgameengine/shaders/integrated.py b/txtgameengine/shaders/integrated.py new file mode 100644 index 0000000..dfa1033 --- /dev/null +++ b/txtgameengine/shaders/integrated.py @@ -0,0 +1,16 @@ +from .shader import Shader +from ..app import base_path + +shader_base_path = base_path / 'builtin_shaders' + + +class BasicShader(Shader): + UNIFORMS = dict() + VERTEX_PATH = shader_base_path / 'basic/vertex.glsl' + FRAGMENT_PATH = shader_base_path / 'basic/fragment.glsl' + + +class TextureShader(Shader): + UNIFORMS = dict(textureSampler="textureSampler") + VERTEX_PATH = shader_base_path / 'texture/vertex.glsl' + FRAGMENT_PATH = shader_base_path / 'texture/fragment.glsl' diff --git a/txtgameengine/shaders/shader.py b/txtgameengine/shaders/shader.py new file mode 100644 index 0000000..61b6aaf --- /dev/null +++ b/txtgameengine/shaders/shader.py @@ -0,0 +1,50 @@ +import typing + +if typing.TYPE_CHECKING: + from ..app import TxtGameApp + + +class Shader: + """Wrapper class for a GL program""" + + UNIFORMS: typing.Dict[str, str] = dict() + VERTEX_PATH: str + FRAGMENT_PATH: str + + def __init__(self, app: 'TxtGameApp'): + self.app = app + self._prog_id = self.app.shaders.load_shaders(self.VERTEX_PATH, self.FRAGMENT_PATH) + self._is_bound = 0 + self.uniform_locations = {} + self._fill_uniforms() + + def _fill_uniforms(self): + with self: + for prop_name, shader_name in self.UNIFORMS.items(): + self.uniform_locations[prop_name] = \ + self.app.shaders.get_uniform_location(self._prog_id, shader_name) + + def __getattr__(self, item): + if item in self.uniform_locations: + return self.uniform_locations[item] + raise AttributeError("Shader '%s' has no attribute or uniform named %s" % (type(self).__name__, item)) + + def _require_bound(self, required=False): + assert self._is_bound > 0 or not required + return self + + def get_uniform_location(self, name: str): + return self.uniform_locations[name] + + def __enter__(self): + self._is_bound += 1 + if self._is_bound == 1: + self.app.shaders.bind_shader(self._prog_id) + + def __exit__(self, exc_type, exc_val, exc_tb): + self._is_bound -= 1 + if self._is_bound == 0: + self.app.shaders.bind_shader(0) + + def __repr__(self): + return '<Shader prog_id=%d uniforms=%r>' % (self._prog_id, self.uniform_locations) |