236 lines
8.9 KiB
Python
236 lines
8.9 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/>.
|
|
|
|
from time import thread_time
|
|
from math import pi, tau, dist
|
|
|
|
from engine import *
|
|
|
|
from game import math
|
|
from game.time import Time
|
|
from game.events import Events
|
|
from game.mouse import Mouse
|
|
from game.keyboard import Keyboard
|
|
from game.generator import Generator
|
|
from game.resources import Archive
|
|
from game.texture import Texture
|
|
from game.shader import Shader
|
|
from game.vertices import Vertices
|
|
from game.camera import Camera
|
|
from game.environment import Environment
|
|
from game.inputs import InputFloat
|
|
from game.batch import Batch
|
|
from game.entity import Entity
|
|
from game.scene import Node, SceneNode, TextureNode, ShaderNode, InputNode, DrawNode, FuncNode
|
|
from game import sea
|
|
|
|
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
|
|
|
|
class PerfNode(Node):
|
|
__slots__ = '_count', '_min', '_max', '_total', '_name'
|
|
|
|
def __init__(self, name, *subnodes):
|
|
Node.__init__(self, *subnodes)
|
|
self._count = None
|
|
self._min = 10000.0
|
|
self._max = 0.0
|
|
self._total = 0.0
|
|
self._name = name
|
|
|
|
def __del__(self):
|
|
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)")
|
|
|
|
def draw(self, time):
|
|
begin = thread_time()
|
|
Node.draw(self, time)
|
|
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
|
|
|
|
class TestEntity(Entity):
|
|
__slots__ = 'translation', 'orientation', 'spawn_translation', 'spawn_orientation'
|
|
|
|
def __init__(self, batch, model, translation, orientation):
|
|
Entity.__init__(self, batch, model, translation, orientation)
|
|
self.translation = batch.translation[self.index]
|
|
self.orientation = batch.orientation[self.index]
|
|
self.spawn_translation = translation
|
|
self.spawn_orientation = orientation
|
|
|
|
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
|
|
camera.set_view(camera_yaw % tau, camera_pitch % tau, camera_distance)
|
|
environment.from_sun(camera.view, sun_direction, sun_power)
|
|
|
|
def update_tests(time, blob, cube, clouds):
|
|
rotation = mat3()
|
|
mat3_rotation(rotation, vec3_up, (time.current * 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, (time.current * 0.43) % tau)
|
|
mat3_rotation(clouds.orientation, vec3_up, (time.current * -0.037) % tau)
|
|
|
|
def update_sea(time, camera, sea_phase):
|
|
camera.to_km()
|
|
sea_phase.update((time.current * 0.023) % 1.0)
|
|
|
|
def create_scene(keyboard, mouse):
|
|
tiles_shader = Shader('tiles', 'common')
|
|
tests_shader = Shader('tests', 'common')
|
|
sea_shader = Shader('sea')
|
|
|
|
camera = Camera()
|
|
camera.set_projection(proj_hfov, proj_ratio, proj_near_z, proj_far_z)
|
|
environment = Environment()
|
|
sea_phase = InputFloat(sea_shader.u_sea_phase, 0.0)
|
|
|
|
print("Generating terrain...")
|
|
generated = Generator(256)
|
|
|
|
heightmap = Texture(
|
|
TEXTURE_FORMAT_FLOAT_32, 256, 256, 0, TEXTURE_FLAG_MIN_LINEAR | TEXTURE_FLAG_MAG_LINEAR,
|
|
generated.packed_heights)
|
|
normalmap = Texture(
|
|
TEXTURE_FORMAT_RGB10_A2, 256, 256, 0, TEXTURE_FLAG_MIN_LINEAR | TEXTURE_FLAG_MAG_LINEAR,
|
|
generated.packed_normals)
|
|
|
|
print("Loading resources...")
|
|
archive = Archive.load('data/rk_island.rkar')
|
|
|
|
print("Building tiles...")
|
|
tiles_texture = Texture(*archive.get_texture('tiles'))
|
|
tiles_vertices = 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')
|
|
tiles_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
|
|
tiles_batch.spawn(model, vec3(float(((mx - 128) * 8) + 4), float(((127 - my) * 8) + 4), 0.0))
|
|
|
|
tests_texture = Texture(*archive.get_texture('tests'))
|
|
tests_vertices = 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_forward = math.vec3_normalize((sun_direction[0], sun_direction[1], 0.0))
|
|
blob_right = math.vec3_cross(blob_forward, math.vec3_up)
|
|
blob_orientation = mat3(vec3(*blob_right), vec3(*blob_forward), vec3_up)
|
|
blob = TestEntity(tests_batch, blob_model, vec3(-100.0, -500.0, 0.0), blob_orientation)
|
|
cube = TestEntity(tests_batch, cube_model, vec3(100.0, -500.0, 0.0), mat3_identity)
|
|
clouds = TestEntity(tests_batch, clouds_model, vec3(0.0, 0.0, 32.0), mat3_identity)
|
|
|
|
sea_polar_textures = sea.load_polar_textures(('data/sea_bump1.png', 'data/sea_bump2.png'))
|
|
sea_detail_texture = sea.load_detail_texture('data/sea_bump.png')
|
|
sea_triangles = sea.sea_triangles(64, proj_far_z - 0.1, proj_ratio)
|
|
|
|
return SceneNode(
|
|
PerfNode('frame',
|
|
TextureNode({1: heightmap, 2: normalmap},
|
|
FuncNode(update_camera, (mouse, camera, environment)),
|
|
TextureNode({0: tiles_texture},
|
|
ShaderNode(tiles_shader,
|
|
InputNode(tiles_shader, camera, environment),
|
|
PerfNode('tiles_batch',
|
|
DrawNode(tiles_batch)))),
|
|
FuncNode(update_tests, (blob, cube, clouds)),
|
|
TextureNode({0: tests_texture},
|
|
ShaderNode(tests_shader,
|
|
InputNode(tests_shader, camera, environment),
|
|
PerfNode('tests_batch',
|
|
DrawNode(tests_batch))))),
|
|
FuncNode(update_sea, (camera, sea_phase)),
|
|
TextureNode({0: sea_polar_textures, 1: sea_detail_texture},
|
|
ShaderNode(sea_shader,
|
|
InputNode(sea_shader, camera, environment, sea_phase),
|
|
PerfNode('sea_triangles',
|
|
DrawNode(sea_triangles))))))
|
|
|
|
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")
|
|
time = Time()
|
|
try:
|
|
while not keyboard.quit:
|
|
events.update()
|
|
time.update()
|
|
clear_buffer(True, True, True)
|
|
scene.draw(time)
|
|
swap_buffers(display)
|
|
except KeyboardInterrupt:
|
|
pass
|
|
|
|
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)
|
|
print("Quitting...")
|
|
render_terminate()
|
|
destroy_display(display)
|