# 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 ctypes from array import array from pathlib import Path _engine = ctypes.cdll.LoadLibrary(Path(__file__).parent / "engine.so") # types.hpp _handle = ctypes.c_void_p def _check_handle(result, func, arguments): assert result return result def _flag(x): return 1 << x def buffer(type, size): return (type * size)() def _void_addr(x): return x.buffer_info()[0] def _ubyte_addr(x): assert x.typecode == 'B' return x.buffer_info()[0] def _ushort_addr(x): assert x.typecode == 'H' return x.buffer_info()[0] def _uint_addr(x): assert x.typecode == 'I' return x.buffer_info()[0] def _float_addr(x): assert x.typecode == 'f' return x.buffer_info()[0] # math.hpp class vec3(ctypes.Structure): _fields_ = ('x', ctypes.c_float), ('y', ctypes.c_float), ('z', ctypes.c_float) def __iter__(self): yield self.x yield self.y yield self.z def set(self, x, y, z): self.x = x self.y = y self.z = z class vec4(ctypes.Structure): _fields_ = ('x', ctypes.c_float), ('y', ctypes.c_float), ('z', ctypes.c_float), ('w', ctypes.c_float) def __iter__(self): yield self.x yield self.y yield self.z yield self.w def set(self, x, y, z, w): self.x = x self.y = y self.z = z self.w = w class mat3(ctypes.Structure): _fields_ = ('right', vec3), ('forward', vec3), ('up', vec3) def __iter__(self): yield self.right yield self.forward yield self.up def set(self, right, forward, up): self.right = right self.forward = forward self.up = up class mat4(ctypes.Structure): _fields_ = ('right', vec4), ('forward', vec4), ('up', vec4), ('origin', vec4) def __iter__(self): yield self.right yield self.forward yield self.up yield self.origin def set(self, right, forward, up, origin): self.right = right self.forward = forward self.up = up self.origin = origin def set_vec3(self, right, forward, up, origin): self.right.set(*right, 0.0) self.forward.set(*forward, 0.0) self.up.set(*up, 0.0) self.origin.set(*origin, 1.0) assert ctypes.sizeof(vec3) == ctypes.sizeof(ctypes.c_float) * 3 assert ctypes.sizeof(vec4) == ctypes.sizeof(ctypes.c_float) * 4 assert ctypes.sizeof(mat3) == ctypes.sizeof(vec3) * 3 assert ctypes.sizeof(mat4) == ctypes.sizeof(vec4) * 4 _vec3_p = ctypes.POINTER(vec3) _vec4_p = ctypes.POINTER(vec4) _mat3_p = ctypes.POINTER(mat3) _mat4_p = ctypes.POINTER(mat4) vec3_right = vec3(1.0, 0.0, 0.0) vec3_forward = vec3(0.0, 1.0, 0.0) vec3_up = vec3(0.0, 0.0, 1.0) vec3_origin = vec3(0.0, 0.0, 0.0) vec4_right = vec4(1.0, 0.0, 0.0, 0.0) vec4_forward = vec4(0.0, 1.0, 0.0, 0.0) vec4_up = vec4(0.0, 0.0, 1.0, 0.0) vec4_origin = vec4(0.0, 0.0, 0.0, 1.0) mat3_identity = mat3( vec3_right, vec3_forward, vec3_up) mat4_identity = mat4( vec4_right, vec4_forward, vec4_up, vec4_origin) vec3_rotate = _engine.rk_vec3_rotate vec3_rotate.restype = None vec3_rotate.argtypes = ( _vec3_p, # ret _vec3_p, # vec3 _vec3_p, # axis ctypes.c_float) # angle vec3_mul_vec3 = _engine.rk_vec3_mul_vec3 vec3_mul_vec3.restype = None vec3_mul_vec3.argtypes = ( _vec3_p, # ret _vec3_p, # a _vec3_p) # b mat3_rotation = _engine.rk_mat3_rotation mat3_rotation.restype = None mat3_rotation.argtypes = ( _mat3_p, # ret _vec3_p, # axis ctypes.c_float) # angle mat3_mul_vec3 = _engine.rk_mat3_mul_vec3 mat3_mul_vec3.restype = None mat3_mul_vec3.argtypes = ( _vec3_p, # ret _mat3_p, # a _vec3_p) # b mat3_mul_mat3 = _engine.rk_mat3_mul_mat3 mat3_mul_mat3.restype = None mat3_mul_mat3.argtypes = ( _mat3_p, # ret _mat3_p, # a _mat3_p) # b mat4_projection = _engine.rk_mat4_projection mat4_projection.restype = None mat4_projection.argtypes = ( _mat4_p, # ret ctypes.c_float, # hfov ctypes.c_float, # ratio ctypes.c_float, # near ctypes.c_float) # far mat4_lookat = _engine.rk_mat4_lookat mat4_lookat.restype = None mat4_lookat.argtypes = ( _mat4_p, # ret _vec3_p, # position _vec3_p) # lookat mat4_orbit = _engine.rk_mat4_orbit mat4_orbit.restype = None mat4_orbit.argtypes = ( _mat4_p, # ret _vec3_p, # origin ctypes.c_float, # yaw ctypes.c_float, # pitch ctypes.c_float) # distance mat4_mul_vec3 = _engine.rk_mat4_mul_vec3 mat4_mul_vec3.restype = None mat4_mul_vec3.argtypes = ( _vec3_p, # ret _mat4_p, # a _vec3_p, # b ctypes.c_float) # w mat4_mul_vec4 = _engine.rk_mat4_mul_vec4 mat4_mul_vec4.restype = None mat4_mul_vec4.argtypes = ( _vec4_p, # ret _mat4_p, # a _vec4_p) # b mat4_mul_mat4 = _engine.rk_mat4_mul_mat4 mat4_mul_mat4.restype = None mat4_mul_mat4.argtypes = ( _mat4_p, # ret _mat4_p, # a _mat4_p) # b # display.hpp create_display = _engine.rk_create_display create_display.restype = _handle create_display.errcheck = _check_handle create_display.argtypes = ( ctypes.c_char_p, # name ctypes.c_uint, # width ctypes.c_uint) # height destroy_display = _engine.rk_destroy_display destroy_display.restype = None destroy_display.argtypes = ( ctypes.c_void_p,) # display swap_buffers = _engine.rk_swap_buffers swap_buffers.restype = None swap_buffers.argtypes = ( ctypes.c_void_p,) # display # render.hpp TEXTURE_FORMAT_SRGB8_A8 = 0 TEXTURE_FORMAT_RGBA8 = 1 TEXTURE_FORMAT_RGB10_A2 = 2 TEXTURE_FORMAT_FLOAT_32 = 3 TEXTURE_FORMAT_TYPECODE = ('B', 'B', 'I', 'f') TEXTURE_FORMAT_NELEMS = (4, 4, 1, 1) TEXTURE_FLAG_3D = _flag(0) TEXTURE_FLAG_MIPMAPS = _flag(1) TEXTURE_FLAG_MIN_NEAREST = 0 TEXTURE_FLAG_MIN_LINEAR = _flag(2) TEXTURE_FLAG_MAG_NEAREST = 0 TEXTURE_FLAG_MAG_LINEAR = _flag(3) VERTEX_FORMAT_VEC3_FLOAT = 1 VERTEX_FORMAT_VEC3_SHORT = 2 VERTEX_FORMAT_VEC3_INT10 = 3 VERTEX_FORMAT_VEC3_UINT10 = 4 VERTEX_FORMAT_MAT3_FLOAT = 5 VERTEX_FORMAT_MAT3_INT10 = 6 VERTEX_FORMAT_NORMALIZE = _flag(7) _VERTEX_FORMAT_MASK = VERTEX_FORMAT_NORMALIZE - 1 def vertex_format(*format): return array('B', format).tobytes() _VERTEX_TYPES = ( None, vec3, # VERTEX_FORMAT_VEC3_FLOAT vec3, # VERTEX_FORMAT_VEC3_SHORT vec3, # VERTEX_FORMAT_VEC3_INT10 vec3, # VERTEX_FORMAT_VEC3_UINT10 mat3, # VERTEX_FORMAT_MAT3_FLOAT mat3) # VERTEX_FORMAT_MAT3_INT10 def vertex_type(format): return _VERTEX_TYPES[format & _VERTEX_FORMAT_MASK] INSTANCE_FLAG_SPAWNED = _flag(0) INSTANCE_FLAG_VISIBLE = _flag(1) BATCH_MAX_SIZE = 65536 render_initialize = _engine.rk_render_initialize render_initialize.restype = None render_initialize.argtypes = ( ctypes.c_bool,) # debug render_terminate = _engine.rk_render_terminate render_terminate.restype = None _create_shader = _engine.rk_create_shader _create_shader.restype = _handle _create_shader.errcheck = _check_handle _create_shader.argtypes = ( ctypes.c_uint, # vert_nlines ctypes.POINTER(ctypes.c_char_p), # vert_lines ctypes.c_uint, # frag_nlines ctypes.POINTER(ctypes.c_char_p)) # frag_lines def create_shader(vert_lines, frag_lines): vert_nlines = len(vert_lines) vert_type = ctypes.c_char_p * vert_nlines vert_lines = vert_type(*map(ctypes.c_char_p, vert_lines)) frag_nlines = len(frag_lines) frag_type = ctypes.c_char_p * frag_nlines frag_lines = frag_type(*map(ctypes.c_char_p, frag_lines)) return _create_shader(vert_nlines, vert_lines, frag_nlines, frag_lines) resolve_input = _engine.rk_resolve_input resolve_input.restype = _handle resolve_input.argtypes = ( ctypes.c_void_p, # shader ctypes.c_char_p) # name resolve_param = _engine.rk_resolve_param resolve_param.restype = _handle resolve_param.argtypes = ( ctypes.c_void_p, # shader ctypes.c_char_p) # name _create_texture = _engine.rk_create_texture _create_texture.restype = _handle _create_texture.errcheck = _check_handle _create_texture.argtypes = ( ctypes.c_uint, # format ctypes.c_uint, # width ctypes.c_uint, # height ctypes.c_uint, # nlevels ctypes.c_uint, # flags ctypes.c_void_p) # pixels def create_texture(format, width, height, nlevels, flags, pixels): assert pixels.typecode == TEXTURE_FORMAT_TYPECODE[format] assert len(pixels) == width * height * max(1, nlevels) * TEXTURE_FORMAT_NELEMS[format] return _create_texture(format, width, height, nlevels, flags, _void_addr(pixels)) _create_triangles = _engine.rk_create_triangles _create_triangles.restype = _handle _create_triangles.errcheck = _check_handle _create_triangles.argtypes = ( ctypes.c_uint, # nvertices ctypes.c_void_p) # vertices def create_triangles(vertices): assert len(vertices) % 9 == 0 return _create_triangles(len(vertices) // 3, _float_addr(vertices)) _create_vertices = _engine.rk_create_vertices _create_vertices.restype = _handle _create_vertices.errcheck = _check_handle _create_vertices.argtypes = ( ctypes.c_char_p, # format ctypes.c_uint, # nvertices ctypes.c_void_p, # vertices ctypes.c_uint, # nindices ctypes.c_void_p, # vertices ctypes.c_uint, # nmeshes ctypes.c_void_p) # meshes def create_vertices(format, nvertices, vertices, indices, meshes): assert len(meshes) % 2 == 0 return _create_vertices(format, nvertices, _ubyte_addr(vertices), len(indices), _ushort_addr(indices), len(meshes) // 2, _uint_addr(meshes)) create_batch = _engine.rk_create_batch create_batch.restype = _handle create_batch.errcheck = _check_handle create_batch.argtypes = ( ctypes.c_void_p, # vertices ctypes.c_uint, # max_size ctypes.c_char_p) # params_format fill_batch = _engine.rk_fill_batch fill_batch.restype = None fill_batch.argtypes = ( ctypes.c_void_p, # batch ctypes.c_uint, # count ctypes.POINTER(ctypes.c_ubyte), # flags ctypes.POINTER(ctypes.c_ushort), # meshes ctypes.POINTER(ctypes.c_void_p)) # params clear_buffer = _engine.rk_clear_buffer clear_buffer.restype = None clear_buffer.argtypes = ( ctypes.c_bool, # pixels ctypes.c_bool, # depth ctypes.c_bool) # stencil select_shader = _engine.rk_select_shader select_shader.restype = None select_shader.argtypes = ( ctypes.c_void_p,) # shader set_input_float = _engine.rk_set_input_float set_input_float.restype = None set_input_float.argtypes = ( ctypes.c_void_p, # input ctypes.c_float) # value set_input_vec3 = _engine.rk_set_input_vec3 set_input_vec3.restype = None set_input_vec3.argtypes = ( ctypes.c_void_p, # input _vec3_p) # value set_input_mat3 = _engine.rk_set_input_mat3 set_input_mat3.restype = None set_input_mat3.argtypes = ( ctypes.c_void_p, # input _mat3_p) # value set_input_mat4 = _engine.rk_set_input_mat4 set_input_mat4.restype = None set_input_mat4.argtypes = ( ctypes.c_void_p, # input _mat4_p) # value set_param_vec3 = _engine.rk_set_param_vec3 set_param_vec3.restype = None set_param_vec3.argtypes = ( ctypes.c_uint, # layout _vec3_p) # value set_param_mat3 = _engine.rk_set_param_mat3 set_param_mat3.restype = None set_param_mat3.argtypes = ( ctypes.c_uint, # layout _mat3_p) # value select_texture = _engine.rk_select_texture select_texture.restype = None select_texture.argtypes = ( ctypes.c_uint, # slot ctypes.c_void_p) # texture draw_triangles = _engine.rk_draw_triangles draw_triangles.restype = None draw_triangles.argtypes = ( ctypes.c_void_p,) # triangles draw_batch = _engine.rk_draw_batch draw_batch.restype = None draw_batch.argtypes = ( ctypes.c_void_p,) # batch unselect_texture = _engine.rk_unselect_texture unselect_texture.restype = None unselect_texture.argtypes = ( ctypes.c_uint, # slot ctypes.c_void_p) # texture unselect_shader = _engine.rk_unselect_shader unselect_shader.restype = None unselect_shader.argtypes = ( ctypes.c_void_p,) # shader destroy_batch = _engine.rk_destroy_batch destroy_batch.restype = None destroy_batch.argtypes = ( ctypes.c_void_p,) # batch destroy_triangles = _engine.rk_destroy_triangles destroy_triangles.restype = None destroy_triangles.argtypes = ( ctypes.c_void_p,) # triangles destroy_vertices = _engine.rk_destroy_vertices destroy_vertices.restype = None destroy_vertices.argtypes = ( ctypes.c_void_p,) # vertices destroy_texture = _engine.rk_destroy_texture destroy_texture.restype = None destroy_texture.argtypes = ( ctypes.c_void_p,) # texture destroy_shader = _engine.rk_destroy_shader destroy_shader.restype = None destroy_shader.argtypes = ( ctypes.c_void_p,) # shader # events.hpp EVENT_FOCUS_IN = 0 EVENT_FOCUS_OUT = 1 EVENT_KEY_PRESS = 2 EVENT_KEY_RELEASE = 3 EVENT_BUTTON_PRESS = 4 EVENT_BUTTON_RELEASE = 5 EVENT_MOTION = 6 BUTTON_LEFT = 1 BUTTON_MIDDLE = 2 BUTTON_RIGHT = 3 BUTTON_WHEEL_UP = 4 BUTTON_WHEEL_DOWN = 5 class _EventKey(ctypes.Structure): _fields_ = ( ('code', ctypes.c_uint), ('symbol', ctypes.c_uint), ('character', ctypes.c_wchar)) class _EventButton(ctypes.Structure): _fields_ = ('index', ctypes.c_uint), class _EventMotion(ctypes.Structure): _fields_ = ( ('x', ctypes.c_int), ('y', ctypes.c_int)) class _Events(ctypes.Union): _fields_ = ( ('key', _EventKey), ('button', _EventButton), ('motion', _EventMotion)) class Event(ctypes.Structure): _fields_ = ('type', ctypes.c_uint), ('data', _Events) create_events = _engine.rk_create_events create_events.restype = _handle create_events.errcheck = _check_handle create_events.argtypes = ( ctypes.c_void_p,) # display destroy_events = _engine.rk_destroy_events destroy_events.restype = None destroy_events.argtypes = ( ctypes.c_void_p, # display ctypes.c_void_p) # events set_key_autorepeat = _engine.rk_set_key_autorepeat set_key_autorepeat.restype = None set_key_autorepeat.argtypes = ( ctypes.c_void_p, # events ctypes.c_bool) # autorepeat set_motion_acceleration = _engine.rk_set_motion_acceleration set_motion_acceleration.restype = None set_motion_acceleration.argtypes = ( ctypes.c_void_p, # events ctypes.c_uint, # numerator ctypes.c_uint, # denominator ctypes.c_uint) # threshold consume_events = _engine.rk_consume_events consume_events.restype = ctypes.c_uint consume_events.argtypes = ( ctypes.c_void_p, # events ctypes.POINTER(Event), # buffer ctypes.c_uint) # max_events