rk_island/game/game.py

295 lines
11 KiB
Python

# 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/>.
import time
from math import pi, tau, dist
from ctypes import Structure
from engine import *
from game import math
from game import shader
from game import triangles
from game import sea
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.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
sun_direction = math.vec3_normalize((1.0, 0.0, 0.5))
sun_power = 1.0
def main():
print("Generating terrain...")
gen_begin = time.thread_time()
generated = Generator(256)
gen_end = time.thread_time()
print("Done: ", round(gen_end - gen_begin, 2), "seconds")
print("Initializing...")
display = create_display(b'RK Island - Drag to rotate, wheel to zoom, q to quit', 1600, 900)
assert display
events = Events(display)
mouse = Mouse(events, wheel = 60, wheel_min = 20)
keyboard = Keyboard(events)
render_initialize(__debug__)
terrain_shader = shader.load('terrain', 'common')
tests_shader = shader.load('tests', 'common')
sky_shader = shader.load('sky')
heightmap = create_texture(
TEXTURE_FORMAT_FLOAT_32, 256, 256, 0, TEXTURE_FLAG_MIN_LINEAR | TEXTURE_FLAG_MAG_LINEAR,
generated.packed_heights)
terrain_heightmap_sampler = resolve_input(terrain_shader, b'u_height_sampler')
tests_heightmap_sampler = resolve_input(tests_shader, b'u_height_sampler')
normalmap = create_texture(
TEXTURE_FORMAT_RGB10_A2, 256, 256, 0, TEXTURE_FLAG_MIN_LINEAR | TEXTURE_FLAG_MAG_LINEAR,
generated.packed_normals)
terrain_normalmap_sampler = resolve_input(terrain_shader, b'u_normal_sampler')
tests_normalmap_sampler = resolve_input(tests_shader, b'u_normal_sampler')
print("Loading resources...")
archive = RuntimeArchive.load('data/rk_island.rkar')
print("Building tiles...")
tiles_texture = archive.get_texture('tiles')
tiles_sampler = resolve_input(terrain_shader, b'u_texture_sampler')
tiles_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')
terrain_batch = Batch(tiles_vertices, generated.size ** 2, 8, translation = PARAM_FORMAT_VEC3_SHORT)
#TODO: generator & for real
vc = generated.volcano_c
vr = generated.volcano_r
for my, mx in generated.map_coords:
vd = dist((mx + 0.5, my + 0.5), vc)
nx, ny, nz, h = generated.unpack(my, mx)
r = generated.rivers[my * generated.size + mx]
if h == 0.0:
continue
if r > 0.0:
model = water_model
elif h < 2.0:
model = sand_model
elif h < 180:
if nz > 0.9:
if ny < -0.01 and nz > 0.93:
model = forest_model
else:
model = grass_model
else:
model = rock_model
elif vd < vr - 3.0 and nz > 0.999:
model = lava_model
elif vd < vr + 2.0:
model = mud_model
elif vd < vr + 6.0 and nz < 0.67:
model = mud_model
else:
model = rock_model
model.spawn(terrain_batch, vec3(float(((mx - 128) * 8) + 4), float(((127 - my) * 8) + 4), 0.0))
tests_texture = archive.get_texture('tests')
tests_sampler = resolve_input(tests_shader, b'u_texture_sampler')
tests_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)
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_right = math.vec3_cross(blob_forward, math.vec3_up)
blob_id = blob_model.spawn(tests_batch,
blob_spawn_translation, mat3(vec3(*blob_right), vec3(*blob_forward), vec3_up))
cube_id = cube_model.spawn(tests_batch, cube_spawn_translation, mat3_identity)
clouds_id = clouds_model.spawn(tests_batch, vec3(0.0, 0.0, 32.0), mat3_identity)
sea_phase = resolve_input(sky_shader, b'u_sea_phase')
sea_polar_textures = sea.load_polar_textures(('data/sea_bump1.png', 'data/sea_bump2.png'))
sea_polar_sampler = resolve_input(sky_shader, b'u_sea_polar_sampler')
sea_detail_texture = sea.load_detail_texture('data/sea_bump.png')
sea_detail_sampler = resolve_input(sky_shader, b'u_sea_detail_sampler')
sky_triangles = create_triangles(triangles.sky_triangles(64, proj_far_z - 0.1, proj_ratio))
camera = Camera()
camera.set_projection(proj_hfov, proj_ratio, proj_near_z, proj_far_z)
terrain_camera_inputs = camera.resolve_inputs(terrain_shader)
tests_camera_inputs = camera.resolve_inputs(tests_shader)
sky_camera_inputs = camera.resolve_inputs(sky_shader)
environment = Environment()
terrain_environment_inputs = environment.resolve_inputs(terrain_shader)
tests_environment_inputs = environment.resolve_inputs(tests_shader)
sky_environment_inputs = environment.resolve_inputs(sky_shader)
blob_translation = tests_batch.translation[blob_id]
cube_translation = tests_batch.translation[cube_id]
cube_orientation = tests_batch.orientation[cube_id]
clouds_orientation = tests_batch.orientation[clouds_id]
print("Running... Ctrl+c to quit")
start_time = time.monotonic()
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:
while True:
current_time = time.monotonic() - start_time
frame_begin = time.thread_time()
events.update()
if keyboard.quit:
break
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)
camera.update_inputs(terrain_camera_inputs)
environment.update_inputs(terrain_environment_inputs)
select_texture(0, tiles_texture, tiles_sampler)
select_texture(1, heightmap, terrain_heightmap_sampler)
select_texture(2, normalmap, terrain_normalmap_sampler)
select_vertices(tiles_vertices)
draw_begin = time.thread_time()
terrain_batch.draw()
draw_end = time.thread_time()
unselect_vertices(tiles_vertices)
unselect_texture(0, tiles_texture)
unselect_texture(1, heightmap)
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)
select_texture(0, tests_texture, tests_sampler)
select_texture(1, heightmap, tests_heightmap_sampler)
select_texture(2, normalmap, tests_normalmap_sampler)
select_vertices(tests_vertices)
tests_batch.draw()
unselect_vertices(tests_vertices)
unselect_texture(0, tests_texture)
unselect_texture(1, heightmap)
unselect_texture(2, normalmap)
unselect_shader(tests_shader)
camera.to_km()
select_shader(sky_shader)
camera.update_inputs(sky_camera_inputs)
environment.update_inputs(sky_environment_inputs)
set_input_float(sea_phase, (current_time * 0.023) % 1.0)
select_texture(0, sea_polar_textures, sea_polar_sampler)
select_texture(1, sea_detail_texture, sea_detail_sampler)
draw_triangles(sky_triangles)
unselect_texture(0, sea_polar_textures)
unselect_texture(1, sea_detail_texture)
unselect_shader(sky_shader)
frame_end = time.thread_time()
end_frame()
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:
pass
print("\rDraw *", perf_count,
": min =", round(draw_min * 1000.0, 2),
", max =", round(draw_max * 1000.0, 2),
", avg =", round((draw_avg / perf_count) * 1000.0, 2), "ms")
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...")
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()
destroy_shader(terrain_shader)
destroy_shader(tests_shader)
destroy_shader(sky_shader)
render_terminate()
del events
destroy_display(display)