Automatic shader inputs.

This commit is contained in:
Roz K 2022-12-31 15:22:30 +01:00
parent 09dcaae95b
commit 9fff666ebf
Signed by: roz
GPG Key ID: 51FBF4E483E1C822
4 changed files with 73 additions and 79 deletions

View File

@ -13,14 +13,7 @@
# You should have received a copy of the GNU Affero General Public License # You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
from engine import vec3_origin, mat4, mat4_projection, mat4_orbit, resolve_input, set_input_mat4 from engine import vec3_origin, mat4, mat4_projection, mat4_orbit, 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)
class Camera: class Camera:
__slots__ = 'yaw', 'pitch', 'distance', 'projection', 'view' __slots__ = 'yaw', 'pitch', 'distance', 'projection', 'view'
@ -44,9 +37,6 @@ class Camera:
def to_km(self): def to_km(self):
mat4_orbit(self.view, vec3_origin, self.yaw, self.pitch, self.distance * 0.001) 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'): def set_inputs(self, shader):
return _Inputs(shader, projection, view) set_input_mat4(shader.u_projection, self.projection)
set_input_mat4(shader.u_view, self.view)
def update_inputs(self, inputs):
set_input_mat4(inputs.projection, self.projection)
set_input_mat4(inputs.view, self.view)

View File

@ -14,7 +14,7 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
from math import radians, cos 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 from game.math import vec3_up, vec3_add, vec3_sub, vec3_scale, vec3_mul, vec3_dot
def _angles(start, end): def _angles(start, end):
@ -62,16 +62,6 @@ def _resolve_color(ranges, c):
w, (a, b) = _resolve(ranges, c) w, (a, b) = _resolve(ranges, c)
return vec3_add(a, vec3_scale(b, w)) 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: class Environment:
__slots__ = 'light_direction', 'light_color', 'horizon_color', 'sky_color', 'sun_color' __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.sky_color.set(*vec3_scale(_resolve_color(_sky_color, c), light_power))
self.sun_color.set(*_resolve_color(_sun_color, c)) self.sun_color.set(*_resolve_color(_sun_color, c))
def resolve_inputs(self, shader): def set_inputs(self, shader):
return _Inputs(shader) set_input_vec3(shader.u_light_direction, self.light_direction)
set_input_vec3(shader.u_light_color, self.light_color)
def update_inputs(self, inputs): set_input_vec3(shader.u_horizon_color, self.horizon_color)
set_input_vec3(inputs.light_direction, self.light_direction) set_input_vec3(shader.u_sky_color, self.sky_color)
set_input_vec3(inputs.light_color, self.light_color) set_input_vec3(shader.u_sun_color, self.sun_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)

View File

