Compare commits
14 Commits
9131523ae7
...
master
Author | SHA1 | Date | |
---|---|---|---|
830daec408
|
|||
4783f0ba2d
|
|||
ccc168c896
|
|||
cbae4ec0e7
|
|||
efdbbb5815
|
|||
f487a4fcb7
|
|||
4cb67c0d79
|
|||
78440cce16
|
|||
84fc41e014
|
|||
e90f198fa0
|
|||
a96ea8bb6b
|
|||
7219039980
|
|||
3344ac7d6c
|
|||
89e0b99d3a
|
1
.gitmodules
vendored
1
.gitmodules
vendored
@ -1,3 +1,4 @@
|
||||
[submodule "engine"]
|
||||
path = engine
|
||||
url = https://code.rozk.net/RK/rk_engine.git
|
||||
branch = master
|
||||
|
Binary file not shown.
Before Width: | Height: | Size: 800 KiB After Width: | Height: | Size: 1.3 MiB |
2
engine
2
engine
Submodule engine updated: beca8798bf...7384a014ff
@ -13,43 +13,72 @@
|
||||
# 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/>.
|
||||
|
||||
from ctypes import c_ubyte, c_uint, c_void_p, addressof
|
||||
from ctypes import c_ubyte, c_ushort, c_void_p, POINTER, 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, vertex_type, vertex_format,
|
||||
create_batch, fill_batch, draw_batch, destroy_batch)
|
||||
|
||||
_flags_t = c_ubyte
|
||||
_flags_p = POINTER(_flags_t)
|
||||
_mesh_t = c_ushort
|
||||
_mesh_p = POINTER(_mesh_t)
|
||||
|
||||
class Batch:
|
||||
__slots__ = '_batch', 'vertices', 'size', 'max_size', 'flags', 'meshes', 'params', '_params', '__dict__'
|
||||
__slots__ = ('_batch', 'vertices', 'max_size', 'size',
|
||||
'flags', '_flags', 'mesh', '_meshes', 'param', '_params', '_name', '__dict__')
|
||||
|
||||
def __init__(self, vertices, max_size, max_meshes, **params_decls):
|
||||
def __init__(self, vertices, max_size, **params_decls):
|
||||
assert max_size <= BATCH_MAX_SIZE
|
||||
nparams = len(params_decls)
|
||||
names = params_decls.keys()
|
||||
formats = params_decls.values()
|
||||
self._batch = create_batch(vertices._vertices, max_size, max_meshes, params_format(*formats))
|
||||
if nparams:
|
||||
self._batch = create_batch(vertices._vertices, max_size, vertex_format(*params_decls.values()))
|
||||
else:
|
||||
self._batch = create_batch(vertices._vertices, max_size, None)
|
||||
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))
|
||||
for name, value in zip(names, self.params):
|
||||
setattr(self, name, value)
|
||||
self.size = 0
|
||||
self.flags = buffer(_flags_t, max_size)
|
||||
self._flags = _flags_p(self.flags)
|
||||
self.mesh = buffer(_mesh_t, max_size)
|
||||
self._meshes = _mesh_p(self.mesh)
|
||||
if nparams:
|
||||
self.param = tuple(map(lambda f: buffer(vertex_type(f), max_size), params_decls.values()))
|
||||
self._params = (c_void_p * nparams)(*map(addressof, self.param))
|
||||
self._name = dict(zip(params_decls.keys(), range(nparams)))
|
||||
for name, value in zip(params_decls.keys(), self.param):
|
||||
setattr(self, name, value)
|
||||
else:
|
||||
self.param = None
|
||||
self._params = None
|
||||
self._name = None
|
||||
|
||||
def __del__(self):
|
||||
destroy_batch(self._batch)
|
||||
|
||||
def spawn(self, model, *params):
|
||||
assert len(params) == len(self.params)
|
||||
def spawn(self, flags, mesh, **params):
|
||||
index = self.size
|
||||
assert index < self.max_size
|
||||
self.flags[index] = model.flags | INSTANCE_FLAG_SPAWNED
|
||||
self.meshes[index] = model.mesh
|
||||
for self_params, param in zip(self.params, params):
|
||||
self_params[index] = param
|
||||
self.flags[index] = flags | INSTANCE_FLAG_SPAWNED
|
||||
self.mesh[index] = mesh
|
||||
for name, value in params.items():
|
||||
getattr(self, name)[index] = value
|
||||
self.size += 1
|
||||
return index
|
||||
|
||||
def freeze(self, flags = None, mesh = None, **params):
|
||||
if flags is not None:
|
||||
self._flags = None if flags else _flags_p(self.flags)
|
||||
if mesh is not None:
|
||||
self._meshes = None if mesh else _mesh_p(self.mesh)
|
||||
for name, value in params.items():
|
||||
if value is not None:
|
||||
index = self._name[name]
|
||||
self._params[index] = None if value else addressof(self.param[index])
|
||||
|
||||
def fill(self):
|
||||
fill_batch(self._batch, self.size, self._flags, self._meshes, self._params)
|
||||
|
||||
def draw(self):
|
||||
draw_batch(self._batch, self.size, self.flags, self.meshes, self._params)
|
||||
fill_batch(self._batch, self.size, self._flags, self._meshes, self._params)
|
||||
draw_batch(self._batch)
|
||||
|
@ -16,5 +16,5 @@
|
||||
class Entity:
|
||||
__slots__ = 'index'
|
||||
|
||||
def __init__(self, batch, model, *params):
|
||||
self.index = batch.spawn(model, *params)
|
||||
def __init__(self, batch, flags, mesh, **params):
|
||||
self.index = batch.spawn(flags, mesh, **params)
|
||||
|
119
game/game.py
119
game/game.py
@ -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
|
||||
@ -56,13 +56,14 @@ class PerfNode(Node):
|
||||
self._name = name
|
||||
|
||||
def __del__(self):
|
||||
avg = round(self._total / self._count, 2)
|
||||
print(self._name, "*", self._count,
|
||||
": min =", round(self._min, 2), ", max =", round(self._max, 2), ", avg =", avg, "(ms)")
|
||||
if self._count:
|
||||
avg = round(self._total / self._count, 2)
|
||||
print(self._name, "*", self._count,
|
||||
": min =", round(self._min, 2), ", max =", round(self._max, 2), ", avg =", avg, "(ms)")
|
||||
|
||||
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
|
||||
@ -75,8 +76,10 @@ class PerfNode(Node):
|
||||
class TestEntity(Entity):
|
||||
__slots__ = 'translation', 'orientation', 'spawn_translation', 'spawn_orientation'
|
||||
|
||||
def __init__(self, batch, model, translation, orientation):
|
||||
Entity.__init__(self, batch, model, translation, orientation)
|
||||
def __init__(self, batch, mesh, translation, orientation):
|
||||
Entity.__init__(self, batch, INSTANCE_FLAG_VISIBLE, mesh,
|
||||
translation = translation,
|
||||
orientation = orientation)
|
||||
self.translation = batch.translation[self.index]
|
||||
self.orientation = batch.orientation[self.index]
|
||||
self.spawn_translation = translation
|
||||
@ -126,15 +129,15 @@ def create_scene(keyboard, mouse):
|
||||
|
||||
print("Building tiles...")
|
||||
tiles_texture = Texture(*archive.get_texture('tiles'))
|
||||
tiles_vertices = Vertices(*archive.get_vertices('tiles'))
|
||||
water_model = archive.get_model('water')
|
||||
sand_model = archive.get_model('sand')
|
||||
grass_model = archive.get_model('grass')
|
||||
forest_model = archive.get_model('forest')
|
||||
rock_model = archive.get_model('rock')
|
||||
mud_model = archive.get_model('mud')
|
||||
lava_model = archive.get_model('lava')
|
||||
tiles_batch = Batch(tiles_vertices, generated.size ** 2, 8, translation = PARAM_FORMAT_VEC3_SHORT)
|
||||
tiles_vertices = archive.get_vertices('tiles')
|
||||
water_mesh = tiles_vertices.get_mesh('water')
|
||||
sand_mesh = tiles_vertices.get_mesh('sand')
|
||||
grass_mesh = tiles_vertices.get_mesh('grass')
|
||||
forest_mesh = tiles_vertices.get_mesh('forest')
|
||||
rock_mesh = tiles_vertices.get_mesh('rock')
|
||||
mud_mesh = tiles_vertices.get_mesh('mud')
|
||||
lava_mesh = tiles_vertices.get_mesh('lava')
|
||||
tiles_batch = Batch(Vertices(*tiles_vertices), generated.size ** 2, translation = VERTEX_FORMAT_VEC3_SHORT)
|
||||
|
||||
#TODO: generator & for real
|
||||
vc = generated.volcano_c
|
||||
@ -146,66 +149,74 @@ def create_scene(keyboard, mouse):
|
||||
if h == 0.0:
|
||||
continue
|
||||
if r > 0.0:
|
||||
model = water_model
|
||||
mesh = water_mesh
|
||||
elif h < 2.0:
|
||||
model = sand_model
|
||||
mesh = sand_mesh
|
||||
elif h < 180:
|
||||
if nz > 0.9:
|
||||
if ny < -0.01 and nz > 0.93:
|
||||
model = forest_model
|
||||
mesh = forest_mesh
|
||||
else:
|
||||
model = grass_model
|
||||
mesh = grass_mesh
|
||||
else:
|
||||
model = rock_model
|
||||
mesh = rock_mesh
|
||||
elif vd < vr - 3.0 and nz > 0.999:
|
||||
model = lava_model
|
||||
mesh = lava_mesh
|
||||
elif vd < vr + 2.0:
|
||||
model = mud_model
|
||||
mesh = mud_mesh
|
||||
elif vd < vr + 6.0 and nz < 0.67:
|
||||
model = mud_model
|
||||
mesh = mud_mesh
|
||||
else:
|
||||
model = rock_model
|
||||
tiles_batch.spawn(model, vec3(float(((mx - 128) * 8) + 4), float(((127 - my) * 8) + 4), 0.0))
|
||||
mesh = rock_mesh
|
||||
tiles_batch.spawn(INSTANCE_FLAG_VISIBLE, mesh,
|
||||
translation = vec3(float(((mx - 128) * 8) + 4), float(((127 - my) * 8) + 4), 0.0))
|
||||
tiles_batch.fill()
|
||||
tiles_batch.freeze(flags = True, mesh = True, translation = True)
|
||||
|
||||
tests_texture = Texture(*archive.get_texture('tests'))
|
||||
tests_vertices = Vertices(*archive.get_vertices('tests'))
|
||||
blob_model = archive.get_model('blob')
|
||||
cube_model = archive.get_model('cube')
|
||||
clouds_model = archive.get_model('clouds')
|
||||
tests_batch = Batch(tests_vertices, 3, 3,
|
||||
translation = PARAM_FORMAT_VEC3_FLOAT,
|
||||
orientation = PARAM_FORMAT_MAT3_INT10 | PARAM_FORMAT_NORMALIZE)
|
||||
tests_vertices = archive.get_vertices('tests')
|
||||
blob_mesh = tests_vertices.get_mesh('blob')
|
||||
cube_mesh = tests_vertices.get_mesh('cube')
|
||||
clouds_mesh = tests_vertices.get_mesh('clouds')
|
||||
tests_batch = Batch(Vertices(*tests_vertices), 3,
|
||||
translation = VERTEX_FORMAT_VEC3_FLOAT,
|
||||
orientation = VERTEX_FORMAT_MAT3_INT10 | VERTEX_FORMAT_NORMALIZE)
|
||||
blob_forward = math.vec3_normalize((sun_direction[0], sun_direction[1], 0.0))
|
||||
blob_right = math.vec3_cross(blob_forward, math.vec3_up)
|
||||
blob_orientation = mat3(vec3(*blob_right), vec3(*blob_forward), vec3_up)
|
||||
blob = TestEntity(tests_batch, blob_model, vec3(-100.0, -500.0, 0.0), blob_orientation)
|
||||
cube = TestEntity(tests_batch, cube_model, vec3(100.0, -500.0, 0.0), mat3_identity)
|
||||
clouds = TestEntity(tests_batch, clouds_model, vec3(0.0, 0.0, 32.0), mat3_identity)
|
||||
blob = TestEntity(tests_batch, blob_mesh, translation = vec3(-100.0, -500.0, 0.0), orientation = blob_orientation)
|
||||
cube = TestEntity(tests_batch, cube_mesh, translation = vec3(100.0, -500.0, 0.0), orientation = mat3_identity)
|
||||
clouds = TestEntity(tests_batch, clouds_mesh, translation = vec3(0.0, 0.0, 32.0), orientation = mat3_identity)
|
||||
tests_batch.fill()
|
||||
tests_batch.freeze(flags = True, mesh = True)
|
||||
|
||||
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_polar = sea.load_polar_textures(('data/sea_bump1.png', 'data/sea_bump2.png'))
|
||||
sea_detail = 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, sea_shader.u_detail_sampler: sea_detail},
|
||||
ShaderGroup(sea_shader,
|
||||
InputNode(sea_shader, camera, environment, sea_phase),
|
||||
PerfNode('sea_triangles',
|
||||
PerfGroup('sea_triangles',
|
||||
DrawNode(sea_triangles))))))
|
||||
|
||||
def loop(display):
|
||||
@ -214,14 +225,14 @@ def loop(display):
|
||||
mouse = Mouse(events, wheel = 60, wheel_min = 20)
|
||||
scene = create_scene(keyboard, mouse)
|
||||
print("Running... Ctrl+c to quit")
|
||||
time = Time()
|
||||
try:
|
||||
time = Time()
|
||||
while not keyboard.quit:
|
||||
events.update()
|
||||
time.update()
|
||||
clear_buffer(True, True, True)
|
||||
scene.draw(time)
|
||||
swap_buffers(display)
|
||||
time.update()
|
||||
except KeyboardInterrupt:
|
||||
pass
|
||||
|
||||
|
@ -22,7 +22,7 @@ from itertools import starmap, chain, repeat
|
||||
from engine import *
|
||||
|
||||
from game.math import float_s8, vec3_srgb8a8
|
||||
from game.resources import load_png, TextureData, VerticesData, ModelData, Archive
|
||||
from game.resources import load_png, TextureData, VerticesData, Archive
|
||||
|
||||
_texture_flags = TEXTURE_FLAG_MIPMAPS | TEXTURE_FLAG_MIN_LINEAR | TEXTURE_FLAG_MAG_NEAREST
|
||||
|
||||
@ -195,7 +195,7 @@ class ObjArchive(Archive):
|
||||
|
||||
vertices = []
|
||||
indices = []
|
||||
models = {}
|
||||
meshes = {}
|
||||
for name, mesh in sorted(objects.items()):
|
||||
offset = len(indices)
|
||||
assert offset < 65536
|
||||
@ -213,28 +213,26 @@ class ObjArchive(Archive):
|
||||
assert count < 65536
|
||||
vertices.extend(mesh_vertices)
|
||||
indices.extend(mesh_indices)
|
||||
models[name] = (offset, count)
|
||||
meshes[name] = (offset, count)
|
||||
|
||||
name = str(objpath.stem)
|
||||
assert name not in self.vertices_db.keys()
|
||||
#TODO: move to math
|
||||
def pack_10(_x):
|
||||
return round(_x * (512.0 if _x < 0.0 else 511.0)) & 1023
|
||||
assert _x >= -1.0 and _x <= 1.0
|
||||
return round(_x * 511.0) & 1023
|
||||
# return ((round(_x * 1023.0) - 1) // 2) & 1023
|
||||
def pack_u10(_x):
|
||||
return round(_x * 1023.0) & 1023
|
||||
assert _x >= 0.0 and _x <= 1.0
|
||||
return round(_x * 1023.0)
|
||||
def pack_vertex(_px, _py, _pz, _nx, _ny, _nz, _s, _t, _tl):
|
||||
n = (pack_10(_nz) << 20) | (pack_10(_ny) << 10) | pack_10(_nx)
|
||||
t = ((_tl & 1023) << 20) | (pack_u10(_t) << 10) | pack_u10(_s)
|
||||
return struct.pack('fffII', _px, _py, _pz, n, t)
|
||||
self.vertices_db[name] = VerticesData(name,
|
||||
array('B', b''.join(starmap(pack_vertex, vertices))),
|
||||
array('H', indices))
|
||||
for name, (offset, count) in sorted(models.items()):
|
||||
if name[0] != '_':
|
||||
print("Storing", name)
|
||||
assert name not in self.models_db.keys()
|
||||
self.models_db[name] = ModelData(
|
||||
name, INSTANCE_FLAG_VISIBLE, offset | count << 16)
|
||||
array('H', indices),
|
||||
meshes)
|
||||
|
||||
if __name__ == '__main__':
|
||||
if len(sys.argv) < 3:
|
||||
|
@ -13,6 +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/>.
|
||||
|
||||
from itertools import chain
|
||||
import struct
|
||||
from array import array
|
||||
from pathlib import Path
|
||||
@ -48,7 +49,7 @@ def _read_struct(file, format):
|
||||
_read_magic(file, b'RK')
|
||||
return struct.unpack(format, data)
|
||||
|
||||
def _write_struct(file, format, elems):
|
||||
def _write_struct(file, format, *elems):
|
||||
assert format
|
||||
data = struct.pack(format, *elems)
|
||||
size = file.write(data)
|
||||
@ -56,7 +57,7 @@ def _write_struct(file, format, elems):
|
||||
_write_magic(file, b'RK')
|
||||
|
||||
def _read_string(file):
|
||||
length, = _read_struct(file, 'B')
|
||||
length, = _read_struct(file, 'I')
|
||||
assert length
|
||||
data = file.read(length)
|
||||
assert len(data) == length
|
||||
@ -66,7 +67,7 @@ def _read_string(file):
|
||||
def _write_string(file, string):
|
||||
data = bytes(string, encoding='ascii')
|
||||
assert data and len(data) < 256
|
||||
_write_struct(file, 'B', (len(data),))
|
||||
_write_struct(file, 'I', len(data))
|
||||
size = file.write(data)
|
||||
assert size == len(data)
|
||||
_write_magic(file, b'RK')
|
||||
@ -96,7 +97,7 @@ def _read_blob(file):
|
||||
|
||||
def _write_blob(file, array):
|
||||
assert array
|
||||
_write_struct(file, 'II', (ord(array.typecode), len(array)))
|
||||
_write_struct(file, 'II', ord(array.typecode), len(array))
|
||||
array.tofile(file)
|
||||
_write_magic(file, b'RK')
|
||||
|
||||
@ -124,7 +125,7 @@ class TextureData:
|
||||
|
||||
@classmethod
|
||||
def from_archive(cls, file):
|
||||
_read_magic(file, b'TX')
|
||||
_read_magic(file, b'TXTR')
|
||||
name = _read_string(file)
|
||||
format, width, height, nlevels, flags = _read_struct(file, 'IIIII')
|
||||
pixels = _read_blob(file)
|
||||
@ -133,70 +134,61 @@ class TextureData:
|
||||
return cls(name, format, width, height, nlevels, flags, pixels)
|
||||
|
||||
def to_archive(self, file):
|
||||
_write_magic(file, b'TX')
|
||||
_write_magic(file, b'TXTR')
|
||||
_write_string(file, self.name)
|
||||
_write_struct(file, 'IIIII', (self.format, self.width, self.height, self.nlevels, self.flags))
|
||||
_write_struct(file, 'IIIII', self.format, self.width, self.height, self.nlevels, self.flags)
|
||||
_write_blob(file, self.pixels)
|
||||
|
||||
class VerticesData:
|
||||
__slots__ = 'name', 'vertices', 'indices'
|
||||
__slots__ = 'name', 'vertices', 'indices', 'meshes', 'meshes_db'
|
||||
|
||||
def __init__(self, name, vertices, indices):
|
||||
def __init__(self, name, vertices, indices, meshes):
|
||||
if len(vertices) % VERTEX_SIZE != 0:
|
||||
raise RuntimeError("Vertex format mismatch!")
|
||||
self.name = name
|
||||
self.vertices = vertices
|
||||
self.indices = indices
|
||||
self.meshes = meshes.values()
|
||||
self.meshes_db = dict(zip(meshes.keys(), range(len(meshes))))
|
||||
|
||||
def __iter__(self):
|
||||
yield VERTEX_FORMAT
|
||||
yield len(self.vertices) // VERTEX_SIZE
|
||||
yield self.vertices
|
||||
yield self.indices
|
||||
yield array('I', chain.from_iterable(self.meshes))
|
||||
|
||||
def get_mesh(self, name):
|
||||
return self.meshes_db[name]
|
||||
|
||||
@classmethod
|
||||
def from_archive(cls, file):
|
||||
_read_magic(file, b'VT')
|
||||
_read_magic(file, b'VERT')
|
||||
name = _read_string(file)
|
||||
nvertices, nindices = _read_struct(file, 'II')
|
||||
nvertices, nindices, nmeshes = _read_struct(file, 'III')
|
||||
vertices = _read_array(file, 'B', nvertices * VERTEX_SIZE)
|
||||
indices = _read_array(file, 'H', nindices)
|
||||
return cls(name, vertices, indices)
|
||||
names = [_read_string(file) for _ in range(nmeshes)]
|
||||
meshes = [_read_struct(file, 'II') for _ in range(nmeshes)]
|
||||
return cls(name, vertices, indices, dict(zip(names, meshes)))
|
||||
|
||||
def to_archive(self, file):
|
||||
_write_magic(file, b'VT')
|
||||
_write_magic(file, b'VERT')
|
||||
_write_string(file, self.name)
|
||||
_write_struct(file, 'II', (len(self.vertices) // VERTEX_SIZE, len(self.indices)))
|
||||
_write_struct(file, 'III', len(self.vertices) // VERTEX_SIZE, len(self.indices), len(self.meshes_db))
|
||||
_write_array(file, self.vertices)
|
||||
_write_array(file, self.indices)
|
||||
|
||||
class ModelData:
|
||||
__slots__ = 'name', 'flags', 'mesh'
|
||||
|
||||
def __init__(self, name, flags, mesh):
|
||||
self.name = name
|
||||
self.flags = flags
|
||||
self.mesh = mesh
|
||||
|
||||
@classmethod
|
||||
def from_archive(cls, file):
|
||||
_read_magic(file, b'MD')
|
||||
name = _read_string(file)
|
||||
flags, mesh = _read_struct(file, 'II')
|
||||
return ModelData(name, flags, mesh)
|
||||
|
||||
def to_archive(self, file):
|
||||
_write_magic(file, b'MD')
|
||||
_write_string(file, self.name)
|
||||
_write_struct(file, 'II', (self.flags, self.mesh))
|
||||
for name in self.meshes_db.keys():
|
||||
_write_string(file, name)
|
||||
for mesh in self.meshes:
|
||||
_write_struct(file, 'II', *mesh)
|
||||
|
||||
class Archive:
|
||||
__slots__ = 'textures_db', 'vertices_db', 'models_db'
|
||||
__slots__ = 'textures_db', 'vertices_db'
|
||||
|
||||
def __init__(self, textures_db = None, vertices_db = None, models_db = None):
|
||||
def __init__(self, textures_db = None, vertices_db = None):
|
||||
self.textures_db = textures_db or {}
|
||||
self.vertices_db = vertices_db or {}
|
||||
self.models_db = models_db or {}
|
||||
|
||||
def get_texture(self, name):
|
||||
return self.textures_db[name]
|
||||
@ -204,36 +196,27 @@ class Archive:
|
||||
def get_vertices(self, name):
|
||||
return self.vertices_db[name]
|
||||
|
||||
def get_model(self, name):
|
||||
return self.models_db[name]
|
||||
|
||||
@classmethod
|
||||
def from_archive(cls, file):
|
||||
textures_db = {}
|
||||
vertices_db = {}
|
||||
models_db = {}
|
||||
_read_magic(file, b'RKAR')
|
||||
ntextures, nvertices, nmodels = _read_struct(file, 'III')
|
||||
ntextures, nvertices = _read_struct(file, 'II')
|
||||
for _ in range(ntextures):
|
||||
data = TextureData.from_archive(file)
|
||||
textures_db[data.name] = data
|
||||
for _ in range(nvertices):
|
||||
data = VerticesData.from_archive(file)
|
||||
vertices_db[data.name] = data
|
||||
for _ in range(nmodels):
|
||||
data = ModelData.from_archive(file)
|
||||
models_db[data.name] = data
|
||||
return cls(textures_db, vertices_db, models_db)
|
||||
return cls(textures_db, vertices_db)
|
||||
|
||||
def to_archive(self, file):
|
||||
_write_magic(file, b'RKAR')
|
||||
_write_struct(file, 'III', (len(self.textures_db), len(self.vertices_db), len(self.models_db)))
|
||||
_write_struct(file, 'II', len(self.textures_db), len(self.vertices_db))
|
||||
for _, data in self.textures_db.items():
|
||||
data.to_archive(file)
|
||||
for _, data in self.vertices_db.items():
|
||||
data.to_archive(file)
|
||||
for _, data in self.models_db.items():
|
||||
data.to_archive(file)
|
||||
|
||||
@classmethod
|
||||
def load(cls, filename):
|
||||
|
@ -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:
|
||||
|
@ -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__'
|
||||
|
@ -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));
|
||||
|
@ -18,8 +18,8 @@ from engine import create_vertices, destroy_vertices
|
||||
class Vertices:
|
||||
__slots__ = '_vertices'
|
||||
|
||||
def __init__(self, format, nvertices, vertices, indices):
|
||||
self._vertices = create_vertices(format, nvertices, vertices, indices)
|
||||
def __init__(self, format, nvertices, vertices, indices, meshes):
|
||||
self._vertices = create_vertices(format, nvertices, vertices, indices, meshes)
|
||||
|
||||
def __del__(self):
|
||||
destroy_vertices(self._vertices)
|
||||
|
Reference in New Issue
Block a user