Compare commits

..

4 Commits

Author SHA1 Message Date
a96ea8bb6b
Bump engine submodule and add static batch. 2023-01-02 17:20:34 +01:00
7219039980
Split scene groups and nodes. 2023-01-02 12:43:25 +01:00
3344ac7d6c
Rename samplers in sea shader. 2023-01-02 12:39:23 +01:00
89e0b99d3a
Automatic uniforms with binding. 2023-01-02 12:33:34 +01:00
6 changed files with 68 additions and 41 deletions

2
engine

@ -1 +1 @@
Subproject commit beca8798bf91edba954d31eb4e1a619ec2140c83 Subproject commit ed87f292ffa3cf89903cddcdec396115893f7d66

View File

@ -15,11 +15,12 @@
from ctypes import c_ubyte, c_uint, c_void_p, addressof from ctypes import c_ubyte, c_uint, c_void_p, addressof
from engine import (vec3, mat3, buffer, from engine import (
INSTANCE_FLAG_SPAWNED, BATCH_MAX_SIZE, param_type, params_format, create_batch, draw_batch, destroy_batch) buffer, INSTANCE_FLAG_SPAWNED, BATCH_MAX_SIZE, param_type, params_format,
create_batch, fill_batch, draw_batch, destroy_batch)
class Batch: class Batch:
__slots__ = '_batch', 'vertices', 'size', 'max_size', 'flags', 'meshes', 'params', '_params', '__dict__' __slots__ = '_batch', '_static', 'vertices', 'size', 'max_size', 'flags', 'meshes', 'params', '_params', '__dict__'
def __init__(self, vertices, max_size, max_meshes, **params_decls): def __init__(self, vertices, max_size, max_meshes, **params_decls):
assert max_size <= BATCH_MAX_SIZE assert max_size <= BATCH_MAX_SIZE
@ -27,13 +28,17 @@ class Batch:
names = params_decls.keys() names = params_decls.keys()
formats = params_decls.values() formats = params_decls.values()
self._batch = create_batch(vertices._vertices, max_size, max_meshes, params_format(*formats)) self._batch = create_batch(vertices._vertices, max_size, max_meshes, params_format(*formats))
self._static = False
self.vertices = vertices self.vertices = vertices
self.size = 0 self.size = 0
self.max_size = max_size self.max_size = max_size
self.flags = buffer(c_ubyte, max_size) self.flags = buffer(c_ubyte, max_size)
self.meshes = buffer(c_uint, max_size) self.meshes = buffer(c_uint, max_size)
self.params = tuple(map(lambda f: buffer(param_type(f), max_size), formats)) self.params = tuple(map(lambda f: buffer(param_type(f), max_size), formats))
if nparams:
self._params = (c_void_p * nparams)(*map(addressof, self.params)) self._params = (c_void_p * nparams)(*map(addressof, self.params))
else:
self._params = None
for name, value in zip(names, self.params): for name, value in zip(names, self.params):
setattr(self, name, value) setattr(self, name, value)
@ -51,5 +56,11 @@ class Batch:
self.size += 1 self.size += 1
return index return index
def make_static(self):
fill_batch(self._batch, self.size, self.flags, self.meshes, self._params)
self._static = True
def draw(self): def draw(self):
draw_batch(self._batch, self.size, self.flags, self.meshes, self._params) if not self._static:
fill_batch(self._batch, self.size, self.flags, self.meshes, self._params)
draw_batch(self._batch)

View File

