rk_island/game/game.py

247 lines
9.6 KiB
Python
Raw Normal View History

2022-08-28 05:09:52 +02:00
# 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/>.
2023-01-01 07:45:01 +01:00
from time import thread_time
2022-08-28 05:09:52 +02:00
from math import pi, tau, dist
from engine import *
2022-12-18 05:40:52 +01:00
from game import math
2022-12-31 19:16:44 +01:00
from game.time import Time
from game.events import Events
from game.mouse import Mouse
from game.keyboard import Keyboard
2022-12-23 06:36:24 +01:00
from game.generator import Generator
2022-12-31 20:24:08 +01:00
from game.resources import Archive
2022-12-31 19:16:44 +01:00
from game.texture import Texture
from game.shader import Shader
2022-12-31 20:24:08 +01:00
from game.vertices import Vertices
2022-12-18 06:35:20 +01:00
from game.camera import Camera
from game.environment import Environment
2022-12-31 19:16:44 +01:00
from game.inputs import InputFloat
from game.batch import Batch
2022-12-31 21:02:46 +01:00
from game.entity import Entity
2023-01-02 12:43:25 +01:00
from game.scene import Group, SceneGroup, TextureGroup, ShaderGroup, InputNode, DrawNode, FuncNode
2022-12-31 19:16:44 +01:00
from game import sea
2022-12-18 05:40:52 +01:00
proj_hfov = pi * 0.25
proj_ratio = 16.0 / 9.0
proj_near_z = 8.0
proj_far_z = 3000.0
2022-12-29 14:36:30 +01:00
sun_direction = math.vec3_normalize((1.0, 0.0, 0.5))
sun_power = 1.0
2023-01-02 12:43:25 +01:00
class PerfGroup(Group):
2023-01-01 07:45:01 +01:00
__slots__ = '_count', '_min', '_max', '_total', '_name'
def __init__(self, name, *subnodes):
2023-01-02 12:43:25 +01:00
Group.__init__(self, *subnodes)
2023-01-01 07:45:01 +01:00
self._count = None
self._min = 10000.0
self._max = 0.0
self._total = 0.0
self._name = name
def __del__(self):
2023-01-03 14:10:04 +01:00
if self._count:
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)")
2023-01-01 07:45:01 +01:00
def draw(self, time):
begin = thread_time()
2023-01-02 12:43:25 +01:00
Group.draw(self, time)
2023-01-01 07:45:01 +01:00
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
2022-12-31 21:02:46 +01:00
class TestEntity(Entity):
__slots__ = 'translation', 'orientation', 'spawn_translation', 'spawn_orientation'
def __init__(self, batch, mesh, translation, orientation):
Entity.__init__(self, batch, INSTANCE_FLAG_VISIBLE, mesh,
translation = translation,
orientation = orientation)
2022-12-31 21:02:46 +01:00
self.translation = batch.translation[self.index]
self.orientation = batch.orientation[self.index]
self.spawn_translation = translation
self.spawn_orientation = orientation
2022-12-31 19:16:44 +01:00
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
2022-12-31 22:30:57 +01:00
camera.set_view(camera_yaw % tau, camera_pitch % tau, camera_distance)
2022-12-31 19:16:44 +01:00
environment.from_sun(camera.view, sun_direction, sun_power)
2022-12-31 21:02:46 +01:00
def update_tests(time, blob, cube, clouds):
2022-12-31 19:16:44 +01:00
rotation = mat3()
mat3_rotation(rotation, vec3_up, (time.current * 0.21) % tau)
2022-12-31 21:02:46 +01:00
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)
2022-12-31 19:16:44 +01:00
def update_sea(time, camera, sea_phase):
camera.to_km()
sea_phase.update((time.current * 0.023) % 1.0)
2022-08-28 05:09:52 +02:00
2022-12-31 20:24:08 +01:00
def create_scene(keyboard, mouse):
2022-12-31 19:17:32 +01:00
tiles_shader = Shader('tiles', 'common')
2022-12-31 15:22:30 +01:00
tests_shader = Shader('tests', 'common')
2022-12-31 19:27:23 +01:00
sea_shader = Shader('sea')
2022-08-28 05:09:52 +02:00
2022-12-31 19:16:44 +01:00
camera = Camera()
camera.set_projection(proj_hfov, proj_ratio, proj_near_z, proj_far_z)
environment = Environment()
2022-12-31 19:27:23 +01:00
sea_phase = InputFloat(sea_shader.u_sea_phase, 0.0)
2022-12-31 19:16:44 +01:00
print("Generating terrain...")
generated = Generator(256)
heightmap = Texture(
2022-12-25 04:35:09 +01:00
TEXTURE_FORMAT_FLOAT_32, 256, 256, 0, TEXTURE_FLAG_MIN_LINEAR | TEXTURE_FLAG_MAG_LINEAR,
generated.packed_heights)
2022-12-31 19:16:44 +01:00
normalmap = Texture(
TEXTURE_FORMAT_RGB10_A2, 256, 256, 0, TEXTURE_FLAG_MIN_LINEAR | TEXTURE_FLAG_MAG_LINEAR,
generated.packed_normals)
print("Loading resources...")
2022-12-31 20:24:08 +01:00
archive = Archive.load('data/rk_island.rkar')
print("Building tiles...")
2022-12-31 20:24:08 +01:00
tiles_texture = Texture(*archive.get_texture('tiles'))
2023-01-04 09:38:27 +01:00
tiles_vertices = archive.get_vertices('tiles')
water_mesh = tiles_vertices.get_mesh('water')
sand_mesh = tiles_vertices.get_mesh('sand')
grass_mesh = tiles_vertices.get_mesh('grass')
forest_mesh = tiles_vertices.get_mesh('forest')
rock_mesh = tiles_vertices.get_mesh('rock')
mud_mesh = tiles_vertices.get_mesh('mud')
lava_mesh = tiles_vertices.get_mesh('lava')
tiles_batch = Batch(Vertices(*tiles_vertices), generated.size ** 2, translation = PARAM_FORMAT_VEC3_SHORT)
2022-08-28 05:09:52 +02:00
#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:
mesh = water_mesh
2022-08-28 05:09:52 +02:00
elif h < 2.0:
mesh = sand_mesh
2022-08-28 05:09:52 +02:00
elif h < 180:
if nz > 0.9:
if ny < -0.01 and nz > 0.93:
mesh = forest_mesh
2022-08-28 05:09:52 +02:00
else:
mesh = grass_mesh
2022-08-28 05:09:52 +02:00
else:
mesh = rock_mesh
2022-08-28 05:09:52 +02:00
elif vd < vr - 3.0 and nz > 0.999:
mesh = lava_mesh
2022-08-28 05:09:52 +02:00
elif vd < vr + 2.0:
mesh = mud_mesh
2022-08-28 05:09:52 +02:00
elif vd < vr + 6.0 and nz < 0.67:
mesh = mud_mesh
2022-08-28 05:09:52 +02:00
else:
mesh = rock_mesh
tiles_batch.spawn(INSTANCE_FLAG_VISIBLE, mesh,
translation = vec3(float(((mx - 128) * 8) + 4), float(((127 - my) * 8) + 4), 0.0))
tiles_batch.fill()
tiles_batch.freeze(flags = True, mesh = True, translation = True)
2022-12-31 20:24:08 +01:00
tests_texture = Texture(*archive.get_texture('tests'))
2023-01-04 09:38:27 +01:00
tests_vertices = archive.get_vertices('tests')
blob_mesh = tests_vertices.get_mesh('blob')
cube_mesh = tests_vertices.get_mesh('cube')
clouds_mesh = tests_vertices.get_mesh('clouds')
tests_batch = Batch(Vertices(*tests_vertices), 3,
translation = PARAM_FORMAT_VEC3_FLOAT,
orientation = PARAM_FORMAT_MAT3_INT10 | PARAM_FORMAT_NORMALIZE)
blob_forward = math.vec3_normalize((sun_direction[0], sun_direction[1], 0.0))
blob_right = math.vec3_cross(blob_forward, math.vec3_up)
2022-12-31 21:02:46 +01:00
blob_orientation = mat3(vec3(*blob_right), vec3(*blob_forward), vec3_up)
blob = TestEntity(tests_batch, blob_mesh, translation = vec3(-100.0, -500.0, 0.0), orientation = blob_orientation)
cube = TestEntity(tests_batch, cube_mesh, translation = vec3(100.0, -500.0, 0.0), orientation = mat3_identity)
clouds = TestEntity(tests_batch, clouds_mesh, translation = vec3(0.0, 0.0, 32.0), orientation = mat3_identity)
tests_batch.fill()
tests_batch.freeze(flags = True, mesh = True)
2022-08-28 05:09:52 +02:00
2023-01-04 09:38:27 +01:00
sea_polar = sea.load_polar_textures(('data/sea_bump1.png', 'data/sea_bump2.png'))
sea_detail = sea.load_detail_texture('data/sea_bump.png')
2022-12-31 19:32:39 +01:00
sea_triangles = sea.sea_triangles(64, proj_far_z - 0.1, proj_ratio)
2022-12-31 19:16:44 +01:00
2023-01-02 12:33:34 +01:00
assert tiles_shader.u_height_sampler == tests_shader.u_height_sampler
assert tiles_shader.u_normal_sampler == tests_shader.u_normal_sampler
2023-01-02 12:43:25 +01:00
return SceneGroup(
PerfGroup('frame',
TextureGroup({tiles_shader.u_height_sampler: heightmap, tiles_shader.u_normal_sampler: normalmap},
2023-01-01 07:45:01 +01:00
FuncNode(update_camera, (mouse, camera, environment)),
2023-01-02 12:43:25 +01:00
TextureGroup({tiles_shader.u_texture_sampler: tiles_texture},
ShaderGroup(tiles_shader,
2023-01-01 07:45:01 +01:00
InputNode(tiles_shader, camera, environment),
2023-01-02 12:43:25 +01:00
PerfGroup('tiles_batch',
2023-01-01 07:45:01 +01:00
DrawNode(tiles_batch)))),
FuncNode(update_tests, (blob, cube, clouds)),
2023-01-02 12:43:25 +01:00
TextureGroup({tests_shader.u_texture_sampler: tests_texture},
ShaderGroup(tests_shader,
2023-01-01 07:45:01 +01:00
InputNode(tests_shader, camera, environment),
2023-01-02 12:43:25 +01:00
PerfGroup('tests_batch',
2023-01-01 07:45:01 +01:00
DrawNode(tests_batch))))),
FuncNode(update_sea, (camera, sea_phase)),
2023-01-04 09:38:27 +01:00
TextureGroup({sea_shader.u_polar_sampler: sea_polar, sea_shader.u_detail_sampler: sea_detail},
2023-01-02 12:43:25 +01:00
ShaderGroup(sea_shader,
2023-01-01 07:45:01 +01:00
InputNode(sea_shader, camera, environment, sea_phase),
2023-01-02 12:43:25 +01:00
PerfGroup('sea_triangles',
2023-01-01 07:45:01 +01:00
DrawNode(sea_triangles))))))
2022-08-28 05:09:52 +02:00
2022-12-31 20:24:08 +01:00
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")
2022-08-28 05:09:52 +02:00
try:
2023-01-04 09:38:27 +01:00
time = Time()
2022-12-31 19:16:44 +01:00
while not keyboard.quit:
events.update()
2023-01-01 08:11:38 +01:00
clear_buffer(True, True, True)
2022-12-31 19:16:44 +01:00
scene.draw(time)
2022-12-24 11:35:14 +01:00
swap_buffers(display)
2023-01-04 09:38:27 +01:00
time.update()
2022-08-28 05:09:52 +02:00
except KeyboardInterrupt:
pass
2022-12-31 20:24:08 +01:00
def main():
print("Initializing...")
display = create_display(b'RK Island - Drag to rotate, wheel to zoom, q to quit', 1600, 900)
render_initialize(__debug__)
loop(display)
2022-08-28 05:09:52 +02:00
print("Quitting...")
2022-12-24 11:35:14 +01:00
render_terminate()
destroy_display(display)