@ -20,9 +20,9 @@ from ctypes import Structure
from engine import * from engine import *
from game import math from game import math
from game import shader
from game import triangles from game import triangles
from game import sea from game import sea
from game.shader import Shader
from game.generator import Generator from game.generator import Generator
from game.resources import RuntimeArchive from game.resources import RuntimeArchive
from game.batch import Batch from game.batch import Batch
@ -53,9 +53,9 @@ def main():
mouse = Mouse(events, wheel = 60, wheel_min = 20) mouse = Mouse(events, wheel = 60, wheel_min = 20)
keyboard = Keyboard(events) keyboard = Keyboard(events)
render_initialize(__debug__) render_initialize(__debug__)
terrain_shader = shader.load('terrain', 'common') terrain_shader = Shader('terrain', 'common')
tests_shader = shader.load('tests', 'common') tests_shader = Shader('tests', 'common')
sky_shader = shader.load('sky') sky_shader = Shader('sky')
heightmap = create_texture( heightmap = create_texture(
TEXTURE_FORMAT_FLOAT_32, 256, 256, 0, TEXTURE_FLAG_MIN_LINEAR | TEXTURE_FLAG_MAG_LINEAR, 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) 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) 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_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') 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)) sky_triangles = create_triangles(triangles.sky_triangles(64, proj_far_z - 0.1, proj_ratio))
camera = Camera() camera = Camera()
camera.set_projection(proj_hfov, proj_ratio, proj_near_z, proj_far_z) 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() 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] blob_translation = tests_batch.translation[blob_id]
cube_translation = tests_batch.translation[cube_id] cube_translation = tests_batch.translation[cube_id]
@ -176,9 +168,9 @@ def main():
camera.set_view(camera_yaw, camera_pitch, camera_distance) camera.set_view(camera_yaw, camera_pitch, camera_distance)
environment.from_sun(camera.view, sun_direction, sun_power) environment.from_sun(camera.view, sun_direction, sun_power)
select_shader(terrain_shader) terrain_shader.select()
camera.update_inputs(terrain_camera_inputs) camera.set_inputs(terrain_shader)
environment.update_inputs(terrain_environment_inputs) environment.set_inputs(terrain_shader)
select_texture(0, tiles_texture) select_texture(0, tiles_texture)
select_texture(1, heightmap) select_texture(1, heightmap)
select_texture(2, normalmap) select_texture(2, normalmap)
@ -188,7 +180,7 @@ def main():
unselect_texture(0, tiles_texture) unselect_texture(0, tiles_texture)
unselect_texture(1, heightmap) unselect_texture(1, heightmap)
unselect_texture(2, normalmap) unselect_texture(2, normalmap)
unselect_shader(terrain_shader) terrain_shader.unselect()
rotation = mat3() rotation = mat3()
mat3_rotation(rotation, vec3_up, (current_time * 0.21) % tau) 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(cube_orientation, vec3_up, (current_time * 0.43) % tau)
mat3_rotation(clouds_orientation, vec3_up, (current_time * -0.037) % tau) mat3_rotation(clouds_orientation, vec3_up, (current_time * -0.037) % tau)
select_shader(tests_shader) tests_shader.select()
camera.update_inputs(tests_camera_inputs) camera.set_inputs(tests_shader)
environment.update_inputs(tests_environment_inputs) environment.set_inputs(tests_shader)
select_texture(0, tests_texture) select_texture(0, tests_texture)
select_texture(1, heightmap) select_texture(1, heightmap)
select_texture(2, normalmap) select_texture(2, normalmap)
@ -207,20 +199,20 @@ def main():
unselect_texture(0, tests_texture) unselect_texture(0, tests_texture)
unselect_texture(1, heightmap) unselect_texture(1, heightmap)
unselect_texture(2, normalmap) unselect_texture(2, normalmap)
unselect_shader(tests_shader) tests_shader.unselect()
camera.to_km() camera.to_km()
select_shader(sky_shader) sky_shader.select()
camera.update_inputs(sky_camera_inputs) camera.set_inputs(sky_shader)
environment.update_inputs(sky_environment_inputs) environment.set_inputs(sky_shader)
set_input_float(sea_phase, (current_time * 0.023) % 1.0) set_input_float(sky_shader.u_sea_phase, (current_time * 0.023) % 1.0)
select_texture(0, sea_polar_textures) select_texture(0, sea_polar_textures)
select_texture(1, sea_detail_texture) select_texture(1, sea_detail_texture)
draw_triangles(sky_triangles) draw_triangles(sky_triangles)
unselect_texture(0, sea_polar_textures) unselect_texture(0, sea_polar_textures)
unselect_texture(1, sea_detail_texture) unselect_texture(1, sea_detail_texture)
unselect_shader(sky_shader) sky_shader.unselect()
frame_end = time.thread_time() frame_end = time.thread_time()
end_frame() end_frame()
@ -272,9 +264,9 @@ def main():
destroy_texture(normalmap) destroy_texture(normalmap)
destroy_triangles(sky_triangles) destroy_triangles(sky_triangles)
archive.destroy() archive.destroy()
destroy_shader(terrain_shader) del terrain_shader
destroy_shader(tests_shader) del tests_shader
destroy_shader(sky_shader) del sky_shader
render_terminate() render_terminate()
del events del events
destroy_display(display) destroy_display(display)

View File

@ -15,9 +15,7 @@
from pathlib import Path from pathlib import Path
from engine import load_shader from engine import load_shader, resolve_input, select_shader, unselect_shader, destroy_shader
#TODO: preprocessing (at least includes)
def _cleanup(line): def _cleanup(line):
return line.partition('//')[0].strip() return line.partition('//')[0].strip()
@ -34,9 +32,9 @@ def _subst(line):
return lines return lines
return [line] return [line]
def _load_source(_path): def _load_source(path):
assert _path.exists() assert path.exists()
lines = filter(_filter, map(_cleanup, open(_path, 'rt'))) lines = filter(_filter, map(_cleanup, open(path, 'rt')))
source = [] source = []
for line in lines: for line in lines:
source.extend(_subst(line)) source.extend(_subst(line))
@ -45,14 +43,41 @@ def _load_source(_path):
def _convert(line): def _convert(line):
return bytes(line, 'utf-8') + b'\n' return bytes(line, 'utf-8') + b'\n'
def load(vert_name, frag_name = ''): def _parse(shader, vert_lines, frag_lines):
path = Path('.') / 'game' / 'shaders' uniforms = []
if not frag_name: for line in vert_lines:
frag_name = vert_name if line.startswith('uniform '):
print("Loading vertex shader", vert_name) uniforms.append(line.split()[-1].strip(';'))
vert_lines = list(map(_convert, _load_source(path / (vert_name + '_opengles.vert')))) for line in frag_lines:
assert vert_lines if line.startswith('uniform '):
print("Loading fragment shader", frag_name) name = line.split()[-1].strip(';')
frag_lines = list(map(_convert, _load_source(path / (frag_name + '_opengles.frag')))) if name not in uniforms:
assert frag_lines uniforms.append(name)
return load_shader(vert_lines, frag_lines) 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)