# The contents of this file are subject to the Common Public Attribution License # Version 1.0 (the “License”); you may not use this file except in compliance # with the License. You may obtain a copy of the License at # https://myou.dev/licenses/LICENSE-CPAL. The License is based on the Mozilla # Public License Version 1.1 but Sections 14 and 15 have been added to cover use # of software over a computer network and provide for limited attribution for # the Original Developer. In addition, Exhibit A has been modified to be # consistent with Exhibit B. # # Software distributed under the License is distributed on an “AS IS” basis, # WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for # the specific language governing rights and limitations under the License. # # The Original Code is Myou Engine. # # the Original Developer is the Initial Developer. # # The Initial Developer of the Original Code is the Myou Engine developers. # All portions of the code written by the Myou Engine developers are Copyright # (c) 2024. All Rights Reserved. # # Alternatively, the contents of this file may be used under the terms of the # GNU Affero General Public License version 3 (the [AGPL-3] License), in which # case the provisions of [AGPL-3] License are applicable instead of those above. # # If you wish to allow use of your version of this file only under the terms of # the [AGPL-3] License and not to allow others to use your version of this file # under the CPAL, indicate your decision by deleting the provisions above and # replace them with the notice and other provisions required by the [AGPL-3] # License. If you do not delete the provisions above, a recipient may use your # version of this file under either the CPAL or the [AGPL-3] License. import std/tables import std/options import vmath except Quat import json import arr_ref import ./platform/gl import ./quat import ../libs/loadable/loadable import ./input import loaders/blend_format # All cyclic types have been temporarily moved here, until when it's not necessary type # attributes.nim DataType* = enum Unknown Byte = cGL_BYTE UByte = GL_UNSIGNED_BYTE Short = cGL_SHORT UShort = GL_UNSIGNED_SHORT Int = cGL_INT UInt = GL_UNSIGNED_INT Float = cGL_FLOAT HalfFloat = GL_HALF_FLOAT Attribute* = object name*: string count*: int16 dtype*: DataType offset*: int16 location*: int16 AttributeList* = seq[Attribute] # myou_engine.nim MyouEngine* = ref object scenes*: Table[string, Scene] objects*: Table[string, GameObject] mesh_datas*: Table[string, MeshData] screen*: Screen screens*: seq[Screen] renderer*: RenderManager tone_mapping_library*: string tone_mapping_function*: string use_glsl_tone_mapping*: bool loaders_by_ext*: Table[string, seq[proc(e: MyouEngine): Loader]] glsl_version*: string cache_settings*: CacheSettings all_framebuffers*: seq[Framebuffer] ## private new_scenes*: Table[string, Scene] ## private # gameobject.nim ObjectType* = enum ## private TGameObject TMesh TCamera TLight TArmature GameObject* = ref object of RootObj # TODO: remove otype otype*: ObjectType ## private engine* {.cursor.}: MyouEngine ## private debug*: bool ## private position*: Vec3 rotation*: Quat radius*: float rotation_order*: RotationOrder scale*: Vec3 dimensions*: Vec3 bound_box*: (Vec3, Vec3) object_color*: Vec4 # alpha*: float matrix_parent_inverse*: Mat4 scene* {.cursor.}: Scene source_scene_name*: string # data_dir*: string # dupli_group*: unknown visible*: bool parent* {.cursor.}: GameObject children*: seq[GameObject] auto_update_matrix*: bool world_matrix*: Mat4 bone_matrix_inverse*: Mat4 # probe_cube*: Probe # probe_planar*: Probe properties*: Table[string, JsonNode] # animation_strips*: seq[AnimationStrip] name*: string original_name*: string lod_objects*: seq[GameObject] parent_bone_index*: int # behaviours*: Table[string, Behaviour] body*: Body avg_poly_area*: float avg_poly_length*: float zindex*: float # groups*: seq[string] center*: Vec3 world_center*: Vec4 # pending_bodies*: seq[Body] # physics bodies that depend on this object sqdist*: float ## private flip*: bool ## private sqscale*: float ## private ## Globally squared scale, to avoid rendering zero scale object_render_ubo*: UBO ## private object_ubo*: UBO ## private # mesh.nim VertexBufferAttribs* = object vb*: GLuint attribs*: AttributeList VaoSpec* = object vbs*: seq[VertexBufferAttribs] ib*: GLuint # most fields should be private MeshData* = ref object engine* {.cursor.}: MyouEngine # TODO: seq of {.cursor.}s? users*: seq[GameObject] ## private hash*: string varrays*: seq[ArrRef[float32]] iarrays*: seq[ArrRef[uint16]] loaded*: bool vertex_buffers*: seq[GPUBuffer] ## private vertex_starts*: seq[int32] ## private index_buffers*: seq[GPUBuffer] ## private num_indices*: seq[int32] ## private vaos*: seq[GPUVao] ## private vao_specs*: seq[VaoSpec] ## private index_types*: seq[DataType] ## private layout*: AttributeList ## private stride*: int ## private draw_method*: MeshDrawMethod # phy_convex_hull: unknown # phy_mesh: unknown # buffer_load_promises: seq[unknown] # load_promise: unknown # load_resolve: unknown use_tf*: bool ## private tf_vbos*: seq[seq[GPUBuffer]] ## private tf_vaos*: seq[GPUVao] ## private tf_vao_specs*: seq[VaoSpec] ## private tf_layout*: AttributeList ## private when defined(myouUseRenderdoc): name*: string SortSign* = enum BackToFront = -1 FrontToBack = 1 Mesh* = ref object of GameObject data*: MeshData materials*: seq[Material] material_defines*: Table[string, string] passes*: seq[int32] armature*: Armature culled_in_last_frame*: bool ## private last_lod*: Table[string, Mesh] ## private draw_method*: MeshDrawMethod hash*: string layout*: AttributeList vertex_modifiers*: seq[VertexModifier] mesh_id*: int8 bone_index_maps*: seq[Table[int32,int32]] sort_sign*: SortSign # related_textures*: seq[unknown] shape_names*: seq[string] mesh_name*: string skip_upload*: bool generate_tangents*: bool # TODO: this is temporary # because we can get this info from the own mesh array last_polyline_point*, last_polyline_left*: Vec3 MeshDrawMethod* = enum Points = GL_POINTS Lines = GL_LINES LineLoop = GL_LINE_LOOP LineStrip = GL_LINE_STRIP Triangles = GL_TRIANGLES TriangleStrip = GL_TRIANGLE_STRIP TriangleFan = GL_TRIANGLE_FAN CommonMeshAttribute* = enum vertex, color, normal, uv CommonMeshAttributes* = set[CommonMeshAttribute] # camera.nim CameraType* = enum Perspective, Orthographic SensorFit* = enum Auto, Horizontal, Vertical, Cover, Contain Camera* = ref object of GameObject near_plane*: float32 far_plane*: float32 field_of_view*: float32 ortho_scale*: float32 aspect_ratio*: float32 target_aspect_ratio*: float32 cam_type*: CameraType sensor_fit*: SensorFit shift*: Vec2 fov_4*: Option[tuple[top,right,bottom,left: float32]] projection_matrix*: Mat4 projection_matrix_inverse*: Mat4 world_to_screen_matrix*: Mat4 cull_planes*: array[6, Vec4] # camera_render_ubo*: UBO # light.nim LightType* = enum PointLight SunLight SpotLight AreaLight Light* = ref object of GameObject light_type*: LightType shadows*: seq[ShadowManager] use_shadow*: bool color*: Vec3 energy*: float32 diffuse_factor*: float32 specular_factor*: float32 spot_size*: float32 spot_blend*: float32 # view_pos*: Vec3 # dir*: Vec3 # depth_matrix*: Mat4 # cam2depth*: Mat4 # projection_matrix*: Mat4 light_radius*: float32 area_size*: Vec3 cutoff_distance*: float32 # shadows ShadowManager* = ref object of RootObj engine* {.cursor.}: MyouEngine light* {.cursor.}: Light required_texture_count*: int32 uses_color_channels*: int8 uses_depth*: bool shadow_index*: int32 sampler_type*: string material*: Material auto_render*: bool min_light_angle_delta*: float32 last_light_dir*: Vec3 SimpleShadowManager* = ref object of ShadowManager depth_range*: (float32, float32) caster_bounding_points*: seq[Vec3] use_camera*: bool # NOTE: layout must match struct ShadowMapInfo in scene.nim ShadowMapUniform* = object depth_matrix*: Mat4 tex_size*: float32 bias*: float32 pad0, pad1: float32 # scene.nim # NOTE: layout must match struct PointLight in scene.nim PointLightUniform* = object position*: Vec3 radius_squared*: float32 color*: Vec3 padding0: float32 diffuse_power*: float32 specular_power*: float32 padding1: float32 inv_squared_cutoff*: float32 # NOTE: layout must match struct SunLight in scene.nim SunLightUniform* = object direction*: Vec3 padding0: float32 color*: Vec3 shadow_index*: float32 diffuse_power*: float32 specular_power*: float32 padding1: float32 padding2: float32 Scene* = ref object engine* {.cursor.}: MyouEngine name*: string enabled*: bool children*: seq[GameObject] auto_updated_children*: seq[GameObject] mesh_passes*: seq[seq[Mesh]] bg_pass*: seq[Mesh] fg_pass*: seq[Mesh] lights*: seq[Light] armatures*: seq[Armature] objects*: Table[string, GameObject] parents*: Table[string, GameObject] materials*: Table[string, Material] textures*: Table[string, Texture] lighting_UBOs*: seq[UBO] point_light_UBO*: UBO sun_light_UBO*: UBO # spot_light_UBO*: UBO # area_light_UBO*: UBO shadow_maps_UBO*: UBO cubemap_UBO*: UBO sh9_UBO*: UBO active_camera*: Camera physics_enabled*: bool world*: World # render options background_color*: Vec4 shadow_map_resolution*: int32 cubemap_resolution*: int32 max_point_lights*: int32 max_sun_lights*: int32 max_spot_lights*: int32 max_area_lights*: int32 max_shadow_maps*: int32 max_cubemaps*: int32 max_spherical_harmonics*: int32 shadow_maps*: Framebuffer world_material*: Material background_cubemap*: Framebuffer cubemaps*: seq[Framebuffer] cubemap_probes*: seq[CubemapProbe] planar_probes*: seq[PlanarProbe] children_are_ordered*: bool ## private last_shadow_render_tick*: int ## private last_update_matrices_tick*: int ## private pre_draw_callbacks*: seq[proc(scene: Scene, s: float)] post_physics_callbacks*: seq[proc(scene: Scene, s: float)] post_draw_callbacks*: seq[proc(scene: Scene, s: float)] frame_start*: int frame_end*: int anim_fps*: float markers*: OrderedTable[string, float] # extra_data*: unknown data_dir*: string texture_dir*: string original_scene_name*: string foreground_planes*: seq[tuple[material: Material, is_alpha: bool]] debug_draw*: DebugDraw ## private shader_library*: string on_swap_eye*: proc(is_right_eye: bool) background_probe_rendered*: bool ## private add_viewport_automatically*: bool ## private # render.nim RenderCameraData* = object cam2world*: Mat4 world2cam*: Mat4 projection_matrix*: Mat4 projection_matrix_inverse*: Mat4 cull_planes*: array[6, Vec4] clipping_plane*: Vec4 viewport_size*, viewport_size_inv*: Vec2 RenderManager* = ref object engine* {.cursor.}: MyouEngine initialized*: bool # temporary_framebuffers*: Table[int, ByteFramebuffer] render_tick*: int # options use_frustum_culling*: bool show_debug_frustum_culling*: bool use_sort_faces*: bool use_sort_faces_opaque*: bool force_pot_buffers*: bool # unbind_textures_on_draw_viewport*: bool # unbind_textures_on_draw_mesh*: bool use_debug_draw*: bool max_buffer_size_upload*: int use_draw_background_first*: bool # detected parameters and builtins max_texture_size*: int32 max_textures*: int32 max_uniform_block_size*: int32 max_uniform_buffer_bindings*: int32 # has_float_texture_support*: bool # has_float_fb_support*: bool bg_mesh*: Mesh # private no_material*: Material blank_texture*: Texture # internal state (meant to be private) front_face_is_cw*: bool ## private cull_face_enabled*: bool ## private # bound_textures*: seq[Texture] # active_texture*: int32 # next_texture*: int32 bound_ubos*: seq[UBO] ## private next_ubo*: int32 ## private check_framebuffers*: bool was_right_eye*: bool num_views*: int8 # internal state, public? indices_drawn*: int meshes_drawn*: int effect_ratio*: float used_texture_memory*: int camera_render_ubo*: UBO queue*: seq[proc()] # Uniforms that are common to all objects of the same view # Must match layout in render.nim CameraRenderUniform* = object view_matrix*: Mat4 # TODO: add view_matrix_inverse3 which is much more used than mat4 # and measure performance or compiled shader ops # (maybe just add a separate translation vec4) view_matrix_inverse*: Mat4 projection_matrix*: Mat4 projection_matrix_inverse*: Mat4 cull_plane*: Vec4 viewport_size*, viewport_size_inv*: Vec2 # roughness_bias*: float32 # fixed_z*: float32 # pad1, pad2: float32 # Uniforms that are unique for each object and view # Must match layout in render.nim ObjectRenderUniform* = object # using an array of Vec4 instead of Mat3 # because of alignment model_view_matrix*: Mat4 model_view_matrix_inverse*: Mat4 normal_matrix*: array[3, Vec4] # Uniforms for each object that are independent from view # Must match layout in render.nim ObjectUniform* = object model_matrix*: Mat4 model_matrix_inverse*: Mat4 mesh_center*: Vec3 pad0: float32 mesh_inv_dimensions*: Vec3 average_world_scale*: float32 color*: Vec4 # ubo.nim UBO* = ref object renderer* {.cursor.}: RenderManager name*: string size32*: int byte_storage*: ArrRef[byte] buffer*: GPUBuffer binding_point*: int32 # needs_update*: bool # material.nim VaryingType* = enum Unused ViewPosition WorldPosition ProjPosition ViewNormal WorldNormal ObjectNormal Uv VertexColor Tangent Orco # TODO this should go by shader Varying* = object vtype*: VaryingType varname*, attname*, gltype*: string multiplier*: float32 EsPrecision* = enum EsPrecisionNone EsPrecisionLow EsPrecisionMedium EsPrecisionHigh Material* = ref object engine* {.cursor.}: MyouEngine name*: string textures*: OrderedTable[string, Texture] ubos*: seq[UBO] varyings*: seq[Varying] vertex*: string fragment*: string double_sided*: bool # has_normal* (unused) fixed_z*: Option[float] # TODO: have option to set as uniform point_size*: Option[float] animation_strips*: seq[AnimationStrip] glsl_version*: string es_precision*: EsPrecision defines*: Table[string, string] ### scene* {.cursor.}: Scene shaders*: Table[AttributeList, Shader] ## private users*: seq[GameObject] ## private render_scene* {.cursor.}: Scene ## private has_texture_list_checked*: bool ## private shader_library*: string ## private use_debug_shaders*: bool ## private last_shader* {.cursor.}: Shader ## private feedback_varyings*: seq[string] # TODO: choose better names Shader* = ref object engine* {.cursor.}: MyouEngine # do we need this? id*: int program*: GPUProgram material* {.cursor.}: Material # do we need this? # TODO: combine in single seq? ubos*: seq[UBO] ubo_indices*: seq[GLuint] camera_render_ubo_index*: GLuint object_render_ubo_index*: GLuint object_ubo_index*: GLuint texture_locations*: seq[GLint] shadowmap_location*: GLint cubemap_locations*: seq[GLint] when not defined(release): vs_code*, fs_code*: string # texture.nim TextureType* = enum Tex2D Tex2DArray Tex3D TexCube TexExternal TextureFormat* = enum # TODO: signed normalized formats # TODO: uncommon formats SRGB_u8 SRGB_Alpha_u8 R_u8 RG_u8 RGB_u8 RGBA_u8 R_u16 RG_u16 RGB_u16 RGBA_u16 R_f16 RG_f16 RGB_f16 RGBA_f16 R_f32 RG_f32 RGB_f32 RGBA_f32 Depth_u16 Depth_u24 # Depth_u24_s8 Depth_f32 TextureExtrapolation* = enum Clamp Border Repeat MirroredRepeat TextureFilter* = enum Nearest ## Without mipmaps Linear ## Without mipmaps Pixellated ## Mip mapped version of Nearest Trilinear Anisotropic TextureStorage* = ref object ## private unit*: GLint target*: GLenum tex*: GPUTexture iformat*: GLenum format*, gltype*: GLenum # index of the first layer (or only layer if not using tiles) layer*: float32 # when tile size is not 1, each tile will be in a contiguous layer tile_size*: Vec2 Texture* = ref object of RootObj engine* {.cursor.}: MyouEngine name*: string storage*: TextureStorage ## private loaded*: bool last_used_shader* {.cursor.}: Shader ## private sampler_object*: GLuint ## private width*, height*, depth*: int tex_type*: TextureType format*: TextureFormat filter*: TextureFilter is_framebuffer_active*: bool ## private is_sRGB*: bool is_compressed*: bool mipmap_range*: (int, int) # framebuffer.nim FramebufferDepthType* = enum DepthNone DepthRenderBuffer DepthTexture Framebuffer* = ref object of RootObj engine* {.cursor.}: MyouEngine width*, height*, layer_count*: int format*: TextureFormat depth_type*: FramebufferDepthType texture*, depth_texture*: Texture is_complete*: bool has_mipmap*: bool current_width*, current_height*: int current_mipmap_level*: int use_sRGB*: bool # API objects framebuffer*, render_buffer*: GLuint TexturePixels* = object pixels*: ArrRef[float32] width*, height*: int format*: TextureFormat tex_type*: TextureType # effect_shaders.nim # should have same layout as EffectShaderInput # in effect_shaders.nim EffectShaderInput* = object # resolution for the current mip level size*: Vec3 # absolute lod at which resolution is 1x1x1 px_lod*: float32 # size of a pixel (inverse of the size) px_size*: Vec3 # current mip level being rendered (only non zero when generating mipmaps) # (this has nothing to do with the input, we're just using the spare UBO space) lod*: float32 # cubemap_probe.nim ProbeUpdateStrategy* = enum UpdateNever UpdateOnFirstUse UpdateAlways ProbeInfluenceType* = enum SphereInfluence BoxInfluence ProbeParallaxType* = enum NoParallax = -1 SphereParallax BoxParallax CubemapProbe* = ref object of GameObject cubemap*: Framebuffer ubo_index*: int32 resolution*: int32 influence_type*: ProbeInfluenceType parallax_type*: ProbeParallaxType influence_distance*: float32 parallax_distance*: float32 falloff*: float32 intensity*: float32 clipping_start*: float32 clipping_end*: float32 update_strategy*: ProbeUpdateStrategy used*: bool # NOTE: layout must match struct CubemapInfo in scene.nim CubemapProbeUniform* = object world2cube*: Mat4 resolution*: float32 falloff_inv*: float32 influence_type*: float32 parallax_type*: float32 par_dist_rel_inv*: float32 intensity*: float32 roughness_lod*: float32 pad0: float32 # NOTE: layout must match SphericalHarmonics in scene.nim SH9Uniform* = object coefficients*: array[1, array[9, Vec4]] transform*: Mat4 # planar_probe.nim PlanarProbe* = ref object of GameObject influence_distance*: float32 falloff*: float32 # loader_base.nim Loader* = ref object of RootObj engine* {.cursor.}: MyouEngine shader_library*: string shader_textures*: Table[string, Texture] # on_destroy*: OnDestroy path_handler*: proc(path: string): string override_textures_sampler_type*: Table[string, string] # texture_optimize.nim EncodingSpeed* = enum UltraFast VeryFast Fast Basic Slow VerySlow Slowest RgbBcFmt* = enum BC1 = 1 ## 4 BPP (0.5 bytes per pixel), very small but low quality. BC7 = 7 ## 8 BPP (1 byte per pixel), larger but very good quality. BlockSize* = enum Bs4x4 = 0x4_4 ## 8.00 BPP Bs5x4 = 0x5_4 ## 6.40 BPP Bs5x5 = 0x5_5 ## 5.12 BPP Bs6x5 = 0x6_5 ## 4.27 BPP Bs6x6 = 0x6_6 ## 3.56 BPP Bs8x5 = 0x8_5 ## 3.20 BPP Bs8x6 = 0x8_6 ## 2.67 BPP Bs8x8 = 0x8_8 ## 2.00 BPP (below 10x6) Bs10x5 = 0xA_5 ## 2.56 BPP Bs10x6 = 0xA_6 ## 2.13 BPP Bs10x8 = 0xA_8 ## 1.60 BPP Bs10x10 = 0xA_A ## 1.28 BPP Bs12x10 = 0xC_A ## 1.07 BPP Bs12x12 = 0xC_C ## 0.89 BPP # TODO: 3D block sizes CacheSettings* = object compress_textures*: bool ## Whether to compress textures on load ## when not loaded from cache cache use_cache*: bool ## Whether to try to load from cache save_cache*: bool ## Whether to save compressed textures cache_dir*: string ## Cache directory quality_speed*: EncodingSpeed ## Whether you need them fast or good bc_format_for_RGB*: RgbBcFmt ## Which BCn to use for RGB images. ## BC7 is the best but BC1 is half the ## size and encodes very fast astc_block_size*: BlockSize ## Defines quality and size of ASTC. ## 4x4 is the best but biggest, ## 6x6 is a good balance, ## 8x8 is bad but very small. astc_block_1ch*: BlockSize ## Block size for 1 channel textures, ## since less data has to be encoded astc_block_2ch*: BlockSize ## Block size for 2 channel textures compress_all_formats*: bool ## Encode textures for all platforms cache_dir_bc*: string ## Cache directory for writing bc cache_dir_astc*: string ## Cache directory for writing astc # INCOMPLETE Armature* = ref object of GameObject bone_list*: seq[Bone] bones*: Table[string, Bone] Bone = ref object blength*: float object_children*: seq[GameObject] matrix*: Mat4 parent_object*: GameObject World* = ref object of RootObj enabled*: bool DebugDraw* = ref object of RootObj Body* = ref object Screen* = ref object of RootObj engine* {.cursor.}: MyouEngine width*, height*: int32 orientation*: int8 viewports*: seq[Viewport] enabled*: bool framebuffer*: Framebuffer window*: pointer ## private pre_draw*, post_draw*: proc(self: Screen) last_x*, last_y*: float ## private last_buttons*, last_mods*: int8 ## private key_callbacks*: seq[proc(event: KeyEvent)] ## private char_callbacks*: seq[proc(codepoint: uint32)] ## private mouse_move_callbacks*: seq[proc(event: MouseMoveEvent)] ## private mouse_button_callbacks*: seq[proc(event: MouseButtonEvent)] ## private mouse_wheel_callbacks*: seq[proc(event: MouseWheelEvent)] ## private frame_interval*: int is_touch*: bool # workaround for emscripten, see glfm_wrap touches*: seq[(int32, Vec2)] prev_touches*: seq[(int32, Vec2)] resize_callbacks*: seq[proc(screen: Screen)] ## private platform_event_callbacks*: seq[proc(screen: Screen, e: PlatformEvent)] ## private break_current_callbacks*: bool ## private PlatformEvent* = enum PlatformPause PlatformResume PlatformMemoryWarning PlatformFocus PlatformKeyboardShown PlatformKeyboardHidden Viewport* = ref object camera*, debug_camera*: Camera width*, height*: int effects*: seq[int] requires_float_buffers*: bool is_right_eye*: bool clear_color*: bool clear_depth*: bool rect*: (float32,float32,float32,float32) rect_pix*: (int32,int32,int32,int32) VertexModifier* = object of RootObj get_code*: proc(v: seq[Varying]): (seq[string], seq[string], seq[string], seq[string]) prepare_mesh*: proc(m: Mesh) AnimationStrip* = object const HARDCODED_MAXTEXTURES* = 32 const HARDCODED_MAXUBOS* = 80 # LOADERS type BlendLoader* = ref object of Loader blend_file_path*: string ## private blend_file*: BlendFile ## private use_indices*: bool ## private cached_materials*: Table[(FNode, string, bool), (string, seq[Varying], OrderedTable[string, string], OrderedTable[string, TexturePixels])] ## private resource*: LoadableResource template enqueue*(renderer: RenderManager, fun: untyped) = ## Run a proc after the renderer has been initialized. ## ## If it was already initialized, it runs it immediately. let f = fun if renderer.initialized: f() else: renderer.queue.add f when not defined(release): from sugar import dump export sugar.dump