# 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 . 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) 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) 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) tests_batch.draw() 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)