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
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from engine import (
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)
from engine import vec3_origin, mat4, mat4_projection, mat4_orbit, resolve_input, set_input_mat4
class _Inputs:
__slots__ = 'projection', 'view'
@ -29,30 +23,26 @@ class _Inputs:
self.view = resolve_input(shader, view)
class Camera:
__slots__ = 'origin', 'lookat', 'rotation', 'projection', 'view'
__slots__ = 'yaw', 'pitch', 'distance', 'projection', 'view'
def __init__(self):
self.origin = vec3()
self.lookat = vec3()
self.rotation = mat3()
self.yaw = 0.0
self.pitch = 0.0
self.distance = 0.0
self.projection = mat4()
self.view = mat4()
def set_projection(self, 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):
self.origin.set(*origin)
self.lookat.set(*lookat)
mat3_rotation(self.rotation, axis, angle)
mat3_mul_vec3(self.origin, self.rotation, self.origin)
mat3_mul_vec3(self.lookat, self.rotation, self.lookat)
mat4_lookat(self.view, self.origin, self.lookat)
def set_view(self, yaw, pitch, distance):
self.yaw = yaw
self.pitch = pitch
self.distance = distance
mat4_orbit(self.view, vec3_origin, yaw, pitch, distance)
def to_km(self):
vec3_mul_vec3(self.origin, _m2km, self.origin)
vec3_mul_vec3(self.lookat, _m2km, self.lookat)
mat4_lookat(self.view, self.origin, self.lookat)
mat4_orbit(self.view, vec3_origin, self.yaw, self.pitch, self.distance * 0.001)
def resolve_inputs(self, shader, projection = b'u_projection', view = b'u_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.camera import Camera
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_ratio = 16.0 / 9.0
proj_near_z = 8.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_power = 1.0
@ -48,8 +48,10 @@ def main():
print("Done: ", round(gen_end - gen_begin, 2), "seconds")
print("Initializing...")
display = create_display(b'RK Island', 1600, 900)
events = create_events(display)
display = create_display(b'RK Island - Drag to rotate, wheel to zoom, q to quit', 1600, 900)
events = Events(display)
mouse = Mouse(events, wheel = 60, wheel_min = 20)
keyboard = Keyboard(events)
render_initialize()
terrain_shader = shader.load('terrain', 'common')
tests_shader = shader.load('tests', 'common')
@ -157,7 +159,7 @@ def main():
cube_orientation = tests_batch.params[cube_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()
current_time = 0.0
frame_min = 10000.0
@ -167,22 +169,21 @@ def main():
draw_max = 0.0
draw_avg = 0.0
perf_count = 0
events_buffer = buffer(Event, 16)
try:
for frame in range(10000):
current_time = time.monotonic() - start_time
begin_frame()
frame_begin = time.thread_time()
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)
events.update()
if keyboard.quit:
break
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)
select_shader(terrain_shader)
@ -201,6 +202,13 @@ def main():
unselect_texture(2, normalmap)
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)
camera.update_inputs(tests_camera_inputs)
environment.update_inputs(tests_environment_inputs)
@ -243,23 +251,6 @@ def main():
frame_avg += frame_ms
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:
pass
@ -274,10 +265,6 @@ def main():
", avg =", round((frame_avg / perf_count) * 1000.0, 2), "ms")
# 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.34 , avg = 0.27 ms
@ -300,5 +287,5 @@ def main():
destroy_shader(tests_shader)
destroy_shader(sky_shader)
render_terminate()
destroy_events(display, events)
del events
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)