Compare commits

..

2 Commits

6 changed files with 170 additions and 60 deletions

2
engine

@ -1 +1 @@
Subproject commit 269d7821c82757cf994370f9ed4b2a6d36e34983 Subproject commit 33abfd834ad5c9541796ef3f8a4999539a2f91f8

View File

@ -13,13 +13,7 @@
# You should have received a copy of the GNU Affero General Public License # You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
from engine import ( from engine import vec3_origin, mat4, mat4_projection, mat4_orbit, resolve_input, set_input_mat4
vec3, vec3_mul_vec3,
mat3, mat3_rotation, mat3_mul_vec3,
mat4, mat4_projection, mat4_lookat,
resolve_input, set_input_mat4)
_m2km = vec3(0.001, 0.001, 0.001)
class _Inputs: class _Inputs:
__slots__ = 'projection', 'view' __slots__ = 'projection', 'view'
@ -29,30 +23,26 @@ class _Inputs:
self.view = resolve_input(shader, view) self.view = resolve_input(shader, view)
class Camera: class Camera:
__slots__ = 'origin', 'lookat', 'rotation', 'projection', 'view' __slots__ = 'yaw', 'pitch', 'distance', 'projection', 'view'
def __init__(self): def __init__(self):
self.origin = vec3() self.yaw = 0.0
self.lookat = vec3() self.pitch = 0.0
self.rotation = mat3() self.distance = 0.0
self.projection = mat4() self.projection = mat4()
self.view = mat4() self.view = mat4()
def set_projection(self, half_fov, ratio, near_z, far_z): def set_projection(self, half_fov, ratio, near_z, far_z):
mat4_projection(self.projection, half_fov, ratio, near_z, far_z) mat4_projection(self.projection, half_fov, ratio, near_z, far_z)
def set_view(self, origin, lookat, axis, angle): def set_view(self, yaw, pitch, distance):
self.origin.set(*origin) self.yaw = yaw
self.lookat.set(*lookat) self.pitch = pitch
mat3_rotation(self.rotation, axis, angle) self.distance = distance
mat3_mul_vec3(self.origin, self.rotation, self.origin) mat4_orbit(self.view, vec3_origin, yaw, pitch, distance)
mat3_mul_vec3(self.lookat, self.rotation, self.lookat)
mat4_lookat(self.view, self.origin, self.lookat)
def to_km(self): def to_km(self):
vec3_mul_vec3(self.origin, _m2km, self.origin) mat4_orbit(self.view, vec3_origin, self.yaw, self.pitch, self.distance * 0.001)
vec3_mul_vec3(self.lookat, _m2km, self.lookat)
mat4_lookat(self.view, self.origin, self.lookat)
def resolve_inputs(self, shader, projection = b'u_projection', view = b'u_view'): def resolve_inputs(self, shader, projection = b'u_projection', view = b'u_view'):
return _Inputs(shader, projection, view) return _Inputs(shader, projection, view)

54
game/events.py Normal file
View File

@ -0,0 +1,54 @@
# 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 (
buffer,
EVENT_FOCUS_IN, EVENT_FOCUS_OUT,
EVENT_KEY_PRESS, EVENT_KEY_RELEASE,
EVENT_BUTTON_PRESS, EVENT_BUTTON_RELEASE,
EVENT_MOTION,
Event, create_events, destroy_events, consume_events)
_max_type = max(
EVENT_FOCUS_IN, EVENT_FOCUS_OUT,
EVENT_KEY_PRESS, EVENT_KEY_RELEASE,
EVENT_BUTTON_PRESS, EVENT_BUTTON_RELEASE,
EVENT_MOTION)
_max_events = 64
class Events:
__slots__ = '_display', '_events', '_buffer', '_handlers'
def __init__(self, display):
self._display = display
self._events = create_events(display)
self._buffer = buffer(Event, _max_events)
self._handlers = [[] for _ in range(_max_type + 1)]
def __del__(self):
destroy_events(self._display, self._events)
def register(self, type, handler):
assert type <= _max_type
self._handlers[type].append(handler)
def update(self):
nevents = consume_events(self._events, self._buffer, _max_events)
if nevents:
for event in self._buffer[:nevents]:
data = event.data
for handler in self._handlers[event.type]:
handler(data)

