Compare commits
	
		
			12 Commits
		
	
	
		
			9fff666ebf
			...
			9131523ae7
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
						
						
							
						
						9131523ae7
	
				 | 
					
					
						|||
| 
						
						
							
						
						3621a340f1
	
				 | 
					
					
						|||
| 
						
						
							
						
						8e49c16929
	
				 | 
					
					
						|||
| 
						
						
							
						
						7c2d3d81ae
	
				 | 
					
					
						|||
| 
						
						
							
						
						dd786ef2b4
	
				 | 
					
					
						|||
| 
						
						
							
						
						acc2e4cd28
	
				 | 
					
					
						|||
| 
						
						
							
						
						af9365c6e0
	
				 | 
					
					
						|||
| 
						
						
							
						
						513d79a67c
	
				 | 
					
					
						|||
| 
						
						
							
						
						f66c6ea6e9
	
				 | 
					
					
						|||
| 
						
						
							
						
						9e77e6680b
	
				 | 
					
					
						|||
| 
						
						
							
						
						f4bfb7fee0
	
				 | 
					
					
						|||
| 
						
						
							
						
						6485da7d67
	
				 | 
					
					
						
							
								
								
									
										2
									
								
								engine
									
									
									
									
									
								
							
							
								
								
								
								
								
							
						
						
									
										2
									
								
								engine
									
									
									
									
									
								
							 Submodule engine updated: ae3333e22e...beca8798bf
									
								
							@ -19,14 +19,15 @@ from engine import (vec3, mat3, buffer,
 | 
				
			|||||||
    INSTANCE_FLAG_SPAWNED, BATCH_MAX_SIZE, param_type, params_format, create_batch, draw_batch, destroy_batch)
 | 
					    INSTANCE_FLAG_SPAWNED, BATCH_MAX_SIZE, param_type, params_format, create_batch, draw_batch, destroy_batch)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class Batch:
 | 
					class Batch:
 | 
				
			||||||
    __slots__ = '_batch', 'size', 'max_size', 'flags', 'meshes', 'params', '_params', '__dict__'
 | 
					    __slots__ = '_batch', 'vertices', 'size', 'max_size', 'flags', 'meshes', 'params', '_params', '__dict__'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def __init__(self, vertices, max_size, max_meshes, **params_decls):
 | 
					    def __init__(self, vertices, max_size, max_meshes, **params_decls):
 | 
				
			||||||
        assert max_size <= BATCH_MAX_SIZE
 | 
					        assert max_size <= BATCH_MAX_SIZE
 | 
				
			||||||
        nparams = len(params_decls)
 | 
					        nparams = len(params_decls)
 | 
				
			||||||
        names = params_decls.keys()
 | 
					        names = params_decls.keys()
 | 
				
			||||||
        formats = params_decls.values()
 | 
					        formats = params_decls.values()
 | 
				
			||||||
        self._batch = create_batch(vertices, max_size, max_meshes, params_format(*formats))
 | 
					        self._batch = create_batch(vertices._vertices, max_size, max_meshes, params_format(*formats))
 | 
				
			||||||
 | 
					        self.vertices = vertices
 | 
				
			||||||
        self.size = 0
 | 
					        self.size = 0
 | 
				
			||||||
        self.max_size = max_size
 | 
					        self.max_size = max_size
 | 
				
			||||||
        self.flags = buffer(c_ubyte, max_size)
 | 
					        self.flags = buffer(c_ubyte, max_size)
 | 
				
			||||||
@ -39,12 +40,12 @@ class Batch:
 | 
				
			|||||||
    def __del__(self):
 | 
					    def __del__(self):
 | 
				
			||||||
        destroy_batch(self._batch)
 | 
					        destroy_batch(self._batch)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def append(self, flags, mesh, params):
 | 
					    def spawn(self, model, *params):
 | 
				
			||||||
        assert len(params) == len(self.params)
 | 
					        assert len(params) == len(self.params)
 | 
				
			||||||
        index = self.size
 | 
					        index = self.size
 | 
				
			||||||
        assert index < self.max_size
 | 
					        assert index < self.max_size
 | 
				
			||||||
        self.flags[index] = flags | INSTANCE_FLAG_SPAWNED
 | 
					        self.flags[index] = model.flags | INSTANCE_FLAG_SPAWNED
 | 
				
			||||||
        self.meshes[index] = mesh
 | 
					        self.meshes[index] = model.mesh
 | 
				
			||||||
        for self_params, param in zip(self.params, params):
 | 
					        for self_params, param in zip(self.params, params):
 | 
				
			||||||
            self_params[index] = param
 | 
					            self_params[index] = param
 | 
				
			||||||
        self.size += 1
 | 
					        self.size += 1
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										20
									
								
								game/entity.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										20
									
								
								game/entity.py
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,20 @@
 | 
				
			|||||||
 | 
					# 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/>.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class Entity:
 | 
				
			||||||
 | 
					    __slots__ = 'index'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def __init__(self, batch, model, *params):
 | 
				
			||||||
 | 
					        self.index = batch.spawn(model, *params)
 | 
				
			||||||
							
								
								
									
										299
									
								
								game/game.py
									
									
									
									
									
								
							
							
						
						
									
										299
									
								
								game/game.py
									
									
									
									
									
								
							@ -13,24 +13,28 @@
 | 
				
			|||||||
# You should have received a copy of the GNU Affero General Public License
 | 
					# 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/>.
 | 
					# along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import time
 | 
					from time import thread_time
 | 
				
			||||||
from math import pi, tau, dist
 | 
					from math import pi, tau, dist
 | 
				
			||||||
from ctypes import Structure
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
from engine import *
 | 
					from engine import *
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from game import math
 | 
					from game import math
 | 
				
			||||||
