Compare commits

...

4 Commits

Author SHA1 Message Date
Roz K a96ea8bb6b
Bump engine submodule and add static batch. 2023-01-02 17:20:34 +01:00
Roz K 7219039980
Split scene groups and nodes. 2023-01-02 12:43:25 +01:00
Roz K 3344ac7d6c
Rename samplers in sea shader. 2023-01-02 12:39:23 +01:00
Roz K 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 engine import (vec3, mat3, buffer,
INSTANCE_FLAG_SPAWNED, BATCH_MAX_SIZE, param_type, params_format, create_batch, draw_batch, destroy_batch)
from engine import (
buffer, INSTANCE_FLAG_SPAWNED, BATCH_MAX_SIZE, param_type, params_format,
create_batch, fill_batch, draw_batch, destroy_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):
assert max_size <= BATCH_MAX_SIZE
@ -27,13 +28,17 @@ class Batch:
names = params_decls.keys()
formats = params_decls.values()
self._batch = create_batch(vertices._vertices, max_size, max_meshes, params_format(*formats))
self._static = False
self.vertices = vertices
self.size = 0
self.max_size = max_size
self.flags = buffer(c_ubyte, max_size)
self.meshes = buffer(c_uint, max_size)
self.params = tuple(map(lambda f: buffer(param_type(f), max_size), formats))
self._params = (c_void_p * nparams)(*map(addressof, self.params))
if nparams:
self._params = (c_void_p * nparams)(*map(addressof, self.params))
else:
self._params = None
for name, value in zip(names, self.params):
setattr(self, name, value)
@ -51,5 +56,11 @@ class Batch:
self.size += 1
return index
def make_static(self):
fill_batch(self._batch, self.size, self.flags, self.meshes, self._params)
self._static = True
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.batch import Batch
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
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_power = 1.0
class PerfNode(Node):
class PerfGroup(Group):
__slots__ = '_count', '_min', '_max', '_total', '_name'
def __init__(self, name, *subnodes):
Node.__init__(self, *subnodes)
Group.__init__(self, *subnodes)
self._count = None
self._min = 10000.0
self._max = 0.0
@ -62,7 +62,7 @@ class PerfNode(Node):
def draw(self, time):
begin = thread_time()
Node.draw(self, time)
Group.draw(self, time)
elapsed = (thread_time() - begin) * 1000.0
if self._count is None:
self._count = 0
@ -166,6 +166,7 @@ def create_scene(keyboard, mouse):
else:
model = rock_model
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_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_triangles = sea.sea_triangles(64, proj_far_z - 0.1, proj_ratio)
return SceneNode(
PerfNode('frame',
TextureNode({1: heightmap, 2: normalmap},
assert tiles_shader.u_height_sampler == tests_shader.u_height_sampler
assert tiles_shader.u_normal_sampler == tests_shader.u_normal_sampler
return SceneGroup(
PerfGroup('frame',
TextureGroup({tiles_shader.u_height_sampler: heightmap, tiles_shader.u_normal_sampler: normalmap},
FuncNode(update_camera, (mouse, camera, environment)),
TextureNode({0: tiles_texture},
ShaderNode(tiles_shader,
TextureGroup({tiles_shader.u_texture_sampler: tiles_texture},
ShaderGroup(tiles_shader,
InputNode(tiles_shader, camera, environment),
PerfNode('tiles_batch',
PerfGroup('tiles_batch',
DrawNode(tiles_batch)))),
FuncNode(update_tests, (blob, cube, clouds)),
TextureNode({0: tests_texture},
ShaderNode(tests_shader,
TextureGroup({tests_shader.u_texture_sampler: tests_texture},
ShaderGroup(tests_shader,
InputNode(tests_shader, camera, environment),
PerfNode('tests_batch',
PerfGroup('tests_batch',
DrawNode(tests_batch))))),
FuncNode(update_sea, (camera, sea_phase)),
TextureNode({0: sea_polar_textures, 1: sea_detail_texture},
ShaderNode(sea_shader,
TextureGroup(
{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),
PerfNode('sea_triangles',
PerfGroup('sea_triangles',
DrawNode(sea_triangles))))))
def loop(display):

View File

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

View File

@ -24,7 +24,7 @@ def _filter(line):
return line
def _subst(line):
if line.startswith('#include '):
if line.startswith('#include'):
path = Path('.') / 'game' / 'shaders'
lines = []
for name in line.split()[1:]:
@ -45,16 +45,27 @@ def _convert(line):
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 '):
bindings = {}
def collect(line):
if line.startswith('uniform'):
name = line.split()[-1].strip(';')
if name not in uniforms:
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:
setattr(shader, name, resolve_input(shader._shader, bytes(name, 'utf-8')))
for name, value in bindings.items():
setattr(shader, name, value)
class Shader:
__slots__ = '_shader', '__dict__'

View File

@ -28,8 +28,8 @@ uniform float u_sea_phase;
#define u_up u_view[2].xyz
#define u_origin u_view[3].xyz
layout(binding=0) uniform highp sampler2DArray u_sea_polar_sampler;
layout(binding=1) uniform highp sampler2D u_sea_detail_sampler;
layout(binding=0) uniform highp sampler2DArray u_polar_sampler;
layout(binding=1) uniform highp sampler2D u_detail_sampler;
const float c_sea_radius = 637.1;
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
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(
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
s = (u_sea_phase + dot(sea_position, u_right)) * 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
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));