Compare commits
12 Commits
9fff666ebf
...
9131523ae7
Author | SHA1 | Date | |
---|---|---|---|
9131523ae7 | |||
3621a340f1 | |||
8e49c16929 | |||
7c2d3d81ae | |||
dd786ef2b4 | |||
acc2e4cd28 | |||
af9365c6e0 | |||
513d79a67c | |||
f66c6ea6e9 | |||
9e77e6680b | |||
f4bfb7fee0 | |||
6485da7d67 |
2
engine
2
engine
@ -1 +1 @@
|
|||||||
Subproject commit ae3333e22e36667a0c02817fafb16ab7b8d1341b
|
Subproject commit beca8798bf91edba954d31eb4e1a619ec2140c83
|
@ -19,14 +19,15 @@ from engine import (vec3, mat3, buffer,
|
|||||||
INSTANCE_FLAG_SPAWNED, BATCH_MAX_SIZE, param_type, params_format, create_batch, draw_batch, destroy_batch)
|
INSTANCE_FLAG_SPAWNED, BATCH_MAX_SIZE, param_type, params_format, create_batch, draw_batch, destroy_batch)
|
||||||
|
|
||||||
class Batch:
|
class Batch:
|
||||||
__slots__ = '_batch', 'size', 'max_size', 'flags', 'meshes', 'params', '_params', '__dict__'
|
__slots__ = '_batch', '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
|
||||||
nparams = len(params_decls)
|
nparams = len(params_decls)
|
||||||
names = params_decls.keys()
|
names = params_decls.keys()
|
||||||
formats = params_decls.values()
|
formats = params_decls.values()
|
||||||
self._batch = create_batch(vertices, max_size, max_meshes, params_format(*formats))
|
self._batch = create_batch(vertices._vertices, max_size, max_meshes, params_format(*formats))
|
||||||
|
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)
|
||||||
@ -39,12 +40,12 @@ class Batch:
|
|||||||
def __del__(self):
|
def __del__(self):
|
||||||
destroy_batch(self._batch)
|
destroy_batch(self._batch)
|
||||||
|
|
||||||
def append(self, flags, mesh, params):
|
def spawn(self, model, *params):
|
||||||
assert len(params) == len(self.params)
|
assert len(params) == len(self.params)
|
||||||
index = self.size
|
index = self.size
|
||||||
assert index < self.max_size
|
assert index < self.max_size
|
||||||
self.flags[index] = flags | INSTANCE_FLAG_SPAWNED
|
self.flags[index] = model.flags | INSTANCE_FLAG_SPAWNED
|
||||||
self.meshes[index] = mesh
|
self.meshes[index] = model.mesh
|
||||||
for self_params, param in zip(self.params, params):
|
for self_params, param in zip(self.params, params):
|
||||||
self_params[index] = param
|
self_params[index] = param
|
||||||
self.size += 1
|
self.size += 1
|
||||||
|
20
game/entity.py
Normal file
20
game/entity.py
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
# Copyright (C) 2022 RozK
|
||||||
|
#
|
||||||
|
# This program is free software: you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU Affero General Public License as published by
|
||||||
|
# the Free Software Foundation, either version 3 of the License, or
|
||||||
|
# (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU Affero General Public License for more details.
|
||||||
|
#
|
||||||
|
# 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 Entity:
|
||||||
|
__slots__ = 'index'
|
||||||
|
|
||||||
|
def __init__(self, batch, model, *params):
|
||||||
|
self.index = batch.spawn(model, *params)
|
299
game/game.py
299
game/game.py
@ -13,24 +13,28 @@
|
|||||||
# 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/>.
|
||||||
|
|
||||||
import time
|
from time import thread_time
|
||||||
from math import pi, tau, dist
|
from math import pi, tau, dist
|
||||||
from ctypes import Structure
|
|
||||||
|
|
||||||
from engine import *
|
from engine import *
|
||||||
|
|
||||||
from game import math
|
from game import math
|
||||||
from game import triangles
|
from game.time import Time
|
||||||
from game import sea
|
|
||||||
from game.shader import Shader
|
|
||||||
from game.generator import Generator
|
|
||||||
from game.resources import RuntimeArchive
|
|
||||||
from game.batch import Batch
|
|
||||||
from game.camera import Camera
|
|
||||||
from game.environment import Environment
|
|
||||||
from game.events import Events
|
from game.events import Events
|
||||||
from game.mouse import Mouse
|
from game.mouse import Mouse
|
||||||
from game.keyboard import Keyboard
|
from game.keyboard import Keyboard
|
||||||
|
from game.generator import Generator
|
||||||
|
from game.resources import Archive
|
||||||
|
from game.texture import Texture
|
||||||
|
from game.shader import Shader
|
||||||
|
from game.vertices import Vertices
|
||||||
|
from game.camera import Camera
|
||||||
|
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 import sea
|
||||||
|
|
||||||
proj_hfov = pi * 0.25
|
proj_hfov = pi * 0.25
|
||||||
proj_ratio = 16.0 / 9.0
|
proj_ratio = 16.0 / 9.0
|
||||||
@ -40,36 +44,89 @@ 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
|
||||||
|
|
||||||
def main():
|
class PerfNode(Node):
|
||||||
print("Generating terrain...")
|
__slots__ = '_count', '_min', '_max', '_total', '_name'
|
||||||
gen_begin = time.thread_time()
|
|
||||||
generated = Generator(256)
|
|
||||||
gen_end = time.thread_time()
|
|
||||||
print("Done: ", round(gen_end - gen_begin, 2), "seconds")
|
|
||||||
|
|
||||||
print("Initializing...")
|
def __init__(self, name, *subnodes):
|
||||||
display = create_display(b'RK Island - Drag to rotate, wheel to zoom, q to quit', 1600, 900)
|
Node.__init__(self, *subnodes)
|
||||||
events = Events(display)
|
self._count = None
|
||||||
mouse = Mouse(events, wheel = 60, wheel_min = 20)
|
self._min = 10000.0
|
||||||
keyboard = Keyboard(events)
|
self._max = 0.0
|
||||||
render_initialize(__debug__)
|
self._total = 0.0
|
||||||
terrain_shader = Shader('terrain', 'common')
|
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)")
|
||||||
|
|
||||||
|
def draw(self, time):
|
||||||
|
begin = thread_time()
|
||||||
|
Node.draw(self, time)
|
||||||
|
elapsed = (thread_time() - begin) * 1000.0
|
||||||
|
if self._count is None:
|
||||||
|
self._count = 0
|
||||||
|
else:
|
||||||
|
self._count += 1
|
||||||
|
self._min = min(self._min, elapsed)
|
||||||
|
self._max = max(self._max, elapsed)
|
||||||
|
self._total += elapsed
|
||||||
|
|
||||||
|
class TestEntity(Entity):
|
||||||
|
__slots__ = 'translation', 'orientation', 'spawn_translation', 'spawn_orientation'
|
||||||
|
|
||||||
|
def __init__(self, batch, model, translation, orientation):
|
||||||
|
Entity.__init__(self, batch, model, translation, orientation)
|
||||||
|
self.translation = batch.translation[self.index]
|
||||||
|
self.orientation = batch.orientation[self.index]
|
||||||
|
self.spawn_translation = translation
|
||||||
|
self.spawn_orientation = orientation
|
||||||
|
|
||||||
|
def update_camera(time, mouse, camera, environment):
|
||||||
|
camera_yaw = mouse.drag[0] * 0.001
|
||||||
|
camera_pitch = mouse.drag[1] * 0.001 + pi * 0.25
|
||||||
|
camera_distance = mouse.wheel * 20.0
|
||||||
|
camera.set_view(camera_yaw % tau, camera_pitch % tau, camera_distance)
|
||||||
|
environment.from_sun(camera.view, sun_direction, sun_power)
|
||||||
|
|
||||||
|
def update_tests(time, blob, cube, clouds):
|
||||||
|
rotation = mat3()
|
||||||
|
mat3_rotation(rotation, vec3_up, (time.current * 0.21) % tau)
|
||||||
|
mat3_mul_vec3(blob.translation, rotation, blob.spawn_translation)
|
||||||
|
mat3_mul_vec3(cube.translation, rotation, cube.spawn_translation)
|
||||||
|
mat3_rotation(cube.orientation, vec3_up, (time.current * 0.43) % tau)
|
||||||
|
mat3_rotation(clouds.orientation, vec3_up, (time.current * -0.037) % tau)
|
||||||
|
|
||||||
|
def update_sea(time, camera, sea_phase):
|
||||||
|
camera.to_km()
|
||||||
|
sea_phase.update((time.current * 0.023) % 1.0)
|
||||||
|
|
||||||
|
def create_scene(keyboard, mouse):
|
||||||
|
tiles_shader = Shader('tiles', 'common')
|
||||||
tests_shader = Shader('tests', 'common')
|
tests_shader = Shader('tests', 'common')
|
||||||
sky_shader = Shader('sky')
|
sea_shader = Shader('sea')
|
||||||
|
|
||||||
heightmap = create_texture(
|
camera = Camera()
|
||||||
|
camera.set_projection(proj_hfov, proj_ratio, proj_near_z, proj_far_z)
|
||||||
|
environment = Environment()
|
||||||
|
sea_phase = InputFloat(sea_shader.u_sea_phase, 0.0)
|
||||||
|
|
||||||
|
print("Generating terrain...")
|
||||||
|
generated = Generator(256)
|
||||||
|
|
||||||
|
heightmap = 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,
|
||||||
generated.packed_heights)
|
generated.packed_heights)
|
||||||
normalmap = create_texture(
|
normalmap = Texture(
|
||||||
TEXTURE_FORMAT_RGB10_A2, 256, 256, 0, TEXTURE_FLAG_MIN_LINEAR | TEXTURE_FLAG_MAG_LINEAR,
|
TEXTURE_FORMAT_RGB10_A2, 256, 256, 0, TEXTURE_FLAG_MIN_LINEAR | TEXTURE_FLAG_MAG_LINEAR,
|
||||||
generated.packed_normals)
|
generated.packed_normals)
|
||||||
|
|
||||||
print("Loading resources...")
|
print("Loading resources...")
|
||||||
archive = RuntimeArchive.load('data/rk_island.rkar')
|
archive = Archive.load('data/rk_island.rkar')
|
||||||
|
|
||||||
print("Building tiles...")
|
print("Building tiles...")
|
||||||
tiles_texture = archive.get_texture('tiles')
|
tiles_texture = Texture(*archive.get_texture('tiles'))
|
||||||
tiles_vertices = archive.get_vertices('tiles')
|
tiles_vertices = Vertices(*archive.get_vertices('tiles'))
|
||||||
water_model = archive.get_model('water')
|
water_model = archive.get_model('water')
|
||||||
sand_model = archive.get_model('sand')
|
sand_model = archive.get_model('sand')
|
||||||
grass_model = archive.get_model('grass')
|
grass_model = archive.get_model('grass')
|
||||||
@ -77,7 +134,7 @@ def main():
|
|||||||
rock_model = archive.get_model('rock')
|
rock_model = archive.get_model('rock')
|
||||||
mud_model = archive.get_model('mud')
|
mud_model = archive.get_model('mud')
|
||||||
lava_model = archive.get_model('lava')
|
lava_model = archive.get_model('lava')
|
||||||
terrain_batch = Batch(tiles_vertices, generated.size ** 2, 8, translation = PARAM_FORMAT_VEC3_SHORT)
|
tiles_batch = Batch(tiles_vertices, generated.size ** 2, 8, translation = PARAM_FORMAT_VEC3_SHORT)
|
||||||
|
|
||||||
#TODO: generator & for real
|
#TODO: generator & for real
|
||||||
vc = generated.volcano_c
|
vc = generated.volcano_c
|
||||||
@ -108,165 +165,71 @@ def main():
|
|||||||
model = mud_model
|
model = mud_model
|
||||||
else:
|
else:
|
||||||
model = rock_model
|
model = rock_model
|
||||||
model.spawn(terrain_batch, 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))
|
||||||
|
|
||||||
tests_texture = archive.get_texture('tests')
|
tests_texture = Texture(*archive.get_texture('tests'))
|
||||||
tests_vertices = archive.get_vertices('tests')
|
tests_vertices = Vertices(*archive.get_vertices('tests'))
|
||||||
blob_model = archive.get_model('blob')
|
blob_model = archive.get_model('blob')
|
||||||
cube_model = archive.get_model('cube')
|
cube_model = archive.get_model('cube')
|
||||||
clouds_model = archive.get_model('clouds')
|
clouds_model = archive.get_model('clouds')
|
||||||
tests_batch = Batch(tests_vertices, 3, 3,
|
tests_batch = Batch(tests_vertices, 3, 3,
|
||||||
translation = PARAM_FORMAT_VEC3_FLOAT,
|
translation = PARAM_FORMAT_VEC3_FLOAT,
|
||||||
orientation = PARAM_FORMAT_MAT3_INT10 | PARAM_FORMAT_NORMALIZE)
|
orientation = PARAM_FORMAT_MAT3_INT10 | PARAM_FORMAT_NORMALIZE)
|
||||||
blob_spawn_translation = vec3(-100.0, -500.0, 0.0)
|
|
||||||
cube_spawn_translation = vec3(100.0, -500.0, 0.0)
|
|
||||||
blob_forward = math.vec3_normalize((sun_direction[0], sun_direction[1], 0.0))
|
blob_forward = math.vec3_normalize((sun_direction[0], sun_direction[1], 0.0))
|
||||||
blob_right = math.vec3_cross(blob_forward, math.vec3_up)
|
blob_right = math.vec3_cross(blob_forward, math.vec3_up)
|
||||||
blob_id = blob_model.spawn(tests_batch,
|
blob_orientation = mat3(vec3(*blob_right), vec3(*blob_forward), vec3_up)
|
||||||
blob_spawn_translation, 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_id = cube_model.spawn(tests_batch, cube_spawn_translation, mat3_identity)
|
cube = TestEntity(tests_batch, cube_model, vec3(100.0, -500.0, 0.0), mat3_identity)
|
||||||
clouds_id = clouds_model.spawn(tests_batch, vec3(0.0, 0.0, 32.0), mat3_identity)
|
clouds = TestEntity(tests_batch, clouds_model, vec3(0.0, 0.0, 32.0), mat3_identity)
|
||||||
|
|
||||||
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))
|
sea_triangles = sea.sea_triangles(64, proj_far_z - 0.1, proj_ratio)
|
||||||
|
|
||||||
camera = Camera()
|
return SceneNode(
|
||||||
camera.set_projection(proj_hfov, proj_ratio, proj_near_z, proj_far_z)
|
PerfNode('frame',
|
||||||
environment = Environment()
|
TextureNode({1: heightmap, 2: normalmap},
|
||||||
|
FuncNode(update_camera, (mouse, camera, environment)),
|
||||||
blob_translation = tests_batch.translation[blob_id]
|
TextureNode({0: tiles_texture},
|
||||||
cube_translation = tests_batch.translation[cube_id]
|
ShaderNode(tiles_shader,
|
||||||
cube_orientation = tests_batch.orientation[cube_id]
|
InputNode(tiles_shader, camera, environment),
|
||||||
clouds_orientation = tests_batch.orientation[clouds_id]
|
PerfNode('tiles_batch',
|
||||||
|
DrawNode(tiles_batch)))),
|
||||||
|
FuncNode(update_tests, (blob, cube, clouds)),
|
||||||
|
TextureNode({0: tests_texture},
|
||||||
|
ShaderNode(tests_shader,
|
||||||
|
InputNode(tests_shader, camera, environment),
|
||||||
|
PerfNode('tests_batch',
|
||||||
|
DrawNode(tests_batch))))),
|
||||||
|
FuncNode(update_sea, (camera, sea_phase)),
|
||||||
|
TextureNode({0: sea_polar_textures, 1: sea_detail_texture},
|
||||||
|
ShaderNode(sea_shader,
|
||||||
|
InputNode(sea_shader, camera, environment, sea_phase),
|
||||||
|
PerfNode('sea_triangles',
|
||||||
|
DrawNode(sea_triangles))))))
|
||||||
|
|
||||||
|
def loop(display):
|
||||||
|
events = Events(display)
|
||||||
|
keyboard = Keyboard(events)
|
||||||
|
mouse = Mouse(events, wheel = 60, wheel_min = 20)
|
||||||
|
scene = create_scene(keyboard, mouse)
|
||||||
print("Running... Ctrl+c to quit")
|
print("Running... Ctrl+c to quit")
|
||||||
start_time = time.monotonic()
|
time = Time()
|
||||||
current_time = 0.0
|
|
||||||
frame = 0
|
|
||||||
frame_min = 10000.0
|
|
||||||
frame_max = 0.0
|
|
||||||
frame_avg = 0.0
|
|
||||||
draw_min = 10000.0
|
|
||||||
draw_max = 0.0
|
|
||||||
draw_avg = 0.0
|
|
||||||
perf_count = 0
|
|
||||||
try:
|
try:
|
||||||
while True:
|
while not keyboard.quit:
|
||||||
current_time = time.monotonic() - start_time
|
|
||||||
frame_begin = time.thread_time()
|
|
||||||
|
|
||||||
events.update()
|
events.update()
|
||||||
if keyboard.quit:
|
time.update()
|
||||||
break
|
clear_buffer(True, True, True)
|
||||||
|
scene.draw(time)
|
||||||
begin_frame()
|
|
||||||
|
|
||||||
camera_distance = mouse.wheel * 20.0
|
|
||||||
camera_yaw = mouse.drag[0] * 0.001
|
|
||||||
camera_pitch = mouse.drag[1] * 0.001 + pi * 0.25
|
|
||||||
camera.set_view(camera_yaw, camera_pitch, camera_distance)
|
|
||||||
environment.from_sun(camera.view, sun_direction, sun_power)
|
|
||||||
|
|
||||||
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)
|
|
||||||
draw_begin = time.thread_time()
|
|
||||||
terrain_batch.draw()
|
|
||||||
draw_end = time.thread_time()
|
|
||||||
unselect_texture(0, tiles_texture)
|
|
||||||
unselect_texture(1, heightmap)
|
|
||||||
unselect_texture(2, normalmap)
|
|
||||||
terrain_shader.unselect()
|
|
||||||
|
|
||||||
rotation = mat3()
|
|
||||||
mat3_rotation(rotation, vec3_up, (current_time * 0.21) % tau)
|
|
||||||
mat3_mul_vec3(blob_translation, rotation, blob_spawn_translation)
|
|
||||||
mat3_mul_vec3(cube_translation, rotation, cube_spawn_translation)
|
|
||||||
mat3_rotation(cube_orientation, vec3_up, (current_time * 0.43) % tau)
|
|
||||||
mat3_rotation(clouds_orientation, vec3_up, (current_time * -0.037) % tau)
|
|
||||||
|
|
||||||
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)
|
|
||||||
tests_batch.draw()
|
|
||||||
unselect_texture(0, tests_texture)
|
|
||||||
unselect_texture(1, heightmap)
|
|
||||||
unselect_texture(2, normalmap)
|
|
||||||
tests_shader.unselect()
|
|
||||||
|
|
||||||
camera.to_km()
|
|
||||||
|
|
||||||
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)
|
|
||||||
sky_shader.unselect()
|
|
||||||
|
|
||||||
frame_end = time.thread_time()
|
|
||||||
end_frame()
|
|
||||||
swap_buffers(display)
|
swap_buffers(display)
|
||||||
|
|
||||||
if frame > 0:
|
|
||||||
draw_ms = draw_end - draw_begin
|
|
||||||
draw_min = min(draw_min, draw_ms)
|
|
||||||
draw_max = max(draw_max, draw_ms)
|
|
||||||
draw_avg += draw_ms
|
|
||||||
frame_ms = frame_end - frame_begin
|
|
||||||
frame_min = min(frame_min, frame_ms)
|
|
||||||
frame_max = max(frame_max, frame_ms)
|
|
||||||
frame_avg += frame_ms
|
|
||||||
perf_count += 1
|
|
||||||
frame += 1
|
|
||||||
|
|
||||||
except KeyboardInterrupt:
|
except KeyboardInterrupt:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
print("\rDraw *", perf_count,
|
def main():
|
||||||
": min =", round(draw_min * 1000.0, 2),
|
print("Initializing...")
|
||||||
", max =", round(draw_max * 1000.0, 2),
|
display = create_display(b'RK Island - Drag to rotate, wheel to zoom, q to quit', 1600, 900)
|
||||||
", avg =", round((draw_avg / perf_count) * 1000.0, 2), "ms")
|
render_initialize(__debug__)
|
||||||
|
loop(display)
|
||||||
print("\rFrame *", perf_count,
|
|
||||||
": min =", round(frame_min * 1000.0, 2),
|
|
||||||
", max =", round(frame_max * 1000.0, 2),
|
|
||||||
", avg =", round((frame_avg / perf_count) * 1000.0, 2), "ms")
|
|
||||||
|
|
||||||
# seed 666
|
|
||||||
# for _ in range(10000):
|
|
||||||
# current_time = 0 # time.monotonic() - start_time
|
|
||||||
|
|
||||||
# Draw * 9999 : min = 0.14 , max = 0.43 , avg = 0.19 ms
|
|
||||||
# Draw * 9999 : min = 0.14 , max = 0.35 , avg = 0.19 ms
|
|
||||||
# Draw * 9999 : min = 0.13 , max = 0.44 , avg = 0.18 ms
|
|
||||||
|
|
||||||
# Frame * 9999 : min = 0.21 , max = 0.7 , avg = 0.33 ms
|
|
||||||
# Frame * 9999 : min = 0.2 , max = 0.54 , avg = 0.31 ms
|
|
||||||
# Frame * 9999 : min = 0.19 , max = 0.6 , avg = 0.29 ms
|
|
||||||
|
|
||||||
print("Quitting...")
|
print("Quitting...")
|
||||||
del tests_batch
|
|
||||||
del terrain_batch
|
|
||||||
destroy_texture(sea_polar_textures)
|
|
||||||
destroy_texture(sea_detail_texture)
|
|
||||||
destroy_texture(heightmap)
|
|
||||||
destroy_texture(normalmap)
|
|
||||||
destroy_triangles(sky_triangles)
|
|
||||||
archive.destroy()
|
|
||||||
del terrain_shader
|
|
||||||
del tests_shader
|
|
||||||
del sky_shader
|
|
||||||
render_terminate()
|
render_terminate()
|
||||||
del events
|
|
||||||
destroy_display(display)
|
destroy_display(display)
|
||||||
|
29
game/inputs.py
Normal file
29
game/inputs.py
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
# Copyright (C) 2022 RozK
|
||||||
|
#
|
||||||
|
# This program is free software: you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU Affero General Public License as published by
|
||||||
|
# the Free Software Foundation, either version 3 of the License, or
|
||||||
|
# (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU Affero General Public License for more details.
|
||||||
|
#
|
||||||
|
# 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 engine import set_input_float
|
||||||
|
|
||||||
|
class InputFloat:
|
||||||
|
__slots__ = '_input', 'value'
|
||||||
|
|
||||||
|
def __init__(self, input, value):
|
||||||
|
self._input = input
|
||||||
|
self.value = value
|
||||||
|
|
||||||
|
def update(self, value):
|
||||||
|
self.value = value
|
||||||
|
|
||||||
|
def set_inputs(self, shader):
|
||||||
|
set_input_float(self._input, self.value)
|
@ -16,9 +16,7 @@
|
|||||||
import struct
|
import struct
|
||||||
from array import array
|
from array import array
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
import png
|
import png
|
||||||
|
|
||||||
import engine
|
import engine
|
||||||
|
|
||||||
VERTEX_SIZE = 20
|
VERTEX_SIZE = 20
|
||||||
@ -116,6 +114,14 @@ class TextureData:
|
|||||||
self.flags = flags
|
self.flags = flags
|
||||||
self.pixels = pixels
|
self.pixels = pixels
|
||||||
|
|
||||||
|
def __iter__(self):
|
||||||
|
yield self.format
|
||||||
|
yield self.width
|
||||||
|
yield self.height
|
||||||
|
yield self.nlevels
|
||||||
|
yield self.flags
|
||||||
|
yield self.pixels
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def from_archive(cls, file):
|
def from_archive(cls, file):
|
||||||
_read_magic(file, b'TX')
|
_read_magic(file, b'TX')
|
||||||
@ -132,9 +138,6 @@ class TextureData:
|
|||||||
_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)
|
_write_blob(file, self.pixels)
|
||||||
|
|
||||||
def create_texture(data):
|
|
||||||
return engine.create_texture(data.format, data.width, data.height, data.nlevels, data.flags, data.pixels)
|
|
||||||
|
|
||||||
class VerticesData:
|
class VerticesData:
|
||||||
__slots__ = 'name', 'vertices', 'indices'
|
__slots__ = 'name', 'vertices', 'indices'
|
||||||
|
|
||||||
@ -145,6 +148,12 @@ class VerticesData:
|
|||||||
self.vertices = vertices
|
self.vertices = vertices
|
||||||
self.indices = indices
|
self.indices = indices
|
||||||
|
|
||||||
|
def __iter__(self):
|
||||||
|
yield VERTEX_FORMAT
|
||||||
|
yield len(self.vertices) // VERTEX_SIZE
|
||||||
|
yield self.vertices
|
||||||
|
yield self.indices
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def from_archive(cls, file):
|
def from_archive(cls, file):
|
||||||
_read_magic(file, b'VT')
|
_read_magic(file, b'VT')
|
||||||
@ -161,9 +170,6 @@ class VerticesData:
|
|||||||
_write_array(file, self.vertices)
|
_write_array(file, self.vertices)
|
||||||
_write_array(file, self.indices)
|
_write_array(file, self.indices)
|
||||||
|
|
||||||
def create_vertices(data):
|
|
||||||
return engine.create_vertices(VERTEX_FORMAT, len(data.vertices) // VERTEX_SIZE, data.vertices, data.indices)
|
|
||||||
|
|
||||||
class ModelData:
|
class ModelData:
|
||||||
__slots__ = 'name', 'flags', 'mesh'
|
__slots__ = 'name', 'flags', 'mesh'
|
||||||
|
|
||||||
@ -184,19 +190,6 @@ class ModelData:
|
|||||||
_write_string(file, self.name)
|
_write_string(file, self.name)
|
||||||
_write_struct(file, 'II', (self.flags, self.mesh))
|
_write_struct(file, 'II', (self.flags, self.mesh))
|
||||||
|
|
||||||
class Model:
|
|
||||||
__slots__ = 'flags', 'mesh'
|
|
||||||
|
|
||||||
def __init__(self, flags, mesh):
|
|
||||||
self.flags = flags
|
|
||||||
self.mesh = mesh
|
|
||||||
|
|
||||||
def spawn(self, batch, *params):
|
|
||||||
return batch.append(self.flags, self.mesh, params)
|
|
||||||
|
|
||||||
def create_model(data):
|
|
||||||
return Model(data.flags, data.mesh)
|
|
||||||
|
|
||||||
class Archive:
|
class Archive:
|
||||||
__slots__ = 'textures_db', 'vertices_db', 'models_db'
|
__slots__ = 'textures_db', 'vertices_db', 'models_db'
|
||||||
|
|
||||||
@ -205,15 +198,6 @@ class Archive:
|
|||||||
self.vertices_db = vertices_db or {}
|
self.vertices_db = vertices_db or {}
|
||||||
self.models_db = models_db or {}
|
self.models_db = models_db or {}
|
||||||
|
|
||||||
def destroy(self):
|
|
||||||
for vertices in self.vertices_db.values():
|
|
||||||
engine.destroy_vertices(vertices)
|
|
||||||
for texture in self.textures_db.values():
|
|
||||||
engine.destroy_texture(texture)
|
|
||||||
self.textures_db.clear()
|
|
||||||
self.vertices_db.clear()
|
|
||||||
self.models_db.clear()
|
|
||||||
|
|
||||||
def get_texture(self, name):
|
def get_texture(self, name):
|
||||||
return self.textures_db[name]
|
return self.textures_db[name]
|
||||||
|
|
||||||
@ -223,18 +207,6 @@ class Archive:
|
|||||||
def get_model(self, name):
|
def get_model(self, name):
|
||||||
return self.models_db[name]
|
return self.models_db[name]
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def _new_texture(cls, data):
|
|
||||||
return data
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def _new_vertices(cls, data):
|
|
||||||
return data
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def _new_model(cls, data):
|
|
||||||
return data
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def from_archive(cls, file):
|
def from_archive(cls, file):
|
||||||
textures_db = {}
|
textures_db = {}
|
||||||
@ -244,22 +216,15 @@ class Archive:
|
|||||||
ntextures, nvertices, nmodels = _read_struct(file, 'III')
|
ntextures, nvertices, nmodels = _read_struct(file, 'III')
|
||||||
for _ in range(ntextures):
|
for _ in range(ntextures):
|
||||||
data = TextureData.from_archive(file)
|
data = TextureData.from_archive(file)
|
||||||
textures_db[data.name] = cls._new_texture(data)
|
textures_db[data.name] = data
|
||||||
for _ in range(nvertices):
|
for _ in range(nvertices):
|
||||||
data = VerticesData.from_archive(file)
|
data = VerticesData.from_archive(file)
|
||||||
vertices_db[data.name] = cls._new_vertices(data)
|
vertices_db[data.name] = data
|
||||||
for _ in range(nmodels):
|
for _ in range(nmodels):
|
||||||
data = ModelData.from_archive(file)
|
data = ModelData.from_archive(file)
|
||||||
models_db[data.name] = cls._new_model(data)
|
models_db[data.name] = data
|
||||||
return cls(textures_db, vertices_db, models_db)
|
return cls(textures_db, vertices_db, models_db)
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def load(cls, filename):
|
|
||||||
file = open(Path(filename), 'rb')
|
|
||||||
archive = cls.from_archive(file)
|
|
||||||
file.close()
|
|
||||||
return archive
|
|
||||||
|
|
||||||
def to_archive(self, file):
|
def to_archive(self, file):
|
||||||
_write_magic(file, b'RKAR')
|
_write_magic(file, b'RKAR')
|
||||||
_write_struct(file, 'III', (len(self.textures_db), len(self.vertices_db), len(self.models_db)))
|
_write_struct(file, 'III', (len(self.textures_db), len(self.vertices_db), len(self.models_db)))
|
||||||
@ -270,20 +235,14 @@ class Archive:
|
|||||||
for _, data in self.models_db.items():
|
for _, data in self.models_db.items():
|
||||||
data.to_archive(file)
|
data.to_archive(file)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def load(cls, filename):
|
||||||
|
file = open(Path(filename), 'rb')
|
||||||
|
archive = cls.from_archive(file)
|
||||||
|
file.close()
|
||||||
|
return archive
|
||||||
|
|
||||||
def save(self, filename):
|
def save(self, filename):
|
||||||
file = open(Path(filename), 'wb')
|
file = open(Path(filename), 'wb')
|
||||||
self.to_archive(file)
|
self.to_archive(file)
|
||||||
file.close()
|
file.close()
|
||||||
|
|
||||||
class RuntimeArchive(Archive):
|
|
||||||
@classmethod
|
|
||||||
def _new_texture(cls, data):
|
|
||||||
return create_texture(data)
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def _new_vertices(cls, data):
|
|
||||||
return create_vertices(data)
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def _new_model(cls, data):
|
|
||||||
return create_model(data)
|
|
||||||
|
85
game/scene.py
Normal file
85
game/scene.py
Normal file
@ -0,0 +1,85 @@
|
|||||||
|
# Copyright (C) 2022 RozK
|
||||||
|
#
|
||||||
|
# This program is free software: you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU Affero General Public License as published by
|
||||||
|
# the Free Software Foundation, either version 3 of the License, or
|
||||||
|
# (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU Affero General Public License for more details.
|
||||||
|
#
|
||||||
|
# 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:
|
||||||
|
__slots__ = '_subnodes'
|
||||||
|
|
||||||
|
def __init__(self, *subnodes):
|
||||||
|
self._subnodes = subnodes
|
||||||
|
|
||||||
|
def draw(self, time):
|
||||||
|
for subnode in self._subnodes:
|
||||||
|
subnode.draw(time)
|
||||||
|
|
||||||
|
class SceneNode(Node):
|
||||||
|
pass
|
||||||
|
|
||||||
|
class TextureNode(Node):
|
||||||
|
__slots__ = '_textures'
|
||||||
|
|
||||||
|
def __init__(self, textures, *subnodes):
|
||||||
|
Node.__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)
|
||||||
|
for slot, texture in items:
|
||||||
|
texture.unselect(slot)
|
||||||
|
|
||||||
|
class ShaderNode(Node):
|
||||||
|
__slots__ = '_shader'
|
||||||
|
|
||||||
|
def __init__(self, shader, *subnodes):
|
||||||
|
Node.__init__(self, *subnodes)
|
||||||
|
self._shader = shader
|
||||||
|
|
||||||
|
def draw(self, time):
|
||||||
|
self._shader.select()
|
||||||
|
Node.draw(self, time)
|
||||||
|
self._shader.unselect()
|
||||||
|
|
||||||
|
class InputNode:
|
||||||
|
__slots__ = '_shader', '_inputs'
|
||||||
|
|
||||||
|
def __init__(self, shader, *inputs):
|
||||||
|
self._shader = shader
|
||||||
|
self._inputs = inputs
|
||||||
|
|
||||||
|
def draw(self, time):
|
||||||
|
shader = self._shader
|
||||||
|
for input in self._inputs:
|
||||||
|
input.set_inputs(shader)
|
||||||
|
|
||||||
|
class DrawNode:
|
||||||
|
__slots__ = '_drawable'
|
||||||
|
|
||||||
|
def __init__(self, drawable):
|
||||||
|
self._drawable = drawable
|
||||||
|
|
||||||
|
def draw(self, time):
|
||||||
|
self._drawable.draw()
|
||||||
|
|
||||||
|
class FuncNode:
|
||||||
|
__slots__ = '_func', '_params'
|
||||||
|
|
||||||
|
def __init__(self, func, params):
|
||||||
|
self._func = func
|
||||||
|
self._params = params
|
||||||
|
|
||||||
|
def draw(self, time):
|
||||||
|
self._func(time, *self._params)
|
43
game/sea.py
43
game/sea.py
@ -13,14 +13,15 @@
|
|||||||
# 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 itertools import product
|
from itertools import chain, product
|
||||||
from math import tau, cos, sin, copysign
|
from math import tau, cos, sin, copysign
|
||||||
from array import array
|
from array import array
|
||||||
|
|
||||||
from engine import (create_texture,
|
from engine import TEXTURE_FORMAT_RGB10_A2, TEXTURE_FORMAT_TYPECODE, TEXTURE_FLAG_MIN_LINEAR, TEXTURE_FLAG_MAG_LINEAR
|
||||||
TEXTURE_FORMAT_RGB10_A2, TEXTURE_FORMAT_TYPECODE, TEXTURE_FLAG_MIN_LINEAR, TEXTURE_FLAG_MAG_LINEAR)
|
|
||||||
|
|
||||||
from game.math import vec3_scale, vec3_direction, vec3_cross, vec3_normal_rgb10a2
|
from game.math import vec3_scale, vec3_direction, vec3_cross, vec3_normal_rgb10a2
|
||||||
|
from game.texture import Texture
|
||||||
|
from game.triangles import Triangles
|
||||||
from game.resources import load_png
|
from game.resources import load_png
|
||||||
|
|
||||||
_format = TEXTURE_FORMAT_RGB10_A2
|
_format = TEXTURE_FORMAT_RGB10_A2
|
||||||
@ -51,7 +52,7 @@ def load_polar_textures(paths, waves_height = 0.008):
|
|||||||
_width, _height, normals = load_texture(path)
|
_width, _height, normals = load_texture(path)
|
||||||
assert _width == width and _height == height
|
assert _width == width and _height == height
|
||||||
data.extend(list(map(_conv, normals)))
|
data.extend(list(map(_conv, normals)))
|
||||||
return create_texture(_format, width, height, len(paths), _flags, data)
|
return Texture(_format, width, height, len(paths), _flags, data)
|
||||||
|
|
||||||
def load_detail_texture(path, scale = 0.5, waves_height = 0.002):
|
def load_detail_texture(path, scale = 0.5, waves_height = 0.002):
|
||||||
width, height, data = load_png(path)
|
width, height, data = load_png(path)
|
||||||
@ -68,4 +69,36 @@ def load_detail_texture(path, scale = 0.5, waves_height = 0.002):
|
|||||||
return vec3_scale(n, copysign(1.0, n[2]))
|
return vec3_scale(n, copysign(1.0, n[2]))
|
||||||
normals = list(map(normal, product(range(height), range(width)), data))
|
normals = list(map(normal, product(range(height), range(width)), data))
|
||||||
data = array(_typecode, list(map(_conv, normals)))
|
data = array(_typecode, list(map(_conv, normals)))
|
||||||
return create_texture(_format, width, height, 0, _flags, data)
|
return Texture(_format, width, height, 0, _flags, data)
|
||||||
|
|
||||||
|
# TODO: with FOV
|
||||||
|
def sea_triangles(vsubdivs, distance, projection_ratio):
|
||||||
|
assert vsubdivs > 0
|
||||||
|
vertices = []
|
||||||
|
hsubdivs = round(vsubdivs * projection_ratio)
|
||||||
|
z = -distance
|
||||||
|
width = distance * projection_ratio
|
||||||
|
height = distance
|
||||||
|
startx = width * -0.5
|
||||||
|
starty = height * -0.5
|
||||||
|
stepx = width / hsubdivs
|
||||||
|
stepy = height / vsubdivs
|
||||||
|
y1 = starty
|
||||||
|
y2 = y1 + stepy
|
||||||
|
for sy in range(vsubdivs):
|
||||||
|
x1 = startx
|
||||||
|
x2 = x1 + stepx
|
||||||
|
for sx in range(hsubdivs):
|
||||||
|
a = (x1, y2, z)
|
||||||
|
b = (x2, y1, z)
|
||||||
|
vertices.append((x1, y1, z))
|
||||||
|
vertices.append(b)
|
||||||
|
vertices.append(a)
|
||||||
|
vertices.append((x2, y2, z))
|
||||||
|
vertices.append(a)
|
||||||
|
vertices.append(b)
|
||||||
|
x1 = x2
|
||||||
|
x2 += stepx
|
||||||
|
y1 = y2
|
||||||
|
y2 += stepy
|
||||||
|
return Triangles(array('f', chain.from_iterable(vertices)))
|
||||||
|
@ -15,7 +15,7 @@
|
|||||||
|
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
from engine import load_shader, resolve_input, select_shader, unselect_shader, destroy_shader
|
from engine import create_shader, resolve_input, select_shader, unselect_shader, destroy_shader
|
||||||
|
|
||||||
def _cleanup(line):
|
def _cleanup(line):
|
||||||
return line.partition('//')[0].strip()
|
return line.partition('//')[0].strip()
|
||||||
@ -70,7 +70,7 @@ class Shader:
|
|||||||
print("Loading fragment shader", frag_name)
|
print("Loading fragment shader", frag_name)
|
||||||
frag_lines = _load_source(path / (frag_name + '_opengles.frag'))
|
frag_lines = _load_source(path / (frag_name + '_opengles.frag'))
|
||||||
assert frag_lines
|
assert frag_lines
|
||||||
self._shader = load_shader(list(map(_convert, vert_lines)), list(map(_convert, frag_lines)))
|
self._shader = create_shader(list(map(_convert, vert_lines)), list(map(_convert, frag_lines)))
|
||||||
_parse(self, vert_lines, frag_lines)
|
_parse(self, vert_lines, frag_lines)
|
||||||
|
|
||||||
def __del__(self):
|
def __del__(self):
|
||||||
|
31
game/texture.py
Normal file
31
game/texture.py
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
# Copyright (C) 2022 RozK
|
||||||
|
#
|
||||||
|
# This program is free software: you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU Affero General Public License as published by
|
||||||
|
# the Free Software Foundation, either version 3 of the License, or
|
||||||
|
# (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU Affero General Public License for more details.
|
||||||
|
#
|
||||||
|
# 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 engine import create_texture, select_texture, unselect_texture, destroy_texture
|
||||||
|
|
||||||
|
class Texture:
|
||||||
|
__slots__ = '_texture'
|
||||||
|
|
||||||
|
def __init__(self, format, width, height, nlevels, flags, pixels):
|
||||||
|
self._texture = create_texture(format, width, height, nlevels, flags, pixels)
|
||||||
|
|
||||||
|
def __del__(self):
|
||||||
|
destroy_texture(self._texture)
|
||||||
|
|
||||||
|
def select(self, slot):
|
||||||
|
select_texture(slot, self._texture)
|
||||||
|
|
||||||
|
def unselect(self, slot):
|
||||||
|
unselect_texture(slot, self._texture)
|
29
game/time.py
Normal file
29
game/time.py
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
# Copyright (C) 2022 RozK
|
||||||
|
#
|
||||||
|
# This program is free software: you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU Affero General Public License as published by
|
||||||
|
# the Free Software Foundation, either version 3 of the License, or
|
||||||
|
# (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU Affero General Public License for more details.
|
||||||
|
#
|
||||||
|
# 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 time import monotonic
|
||||||
|
|
||||||
|
class Time:
|
||||||
|
__slots__ = '_start', 'current', 'delta'
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
self._start = monotonic()
|
||||||
|
self.current = 0.0
|
||||||
|
self.delta = 0.0
|
||||||
|
|
||||||
|
def update(self):
|
||||||
|
current = monotonic() - self._start
|
||||||
|
self.delta = current - self.current
|
||||||
|
self.current = current
|
@ -13,38 +13,16 @@
|
|||||||
# 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 itertools import chain
|
from engine import create_triangles, draw_triangles, destroy_triangles
|
||||||
from array import array
|
|
||||||
from math import cos, sin
|
|
||||||
|
|
||||||
# TODO: with FOV
|
class Triangles:
|
||||||
def sky_triangles(vsubdivs, distance, projection_ratio):
|
__slots__ = '_triangles'
|
||||||
assert vsubdivs > 0
|
|
||||||
vertices = []
|
def __init__(self, triangles):
|
||||||
hsubdivs = round(vsubdivs * projection_ratio)
|
self._triangles = create_triangles(triangles)
|
||||||
z = -distance
|
|
||||||
width = distance * projection_ratio
|
def __del__(self):
|
||||||
height = distance
|
destroy_triangles(self._triangles)
|
||||||
startx = width * -0.5
|
|
||||||
starty = height * -0.5
|
def draw(self):
|
||||||
stepx = width / hsubdivs
|
draw_triangles(self._triangles)
|
||||||
stepy = height / vsubdivs
|
|
||||||
y1 = starty
|
|
||||||
y2 = y1 + stepy
|
|
||||||
for sy in range(vsubdivs):
|
|
||||||
x1 = startx
|
|
||||||
x2 = x1 + stepx
|
|
||||||
for sx in range(hsubdivs):
|
|
||||||
a = (x1, y2, z)
|
|
||||||
b = (x2, y1, z)
|
|
||||||
vertices.append((x1, y1, z))
|
|
||||||
vertices.append(b)
|
|
||||||
vertices.append(a)
|
|
||||||
vertices.append((x2, y2, z))
|
|
||||||
vertices.append(a)
|
|
||||||
vertices.append(b)
|
|
||||||
x1 = x2
|
|
||||||
x2 += stepx
|
|
||||||
y1 = y2
|
|
||||||
y2 += stepy
|
|
||||||
return array('f', chain.from_iterable(vertices))
|
|
||||||
|
25
game/vertices.py
Normal file
25
game/vertices.py
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
# Copyright (C) 2022 RozK
|
||||||
|
#
|
||||||
|
# This program is free software: you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU Affero General Public License as published by
|
||||||
|
# the Free Software Foundation, either version 3 of the License, or
|
||||||
|
# (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU Affero General Public License for more details.
|
||||||
|
#
|
||||||
|
# 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 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 __del__(self):
|
||||||
|
destroy_vertices(self._vertices)
|
Loading…
Reference in New Issue
Block a user