from game import triangles
 | 
					from game.time import Time
 | 
				
			||||||
from game import sea
 | 
					 | 
				
			||||||
from game.shader import Shader
 | 
					 | 
				
			||||||
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.events import Events
 | 
				
			||||||
from game.mouse import Mouse
 | 
					from game.mouse import Mouse
 | 
				
			||||||
from game.keyboard import Keyboard
 | 
					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_hfov = pi * 0.25
 | 
				
			||||||
proj_ratio = 16.0 / 9.0
 | 
					proj_ratio = 16.0 / 9.0
 | 
				
			||||||
@ -40,36 +44,89 @@ proj_far_z = 3000.0
 | 
				
			|||||||
sun_direction = math.vec3_normalize((1.0, 0.0, 0.5))
 | 
					sun_direction = math.vec3_normalize((1.0, 0.0, 0.5))
 | 
				
			||||||
sun_power = 1.0
 | 
					sun_power = 1.0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def main():
 | 
					class PerfNode(Node):
 | 
				
			||||||
    print("Generating terrain...")
 | 
					    __slots__ = '_count', '_min', '_max', '_total', '_name'
 | 
				
			||||||
    gen_begin = time.thread_time()
 | 
					 | 
				
			||||||
    generated = Generator(256)
 | 
					 | 
				
			||||||
    gen_end = time.thread_time()
 | 
					 | 
				
			||||||
    print("Done: ", round(gen_end - gen_begin, 2), "seconds")
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    print("Initializing...")
 | 
					    def __init__(self, name, *subnodes):
 | 
				
			||||||
    display = create_display(b'RK Island - Drag to rotate, wheel to zoom, q to quit', 1600, 900)
 | 
					        Node.__init__(self, *subnodes)
 | 
				
			||||||
    events = Events(display)
 | 
					        self._count = None
 | 
				
			||||||
    mouse = Mouse(events, wheel = 60, wheel_min = 20)
 | 
					        self._min = 10000.0
 | 
				
			||||||
    keyboard = Keyboard(events)
 | 
					        self._max = 0.0
 | 
				
			||||||
    render_initialize(__debug__)
 | 
					        self._total = 0.0
 | 
				
			||||||
    terrain_shader = Shader('terrain', 'common')
 | 
					        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')
 | 
					    tests_shader = Shader('tests', 'common')
 | 
				
			||||||
    sky_shader = Shader('sky')
 | 
					    sea_shader = Shader('sea')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    heightmap = create_texture(
 | 
					    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,
 | 
					        TEXTURE_FORMAT_FLOAT_32, 256, 256, 0, TEXTURE_FLAG_MIN_LINEAR | TEXTURE_FLAG_MAG_LINEAR,
 | 
				
			||||||
        generated.packed_heights)
 | 
					        generated.packed_heights)
 | 
				
			||||||
    normalmap = create_texture(
 | 
					    normalmap = Texture(
 | 
				
			||||||
        TEXTURE_FORMAT_RGB10_A2, 256, 256, 0, TEXTURE_FLAG_MIN_LINEAR | TEXTURE_FLAG_MAG_LINEAR,
 | 
					        TEXTURE_FORMAT_RGB10_A2, 256, 256, 0, TEXTURE_FLAG_MIN_LINEAR | TEXTURE_FLAG_MAG_LINEAR,
 | 
				
			||||||
        generated.packed_normals)
 | 
					        generated.packed_normals)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    print("Loading resources...")
 | 
					    print("Loading resources...")
 | 
				
			||||||
    archive = RuntimeArchive.load('data/rk_island.rkar')
 | 
					    archive = Archive.load('data/rk_island.rkar')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    print("Building tiles...")
 | 
					    print("Building tiles...")
 | 
				
			||||||
    tiles_texture = archive.get_texture('tiles')
 | 
					    tiles_texture = Texture(*archive.get_texture('tiles'))
 | 
				
			||||||
    tiles_vertices = archive.get_vertices('tiles')
 | 
					    tiles_vertices = Vertices(*archive.get_vertices('tiles'))
 | 
				
			||||||
    water_model = archive.get_model('water')
 | 
					    water_model = archive.get_model('water')
 | 
				
			||||||
    sand_model = archive.get_model('sand')
 | 
					    sand_model = archive.get_model('sand')
 | 
				
			||||||
    grass_model = archive.get_model('grass')
 | 
					    grass_model = archive.get_model('grass')
 | 
				
			||||||
@ -77,7 +134,7 @@ def main():
 | 
				
			|||||||
    rock_model = archive.get_model('rock')
 | 
					    rock_model = archive.get_model('rock')
 | 
				
			||||||
    mud_model = archive.get_model('mud')
 | 
					    mud_model = archive.get_model('mud')
 | 
				
			||||||
    lava_model = archive.get_model('lava')
 | 
					    lava_model = archive.get_model('lava')
 | 
				
			||||||
    terrain_batch = Batch(tiles_vertices, generated.size ** 2, 8, translation = PARAM_FORMAT_VEC3_SHORT)
 | 
					    tiles_batch = Batch(tiles_vertices, generated.size ** 2, 8, translation = PARAM_FORMAT_VEC3_SHORT)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    #TODO: generator & for real
 | 
					    #TODO: generator & for real
 | 
				
			||||||
    vc = generated.volcano_c
 | 
					    vc = generated.volcano_c
 | 
				
			||||||
