diff --git a/game/camera.py b/game/camera.py index d50e3a9..39c4d63 100644 --- a/game/camera.py +++ b/game/camera.py @@ -13,14 +13,7 @@ # You should have received a copy of the GNU Affero General Public License # along with this program. If not, see . -from engine import vec3_origin, mat4, mat4_projection, mat4_orbit, resolve_input, set_input_mat4 - -class _Inputs: - __slots__ = 'projection', 'view' - - def __init__(self, shader, projection, view): - self.projection = resolve_input(shader, projection) - self.view = resolve_input(shader, view) +from engine import vec3_origin, mat4, mat4_projection, mat4_orbit, set_input_mat4 class Camera: __slots__ = 'yaw', 'pitch', 'distance', 'projection', 'view' @@ -44,9 +37,6 @@ class Camera: def to_km(self): mat4_orbit(self.view, vec3_origin, self.yaw, self.pitch, self.distance * 0.001) - def resolve_inputs(self, shader, projection = b'u_projection', view = b'u_view'): - return _Inputs(shader, projection, view) - - def update_inputs(self, inputs): - set_input_mat4(inputs.projection, self.projection) - set_input_mat4(inputs.view, self.view) + def set_inputs(self, shader): + set_input_mat4(shader.u_projection, self.projection) + set_input_mat4(shader.u_view, self.view) diff --git a/game/environment.py b/game/environment.py index 2c718b6..c2cdc25 100644 --- a/game/environment.py +++ b/game/environment.py @@ -14,7 +14,7 @@ # along with this program. If not, see . from math import radians, cos -from engine import vec3, mat4_mul_vec3, resolve_input, set_input_vec3 +from engine import vec3, mat4_mul_vec3, set_input_vec3 from game.math import vec3_up, vec3_add, vec3_sub, vec3_scale, vec3_mul, vec3_dot def _angles(start, end): @@ -62,16 +62,6 @@ def _resolve_color(ranges, c): w, (a, b) = _resolve(ranges, c) return vec3_add(a, vec3_scale(b, w)) -class _Inputs: - __slots__ = 'light_direction', 'light_color', 'horizon_color', 'sky_color', 'sun_color' - - def __init__(self, shader): - self.light_direction = resolve_input(shader, b'u_light_direction') - self.light_color = resolve_input(shader, b'u_light_color') - self.horizon_color = resolve_input(shader, b'u_horizon_color') - self.sky_color = resolve_input(shader, b'u_sky_color') - self.sun_color = resolve_input(shader, b'u_sun_color') - class Environment: __slots__ = 'light_direction', 'light_color', 'horizon_color', 'sky_color', 'sun_color' @@ -91,12 +81,9 @@ class Environment: self.sky_color.set(*vec3_scale(_resolve_color(_sky_color, c), light_power)) self.sun_color.set(*_resolve_color(_sun_color, c)) - def resolve_inputs(self, shader): - return _Inputs(shader) - - def update_inputs(self, inputs): - set_input_vec3(inputs.light_direction, self.light_direction) - set_input_vec3(inputs.light_color, self.light_color) - set_input_vec3(inputs.horizon_color, self.horizon_color) - set_input_vec3(inputs.sky_color, self.sky_color) - set_input_vec3(inputs.sun_color, self.sun_color) + def set_inputs(self, shader): + set_input_vec3(shader.u_light_direction, self.light_direction) + set_input_vec3(shader.u_light_color, self.light_color) + set_input_vec3(shader.u_horizon_color, self.horizon_color) + set_input_vec3(shader.u_sky_color, self.sky_color) + set_input_vec3(shader.u_sun_color, self.sun_color) diff --git a/game/game.py b/game/game.py index f80eb4f..ad30abe 100644 --- a/game/game.py +++ b/game/game.py @@ -20,9 +20,9 @@ from ctypes import Structure from engine import * from game import math -from game import shader from game import triangles from game import sea +from game.shader import Shader from game.generator import Generator from game.resources import RuntimeArchive from game.batch import Batch @@ -53,9 +53,9 @@ def main(): mouse = Mouse(events, wheel = 60, wheel_min = 20) keyboard = Keyboard(events) render_initialize(__debug__) - terrain_shader = shader.load('terrain', 'common') - tests_shader = shader.load('tests', 'common') - sky_shader = shader.load('sky') + terrain_shader = Shader('terrain', 'common') + tests_shader = Shader('tests', 'common') + sky_shader = Shader('sky') heightmap = create_texture( TEXTURE_FORMAT_FLOAT_32, 256, 256, 0, TEXTURE_FLAG_MIN_LINEAR | TEXTURE_FLAG_MAG_LINEAR, @@ -127,21 +127,13 @@ def main(): cube_id = cube_model.spawn(tests_batch, cube_spawn_translation, mat3_identity) clouds_id = clouds_model.spawn(tests_batch, vec3(0.0, 0.0, 32.0), mat3_identity) - sea_phase = resolve_input(sky_shader, b'u_sea_phase') sea_polar_textures = sea.load_polar_textures(('data/sea_bump1.png', 'data/sea_bump2.png')) sea_detail_texture = sea.load_detail_texture('data/sea_bump.png') sky_triangles = create_triangles(triangles.sky_triangles(64, proj_far_z - 0.1, proj_ratio)) camera = Camera() camera.set_projection(proj_hfov, proj_ratio, proj_near_z, proj_far_z) - terrain_camera_inputs = camera.resolve_inputs(terrain_shader) - tests_camera_inputs = camera.resolve_inputs(tests_shader) - sky_camera_inputs = camera.resolve_inputs(sky_shader) - environment = Environment() - terrain_environment_inputs = environment.resolve_inputs(terrain_shader) - tests_environment_inputs = environment.resolve_inputs(tests_shader) - sky_environment_inputs = environment.resolve_inputs(sky_shader) blob_translation = tests_batch.translation[blob_id] cube_translation = tests_batch.translation[cube_id] @@ -176,9 +168,9 @@ def main(): camera.set_view(camera_yaw, camera_pitch, camera_distance) environment.from_sun(camera.view, sun_direction, sun_power) - select_shader(terrain_shader) - camera.update_inputs(terrain_camera_inputs) - environment.update_inputs(terrain_environment_inputs) + terrain_shader.select() + camera.set_inputs(terrain_shader) + environment.set_inputs(terrain_shader) select_texture(0, tiles_texture) select_texture(1, heightmap) select_texture(2, normalmap) @@ -188,7 +180,7 @@ def main(): unselect_texture(0, tiles_texture) unselect_texture(1, heightmap) unselect_texture(2, normalmap) - unselect_shader(terrain_shader) + terrain_shader.unselect() rotation = mat3() mat3_rotation(rotation, vec3_up, (current_time * 0.21) % tau) @@ -197,9 +189,9 @@ def main(): mat3_rotation(cube_orientation, vec3_up, (current_time * 0.43) % tau) mat3_rotation(clouds_orientation, vec3_up, (current_time * -0.037) % tau) - select_shader(tests_shader) - camera.update_inputs(tests_camera_inputs) - environment.update_inputs(tests_environment_inputs) + tests_shader.select() + camera.set_inputs(tests_shader) + environment.set_inputs(tests_shader) select_texture(0, tests_texture) select_texture(1, heightmap) select_texture(2, normalmap) @@ -207,20 +199,20 @@ def main(): unselect_texture(0, tests_texture) unselect_texture(1, heightmap) unselect_texture(2, normalmap) - unselect_shader(tests_shader) + tests_shader.unselect() camera.to_km() - select_shader(sky_shader) - camera.update_inputs(sky_camera_inputs) - environment.update_inputs(sky_environment_inputs) - set_input_float(sea_phase, (current_time * 0.023) % 1.0) + sky_shader.select() + camera.set_inputs(sky_shader) + environment.set_inputs(sky_shader) + set_input_float(sky_shader.u_sea_phase, (current_time * 0.023) % 1.0) select_texture(0, sea_polar_textures) select_texture(1, sea_detail_texture) draw_triangles(sky_triangles) unselect_texture(0, sea_polar_textures) unselect_texture(1, sea_detail_texture) - unselect_shader(sky_shader) + sky_shader.unselect() frame_end = time.thread_time() end_frame() @@ -272,9 +264,9 @@ def main(): destroy_texture(normalmap) destroy_triangles(sky_triangles) archive.destroy() - destroy_shader(terrain_shader) - destroy_shader(tests_shader) - destroy_shader(sky_shader) + del terrain_shader + del tests_shader + del sky_shader render_terminate() del events destroy_display(display) diff --git a/game/shader.py b/game/shader.py index 76068ac..68374bb 100644 --- a/game/shader.py +++ b/game/shader.py @@ -15,9 +15,7 @@ from pathlib import Path -from engine import load_shader - -#TODO: preprocessing (at least includes) +from engine import load_shader, resolve_input, select_shader, unselect_shader, destroy_shader def _cleanup(line): return line.partition('//')[0].strip() @@ -34,9 +32,9 @@ def _subst(line): return lines return [line] -def _load_source(_path): - assert _path.exists() - lines = filter(_filter, map(_cleanup, open(_path, 'rt'))) +def _load_source(path): + assert path.exists() + lines = filter(_filter, map(_cleanup, open(path, 'rt'))) source = [] for line in lines: source.extend(_subst(line)) @@ -45,14 +43,41 @@ def _load_source(_path): def _convert(line): return bytes(line, 'utf-8') + b'\n' -def load(vert_name, frag_name = ''): - path = Path('.') / 'game' / 'shaders' - if not frag_name: - frag_name = vert_name - print("Loading vertex shader", vert_name) - vert_lines = list(map(_convert, _load_source(path / (vert_name + '_opengles.vert')))) - assert vert_lines - print("Loading fragment shader", frag_name) - frag_lines = list(map(_convert, _load_source(path / (frag_name + '_opengles.frag')))) - assert frag_lines - return load_shader(vert_lines, frag_lines) +def _parse(shader, vert_lines, frag_lines): + uniforms = [] + for line in vert_lines: + if line.startswith('uniform '): + uniforms.append(line.split()[-1].strip(';')) + for line in frag_lines: + if line.startswith('uniform '): + name = line.split()[-1].strip(';') + if name not in uniforms: + uniforms.append(name) + for name in uniforms: + setattr(shader, name, resolve_input(shader._shader, bytes(name, 'utf-8'))) + +class Shader: + __slots__ = '_shader', '__dict__' + + def __init__(self, vert_name, frag_name = ''): + path = Path('.') / 'game' / 'shaders' + assert path.exists() + if not frag_name: + frag_name = vert_name + print("Loading vertex shader", vert_name) + vert_lines = _load_source(path / (vert_name + '_opengles.vert')) + assert vert_lines + print("Loading fragment shader", frag_name) + frag_lines = _load_source(path / (frag_name + '_opengles.frag')) + assert frag_lines + self._shader = load_shader(list(map(_convert, vert_lines)), list(map(_convert, frag_lines))) + _parse(self, vert_lines, frag_lines) + + def __del__(self): + destroy_shader(self._shader) + + def select(self): + select_shader(self._shader) + + def unselect(self): + unselect_shader(self._shader)