/// <summary> /// Creates the default shaders embedded into the binary. /// </summary> private void CreateDefaultShaders() { string defaultVert = Utilities.ReadEmbeddedResource("Emotion.Embedded.Shaders.DefaultVert.glsl"); string defaultFrag = Utilities.ReadEmbeddedResource("Emotion.Embedded.Shaders.DefaultFrag.glsl"); try { ShaderProgram.DefaultVertShader = new Shader(ShaderType.VertexShader, defaultVert); ShaderProgram.DefaultFragShader = new Shader(ShaderType.FragmentShader, defaultFrag); } catch (Exception ex) { // Check if one of the expected exceptions. if (new Regex("gl_arb_gpu_shader5").IsMatch(ex.ToString().ToLower())) { Debugger.Log(MessageType.Warning, MessageSource.GL, "The extension GL_ARB_GPU_SHADER5 was found, but is not supported."); Shader5ExtensionMissing = true; // Cleanup erred ones if any. ShaderProgram.DefaultVertShader?.Destroy(); ShaderProgram.DefaultFragShader?.Destroy(); // Recreate shaders. ShaderProgram.DefaultVertShader = new Shader(ShaderType.VertexShader, defaultVert); ShaderProgram.DefaultFragShader = new Shader(ShaderType.FragmentShader, defaultFrag); } else { throw; } } Helpers.CheckError("making default shaders"); }
/// <summary> /// Start running the engine loop. Can be blocking depending on the host. /// </summary> public static void Run() { // If the debugger is attached, don't wrap in a try-catch so that exceptions can be traced easier. if (System.Diagnostics.Debugger.IsAttached) { InternalRun(); return; } // If no debugger is attached, wrap in a try-catch so that exception logs are generated. try { InternalRun(); } catch (Exception ex) { File.WriteAllText($"Logs{Path.DirectorySeparatorChar}FatalCrash_{DateTime.Now.ToFileTime()}", ex.ToString()); Debugger.Log(MessageType.Error, MessageSource.Engine, $"Emotion engine has encountered a crash.\n{ex}"); // Flush logs. while (Debugger.LogInProgress()) { Task.Delay(1).Wait(); } // Close. Environment.Exit(1); } }
/// <summary> /// Is run every frame by the host. /// </summary> private static void LoopDraw(float frameTime) { // If not focused, or the renderer tells us not to render the frame - don't draw. if (!Host.Focused || !Renderer.RenderFrame()) { Task.Delay(1).Wait(); return; } // Run the thread manager. GLThread.Run(); // Get frame time and increment total time. FrameTime = frameTime; TotalTime += frameTime; // Clear the screen. Renderer.Clear(); Helpers.CheckError("renderer clear"); // Draw the layers. LayerManager.Draw(); Helpers.CheckError("layer draw"); // Finish rendering. Renderer.End(); Helpers.CheckError("renderer end"); // Draw debug. Debugger.Draw(); // Swap buffers. Host.SwapBuffers(); }
private void InitDebug() { Context.ScriptingEngine.Expose("debugAudio", (Action)(() => { lock (_layers) { foreach (var layer in _layers) { Debugger.Log(MessageType.Info, MessageSource.SoundManager, layer.ToString()); } } }), "Dumps the status of the sound manager."); }
/// <summary> /// Prepare the Emotion engine. /// </summary> /// <param name="config">A function to apply initial settings.</param> public static void Setup(Action <Settings> config = null) { // Check if it was already setup. if (IsSetup) { throw new Exception("Context is already setup."); } IsSetup = true; // Initialize debugger so we have access to logging afterward. Debugger.Initialize(); Debugger.Log(MessageType.Info, MessageSource.Engine, $"Starting Emotion v{Meta.Version}"); // If the debugger is attached, don't wrap in a try-catch so that exceptions can be traced easier. if (Debugger.DebugMode) { InternalSetup(config); return; } // If no debugger is attached, wrap in a try-catch so that exception logs are generated. try { InternalSetup(config); } catch (Exception ex) { File.WriteAllText($"Logs{Path.DirectorySeparatorChar}InitCrash_{DateTime.Now.ToFileTime()}", ex.ToString()); Debugger.Log(MessageType.Error, MessageSource.Engine, $"Emotion engine was unable to initialize.\n{ex}"); // Flush logs. while (Debugger.LogInProgress()) { Task.Delay(1).Wait(); } // Close. Environment.Exit(1); } }
/// <summary> /// Is run every tick by the host. /// </summary> /// <param name="frameTime">The time between this tick and the last.</param> private static void LoopUpdate(float frameTime) { // Throttle so the whole CPU isn't used up by update cycles. Useful only when the Update loop runs on a separate thread. //Task.Delay(1).Wait(); // Update debugger. Debugger.Update(); // Update the renderer. Renderer.Update(frameTime); // If not rendering, then don't update user code. // The reason for this is because we are only skipping rendering when frame by frame mode is active, and the layer manager shouldn't trigger at all then. if (Renderer.RenderFrame() && Host.Focused) { LayerManager.Update(); } // Run input. This is outside of a focus check so it can capture the first input when focus is claimed. InputManager.Update(); }
/// <summary> /// WindowsNT bootstrap. /// </summary> private static void WindowsSetup() { // Set current directory to the process string processPath = AppDomain.CurrentDomain.BaseDirectory; if (processPath != Environment.CurrentDirectory + "\\") { Directory.SetCurrentDirectory(AppDomain.CurrentDomain.BaseDirectory); Debugger.Log(MessageType.Warning, MessageSource.Engine, $"Process directory was wrong, set to: {Environment.CurrentDirectory}"); } // Set the DLL path on Windows. string libraryDirectory = Environment.CurrentDirectory + "\\Libraries\\" + (Environment.Is64BitProcess ? "x64" : "x86"); Windows.SetDllDirectory(libraryDirectory); string path = Environment.GetEnvironmentVariable("PATH", EnvironmentVariableTarget.Process); Environment.SetEnvironmentVariable("PATH", path + ";" + libraryDirectory, EnvironmentVariableTarget.Process); Debugger.Log(MessageType.Info, MessageSource.Engine, "Library Folder: " + libraryDirectory); }
internal Renderer() { // Renderer bootstrap. Debugger.Log(MessageType.Info, MessageSource.Renderer, "Loading Emotion OpenTK-GLES Renderer..."); Debugger.Log(MessageType.Info, MessageSource.Renderer, "GL: " + GL.GetString(StringName.Version) + " on " + GL.GetString(StringName.Renderer)); Debugger.Log(MessageType.Info, MessageSource.Renderer, "GLSL: " + GL.GetString(StringName.ShadingLanguageVersion)); // Set execution flags, used for workarounding different GPU behavior. SetFlags(); // Create default shaders. This also sets some shader flags. CreateDefaultShaders(); // Create a default program, and use it. ShaderProgram defaultProgram = new ShaderProgram((Shader)null, null); defaultProgram.Bind(); // Create objects. Camera = new CameraBase(new Vector3(0, 0, 0), new Vector2(Context.Settings.RenderWidth, Context.Settings.RenderHeight)); MatrixStack = new TransformationStack(); // Setup main map buffer. _mainBuffer = new QuadMapBuffer(MaxRenderable); _mainLineBuffer = new LineMapBuffer(MaxRenderable); // Check if the setup encountered any errors. Helpers.CheckError("renderer setup"); // Setup additional GL arguments. GL.Enable(EnableCap.Blend); GL.DepthFunc(DepthFunction.Lequal); GL.Enable(EnableCap.DepthTest); GL.BlendFunc(BlendingFactorSrc.SrcAlpha, BlendingFactorDest.OneMinusSrcAlpha); // Setup debug. SetupDebug(); }
private void SetFlags() { // Flag missing extensions. int extCount = GL.GetInteger(GetPName.NumExtensions); bool found = false; for (int i = 0; i < extCount; i++) { string extension = GL.GetString(StringNameIndexed.Extensions, i); if (extension.ToLower() != "gl_arb_gpu_shader5") { continue; } found = true; break; } if (!found) { Debugger.Log(MessageType.Warning, MessageSource.GL, "The extension GL_ARB_GPU_SHADER5 was not found."); Shader5ExtensionMissing = true; } }
/// <summary> /// Linux bootstrap. /// </summary> private static void LinuxSetup() { // Get the path of the process AKA where the engine was launched from. string processPath = Path.GetDirectoryName(Process.GetCurrentProcess().MainModule.FileName); if (processPath == null) { throw new Exception("Failed to get the process path."); } // Check if the process path is the current path. if (processPath != Environment.CurrentDirectory) { // Set the current path to the process path. Directory.SetCurrentDirectory(processPath); Unix.chdir(processPath); string processName = Process.GetCurrentProcess().ProcessName; string executableName = processName.Replace(processPath + "/", ""); Debugger.Log(MessageType.Warning, MessageSource.Engine, "It seems the process directory is not the executable directory. Will restart from correct directory."); Debugger.Log(MessageType.Warning, MessageSource.Engine, $"Proper directory is: {processPath}"); Debugger.Log(MessageType.Warning, MessageSource.Engine, $"Executable is: {executableName}"); // Stop the debugger so that the new instance can attach itself to the console and perform logging in peace. Debugger.Stop(); // Restart the process. Process.Start(executableName)?.WaitForExit(); Environment.Exit(0); } // Open libraries. Debugger.Log(MessageType.Warning, MessageSource.Engine, $"libsndio.so.6.1 found: {File.Exists("./Libraries/x64/libsndio.so.6.1")}"); Unix.dlopen("./Libraries/x64/libsndio.so.6.1", Unix.RTLD_NOW); }
private static void InternalSetup(Action <Settings> config = null) { // Initiate bootstrap. Debugger.Log(MessageType.Info, MessageSource.Engine, "-------------------------------"); Debugger.Log(MessageType.Info, MessageSource.Engine, $"Executed at: {Environment.CurrentDirectory}"); Debugger.Log(MessageType.Info, MessageSource.Engine, $"Debug Mode / Debugger Attached: {Debugger.DebugMode} / {System.Diagnostics.Debugger.IsAttached}"); Debugger.Log(MessageType.Info, MessageSource.Engine, $"64Bit: {Environment.Is64BitProcess}"); Debugger.Log(MessageType.Info, MessageSource.Engine, $"OS: {CurrentPlatform.OS} ({Environment.OSVersion})"); Debugger.Log(MessageType.Info, MessageSource.Engine, $"CPU: {Environment.ProcessorCount}"); // Run platform specific boot. switch (CurrentPlatform.OS) { case PlatformName.Windows: WindowsSetup(); break; case PlatformName.Linux: LinuxSetup(); break; } Debugger.Log(MessageType.Info, MessageSource.Engine, "-------------------------------"); Debugger.Log(MessageType.Info, MessageSource.Engine, "Bootstrap complete."); // Apply settings and run initial setup function. Settings initial = new Settings(); config?.Invoke(initial); Settings = initial; // Setup thread manager. GLThread.BindThread(); // Create host if not created. if (Host == null) { try { Debugger.Log(MessageType.Trace, MessageSource.Engine, "Creating host..."); if (CurrentPlatform.OS == PlatformName.Windows || CurrentPlatform.OS == PlatformName.Linux || CurrentPlatform.OS == PlatformName.Mac) { Host = new OtkWindow(); Debugger.Log(MessageType.Info, MessageSource.Engine, "Created OpenTK window host."); } else { throw new Exception("Unsupported platform."); } } catch (Exception ex) { Debugger.Log(MessageType.Error, MessageSource.Engine, "Could not create host. Is the system supported?"); Debugger.Log(MessageType.Error, MessageSource.Engine, ex.ToString()); throw; } } // Apply settings and hook. Host.ApplySettings(Settings); Host.SetHooks(LoopUpdate, LoopDraw, Resize); // Start creating modules. // Scripting engine is first to provide the other modules the ability to expose functions. Debugger.Log(MessageType.Trace, MessageSource.Engine, "Creating scripting engine..."); ScriptingEngine = new ScriptingEngine(); // Asset loader is next so other modules - especially the renderer, can access the file system. Debugger.Log(MessageType.Trace, MessageSource.Engine, "Creating asset loader..."); AssetLoader = new AssetLoader(); // The order of the next modules doesn't matter. Debugger.InitializeModule(); Debugger.Log(MessageType.Trace, MessageSource.Engine, "Creating renderer..."); Renderer = new Renderer(); Debugger.Log(MessageType.Trace, MessageSource.Engine, "Creating sound manager..."); SoundManager = new SoundManager(); Debugger.Log(MessageType.Trace, MessageSource.Engine, "Creating layer manager..."); LayerManager = new LayerManager(); Debugger.Log(MessageType.Trace, MessageSource.Engine, "Creating input manager..."); InputManager = new InputManager(); }