View File

@ -28,15 +28,15 @@ from game.resources import RuntimeArchive
from game.batch import Batch from game.batch import Batch
from game.camera import Camera from game.camera import Camera
from game.environment import Environment from game.environment import Environment
from game.events import Events
from game.mouse import Mouse
from game.keyboard import Keyboard
proj_hfov = pi * 0.25 proj_hfov = pi * 0.25
proj_ratio = 16.0 / 9.0 proj_ratio = 16.0 / 9.0
proj_near_z = 8.0 proj_near_z = 8.0
proj_far_z = 3000.0 proj_far_z = 3000.0
camera_origin = (0.0, -1200.0, 500.0)
camera_lookat = (0.0, 500.0, -500.0)
sun_direction = math.vec3_normalize((1.0, 0.0, 1.0)) sun_direction = math.vec3_normalize((1.0, 0.0, 1.0))
sun_power = 1.0 sun_power = 1.0
@ -48,8 +48,10 @@ def main():
print("Done: ", round(gen_end - gen_begin, 2), "seconds") print("Done: ", round(gen_end - gen_begin, 2), "seconds")
print("Initializing...") print("Initializing...")
display = create_display(b'RK Island', 1600, 900) display = create_display(b'RK Island - Drag to rotate, wheel to zoom, q to quit', 1600, 900)
events = create_events(display) events = Events(display)
mouse = Mouse(events, wheel = 60, wheel_min = 20)
keyboard = Keyboard(events)
render_initialize() render_initialize()
terrain_shader = shader.load('terrain', 'common') terrain_shader = shader.load('terrain', 'common')
tests_shader = shader.load('tests', 'common') tests_shader = shader.load('tests', 'common')
@ -157,7 +159,7 @@ def main():
cube_orientation = tests_batch.params[cube_id].orientation cube_orientation = tests_batch.params[cube_id].orientation
clouds_orientation = tests_batch.params[clouds_id].orientation clouds_orientation = tests_batch.params[clouds_id].orientation
print("Running... Click or press q to quit") print("Running... Ctrl+c to quit")
start_time = time.monotonic() start_time = time.monotonic()
current_time = 0.0 current_time = 0.0
frame_min = 10000.0 frame_min = 10000.0
@ -167,22 +169,21 @@ def main():
draw_max = 0.0 draw_max = 0.0
draw_avg = 0.0 draw_avg = 0.0
perf_count = 0 perf_count = 0
events_buffer = buffer(Event, 16)
try: try:
for frame in range(10000): for frame in range(10000):
current_time = time.monotonic() - start_time current_time = time.monotonic() - start_time
begin_frame()
frame_begin = time.thread_time() frame_begin = time.thread_time()
rotation = mat3() events.update()
mat3_rotation(rotation, vec3_up, (current_time * 0.21) % tau) if keyboard.quit:
mat3_mul_vec3(blob_translation, rotation, blob_spawn_translation) break
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)
camera.set_view(camera_origin, camera_lookat, vec3_up, (current_time * 0.05) % tau) 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) environment.from_sun(camera.view, sun_direction, sun_power)
select_shader(terrain_shader) select_shader(terrain_shader)
@ -201,6 +202,13 @@ def main():
unselect_texture(2, normalmap) unselect_texture(2, normalmap)
unselect_shader(terrain_shader) unselect_shader(terrain_shader)
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)
select_shader(tests_shader) select_shader(tests_shader)
camera.update_inputs(tests_camera_inputs) camera.update_inputs(tests_camera_inputs)
environment.update_inputs(tests_environment_inputs) environment.update_inputs(tests_environment_inputs)
@ -243,23 +251,6 @@ def main():
frame_avg += frame_ms frame_avg += frame_ms
perf_count += 1 perf_count += 1
stop = False
while True:
nevents = consume_events(events, events_buffer, len(events_buffer))
if not nevents:
break
for event in events_buffer[:nevents]:
if event.type == EVENT_FOCUS_IN:
print("EVENT_FOCUS_IN")
elif event.type == EVENT_FOCUS_OUT:
print("EVENT_FOCUS_OUT")
elif event.type == EVENT_KEY_PRESS and event.data.key.character == 'q':
stop = True
elif event.type == EVENT_BUTTON_RELEASE:
stop = True
if stop:
break
except KeyboardInterrupt: except KeyboardInterrupt:
pass pass
@ -274,10 +265,6 @@ def main():
", avg =", round((frame_avg / perf_count) * 1000.0, 2), "ms") ", avg =", round((frame_avg / perf_count) * 1000.0, 2), "ms")
# seed 666 # seed 666
# camera_origin = vec3((0.0, -1200.0, 500.0))
# camera_lookat = vec3((0.0, 500.0, -500.0))
# for x in range(10000)
# current_time = 0
# Draw * 9999 : min = 0.2 , max = 1.32 , avg = 0.27 ms # Draw * 9999 : min = 0.2 , max = 1.32 , avg = 0.27 ms
# Draw * 9999 : min = 0.2 , max = 1.34 , avg = 0.27 ms # Draw * 9999 : min = 0.2 , max = 1.34 , avg = 0.27 ms
@ -300,5 +287,5 @@ def main():
destroy_shader(tests_shader) destroy_shader(tests_shader)
destroy_shader(sky_shader) destroy_shader(sky_shader)
render_terminate() render_terminate()
destroy_events(display, events) del events
destroy_display(display) destroy_display(display)

