# 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. ## This module containst the functions to start using Myou Engine. # TODO: runnableExamples is broken because we can't pass compile options ## ```nim ## let engine = newMyouEngine(1024, 768, "My Game") ## ## # Create or load your initial scene here ## ## engine.run() ## ``` import ./types import std/tables when defined(android): const default_gl_version = 320 const default_gl_es = true elif defined(emscripten) or defined(ios): const default_gl_version = 300 const default_gl_es = true else: const default_gl_version = 330 const default_gl_es = false # Forward declarations proc newMyouEngine*( width, height: int32, title = "Myou Engine window", opengl_version = default_gl_version, opengl_es = default_gl_es, glsl_version = "", use_glsl_tone_mapping = true, ): MyouEngine proc get_builtin_shader_library*(use_cubemap_prefiltering = true): string proc get_builtin_shader_textures*(): Table[string, Texture] # End forward declarations import std/strutils import std/monotimes import ./graphics/render import ./screen import ./platform/platform import ./loaders/blend import ./util from loadable import updateLoadableWorkerThreads import arr_ref export arr_ref export tables proc newMyouEngine*( width, height: int32, title = "Myou Engine window", opengl_version = default_gl_version, opengl_es = default_gl_es, glsl_version = "", use_glsl_tone_mapping = true, ): MyouEngine = ## Creates a Myou Engine instance. You need to call this before you can use ## the engine. You also need to call `run <#run,MyouEngine>`_ at the end of ## your main project file. result = new MyouEngine result.glsl_version = if glsl_version != "": glsl_version elif opengl_es: $opengl_version & " es" else: $opengl_version # TODO: move/copy to camera or to scene? # to override it with per-camera exposure settings if opengl_es: assert opengl_version >= 300, "Minimum supported OpenGL ES version is 3.0" else: assert opengl_version >= 330, "Minimum supported OpenGL version is 3.3" if opengl_es or use_glsl_tone_mapping: result.tone_mapping_library = dedent """ float linearrgb_to_srgb(float c){ if (c < 0.0031308) return (c < 0.0) ? 0.0 : c * 12.92; else return 1.055 * pow(c, 1.0 / 2.4) - 0.055; } vec4 linearrgb_to_srgb(vec4 col){ return vec4( linearrgb_to_srgb(col.r), linearrgb_to_srgb(col.g), linearrgb_to_srgb(col.b), col.a ); } #define MYOU_TONE_MAP(x) linearrgb_to_srgb(x) """ result.tone_mapping_function = "MYOU_TONE_MAP" result.use_glsl_tone_mapping = true else: result.tone_mapping_library = "\n#define MYOU_TONE_MAP(x) x\n" echo "assigning renderer" result.renderer = result.newRenderManager # this will call result.renderer.initialize() now or later # but first it will ensure a screen can be created when not defined(nimdoc): init_graphics(result, width, height, title, opengl_version, opengl_es) discard result.newScreen(width, height, title) registerBlendLoader(result) proc get_builtin_shader_library*(use_cubemap_prefiltering = true): string = ## Returns a string with the code of the default shader library of the ## engine. If you use this, you may want to also add the textures given by ## `get_builtin_shader_textures<#get_builtin_shader_textures>`_. return @[ if use_cubemap_prefiltering: "#define PREFILTERED_CUBEMAPS" else: "", # staticOrDebugRead "shaders/debug_text.glsl", staticOrDebugRead "shaders/cube_prefilter.glsl", staticOrDebugRead "shaders/spherical_harmonics.glsl", staticOrDebugRead "shaders/shader_library.glsl", ].join("\n") proc get_builtin_shader_textures*(): Table[string, Texture] = ## Returns a table of textures to be used with the library that is returned ## by `get_builtin_shader_library<#get_builtin_shader_library,bool>`_. discard var last_time: float proc myou_main_loop*(self: MyouEngine) = ## Runs one iteration of the engine main loop. It doesn't swap buffers. ## ## You usually don't need to call this. Use `run <#run,MyouEngine>`_ ## instead. updateLoadableWorkerThreads() # TODO: make a table object that can be iterated while changing, e.g. with a # seq and a dirty flag to update the seq if self.new_scenes.len != 0: for name,scene in self.new_scenes.pairs: self.scenes[name] = scene self.new_scenes.clear() let time = getmonotime().ticks.float/1000000000 let delta_seconds = time - last_time for _,scene in self.scenes.pairs: if not scene.enabled: continue for f in scene.pre_draw_callbacks: f(scene, delta_seconds) self.renderer.draw_all() for _,scene in self.scenes.pairs: if not scene.enabled: continue for f in scene.post_draw_callbacks: f(scene, delta_seconds) last_time = time proc run*(self: MyouEngine) = ## Starts the main loop of the engine. You should call it at the end of your ## main file. In mobile platforms and on web, this function doesn't block, ## and instead it just configures the main loop function. Therefore you ## shouldn't run any code after calling it. last_time = getmonotime().ticks.float/1000000000 when not defined(nimdoc): start_platform_main_loop(self, myou_main_loop) proc loadScene*(self: MyouEngine, uri: string, callback: proc(scene: Scene), name = "", ext = "") = let ext = if ext == "": uri.rsplit('.', 1)[1] else: ext if ext notin self.loaders_by_ext: raise ValueError.newException "File extension not supported: " & ext # TODO: use the next loader if the first one fails let loaders = self.loaders_by_ext[ext] let loader = loaders[0](self) loader.openAssetFile(uri) loader.loadScene(name, nil, callback)