@ -108,165 +165,71 @@ def main():
 | 
				
			|||||||
            model = mud_model
 | 
					            model = mud_model
 | 
				
			||||||
        else:
 | 
					        else:
 | 
				
			||||||
            model = rock_model
 | 
					            model = rock_model
 | 
				
			||||||
        model.spawn(terrain_batch, vec3(float(((mx - 128) * 8) + 4), float(((127 - my) * 8) + 4), 0.0))
 | 
					        tiles_batch.spawn(model, vec3(float(((mx - 128) * 8) + 4), float(((127 - my) * 8) + 4), 0.0))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    tests_texture = archive.get_texture('tests')
 | 
					    tests_texture = Texture(*archive.get_texture('tests'))
 | 
				
			||||||
    tests_vertices = archive.get_vertices('tests')
 | 
					    tests_vertices = Vertices(*archive.get_vertices('tests'))
 | 
				
			||||||
    blob_model = archive.get_model('blob')
 | 
					    blob_model = archive.get_model('blob')
 | 
				
			||||||
    cube_model = archive.get_model('cube')
 | 
					    cube_model = archive.get_model('cube')
 | 
				
			||||||
    clouds_model = archive.get_model('clouds')
 | 
					    clouds_model = archive.get_model('clouds')
 | 
				
			||||||
    tests_batch = Batch(tests_vertices, 3, 3,
 | 
					    tests_batch = Batch(tests_vertices, 3, 3,
 | 
				
			||||||
        translation = PARAM_FORMAT_VEC3_FLOAT,
 | 
					        translation = PARAM_FORMAT_VEC3_FLOAT,
 | 
				
			||||||
        orientation = PARAM_FORMAT_MAT3_INT10 | PARAM_FORMAT_NORMALIZE)
 | 
					        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_forward = math.vec3_normalize((sun_direction[0], sun_direction[1], 0.0))
 | 
				
			||||||
    blob_right = math.vec3_cross(blob_forward, math.vec3_up)
 | 
					    blob_right = math.vec3_cross(blob_forward, math.vec3_up)
 | 
				
			||||||
    blob_id = blob_model.spawn(tests_batch,
 | 
					    blob_orientation = mat3(vec3(*blob_right), vec3(*blob_forward), vec3_up)
 | 
				
			||||||
        blob_spawn_translation, 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_id = cube_model.spawn(tests_batch, cube_spawn_translation, mat3_identity)
 | 
					    cube = TestEntity(tests_batch, cube_model, vec3(100.0, -500.0, 0.0), mat3_identity)
 | 
				
			||||||
    clouds_id = clouds_model.spawn(tests_batch, vec3(0.0, 0.0, 32.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_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_detail_texture = sea.load_detail_texture('data/sea_bump.png')
 | 
				
			||||||
    sky_triangles = create_triangles(triangles.sky_triangles(64, proj_far_z - 0.1, proj_ratio))
 | 
					    sea_triangles = sea.sea_triangles(64, proj_far_z - 0.1, proj_ratio)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    camera = Camera()
 | 
					    return SceneNode(
 | 
				
			||||||
    camera.set_projection(proj_hfov, proj_ratio, proj_near_z, proj_far_z)
 | 
					        PerfNode('frame',
 | 
				
			||||||
    environment = Environment()
 | 
					            TextureNode({1: heightmap, 2: normalmap},
 | 
				
			||||||
 | 
					                FuncNode(update_camera, (mouse, camera, environment)),
 | 
				
			||||||
    blob_translation = tests_batch.translation[blob_id]
 | 
					                TextureNode({0: tiles_texture},
 | 
				
			||||||
    cube_translation = tests_batch.translation[cube_id]
 | 
					                    ShaderNode(tiles_shader,
 | 
				
			||||||
    cube_orientation = tests_batch.orientation[cube_id]
 | 
					                        InputNode(tiles_shader, camera, environment),
 | 
				
			||||||
    clouds_orientation = tests_batch.orientation[clouds_id]
 | 
					                        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")
 | 
					    print("Running... Ctrl+c to quit")
 | 
				
			||||||
    start_time = time.monotonic()
 | 
					    time = Time()
 | 
				
			||||||
    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:
 | 
					    try:
 | 
				
			||||||
        while True:
 | 
					        while not keyboard.quit:
 | 
				
			||||||
            current_time = time.monotonic() - start_time
 | 
					 | 
				
			||||||
            frame_begin = time.thread_time()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            events.update()
 | 
					            events.update()
 | 
				
			||||||
            if keyboard.quit:
 | 
					            time.update()
 | 
				
			||||||
                break
 | 
					            clear_buffer(True, True, True)
 | 
				
			||||||
 | 
					            scene.draw(time)
 | 
				
			||||||
            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)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            terrain_shader.select()
 | 
					 | 
				
			||||||
            camera.set_inputs(terrain_shader)
 | 
					 | 
				
			||||||
            environment.set_inputs(terrain_shader)
 | 
					 | 
				
			||||||
            select_texture(0, tiles_texture)
 | 
					 | 
				
			||||||
            select_texture(1, heightmap)
 | 
					 | 
				
			||||||
            select_texture(2, normalmap)
 | 
					 | 
				
			||||||
            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)
 | 
					 | 
				
			||||||
            terrain_shader.unselect()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            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)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            tests_shader.select()
 | 
					 | 
				
			||||||
            camera.set_inputs(tests_shader)
 | 
					 | 
				
			||||||
            environment.set_inputs(tests_shader)
 | 
					 | 
				
			||||||
            select_texture(0, tests_texture)
 | 
					 | 
				
			||||||
            select_texture(1, heightmap)
 | 
					 | 
				
			||||||
            select_texture(2, normalmap)
 | 
					 | 
				
			||||||
            tests_batch.draw()
 | 
					 | 
				
			||||||
            unselect_texture(0, tests_texture)
 | 
					 | 
				
			||||||
            unselect_texture(1, heightmap)
 | 
					 | 
				
			||||||
            unselect_texture(2, normalmap)
 | 
					 | 
				
			||||||
            tests_shader.unselect()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            camera.to_km()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            sky_shader.select()
 | 
					 | 
				
			||||||
            camera.set_inputs(sky_shader)
 | 
					 | 
				
			||||||
            environment.set_inputs(sky_shader)
 | 
					 | 
				
			||||||
            set_input_float(sky_shader.u_sea_phase, (current_time * 0.023) % 1.0)
 | 
					 | 
				
			||||||
            select_texture(0, sea_polar_textures)
 | 
					 | 
				
			||||||
            select_texture(1, sea_detail_texture)
 | 
					 | 
				
			||||||
            draw_triangles(sky_triangles)
 | 
					 | 
				
			||||||
            unselect_texture(0, sea_polar_textures)
 | 
					 | 
				
			||||||
            unselect_texture(1, sea_detail_texture)
 | 
					 | 
				
			||||||
            sky_shader.unselect()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            frame_end = time.thread_time()
 | 
					 | 
				
			||||||
            end_frame()
 | 
					 | 
				
			||||||
            swap_buffers(display)
 | 
					            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:
 | 
					    except KeyboardInterrupt:
 | 
				
			||||||
        pass
 | 
					        pass
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    print("\rDraw  *", perf_count,
 | 
					def main():
 | 
				
			||||||
        ": min =", round(draw_min * 1000.0, 2),
 | 
					    print("Initializing...")
 | 
				
			||||||
        ", max =", round(draw_max * 1000.0, 2),
 | 
					    display = create_display(b'RK Island - Drag to rotate, wheel to zoom, q to quit', 1600, 900)
 | 
				
			||||||
        ", avg =", round((draw_avg / perf_count) * 1000.0, 2), "ms")
 | 
					    render_initialize(__debug__)
 | 
				
			||||||
 | 
					    loop(display)
 | 
				
			||||||
    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...")
 | 
					    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()
 | 
					 | 
				
			||||||
    del terrain_shader
 | 
					 | 
				
			||||||
    del tests_shader
 | 
					 | 
				
			||||||
    del sky_shader
 | 
					 | 
				
			||||||
    render_terminate()
 | 
					    render_terminate()
 | 
				
			||||||
    del events
 | 
					 | 
				
			||||||
    destroy_display(display)
 | 
					    destroy_display(display)
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										29
									
								
								game/inputs.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										29
									
								
								game/inputs.py
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,29 @@
 | 
				
			|||||||
 | 
					# 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 set_input_float
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class InputFloat:
 | 
				
			||||||
 | 
					    __slots__ = '_input', 'value'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def __init__(self, input, value):
 | 
				
			||||||
 | 
					        self._input = input
 | 
				
			||||||
 | 
					        self.value = value
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def update(self, value):
 | 
				
			||||||
 | 
					        self.value = value
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def set_inputs(self, shader):
 | 
				
			||||||
 | 
					        set_input_float(self._input, self.value)
 | 
				
			||||||
@ -16,9 +16,7 @@
 | 
				
			|||||||
import struct
 | 
					import struct
 | 
				
			||||||
from array import array
 | 
					from array import array
 | 
				
			||||||
from pathlib import Path
 | 
					from pathlib import Path
 | 
				
			||||||
 | 
					 | 
				
			||||||
import png
 | 
					import png
 | 
				
			||||||
 | 
					 | 
				
			||||||
import engine
 | 
					import engine
 | 
				
			||||||
 | 
					
 | 
				
			||||||
VERTEX_SIZE = 20
 | 
					VERTEX_SIZE = 20
 | 
				
			||||||
@ -116,6 +114,14 @@ class TextureData:
 | 
				
			|||||||
        self.flags = flags
 | 
					        self.flags = flags
 | 
				
			||||||
        self.pixels = pixels
 | 
					        self.pixels = pixels
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def __iter__(self):
 | 
				
			||||||
 | 
					        yield self.format
 | 
				
			||||||
 | 
					        yield self.width
 | 
				
			||||||
 | 
					        yield self.height
 | 
				
			||||||
 | 
					        yield self.nlevels
 | 
				
			||||||
 | 
					        yield self.flags
 | 
				
			||||||
 | 
					        yield self.pixels
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @classmethod
 | 
					    @classmethod
 | 
				
			||||||
    def from_archive(cls, file):
 | 
					    def from_archive(cls, file):
 | 
				
			||||||
        _read_magic(file, b'TX')
 | 
					        _read_magic(file, b'TX')
 | 
				
			||||||
@ -132,9 +138,6 @@ class TextureData:
 | 
				
			|||||||
        _write_struct(file, 'IIIII', (self.format, self.width, self.height, self.nlevels, self.flags))
 | 
					        _write_struct(file, 'IIIII', (self.format, self.width, self.height, self.nlevels, self.flags))
 | 
				
			||||||
        _write_blob(file, self.pixels)
 | 
					        _write_blob(file, self.pixels)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def create_texture(data):
 | 
					 | 
				
			||||||
    return engine.create_texture(data.format, data.width, data.height, data.nlevels, data.flags, data.pixels)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
class VerticesData:
 | 
					class VerticesData:
 | 
				
			||||||
    __slots__ = 'name', 'vertices', 'indices'
 | 
					    __slots__ = 'name', 'vertices', 'indices'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -145,6 +148,12 @@ class VerticesData:
 | 
				
			|||||||
        self.vertices = vertices
 | 
					        self.vertices = vertices
 | 
				
			||||||
        self.indices = indices
 | 
					        self.indices = indices
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def __iter__(self):
 | 
				
			||||||
 | 
					        yield VERTEX_FORMAT
 | 
				
			||||||
 | 
					        yield len(self.vertices) // VERTEX_SIZE
 | 
				
			||||||
 | 
					        yield self.vertices
 | 
				
			||||||
 | 
					        yield self.indices
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @classmethod
 | 
					    @classmethod
 | 
				
			||||||
    def from_archive(cls, file):
 | 
					    def from_archive(cls, file):
 | 
				
			||||||
        _read_magic(file, b'VT')
 | 
					        _read_magic(file, b'VT')
 | 
				
			||||||
@ -161,9 +170,6 @@ class VerticesData:
 | 
				
			|||||||
        _write_array(file, self.vertices)
 | 
					        _write_array(file, self.vertices)
 | 
				
			||||||
        _write_array(file, self.indices)
 | 
					        _write_array(file, self.indices)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def create_vertices(data):
 | 
					 | 
				
			||||||
    return engine.create_vertices(VERTEX_FORMAT, len(data.vertices) // VERTEX_SIZE, data.vertices, data.indices)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
class ModelData:
 | 
					class ModelData:
 | 
				
			||||||
    __slots__ = 'name', 'flags', 'mesh'
 | 
					    __slots__ = 'name', 'flags', 'mesh'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -184,19 +190,6 @@ class ModelData:
 | 
				
			|||||||
        _write_string(file, self.name)
 | 
					        _write_string(file, self.name)
 | 
				
			||||||
        _write_struct(file, 'II', (self.flags, self.mesh))
 | 
					        _write_struct(file, 'II', (self.flags, self.mesh))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class Model:
 | 
					 | 
				
			||||||
    __slots__ = 'flags', 'mesh'
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def __init__(self, flags, mesh):
 | 
					 | 
				
			||||||
        self.flags = flags
 | 
					 | 
				
			||||||
        self.mesh = mesh
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def spawn(self, batch, *params):
 | 
					 | 
				
			||||||
        return batch.append(self.flags, self.mesh, params)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
def create_model(data):
 | 
					 | 
				
			||||||
    return Model(data.flags, data.mesh)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
class Archive:
 | 
					class Archive:
 | 
				
			||||||
    __slots__ = 'textures_db', 'vertices_db', 'models_db'
 | 
					    __slots__ = 'textures_db', 'vertices_db', 'models_db'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -205,15 +198,6 @@ class Archive:
 | 
				
			|||||||
        self.vertices_db = vertices_db or {}
 | 
					        self.vertices_db = vertices_db or {}
 | 
				
			||||||
        self.models_db = models_db or {}
 | 
					        self.models_db = models_db or {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def destroy(self):
 | 
					 | 
				
			||||||
        for vertices in self.vertices_db.values():
 | 
					 | 
				
			||||||
            engine.destroy_vertices(vertices)
 | 
					 | 
				
			||||||
        for texture in self.textures_db.values():
 | 
					 | 
				
			||||||
            engine.destroy_texture(texture)
 | 
					 | 
				
			||||||
        self.textures_db.clear()
 | 
					 | 
				
			||||||
        self.vertices_db.clear()
 | 
					 | 
				
			||||||
        self.models_db.clear()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def get_texture(self, name):
 | 
					    def get_texture(self, name):
 | 
				
			||||||
        return self.textures_db[name]
 | 
					        return self.textures_db[name]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -223,18 +207,6 @@ class Archive:
 | 
				
			|||||||
    def get_model(self, name):
 | 
					    def get_model(self, name):
 | 
				
			||||||
        return self.models_db[name]
 | 
					        return self.models_db[name]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @classmethod
 | 
					 | 
				
			||||||
    def _new_texture(cls, data):
 | 
					 | 
				
			||||||
        return data
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    @classmethod
 | 
					 | 
				
			||||||
    def _new_vertices(cls, data):
 | 
					 | 
				
			||||||
        return data
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    @classmethod
 | 
					 | 
				
			||||||
    def _new_model(cls, data):
 | 
					 | 
				
			||||||
        return data
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    @classmethod
 | 
					    @classmethod
 | 
				
			||||||
    def from_archive(cls, file):
 | 
					    def from_archive(cls, file):
 | 
				
			||||||
        textures_db = {}
 | 
					        textures_db = {}
 | 
				
			||||||
@ -244,22 +216,15 @@ class Archive:
 | 
				
			|||||||
        ntextures, nvertices, nmodels = _read_struct(file, 'III')
 | 
					        ntextures, nvertices, nmodels = _read_struct(file, 'III')
 | 
				
			||||||
        for _ in range(ntextures):
 | 
					        for _ in range(ntextures):
 | 
				
			||||||
            data = TextureData.from_archive(file)
 | 
					            data = TextureData.from_archive(file)
 | 
				
			||||||
            textures_db[data.name] = cls._new_texture(data)
 | 
					            textures_db[data.name] = data
 | 
				
			||||||
        for _ in range(nvertices):
 | 
					        for _ in range(nvertices):
 | 
				
			||||||
            data = VerticesData.from_archive(file)
 | 
					            data = VerticesData.from_archive(file)
 | 
				
			||||||
            vertices_db[data.name] = cls._new_vertices(data)
 | 
					            vertices_db[data.name] = data
 | 
				
			||||||
        for _ in range(nmodels):
 | 
					        for _ in range(nmodels):
 | 
				
			||||||
            data = ModelData.from_archive(file)
 | 
					            data = ModelData.from_archive(file)
 | 
				
			||||||
            models_db[data.name] = cls._new_model(data)
 | 
					            models_db[data.name] = data
 | 
				
			||||||
        return cls(textures_db, vertices_db, models_db)
 | 
					        return cls(textures_db, vertices_db, models_db)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @classmethod
 | 
					 | 
				
			||||||
    def load(cls, filename):
 | 
					 | 
				
			||||||
        file = open(Path(filename), 'rb')
 | 
					 | 
				
			||||||
        archive = cls.from_archive(file)
 | 
					 | 
				
			||||||
        file.close()
 | 
					 | 
				
			||||||
        return archive
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def to_archive(self, file):
 | 
					    def to_archive(self, file):
 | 
				
			||||||
        _write_magic(file, b'RKAR')
 | 
					        _write_magic(file, b'RKAR')
 | 
				
			||||||
        _write_struct(file, 'III', (len(self.textures_db), len(self.vertices_db), len(self.models_db)))
 | 
					        _write_struct(file, 'III', (len(self.textures_db), len(self.vertices_db), len(self.models_db)))
 | 
				
			||||||
@ -270,20 +235,14 @@ class Archive:
 | 
				
			|||||||
        for _, data in self.models_db.items():
 | 
					        for _, data in self.models_db.items():
 | 
				
			||||||
            data.to_archive(file)
 | 
					            data.to_archive(file)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @classmethod
 | 
				
			||||||
 | 
					    def load(cls, filename):
 | 
				
			||||||
 | 
					        file = open(Path(filename), 'rb')
 | 
				
			||||||
 | 
					        archive = cls.from_archive(file)
 | 
				
			||||||
 | 
					        file.close()
 | 
				
			||||||
 | 
					        return archive
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def save(self, filename):
 | 
					    def save(self, filename):
 | 
				
			||||||
        file = open(Path(filename), 'wb')
 | 
					        file = open(Path(filename), 'wb')
 | 
				
			||||||
        self.to_archive(file)
 | 
					        self.to_archive(file)
 | 
				
			||||||
        file.close()
 | 
					        file.close()
 | 
				
			||||||
 | 
					 | 
				
			||||||
class RuntimeArchive(Archive):
 | 
					 | 
				
			||||||
    @classmethod
 | 
					 | 
				
			||||||
    def _new_texture(cls, data):
 | 
					 | 
				
			||||||
        return create_texture(data)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    @classmethod
 | 
					 | 
				
			||||||
    def _new_vertices(cls, data):
 | 
					 | 
				
			||||||
        return create_vertices(data)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    @classmethod
 | 
					 | 
				
			||||||
    def _new_model(cls, data):
 | 
					 | 
				
			||||||
        return create_model(data)
 | 
					 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										85
									
								
								game/scene.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										85
									
								
								game/scene.py
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,85 @@
 | 
				
			|||||||
 | 
					# 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/>.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class Node:
 | 
				
			||||||
 | 
					    __slots__ = '_subnodes'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def __init__(self, *subnodes):
 | 
				
			||||||
 | 
					        self._subnodes = subnodes
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def draw(self, time):
 | 
				
			||||||
 | 
					        for subnode in self._subnodes:
 | 
				
			||||||
 | 
					            subnode.draw(time)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class SceneNode(Node):
 | 
				
			||||||
 | 
					    pass
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class TextureNode(Node):
 | 
				
			||||||
 | 
					    __slots__ = '_textures'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def __init__(self, textures, *subnodes):
 | 
				
			||||||
 | 
					        Node.__init__(self, *subnodes)
 | 
				
			||||||
 | 
					        self._textures = textures
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def draw(self, time):
 | 
				
			||||||
 | 
					        items = self._textures.items()
 | 
				
			||||||
 | 
					        for slot, texture in items:
 | 
				
			||||||
 | 
					            texture.select(slot)
 | 
				
			||||||
 | 
					        Node.draw(self, time)
 | 
				
			||||||
 | 
					        for slot, texture in items:
 | 
				
			||||||
 | 
					            texture.unselect(slot)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class ShaderNode(Node):
 | 
				
			||||||
 | 
					    __slots__ = '_shader'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def __init__(self, shader, *subnodes):
 | 
				
			||||||
 | 
					        Node.__init__(self, *subnodes)
 | 
				
			||||||
 | 
					        self._shader = shader
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def draw(self, time):
 | 
				
			||||||
 | 
					        self._shader.select()
 | 
				
			||||||
 | 
					        Node.draw(self, time)
 | 
				
			||||||
 | 
					        self._shader.unselect()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class InputNode:
 | 
				
			||||||
 | 
					    __slots__ = '_shader', '_inputs'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def __init__(self, shader, *inputs):
 | 
				
			||||||
 | 
					        self._shader = shader
 | 
				
			||||||
 | 
					        self._inputs = inputs
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def draw(self, time):
 | 
				
			||||||
 | 
					        shader = self._shader
 | 
				
			||||||
 | 
					        for input in self._inputs:
 | 
				
			||||||
 | 
					            input.set_inputs(shader)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class DrawNode:
 | 
				
			||||||
 | 
					    __slots__ = '_drawable'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def __init__(self, drawable):
 | 
				
			||||||
 | 
					        self._drawable = drawable
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def draw(self, time):
 | 
				
			||||||
 | 
					        self._drawable.draw()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class FuncNode:
 | 
				
			||||||
 | 
					    __slots__ = '_func', '_params'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def __init__(self, func, params):
 | 
				
			||||||
 | 
					        self._func = func
 | 
				
			||||||
 | 
					        self._params = params
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def draw(self, time):
 | 
				
			||||||
 | 
					        self._func(time, *self._params)
 | 
				
			||||||
							
								
								
									
										43
									
								
								game/sea.py
									
									
									
									
									
								
							
							
						
						
									
										43
									
								
								game/sea.py
									
									
									
									
									
								
							@ -13,14 +13,15 @@
 | 
				
			|||||||
# You should have received a copy of the GNU Affero General Public License
 | 
					# 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/>.
 | 
					# along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from itertools import product
 | 
					from itertools import chain, product
 | 
				
			||||||
from math import tau, cos, sin, copysign
 | 
					from math import tau, cos, sin, copysign
 | 
				
			||||||
from array import array
 | 
					from array import array
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from engine import (create_texture,
 | 
					from engine import TEXTURE_FORMAT_RGB10_A2, TEXTURE_FORMAT_TYPECODE, TEXTURE_FLAG_MIN_LINEAR, TEXTURE_FLAG_MAG_LINEAR
 | 
				
			||||||
    TEXTURE_FORMAT_RGB10_A2, TEXTURE_FORMAT_TYPECODE, TEXTURE_FLAG_MIN_LINEAR, TEXTURE_FLAG_MAG_LINEAR)
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
from game.math import vec3_scale, vec3_direction, vec3_cross, vec3_normal_rgb10a2
 | 
					from game.math import vec3_scale, vec3_direction, vec3_cross, vec3_normal_rgb10a2
 | 
				
			||||||
 | 
					from game.texture import Texture
 | 
				
			||||||
 | 
					from game.triangles import Triangles
 | 
				
			||||||
from game.resources import load_png
 | 
					from game.resources import load_png
 | 
				
			||||||
 | 
					
 | 
				
			||||||
_format = TEXTURE_FORMAT_RGB10_A2
 | 
					_format = TEXTURE_FORMAT_RGB10_A2
 | 
				
			||||||
@ -51,7 +52,7 @@ def load_polar_textures(paths, waves_height = 0.008):
 | 
				
			|||||||
        _width, _height, normals = load_texture(path)
 | 
					        _width, _height, normals = load_texture(path)
 | 
				
			||||||
        assert _width == width and _height == height
 | 
					        assert _width == width and _height == height
 | 
				
			||||||
        data.extend(list(map(_conv, normals)))
 | 
					        data.extend(list(map(_conv, normals)))
 | 
				
			||||||
    return create_texture(_format, width, height, len(paths), _flags, data)
 | 
					    return Texture(_format, width, height, len(paths), _flags, data)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def load_detail_texture(path, scale = 0.5, waves_height = 0.002):
 | 
					def load_detail_texture(path, scale = 0.5, waves_height = 0.002):
 | 
				
			||||||
    width, height, data = load_png(path)
 | 
					    width, height, data = load_png(path)
 | 
				
			||||||
@ -68,4 +69,36 @@ def load_detail_texture(path, scale = 0.5, waves_height = 0.002):
 | 
				
			|||||||
        return vec3_scale(n, copysign(1.0, n[2]))
 | 
					        return vec3_scale(n, copysign(1.0, n[2]))
 | 
				
			||||||
    normals = list(map(normal, product(range(height), range(width)), data))
 | 
					    normals = list(map(normal, product(range(height), range(width)), data))
 | 
				
			||||||
    data = array(_typecode, list(map(_conv, normals)))
 | 
					    data = array(_typecode, list(map(_conv, normals)))
 | 
				
			||||||
    return create_texture(_format, width, height, 0, _flags, data)
 | 
					    return Texture(_format, width, height, 0, _flags, data)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# TODO: with FOV
 | 
				
			||||||
 | 
					def sea_triangles(vsubdivs, distance, projection_ratio):
 | 
				
			||||||
 | 
					    assert vsubdivs > 0
 | 
				
			||||||
 | 
					    vertices = []
 | 
				
			||||||
 | 
					    hsubdivs = round(vsubdivs * projection_ratio)
 | 
				
			||||||
 | 
					    z = -distance
 | 
				
			||||||
 | 
					    width = distance * projection_ratio
 | 
				
			||||||
 | 
					    height = distance
 | 
				
			||||||
 | 
					    startx = width * -0.5
 | 
				
			||||||
 | 
					    starty = height * -0.5
 | 
				
			||||||
 | 
					    stepx = width / hsubdivs
 | 
				
			||||||
 | 
					    stepy = height / vsubdivs
 | 
				
			||||||
 | 
					    y1 = starty
 | 
				
			||||||
 | 
					    y2 = y1 + stepy
 | 
				
			||||||
 | 
					    for sy in range(vsubdivs):
 | 
				
			||||||
 | 
					        x1 = startx
 | 
				
			||||||
 | 
					        x2 = x1 + stepx
 | 
				
			||||||
 | 
					        for sx in range(hsubdivs):
 | 
				
			||||||
 | 
					            a = (x1, y2, z)
 | 
				
			||||||
 | 
					            b = (x2, y1, z)
 | 
				
			||||||
 | 
					            vertices.append((x1, y1, z))
 | 
				
			||||||
 | 
					            vertices.append(b)
 | 
				
			||||||
 | 
					            vertices.append(a)
 | 
				
			||||||
 | 
					            vertices.append((x2, y2, z))
 | 
				
			||||||
 | 
					            vertices.append(a)
 | 
				
			||||||
 | 
					            vertices.append(b)
 | 
				
			||||||
 | 
					            x1 = x2
 | 
				
			||||||
 | 
					            x2 += stepx
 | 
				
			||||||
 | 
					        y1 = y2
 | 
				
			||||||
 | 
					        y2 += stepy
 | 
				
			||||||
 | 
					    return Triangles(array('f', chain.from_iterable(vertices)))
 | 
				
			||||||
 | 
				
			|||||||
@ -15,7 +15,7 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
from pathlib import Path
 | 
					from pathlib import Path
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from engine import load_shader, resolve_input, select_shader, unselect_shader, destroy_shader
 | 
					from engine import create_shader, resolve_input, select_shader, unselect_shader, destroy_shader
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def _cleanup(line):
 | 
					def _cleanup(line):
 | 
				
			||||||
    return line.partition('//')[0].strip()
 | 
					    return line.partition('//')[0].strip()
 | 
				
			||||||
@ -70,7 +70,7 @@ class Shader:
 | 
				
			|||||||
        print("Loading fragment shader", frag_name)
 | 
					        print("Loading fragment shader", frag_name)
 | 
				
			||||||
        frag_lines = _load_source(path / (frag_name + '_opengles.frag'))
 | 
					        frag_lines = _load_source(path / (frag_name + '_opengles.frag'))
 | 
				
			||||||
        assert frag_lines
 | 
					        assert frag_lines
 | 
				
			||||||
        self._shader = load_shader(list(map(_convert, vert_lines)), list(map(_convert, frag_lines)))
 | 
					        self._shader = create_shader(list(map(_convert, vert_lines)), list(map(_convert, frag_lines)))
 | 
				
			||||||
        _parse(self, vert_lines, frag_lines)
 | 
					        _parse(self, vert_lines, frag_lines)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def __del__(self):
 | 
					    def __del__(self):
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										31
									
								
								game/texture.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										31
									
								
								game/texture.py
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,31 @@
 | 
				
			|||||||
 | 
					# 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 create_texture, select_texture, unselect_texture, destroy_texture
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class Texture:
 | 
				
			||||||
 | 
					    __slots__ = '_texture'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def __init__(self, format, width, height, nlevels, flags, pixels):
 | 
				
			||||||
 | 
					        self._texture = create_texture(format, width, height, nlevels, flags, pixels)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def __del__(self):
 | 
				
			||||||
 | 
					        destroy_texture(self._texture)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def select(self, slot):
 | 
				
			||||||
 | 
					        select_texture(slot, self._texture)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def unselect(self, slot):
 | 
				
			||||||
 | 
					        unselect_texture(slot, self._texture)
 | 
				
			||||||
							
								
								
									
										29
									
								
								game/time.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										29
									
								
								game/time.py
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,29 @@
 | 
				
			|||||||
 | 
					# 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 monotonic
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class Time:
 | 
				
			||||||
 | 
					    __slots__ = '_start', 'current', 'delta'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def __init__(self):
 | 
				
			||||||
 | 
					        self._start = monotonic()
 | 
				
			||||||
 | 
					        self.current = 0.0
 | 
				
			||||||
 | 
					        self.delta = 0.0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def update(self):
 | 
				
			||||||
 | 
					        current = monotonic() - self._start
 | 
				
			||||||
 | 
					        self.delta = current - self.current
 | 
				
			||||||
 | 
					        self.current = current
 | 
				
			||||||
@ -13,38 +13,16 @@
 | 
				
			|||||||
# You should have received a copy of the GNU Affero General Public License
 | 
					# 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/>.
 | 
					# along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from itertools import chain
 | 
					from engine import create_triangles, draw_triangles, destroy_triangles
 | 
				
			||||||
from array import array
 | 
					 | 
				
			||||||
from math import cos, sin
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
# TODO: with FOV
 | 
					class Triangles:
 | 
				
			||||||
def sky_triangles(vsubdivs, distance, projection_ratio):
 | 
					    __slots__ = '_triangles'
 | 
				
			||||||
    assert vsubdivs > 0
 | 
					
 | 
				
			||||||
    vertices = []
 | 
					    def __init__(self, triangles):
 | 
				
			||||||
    hsubdivs = round(vsubdivs * projection_ratio)
 | 
					        self._triangles = create_triangles(triangles)
 | 
				
			||||||
    z = -distance
 | 
					
 | 
				
			||||||
    width = distance * projection_ratio
 | 
					    def __del__(self):
 | 
				
			||||||
    height = distance
 | 
					        destroy_triangles(self._triangles)
 | 
				
			||||||
    startx = width * -0.5
 | 
					
 | 
				
			||||||
    starty = height * -0.5
 | 
					    def draw(self):
 | 
				
			||||||
    stepx = width / hsubdivs
 | 
					        draw_triangles(self._triangles)
 | 
				
			||||||
    stepy = height / vsubdivs
 | 
					 | 
				
			||||||
    y1 = starty
 | 
					 | 
				
			||||||
    y2 = y1 + stepy
 | 
					 | 
				
			||||||
    for sy in range(vsubdivs):
 | 
					 | 
				
			||||||
        x1 = startx
 | 
					 | 
				
			||||||
        x2 = x1 + stepx
 | 
					 | 
				
			||||||
        for sx in range(hsubdivs):
 | 
					 | 
				
			||||||
            a = (x1, y2, z)
 | 
					 | 
				
			||||||
            b = (x2, y1, z)
 | 
					 | 
				
			||||||
            vertices.append((x1, y1, z))
 | 
					 | 
				
			||||||
            vertices.append(b)
 | 
					 | 
				
			||||||
            vertices.append(a)
 | 
					 | 
				
			||||||
            vertices.append((x2, y2, z))
 | 
					 | 
				
			||||||
            vertices.append(a)
 | 
					 | 
				
			||||||
            vertices.append(b)
 | 
					 | 
				
			||||||
            x1 = x2
 | 
					 | 
				
			||||||
            x2 += stepx
 | 
					 | 
				
			||||||
        y1 = y2
 | 
					 | 
				
			||||||
        y2 += stepy
 | 
					 | 
				
			||||||
    return array('f', chain.from_iterable(vertices))
 | 
					 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										25
									
								
								game/vertices.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										25
									
								
								game/vertices.py
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,25 @@
 | 
				
			|||||||
 | 
					# 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 create_vertices, destroy_vertices
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class Vertices:
 | 
				
			||||||
 | 
					    __slots__ = '_vertices'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def __init__(self, format, nvertices, vertices, indices):
 | 
				
			||||||
 | 
					        self._vertices = create_vertices(format, nvertices, vertices, indices)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def __del__(self):
 | 
				
			||||||
 | 
					        destroy_vertices(self._vertices)
 | 
				
			||||||
		Reference in New Issue
	
	Block a user