@ -33,7 +33,7 @@ from game.environment import Environment
from game.inputs import InputFloat from game.inputs import InputFloat
from game.batch import Batch from game.batch import Batch
from game.entity import Entity from game.entity import Entity
from game.scene import Node, SceneNode, TextureNode, ShaderNode, InputNode, DrawNode, FuncNode from game.scene import Group, SceneGroup, TextureGroup, ShaderGroup, InputNode, DrawNode, FuncNode
from game import sea from game import sea
proj_hfov = pi * 0.25 proj_hfov = pi * 0.25
@ -44,11 +44,11 @@ proj_far_z = 3000.0
sun_direction = math.vec3_normalize((1.0, 0.0, 0.5)) sun_direction = math.vec3_normalize((1.0, 0.0, 0.5))
sun_power = 1.0 sun_power = 1.0
class PerfNode(Node): class PerfGroup(Group):
__slots__ = '_count', '_min', '_max', '_total', '_name' __slots__ = '_count', '_min', '_max', '_total', '_name'
def __init__(self, name, *subnodes): def __init__(self, name, *subnodes):
Node.__init__(self, *subnodes) Group.__init__(self, *subnodes)
self._count = None self._count = None
self._min = 10000.0 self._min = 10000.0
self._max = 0.0 self._max = 0.0
@ -62,7 +62,7 @@ class PerfNode(Node):
def draw(self, time): def draw(self, time):
begin = thread_time() begin = thread_time()
Node.draw(self, time) Group.draw(self, time)
elapsed = (thread_time() - begin) * 1000.0 elapsed = (thread_time() - begin) * 1000.0
if self._count is None: if self._count is None:
self._count = 0 self._count = 0
@ -166,6 +166,7 @@ def create_scene(keyboard, mouse):
else: else:
model = rock_model model = rock_model
tiles_batch.spawn(model, vec3(float(((mx - 128) * 8) + 4), float(((127 - my) * 8) + 4), 0.0)) tiles_batch.spawn(model, vec3(float(((mx - 128) * 8) + 4), float(((127 - my) * 8) + 4), 0.0))
tiles_batch.make_static()
tests_texture = Texture(*archive.get_texture('tests')) tests_texture = Texture(*archive.get_texture('tests'))
tests_vertices = Vertices(*archive.get_vertices('tests')) tests_vertices = Vertices(*archive.get_vertices('tests'))
@ -186,26 +187,30 @@ def create_scene(keyboard, mouse):
sea_detail_texture = sea.load_detail_texture('data/sea_bump.png') sea_detail_texture = sea.load_detail_texture('data/sea_bump.png')
sea_triangles = sea.sea_triangles(64, proj_far_z - 0.1, proj_ratio) sea_triangles = sea.sea_triangles(64, proj_far_z - 0.1, proj_ratio)
return SceneNode( assert tiles_shader.u_height_sampler == tests_shader.u_height_sampler
PerfNode('frame', assert tiles_shader.u_normal_sampler == tests_shader.u_normal_sampler
TextureNode({1: heightmap, 2: normalmap},
return SceneGroup(
PerfGroup('frame',
TextureGroup({tiles_shader.u_height_sampler: heightmap, tiles_shader.u_normal_sampler: normalmap},
FuncNode(update_camera, (mouse, camera, environment)), FuncNode(update_camera, (mouse, camera, environment)),
TextureNode({0: tiles_texture}, TextureGroup({tiles_shader.u_texture_sampler: tiles_texture},
ShaderNode(tiles_shader, ShaderGroup(tiles_shader,
InputNode(tiles_shader, camera, environment), InputNode(tiles_shader, camera, environment),
PerfNode('tiles_batch', PerfGroup('tiles_batch',
DrawNode(tiles_batch)))), DrawNode(tiles_batch)))),
FuncNode(update_tests, (blob, cube, clouds)), FuncNode(update_tests, (blob, cube, clouds)),
TextureNode({0: tests_texture}, TextureGroup({tests_shader.u_texture_sampler: tests_texture},
ShaderNode(tests_shader, ShaderGroup(tests_shader,
InputNode(tests_shader, camera, environment), InputNode(tests_shader, camera, environment),
PerfNode('tests_batch', PerfGroup('tests_batch',
DrawNode(tests_batch))))), DrawNode(tests_batch))))),
FuncNode(update_sea, (camera, sea_phase)), FuncNode(update_sea, (camera, sea_phase)),
TextureNode({0: sea_polar_textures, 1: sea_detail_texture}, TextureGroup(
ShaderNode(sea_shader, {sea_shader.u_polar_sampler: sea_polar_textures, sea_shader.u_detail_sampler: sea_detail_texture},
ShaderGroup(sea_shader,
InputNode(sea_shader, camera, environment, sea_phase), InputNode(sea_shader, camera, environment, sea_phase),
PerfNode('sea_triangles', PerfGroup('sea_triangles',
DrawNode(sea_triangles)))))) DrawNode(sea_triangles))))))
def loop(display): def loop(display):

View File

@ -13,7 +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/>.
class Node: class Group:
__slots__ = '_subnodes' __slots__ = '_subnodes'
def __init__(self, *subnodes): def __init__(self, *subnodes):
@ -23,34 +23,34 @@ class Node:
for subnode in self._subnodes: for subnode in self._subnodes:
subnode.draw(time) subnode.draw(time)
class SceneNode(Node): class SceneGroup(Group):
pass pass
class TextureNode(Node): class TextureGroup(Group):
__slots__ = '_textures' __slots__ = '_textures'
def __init__(self, textures, *subnodes): def __init__(self, textures, *subnodes):
Node.__init__(self, *subnodes) Group.__init__(self, *subnodes)
self._textures = textures self._textures = textures
def draw(self, time): def draw(self, time):
items = self._textures.items() items = self._textures.items()
for slot, texture in items: for slot, texture in items:
texture.select(slot) texture.select(slot)
Node.draw(self, time) Group.draw(self, time)
for slot, texture in items: for slot, texture in items:
texture.unselect(slot) texture.unselect(slot)
class ShaderNode(Node): class ShaderGroup(Group):
__slots__ = '_shader' __slots__ = '_shader'
def __init__(self, shader, *subnodes): def __init__(self, shader, *subnodes):
Node.__init__(self, *subnodes) Group.__init__(self, *subnodes)
self._shader = shader self._shader = shader
def draw(self, time): def draw(self, time):
self._shader.select() self._shader.select()
Node.draw(self, time) Group.draw(self, time)
self._shader.unselect() self._shader.unselect()
class InputNode: class InputNode:

View File

@ -45,16 +45,27 @@ def _convert(line):
def _parse(shader, vert_lines, frag_lines): def _parse(shader, vert_lines, frag_lines):
uniforms = [] uniforms = []
for line in vert_lines: bindings = {}
if line.startswith('uniform '): def collect(line):
uniforms.append(line.split()[-1].strip(';'))
for line in frag_lines:
if line.startswith('uniform'): if line.startswith('uniform'):
name = line.split()[-1].strip(';') name = line.split()[-1].strip(';')
if name not in uniforms: if name not in uniforms:
uniforms.append(name) uniforms.append(name)
elif line.startswith('layout(binding='):
name = line.split()[-1].strip(';')
value = int(line[line.index('=') + 1 : line.index(')')].strip())
if name in bindings:
assert value == bindings[name]
else:
bindings[name] = value
for line in vert_lines:
collect(line)
for line in frag_lines:
collect(line)
for name in uniforms: for name in uniforms:
setattr(shader, name, resolve_input(shader._shader, bytes(name, 'utf-8'))) setattr(shader, name, resolve_input(shader._shader, bytes(name, 'utf-8')))
for name, value in bindings.items():
setattr(shader, name, value)
class Shader: class Shader:
__slots__ = '_shader', '__dict__' __slots__ = '_shader', '__dict__'

View File

@ -28,8 +28,8 @@ uniform float u_sea_phase;
#define u_up u_view[2].xyz #define u_up u_view[2].xyz
#define u_origin u_view[3].xyz #define u_origin u_view[3].xyz
layout(binding=0) uniform highp sampler2DArray u_sea_polar_sampler; layout(binding=0) uniform highp sampler2DArray u_polar_sampler;
layout(binding=1) uniform highp sampler2D u_sea_detail_sampler; layout(binding=1) uniform highp sampler2D u_detail_sampler;
const float c_sea_radius = 637.1; const float c_sea_radius = 637.1;
const float c_sea_radius_sq = c_sea_radius * c_sea_radius; const float c_sea_radius_sq = c_sea_radius * c_sea_radius;
@ -68,13 +68,13 @@ void main(void) {
} }
float t = sqrt(length(sea_position)); //TODO: more accurate float t = sqrt(length(sea_position)); //TODO: more accurate
vec3 sea_polar1 = normalize( vec3 sea_polar1 = normalize(
c_normal_shift + texture(u_sea_polar_sampler, vec3(s, t + u_sea_phase, 0.0)).xyz * c_normal_scale); c_normal_shift + texture(u_polar_sampler, vec3(s, t + u_sea_phase, 0.0)).xyz * c_normal_scale);
vec3 sea_polar2 = normalize( vec3 sea_polar2 = normalize(
c_normal_shift + texture(u_sea_polar_sampler, vec3(s, t - u_sea_phase, 1.0)).xyz * c_normal_scale); c_normal_shift + texture(u_polar_sampler, vec3(s, t - u_sea_phase, 1.0)).xyz * c_normal_scale);
//TODO: vec2 //TODO: vec2
s = (u_sea_phase + dot(sea_position, u_right)) * c_detail_scale; s = (u_sea_phase + dot(sea_position, u_right)) * c_detail_scale;
t = (u_sea_phase + dot(sea_position, u_forward)) * c_detail_scale; t = (u_sea_phase + dot(sea_position, u_forward)) * c_detail_scale;
vec3 sea_detail = normalize(c_normal_shift + texture(u_sea_detail_sampler, vec2(s, t)).xyz * c_normal_scale); vec3 sea_detail = normalize(c_normal_shift + texture(u_detail_sampler, vec2(s, t)).xyz * c_normal_scale);
//TODO: better blending, with earth normal //TODO: better blending, with earth normal
vec4 normal = u_view * vec4(normalize(sea_polar1 + sea_polar2 + sea_detail), 0.0); vec4 normal = u_view * vec4(normalize(sea_polar1 + sea_polar2 + sea_detail), 0.0);
float d = max(0.0, dot(normal.xyz, u_light_direction)); float d = max(0.0, dot(normal.xyz, u_light_direction));