27
game/keyboard.py Normal file
View File

@ -0,0 +1,27 @@
# 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 EVENT_KEY_PRESS
class Keyboard:
__slots__ = 'quit'
def __init__(self, events):
self.quit = False
events.register(EVENT_KEY_PRESS, self.key_press_handler)
def key_press_handler(self, data):
if data.key.character == 'q':
self.quit = True

52
game/mouse.py Normal file
View File

@ -0,0 +1,52 @@
# 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 (
EVENT_BUTTON_PRESS, EVENT_BUTTON_RELEASE, EVENT_MOTION, BUTTON_LEFT, BUTTON_WHEEL_UP, BUTTON_WHEEL_DOWN)
class Mouse:
__slots__ = 'buttons', 'wheel', 'wheel_min', 'position', 'drag'
def __init__(self, events, wheel = 0, wheel_min = None):
self.buttons = 0
self.wheel = wheel
self.wheel_min = wheel_min
self.position = None
self.drag = (0, 0)
events.register(EVENT_BUTTON_PRESS, self.button_press_handler)
events.register(EVENT_BUTTON_RELEASE, self.button_release_handler)
events.register(EVENT_MOTION, self.motion_handler)
def button_press_handler(self, data):
button = data.button.index
self.buttons |= 1 << button
if button == BUTTON_WHEEL_UP:
self.wheel -= 1
if self.wheel_min is not None and self.wheel < self.wheel_min:
self.wheel = self.wheel_min
elif button == BUTTON_WHEEL_DOWN:
self.wheel += 1
def button_release_handler(self, data):
self.buttons &= ~(1 << data.button.index)
def motion_handler(self, data):
new_x = data.motion.x
new_y = data.motion.y
if self.position and (self.buttons & (1 << BUTTON_LEFT)):
prev_x, prev_y = self.position
prev_dx, prev_dy = self.drag
self.drag = (prev_dx + (new_x - prev_x), prev_dy + (new_y - prev_y))
self.position = (new_x, new_y)