From 1c4ca6c97b689d50fdab0690ebd7b8cd802f25e0 Mon Sep 17 00:00:00 2001 From: Harrison Date: Fri, 5 Jun 2020 23:49:45 -0500 Subject: [PATCH] Lots of progress on rendering textures. --- .vscode/launch.json | 6 +- .../Exceptions/OpenGLErrorException.cs | 4 +- src/SlatedGameToolkit.Framework/GameEngine.cs | 12 +- .../Graphics/{GLEnums.cs => GLEnum.cs} | 2 +- .../Graphics/Meshes/RectangleMesh.cs | 39 +------ .../{GLFunctionDelegates.cs => OpenGL.cs} | 54 +++++---- .../Graphics/Programs/GLProgram.cs | 55 +++++++++- .../Graphics/Programs/GLShaderProgram.cs | 25 +++-- .../Graphics/Render/Batch.cs | 44 ++++++-- .../Graphics/Render/Camera.cs | 103 ++++++++++++++++++ .../Graphics/Render/Camera2D.cs | 22 ++++ .../Graphics/Render/IRenderable.cs | 2 + .../Graphics/Render/Renderer.cs | 14 ++- .../Graphics/Render/Sprite2D.cs | 8 +- .../Graphics/Shaders/GLFragmentShader.cs | 12 +- .../Graphics/Shaders/GLShader.cs | 27 ++--- .../Graphics/Shaders/GLVertexArraySet.cs | 31 +++--- .../Graphics/Shaders/GLVertexShader.cs | 11 +- .../Graphics/Shaders/NormalFragmentShader.cs | 21 ++++ .../Graphics/Shaders/NormalVertexShader.cs | 20 ++++ .../Graphics/Textures/GLTexture2DSet.cs | 38 ++++--- .../Graphics/Textures/Texture.cs | 2 + .../Graphics/Window/WindowContext.cs | 25 +++-- .../Graphics/Window/WindowContextsManager.cs | 9 +- .../Loader/AssetManager.cs | 10 +- .../Loader/ILoadable.cs | 4 +- .../Loader/TextureLoader.cs | 14 +-- .../Resources/default.frag | 11 ++ .../Resources/default.vert | 17 +++ .../SlatedGameToolkit.Framework.csproj | 3 + .../{Manager.cs => StateManager.cs} | 29 +++-- .../StateSystem/States/IState.cs | 2 +- .../{System => CommandSystem}/CommandMap.cs | 4 +- .../CommandProcessor.cs | 16 ++- .../Exceptions/FatalUsageException.cs | 14 +++ .../{System => CommandSystem}/IInvocable.cs | 4 +- .../Interaction/ConsoleInteraction.cs | 2 +- .../Interaction/IInteractable.cs | 2 +- .../Interaction/SingleConsoleInteraction.cs | 30 +++++ .../Commands/GraphicalPlaygroundCommand.cs | 6 +- .../Commands/HelpCommand.cs | 4 +- .../Commands/ListEmbeddedResourcesCommand.cs | 42 +++++++ .../Commands/StopCommand.cs | 4 +- src/SlatedGameToolkit.Tools/Program.cs | 33 +++--- .../Resources/Playground/yhdnbgnc.png | Bin 0 -> 41434 bytes .../SlatedGameToolkit.Tools.csproj | 3 + .../MainState.cs | 26 ++++- 47 files changed, 620 insertions(+), 246 deletions(-) rename src/SlatedGameToolkit.Framework/Graphics/{GLEnums.cs => GLEnum.cs} (99%) rename src/SlatedGameToolkit.Framework/Graphics/{GLFunctionDelegates.cs => OpenGL.cs} (72%) create mode 100644 src/SlatedGameToolkit.Framework/Graphics/Render/Camera.cs create mode 100644 src/SlatedGameToolkit.Framework/Graphics/Render/Camera2D.cs create mode 100644 src/SlatedGameToolkit.Framework/Graphics/Shaders/NormalFragmentShader.cs create mode 100644 src/SlatedGameToolkit.Framework/Graphics/Shaders/NormalVertexShader.cs create mode 100644 src/SlatedGameToolkit.Framework/Resources/default.frag create mode 100644 src/SlatedGameToolkit.Framework/Resources/default.vert rename src/SlatedGameToolkit.Framework/StateSystem/{Manager.cs => StateManager.cs} (85%) rename src/SlatedGameToolkit.Tools/{System => CommandSystem}/CommandMap.cs (95%) rename src/SlatedGameToolkit.Tools/{System => CommandSystem}/CommandProcessor.cs (60%) create mode 100644 src/SlatedGameToolkit.Tools/CommandSystem/Exceptions/FatalUsageException.cs rename src/SlatedGameToolkit.Tools/{System => CommandSystem}/IInvocable.cs (93%) rename src/SlatedGameToolkit.Tools/{System => CommandSystem}/Interaction/ConsoleInteraction.cs (89%) rename src/SlatedGameToolkit.Tools/{System => CommandSystem}/Interaction/IInteractable.cs (69%) create mode 100644 src/SlatedGameToolkit.Tools/CommandSystem/Interaction/SingleConsoleInteraction.cs create mode 100644 src/SlatedGameToolkit.Tools/Commands/ListEmbeddedResourcesCommand.cs create mode 100644 src/SlatedGameToolkit.Tools/Resources/Playground/yhdnbgnc.png rename src/SlatedGameToolkit.Tools/Utilities/{GraphicalPlayground => Playground}/MainState.cs (50%) diff --git a/.vscode/launch.json b/.vscode/launch.json index 16ef71c..d917045 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -5,16 +5,16 @@ "version": "0.2.0", "configurations": [ { - "name": ".NET Core Launch (console)", + "name": "SlatedGameToolkit.Tools", "type": "coreclr", "request": "launch", "preLaunchTask": "build", // If you have changed target frameworks, make sure to update the program path. - "program": "${workspaceFolder}/src/SlatedGameToolkit.Tools/bin/Debug/netcoreapp3.1/SlatedGameToolkit.Tools.dll", + "program": "${workspaceFolder}/src/SlatedGameToolkit.Tools/bin/Debug/netcoreapp3.1/SlatedGameToolkit.Tools.exe", "args": [], "cwd": "${workspaceFolder}/src/SlatedGameToolkit.Tools", // For more information about the 'console' field, see https://aka.ms/VSCode-CS-LaunchJson-Console - "console": "internalConsole", + "console": "integratedTerminal", "stopAtEntry": false }, { diff --git a/src/SlatedGameToolkit.Framework/Exceptions/OpenGLErrorException.cs b/src/SlatedGameToolkit.Framework/Exceptions/OpenGLErrorException.cs index 110fdbd..ddef5d6 100644 --- a/src/SlatedGameToolkit.Framework/Exceptions/OpenGLErrorException.cs +++ b/src/SlatedGameToolkit.Framework/Exceptions/OpenGLErrorException.cs @@ -21,8 +21,8 @@ namespace SlatedGameToolkit.Framework.Exceptions /// public static void CheckGLErrorStatus() { uint errorCode = WindowContextsManager.CurrentWindowContext.GetGLStatus(); - if (errorCode != (uint) GLEnums.GL_NO_ERROR) { - throw new OpenGLErrorException(errorCode, "OpenGL error: " + errorCode.ToString()); + if (errorCode != (uint) GLEnum.GL_NO_ERROR) { + throw new OpenGLErrorException(errorCode, string.Format("OpenGL error ({0}): {1}", errorCode, ((GLEnum) errorCode))); } } } diff --git a/src/SlatedGameToolkit.Framework/GameEngine.cs b/src/SlatedGameToolkit.Framework/GameEngine.cs index a7685c1..b408eeb 100644 --- a/src/SlatedGameToolkit.Framework/GameEngine.cs +++ b/src/SlatedGameToolkit.Framework/GameEngine.cs @@ -4,6 +4,7 @@ using SDL2; using Serilog; using Serilog.Core; using SlatedGameToolkit.Framework.Exceptions; +using SlatedGameToolkit.Framework.Graphics; using SlatedGameToolkit.Framework.Graphics.Window; using SlatedGameToolkit.Framework.Input; using SlatedGameToolkit.Framework.Input.Devices; @@ -76,9 +77,10 @@ namespace SlatedGameToolkit.Framework { private static void Loop(Object o) { if (!(o is IState)) throw new InternalFrameworkException(String.Format("Expected initial state object for asynchronous loop. Got {0}", o)); - IState initialState = (IState) o; - Manager manager = new Manager(initialState); + StateManager manager = new StateManager(); try { + IState initialState = (IState) o; + manager.Initialize(initialState); DateTime currentTime = DateTime.Now; TimeSpan timePassedFromLastUpdate = TimeSpan.Zero; TimeSpan timePassedFromLastRender = TimeSpan.Zero; @@ -170,6 +172,8 @@ namespace SlatedGameToolkit.Framework { timePassedFromLastRender = TimeSpan.Zero; } } + } catch (Exception e) { + Logger.Fatal(e.ToString()); } finally { stopped = true; manager.Dispose(); @@ -228,8 +232,8 @@ namespace SlatedGameToolkit.Framework { throw new FrameworkSDLException(); } - if (SDL.SDL_GL_SetAttribute(SDL.SDL_GLattr.SDL_GL_CONTEXT_MAJOR_VERSION, 3) < 0) throw new FrameworkSDLException(); - if (SDL.SDL_GL_SetAttribute(SDL.SDL_GLattr.SDL_GL_CONTEXT_MINOR_VERSION, 1) < 0) throw new FrameworkSDLException(); + if (SDL.SDL_GL_SetAttribute(SDL.SDL_GLattr.SDL_GL_CONTEXT_MAJOR_VERSION, OpenGL.OpenGLMajorVersion) < 0) throw new FrameworkSDLException(); + if (SDL.SDL_GL_SetAttribute(SDL.SDL_GLattr.SDL_GL_CONTEXT_MINOR_VERSION, OpenGL.OpenGLMinorVersion) < 0) throw new FrameworkSDLException(); if (SDL.SDL_GL_SetAttribute(SDL.SDL_GLattr.SDL_GL_CONTEXT_PROFILE_MASK, SDL.SDL_GLprofile.SDL_GL_CONTEXT_PROFILE_CORE) < 0) throw new FrameworkSDLException(); thread = new Thread(Loop); diff --git a/src/SlatedGameToolkit.Framework/Graphics/GLEnums.cs b/src/SlatedGameToolkit.Framework/Graphics/GLEnum.cs similarity index 99% rename from src/SlatedGameToolkit.Framework/Graphics/GLEnums.cs rename to src/SlatedGameToolkit.Framework/Graphics/GLEnum.cs index 3dfc1ae..e795ad5 100644 --- a/src/SlatedGameToolkit.Framework/Graphics/GLEnums.cs +++ b/src/SlatedGameToolkit.Framework/Graphics/GLEnum.cs @@ -1,6 +1,6 @@ namespace SlatedGameToolkit.Framework.Graphics { - public enum GLEnums: uint { + public enum GLEnum: uint { GL_DEPTH_BUFFER_BIT = 0x00000100, GL_STENCIL_BUFFER_BIT = 0x00000400, GL_COLOR_BUFFER_BIT = 0x00004000, diff --git a/src/SlatedGameToolkit.Framework/Graphics/Meshes/RectangleMesh.cs b/src/SlatedGameToolkit.Framework/Graphics/Meshes/RectangleMesh.cs index da337c7..831d871 100644 --- a/src/SlatedGameToolkit.Framework/Graphics/Meshes/RectangleMesh.cs +++ b/src/SlatedGameToolkit.Framework/Graphics/Meshes/RectangleMesh.cs @@ -6,9 +6,9 @@ namespace SlatedGameToolkit.Framework.Graphics.Meshes { public abstract class RectangleMesh : IMeshable { - private bool changed = true; - private Matrix4x4 matRot, matTrans, matScale; - private Vector3 rotation, translation, scale; + private Matrix4x4 matRot; + private bool changed; + private Vector3 rotation; private Vector2 origin, dimensions; protected readonly Vector2[] textureCoords = new Vector2[4]; private ValueTuple[] vertices = new ValueTuple[4]; @@ -78,35 +78,6 @@ namespace SlatedGameToolkit.Framework.Graphics.Meshes public uint[] Elements { get {return indices; } } - public Vector3 Translation - { - get - { - return rotation; - } - set - { - changed = true; - translation = value; - matTrans = Matrix4x4.CreateTranslation(value); - } - } - - public Vector3 Scale - { - get - { - return scale; - } - - set - { - changed = true; - scale = value; - matScale = Matrix4x4.CreateScale(value); - } - } - public Vector3 Rotation { get @@ -122,16 +93,16 @@ namespace SlatedGameToolkit.Framework.Graphics.Meshes } } private void CalculateVertices() { + if (!changed) return; Vector3[] baseVerts = new Vector3[4]; baseVerts[0] = new Vector3(this.origin, 0); baseVerts[1] = new Vector3(this.origin.X + this.dimensions.X, this.origin.Y, 0); baseVerts[2] = new Vector3(baseVerts[1].X, this.origin.Y + this.dimensions.Y, 0); baseVerts[3] = new Vector3(this.origin.X, baseVerts[2].Y, 0); - Matrix4x4 transform = matTrans * matRot * matScale; for (int i = 0; i < vertices.Length; i++) { - vertices[i] = new ValueTuple(Vector3.Transform(baseVerts[i], transform), textureCoords[i]); + vertices[i] = new ValueTuple(Vector3.Transform(baseVerts[i], matRot), textureCoords[i]); } changed = false; } diff --git a/src/SlatedGameToolkit.Framework/Graphics/GLFunctionDelegates.cs b/src/SlatedGameToolkit.Framework/Graphics/OpenGL.cs similarity index 72% rename from src/SlatedGameToolkit.Framework/Graphics/GLFunctionDelegates.cs rename to src/SlatedGameToolkit.Framework/Graphics/OpenGL.cs index e65556c..ba98028 100644 --- a/src/SlatedGameToolkit.Framework/Graphics/GLFunctionDelegates.cs +++ b/src/SlatedGameToolkit.Framework/Graphics/OpenGL.cs @@ -2,23 +2,25 @@ using System; using System.Runtime.InteropServices; using SDL2; using SlatedGameToolkit.Framework.Exceptions; +using SlatedGameToolkit.Framework.Graphics.Programs; +using SlatedGameToolkit.Framework.Graphics.Shaders; namespace SlatedGameToolkit.Framework.Graphics { [UnmanagedFunctionPointer(CallingConvention.Cdecl)] public delegate void GLClearColour(float r, float g, float b); [UnmanagedFunctionPointer(CallingConvention.Cdecl)] - public delegate void GLClear(GLEnums flags); + public delegate void GLClear(GLEnum flags); [UnmanagedFunctionPointer(CallingConvention.Cdecl)] public delegate void GLViewport(int x, int y, int width, int height); [UnmanagedFunctionPointer(CallingConvention.Cdecl)] - public delegate UIntPtr GLCreateShader(GLEnums type); + public delegate UIntPtr GLCreateShader(GLEnum type); [UnmanagedFunctionPointer(CallingConvention.Cdecl)] - public delegate UIntPtr GLShaderSource(UIntPtr shaderHandle, uint count, string program, int[] length); + public delegate UIntPtr GLShaderSource(UIntPtr shaderHandle, uint count, string[] program, int[] length); [UnmanagedFunctionPointer(CallingConvention.Cdecl)] public delegate void GLCompileShader(UIntPtr shaderHandle); [UnmanagedFunctionPointer(CallingConvention.Cdecl)] - public delegate void GLGetShaderLogInfo(UIntPtr shader, uint maxLength, out uint writtenLength, out string output); + public delegate void GLGetShaderInfoLog(UIntPtr shader, uint maxLength, out uint writtenLength, byte[] output); [UnmanagedFunctionPointer(CallingConvention.Cdecl)] public delegate UIntPtr GLCreateProgram(); [UnmanagedFunctionPointer(CallingConvention.Cdecl)] @@ -32,11 +34,11 @@ namespace SlatedGameToolkit.Framework.Graphics [UnmanagedFunctionPointer(CallingConvention.Cdecl)] public delegate void GLBindAttribLocation(UIntPtr program, uint index, string name); [UnmanagedFunctionPointer(CallingConvention.Cdecl)] - public delegate void GLGetProgramInfoLog(UIntPtr program, uint maxLength, out uint writtenLength, out string output); + public delegate void GLGetProgramInfoLog(UIntPtr program, uint maxLength, out uint writtenLength, byte[] output); [UnmanagedFunctionPointer(CallingConvention.Cdecl)] public delegate void GLLinkProgram(UIntPtr program); [UnmanagedFunctionPointer(CallingConvention.Cdecl)] - public delegate void GLProgramParameter(UIntPtr program, GLEnums parameterName, int value); + public delegate void GLProgramParameter(UIntPtr program, GLEnum parameterName, int value); [UnmanagedFunctionPointer(CallingConvention.Cdecl)] public delegate void GLUseProgram(UIntPtr program); [UnmanagedFunctionPointer(CallingConvention.Cdecl)] @@ -46,21 +48,21 @@ namespace SlatedGameToolkit.Framework.Graphics [UnmanagedFunctionPointer(CallingConvention.Cdecl)] public delegate void GLBindProgramPipeline(UIntPtr pipeline); [UnmanagedFunctionPointer(CallingConvention.Cdecl)] - public delegate void GLUseProgramStages(UIntPtr pipeline, GLEnums bitField, UIntPtr program); + public delegate void GLUseProgramStages(UIntPtr pipeline, GLEnum bitField, UIntPtr program); [UnmanagedFunctionPointer(CallingConvention.Cdecl)] public delegate uint GLGetError(); [UnmanagedFunctionPointer(CallingConvention.Cdecl)] public delegate int GLGetAttribLocation(UIntPtr program, string attribName); [UnmanagedFunctionPointer(CallingConvention.Cdecl)] - public delegate void GLBindBuffer(GLEnums target, UIntPtr buffer); + public delegate void GLBindBuffer(GLEnum target, UIntPtr buffer); [UnmanagedFunctionPointer(CallingConvention.Cdecl)] public delegate void GLGenBuffers(uint size, UIntPtr[] buffers); [UnmanagedFunctionPointer(CallingConvention.Cdecl)] - public unsafe delegate void GLBufferData(GLEnums target, uint size, Array data, GLEnums usage); + public unsafe delegate void GLBufferData(GLEnum target, uint size, Array data, GLEnum usage); [UnmanagedFunctionPointer(CallingConvention.Cdecl)] public delegate void GLDeleteBuffers(uint size, UIntPtr[] buffers); [UnmanagedFunctionPointer(CallingConvention.Cdecl)] - public delegate void GLVertexAttribPointer(uint index, int size, GLEnums type, bool normalized, uint stride, uint offset); + public delegate void GLVertexAttribPointer(uint index, int size, GLEnum type, bool normalized, uint stride, uint offset); [UnmanagedFunctionPointer(CallingConvention.Cdecl)] public delegate void GLGenVertexArrays(uint amount, UIntPtr[] arrays); [UnmanagedFunctionPointer(CallingConvention.Cdecl)] @@ -72,33 +74,39 @@ namespace SlatedGameToolkit.Framework.Graphics [UnmanagedFunctionPointer(CallingConvention.Cdecl)] public delegate void GLDisableVertexAttribArray(uint attribIndex); [UnmanagedFunctionPointer(CallingConvention.Cdecl)] - public delegate void GLDrawArrays(GLEnums mode, int first, uint count); + public delegate void GLDrawArrays(GLEnum mode, int first, uint count); [UnmanagedFunctionPointer(CallingConvention.Cdecl)] - public delegate void GLDrawElements(GLEnums mode, uint count, int offset); + public delegate void GLDrawElements(GLEnum mode, uint count, int offset); [UnmanagedFunctionPointer(CallingConvention.Cdecl)] - public delegate void GLMultiDrawArrays(GLEnums mode, int[] first, uint[] count, uint primout); + public delegate void GLMultiDrawArrays(GLEnum mode, int[] first, uint[] count, uint primout); [UnmanagedFunctionPointer(CallingConvention.Cdecl)] - public delegate void GLMultiDrawElements(GLEnums mode, uint[] count, GLEnums type, IntPtr indices, int length); + public delegate void GLMultiDrawElements(GLEnum mode, uint[] count, GLEnum type, uint[] indiceOffsets, int length); [UnmanagedFunctionPointer(CallingConvention.Cdecl)] public delegate void GLGenTextures(uint n, UIntPtr[] textureHandles); [UnmanagedFunctionPointer(CallingConvention.Cdecl)] - public delegate void GLBindTexture(GLEnums target, UIntPtr texture); + public delegate void GLBindTexture(GLEnum target, UIntPtr texture); [UnmanagedFunctionPointer(CallingConvention.Cdecl)] public delegate void GLDeleteTextures(uint n, UIntPtr[] textureHandles); [UnmanagedFunctionPointer(CallingConvention.Cdecl)] - public delegate void GLTexParameteri(GLEnums target, GLEnums pname, int value); + public delegate void GLTexParameteri(GLEnum target, GLEnum pname, int value); [UnmanagedFunctionPointer(CallingConvention.Cdecl)] - public delegate void GLTexParameterf(GLEnums target, GLEnums pname, float value); + public delegate void GLTexParameterf(GLEnum target, GLEnum pname, float value); [UnmanagedFunctionPointer(CallingConvention.Cdecl)] - public delegate void GLTexParameteriv(GLEnums target, GLEnums pname, int[] values); + public delegate void GLTexParameteriv(GLEnum target, GLEnum pname, int[] values); [UnmanagedFunctionPointer(CallingConvention.Cdecl)] - public delegate void GLTexParameterfv(GLEnums target, GLEnums pname, float[] values); + public delegate void GLTexParameterfv(GLEnum target, GLEnum pname, float[] values); [UnmanagedFunctionPointer(CallingConvention.Cdecl)] - public delegate void GLTexImage2D(GLEnums target, int level, int publicFormat, uint width, uint height, int border, GLEnums format, GLEnums type, byte[] data); + public delegate void GLTexImage2D(GLEnum target, int level, int publicFormat, uint width, uint height, int border, GLEnum format, GLEnum type, byte[] data); [UnmanagedFunctionPointer(CallingConvention.Cdecl)] - public delegate void GLGenerateMipMap(GLEnums target); + public delegate void GLGenerateMipmap(GLEnum target); + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate int GLGetUniformLocation(UIntPtr program, string name); + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate void GLUniformMatrix4fv(int index, uint count, bool transpose, float[] matrix); - public static class GLFunctionUtils { + public static class OpenGL { + public const int OpenGLMajorVersion = 3; + public const int OpenGLMinorVersion = 3; /// /// Attempts to retrieve and instantiate an OpenGL function as a C# delegate. /// Make sure the delegates signature is correct as failing to do so will potentially @@ -112,7 +120,7 @@ namespace SlatedGameToolkit.Framework.Graphics public static T RetrieveGLDelegate(string functionName) where T : Delegate { GameEngine.Logger.Debug(String.Format("Retrieving function with name: {0}", functionName)); IntPtr functionAddress = SDL.SDL_GL_GetProcAddress(functionName); - if (functionAddress == null) throw new FrameworkSDLException(); + if (functionAddress.Equals(IntPtr.Zero)) throw new FrameworkSDLException(); return Marshal.GetDelegateForFunctionPointer(functionAddress); } } diff --git a/src/SlatedGameToolkit.Framework/Graphics/Programs/GLProgram.cs b/src/SlatedGameToolkit.Framework/Graphics/Programs/GLProgram.cs index dd9b316..3413fd8 100644 --- a/src/SlatedGameToolkit.Framework/Graphics/Programs/GLProgram.cs +++ b/src/SlatedGameToolkit.Framework/Graphics/Programs/GLProgram.cs @@ -1,7 +1,10 @@ using System; +using System.Numerics; using System.Runtime.InteropServices; +using System.Text; using SDL2; using SlatedGameToolkit.Framework.Exceptions; +using SlatedGameToolkit.Framework.Graphics; using SlatedGameToolkit.Framework.Graphics.Window; namespace SlatedGameToolkit.Framework.Graphics.Programs @@ -21,6 +24,10 @@ namespace SlatedGameToolkit.Framework.Graphics.Programs protected GLGetProgramInfoLog getProgramInfoLog; protected GLProgramParameter programParameter; private GLUseProgram useProgram; + private readonly GLGetAttribLocation getAttribLocation; + private readonly GLGetUniformLocation getUniformLocation; + private readonly GLUniformMatrix4fv uniformMatrix4Fv; + /// /// Creates an OpenGL program. @@ -29,14 +36,43 @@ namespace SlatedGameToolkit.Framework.Graphics.Programs /// internal GLProgram() { this.context = WindowContextsManager.CurrentWindowContext; - createProgram = GLFunctionUtils.RetrieveGLDelegate("glCreateProgram"); - deleteProgram = GLFunctionUtils.RetrieveGLDelegate("glDeleteProgram"); - linkProgram = GLFunctionUtils.RetrieveGLDelegate("glLinkProgram"); - getProgramInfoLog = GLFunctionUtils.RetrieveGLDelegate("glGetProgramInfoLog"); - programParameter = GLFunctionUtils.RetrieveGLDelegate("glProgramParameteri"); - useProgram = GLFunctionUtils.RetrieveGLDelegate("glUseProgram"); + createProgram = OpenGL.RetrieveGLDelegate("glCreateProgram"); + deleteProgram = OpenGL.RetrieveGLDelegate("glDeleteProgram"); + linkProgram = OpenGL.RetrieveGLDelegate("glLinkProgram"); + getProgramInfoLog = OpenGL.RetrieveGLDelegate("glGetProgramInfoLog"); + programParameter = OpenGL.RetrieveGLDelegate("glProgramParameteri"); + useProgram = OpenGL.RetrieveGLDelegate("glUseProgram"); + getAttribLocation = OpenGL.RetrieveGLDelegate("glGetAttribLocation"); + getUniformLocation = OpenGL.RetrieveGLDelegate("glGetUniformLocation"); + uniformMatrix4Fv = OpenGL.RetrieveGLDelegate("glUniformMatrix4fv"); handle = createProgram(); + OpenGLErrorException.CheckGLErrorStatus(); + } + + + public int GetAttributeLocation(string name) { + int res = getAttribLocation(handle, name); + OpenGLErrorException.CheckGLErrorStatus(); + return res; + } + + public int GetUniformLocation(string name) { + int res = getUniformLocation(handle, name); + OpenGLErrorException.CheckGLErrorStatus(); + return res; + } + + public void SetUniformMatrix4x4(int location, Matrix4x4 matrix, bool transpose = false) { + Use(); + float[] mat4 = new float[] { + matrix.M11, matrix.M21, matrix.M31, matrix.M41, + matrix.M21, matrix.M22, matrix.M23, matrix.M24, + matrix.M31, matrix.M32, matrix.M33, matrix.M34, + matrix.M41, matrix.M42, matrix.M43, matrix.M44 + }; + uniformMatrix4Fv(location, 1, transpose, mat4); + OpenGLErrorException.CheckGLErrorStatus(); } /// @@ -44,6 +80,13 @@ namespace SlatedGameToolkit.Framework.Graphics.Programs /// public virtual void Use() { useProgram(handle); + uint written; + byte[] log = new byte[2048]; + getProgramInfoLog(handle, (uint)log.Length, out written, log); + if (written > 0) { + throw new OpenGLException(Encoding.UTF8.GetString(log)); + } + OpenGLErrorException.CheckGLErrorStatus(); } /// diff --git a/src/SlatedGameToolkit.Framework/Graphics/Programs/GLShaderProgram.cs b/src/SlatedGameToolkit.Framework/Graphics/Programs/GLShaderProgram.cs index 62e58ac..9a46d16 100644 --- a/src/SlatedGameToolkit.Framework/Graphics/Programs/GLShaderProgram.cs +++ b/src/SlatedGameToolkit.Framework/Graphics/Programs/GLShaderProgram.cs @@ -1,5 +1,7 @@ using System; +using System.Text; using SlatedGameToolkit.Framework.Exceptions; +using SlatedGameToolkit.Framework.Graphics; using SlatedGameToolkit.Framework.Graphics.Shaders; namespace SlatedGameToolkit.Framework.Graphics.Programs @@ -9,37 +11,38 @@ namespace SlatedGameToolkit.Framework.Graphics.Programs private readonly GLShader[] shaders; private readonly GLAttachShader attachShader; private readonly GLDetachShader detachShader; - private GLBindAttribLocation bindAttribLocation; - public GLShaderProgram(bool separable = true, params GLShader[] shaders) : base() { + public GLShaderProgram(params GLShader[] shaders) : base() { if (shaders.Length == 0) throw new ArgumentException("Requires at least one shader for shader program."); this.shaders = shaders; + + attachShader = OpenGL.RetrieveGLDelegate("glAttachShader"); + detachShader = OpenGL.RetrieveGLDelegate("glDetachShader"); + - attachShader = GLFunctionUtils.RetrieveGLDelegate("glAttachShader"); - bindAttribLocation = GLFunctionUtils.RetrieveGLDelegate("glBindAttribLocation"); - detachShader = GLFunctionUtils.RetrieveGLDelegate("glDetachShader"); - - Use(); - foreach (GLShader shader in shaders) { attachShader(handle, shader.Handle); + OpenGLErrorException.CheckGLErrorStatus(); } linkProgram(handle); uint length; - string log; - getProgramInfoLog(handle, 1024, out length, out log); + byte[] log = new byte[2048]; + getProgramInfoLog(handle, (uint)log.Length, out length, log); if (length > 0) { Dispose(); - throw new OpenGLException(log); + throw new OpenGLException(Encoding.UTF8.GetString(log)); } + OpenGLErrorException.CheckGLErrorStatus(); foreach (GLShader shader in shaders) { detachShader(handle, shader.Handle); + OpenGLErrorException.CheckGLErrorStatus(); } } + /// /// Disposes of the shaders and this program. /// diff --git a/src/SlatedGameToolkit.Framework/Graphics/Render/Batch.cs b/src/SlatedGameToolkit.Framework/Graphics/Render/Batch.cs index 62b64bf..0e63711 100644 --- a/src/SlatedGameToolkit.Framework/Graphics/Render/Batch.cs +++ b/src/SlatedGameToolkit.Framework/Graphics/Render/Batch.cs @@ -5,47 +5,60 @@ using SlatedGameToolkit.Framework.Graphics.Meshes; using SlatedGameToolkit.Framework.Graphics; using SlatedGameToolkit.Framework.Graphics.Textures; using SlatedGameToolkit.Framework.Graphics.Shaders; +using SlatedGameToolkit.Framework.Graphics.Programs; +using SlatedGameToolkit.Framework.Exceptions; namespace SlatedGameToolkit.Framework.Graphics.Render { public class Batch : IDisposable { + private int projLoc, viewLoc, modelLoc; + private Camera camera; + private GLShaderProgram shaderProgram; private Texture texture; private bool disposed; private VertexArray vertexArray; - private const int VERTEX_LENGTH = 6; + private const int VERTEX_LENGTH = 9; private GLMultiDrawElements multiDrawElements; public bool Batching { get; private set; } + + private Matrix4x4 modelsMatrix; private float[] data; private uint[] indices; private uint[] lengths; private uint[] offsets; private uint dataIndex, indicesIndex, lengthsIndex; - public Batch(uint BatchVertexSize = 4096) { - multiDrawElements = GLFunctionUtils.RetrieveGLDelegate("glMultiDrawElements"); + public Batch(Camera camera, GLShaderProgram shaderProgram, uint BatchVertexSize = 4096) { + multiDrawElements = OpenGL.RetrieveGLDelegate("glMultiDrawElements"); + this.camera = camera; + this.shaderProgram = shaderProgram; indices = new uint[BatchVertexSize]; lengths = new uint[BatchVertexSize]; offsets = new uint[BatchVertexSize]; data = new float[BatchVertexSize * VERTEX_LENGTH]; - vertexArray = new VertexArray(); - + GLGetAttribLocation attribLocation = OpenGL.RetrieveGLDelegate("glGetAttribLocation"); VertexAttributeDefinition[] definitions = new VertexAttributeDefinition[3]; - definitions[0] = new VertexAttributeDefinition(0, 3, 3 * sizeof(float), 0); - definitions[1] = new VertexAttributeDefinition(1, 4, 1 * sizeof(float), 3 * sizeof(float)); - definitions[2] = new VertexAttributeDefinition(2, 2, 2 * sizeof(float), 4 * sizeof(float)); + definitions[0] = new VertexAttributeDefinition((uint)shaderProgram.GetAttributeLocation("aPosition"), 3, 3 * sizeof(float), 0); + definitions[1] = new VertexAttributeDefinition((uint)shaderProgram.GetAttributeLocation("aColor"), 4, 4 * sizeof(float), 3 * sizeof(float)); + definitions[2] = new VertexAttributeDefinition((uint)shaderProgram.GetAttributeLocation("aTexCoord"), 2, 2 * sizeof(float), (4 + 3) * sizeof(float)); + modelLoc = shaderProgram.GetUniformLocation("models"); + viewLoc = shaderProgram.GetUniformLocation("view"); + projLoc = shaderProgram.GetUniformLocation("projection"); vertexArray.defineVertexAttributes(definitions: definitions); } - public virtual void Begin(Texture texture) { + public virtual void Begin(Texture texture, Matrix4x4 modelsMatrix) { if (Batching) throw new InvalidOperationException("This batch is already started."); this.texture = texture ?? throw new ArgumentNullException("texture"); this.Batching = true; + this.modelsMatrix = modelsMatrix; } public virtual void Dispose() { if (disposed) return; + if (Batching) End(); disposed = true; vertexArray.Dispose(); } @@ -62,7 +75,13 @@ namespace SlatedGameToolkit.Framework.Graphics.Render data[dataIndex] = vertices[i].Item1.Z; dataIndex++; - data[dataIndex] = color.ToArgb(); + data[dataIndex] = (float) color.A / byte.MaxValue; + dataIndex++; + data[dataIndex] = (float) color.R / byte.MaxValue; + dataIndex++; + data[dataIndex] = (float) color.G / byte.MaxValue; + dataIndex++; + data[dataIndex] = (float) color.B / byte.MaxValue; dataIndex++; data[dataIndex] = vertices[i].Item2.X; @@ -93,7 +112,10 @@ namespace SlatedGameToolkit.Framework.Graphics.Render dataIndex = 0; indicesIndex = 0; lengthsIndex = 0; - multiDrawElements(GLEnums.GL_TRIANGLE_STRIP, lengths, GLEnums.GL_UNSIGNED_INT, IntPtr.Zero, lengths.Length); + shaderProgram.SetUniformMatrix4x4(modelLoc, modelsMatrix); + shaderProgram.SetUniformMatrix4x4(viewLoc, camera.ViewMatrix); + shaderProgram.SetUniformMatrix4x4(projLoc, camera.ProjectionMatrix); + multiDrawElements(GLEnum.GL_TRIANGLE_STRIP, lengths, GLEnum.GL_UNSIGNED_INT, offsets, lengths.Length); } } } \ No newline at end of file diff --git a/src/SlatedGameToolkit.Framework/Graphics/Render/Camera.cs b/src/SlatedGameToolkit.Framework/Graphics/Render/Camera.cs new file mode 100644 index 0000000..74bc458 --- /dev/null +++ b/src/SlatedGameToolkit.Framework/Graphics/Render/Camera.cs @@ -0,0 +1,103 @@ +using System.Numerics; + +namespace SlatedGameToolkit.Framework.Graphics.Render +{ + public class Camera { + private bool viewUpdated, projectionUpdated; + public virtual Vector3 Up { get; set; } + private Vector3 trans, lookAt; + private float nearField, farField; + private float width, height; + private Matrix4x4 lookAtMatrix; + private Matrix4x4 view, projection; + public virtual bool Orthographic { get; set; } + + public virtual Vector3 Position { + get { + return trans; + } + + set { + this.trans = value; + viewUpdated = true; + } + } + + public Vector3 LookAt { + get { + return lookAt; + } + + set { + this.lookAt = value; + lookAtMatrix = Matrix4x4.CreateLookAt(-trans, value, Up); + viewUpdated = true; + } + } + + public virtual float NearField { + get { + return nearField; + } + + set { + nearField = value; + projectionUpdated = true; + } + } + + public virtual float FarField { + get { + return farField; + } + + set { + farField = value; + projectionUpdated = true; + } + } + + public virtual float Width { + get { + return width; + } + set { + width = value; + projectionUpdated = true; + } + } + public virtual float Height { + get { + return height; + } + set { + height = value; + projectionUpdated = true; + } + } + + public Matrix4x4 ViewMatrix { + get { + if (viewUpdated) { + view = Matrix4x4.CreateTranslation(-trans) * lookAtMatrix; + viewUpdated = false; + } + return view; + } + } + + public Matrix4x4 ProjectionMatrix { + get { + if (projectionUpdated) { + if (Orthographic) { + projection = Matrix4x4.CreateOrthographic(width, height, nearField, farField); + } else { + projection = Matrix4x4.CreatePerspective(width, height, nearField, farField); + } + projectionUpdated = false; + } + return projection; + } + } + } +} \ No newline at end of file diff --git a/src/SlatedGameToolkit.Framework/Graphics/Render/Camera2D.cs b/src/SlatedGameToolkit.Framework/Graphics/Render/Camera2D.cs new file mode 100644 index 0000000..a54058d --- /dev/null +++ b/src/SlatedGameToolkit.Framework/Graphics/Render/Camera2D.cs @@ -0,0 +1,22 @@ +using System.Numerics; + +namespace SlatedGameToolkit.Framework.Graphics.Render +{ + public class Camera2D : Camera { + public new Vector2 Position { + get { + return new Vector2(base.Position.X, base.Position.Y); + } + + set { + base.Position = new Vector3(value, 0); + } + } + + public Camera2D() { + this.Orthographic = true; + this.Up = Vector3.UnitY; + this.LookAt = new Vector3(0, 0, 1); + } + } +} \ No newline at end of file diff --git a/src/SlatedGameToolkit.Framework/Graphics/Render/IRenderable.cs b/src/SlatedGameToolkit.Framework/Graphics/Render/IRenderable.cs index 77de1ca..a26f383 100644 --- a/src/SlatedGameToolkit.Framework/Graphics/Render/IRenderable.cs +++ b/src/SlatedGameToolkit.Framework/Graphics/Render/IRenderable.cs @@ -1,3 +1,4 @@ +using System.Drawing; using SlatedGameToolkit.Framework.Graphics.Meshes; using SlatedGameToolkit.Framework.Graphics.Textures; @@ -6,5 +7,6 @@ namespace SlatedGameToolkit.Framework.Graphics.Render public interface IRenderable : IMeshable { Texture Texture { get; } + Color Color { get; } } } \ No newline at end of file diff --git a/src/SlatedGameToolkit.Framework/Graphics/Render/Renderer.cs b/src/SlatedGameToolkit.Framework/Graphics/Render/Renderer.cs index f27c776..a538632 100644 --- a/src/SlatedGameToolkit.Framework/Graphics/Render/Renderer.cs +++ b/src/SlatedGameToolkit.Framework/Graphics/Render/Renderer.cs @@ -1,5 +1,8 @@ using System; using System.Drawing; +using System.Numerics; +using SlatedGameToolkit.Framework.Exceptions; +using SlatedGameToolkit.Framework.Graphics.Programs; using SlatedGameToolkit.Framework.Graphics.Textures; namespace SlatedGameToolkit.Framework.Graphics.Render @@ -13,8 +16,8 @@ namespace SlatedGameToolkit.Framework.Graphics.Render this.batch = batch; } - public Renderer() { - this.batch = new Batch(); + public Renderer(Camera camera, GLShaderProgram program, uint BatchVertexSize = 4096) { + this.batch = new Batch(camera, program, BatchVertexSize); } public void Dispose() @@ -22,14 +25,13 @@ namespace SlatedGameToolkit.Framework.Graphics.Render batch.Dispose(); } - public void Queue(IRenderable renderable, Color color) { + public void Queue(IRenderable renderable, Matrix4x4 modelsMatrix) { if (renderable.Texture != currentTexture) { if (batch.Batching) batch.End(); currentTexture = renderable.Texture; - currentTexture.Use(); - batch.Begin(currentTexture); + batch.Begin(currentTexture, modelsMatrix); } - batch.Add(renderable, color); + batch.Add(renderable, renderable.Color); } public void Render() { diff --git a/src/SlatedGameToolkit.Framework/Graphics/Render/Sprite2D.cs b/src/SlatedGameToolkit.Framework/Graphics/Render/Sprite2D.cs index b7223cc..6aff36f 100644 --- a/src/SlatedGameToolkit.Framework/Graphics/Render/Sprite2D.cs +++ b/src/SlatedGameToolkit.Framework/Graphics/Render/Sprite2D.cs @@ -1,3 +1,4 @@ +using System.Drawing; using SlatedGameToolkit.Framework.Graphics.Meshes; using SlatedGameToolkit.Framework.Graphics.Textures; @@ -6,7 +7,10 @@ namespace SlatedGameToolkit.Framework.Graphics.Render public class Sprite2D : RectangleMesh, IRenderable { public Texture Texture { get; private set; } - public Sprite2D(Texture texture) { + + public Color Color { get; private set; } + + public Sprite2D(Texture texture, Color color) { this.Texture = texture; this.textureCoords[0].X = -1; this.textureCoords[0].Y = -1; @@ -19,6 +23,8 @@ namespace SlatedGameToolkit.Framework.Graphics.Render this.textureCoords[3].X = -1; this.textureCoords[3].Y = 1; + + this.Color = color; } } } \ No newline at end of file diff --git a/src/SlatedGameToolkit.Framework/Graphics/Shaders/GLFragmentShader.cs b/src/SlatedGameToolkit.Framework/Graphics/Shaders/GLFragmentShader.cs index 432dd41..407a4ba 100644 --- a/src/SlatedGameToolkit.Framework/Graphics/Shaders/GLFragmentShader.cs +++ b/src/SlatedGameToolkit.Framework/Graphics/Shaders/GLFragmentShader.cs @@ -1,4 +1,6 @@ +using System.Text; using SlatedGameToolkit.Framework.Exceptions; +using SlatedGameToolkit.Framework.Graphics; namespace SlatedGameToolkit.Framework.Graphics.Shaders { @@ -9,15 +11,15 @@ namespace SlatedGameToolkit.Framework.Graphics.Shaders /// /// A string representing the GLSL code to run. public GLFragmentShader(string shader) : base() { - Handle = createShader(GLEnums.GL_FRAGMENT_SHADER); - shaderSource(Handle, 1, shader, null); + Handle = createShader(GLEnum.GL_FRAGMENT_SHADER); + shaderSource(Handle, 1, new string[] {shader}, null); compileShader(Handle); uint logLength; - string shaderLog; - getShaderLogInfo(Handle, 1024, out logLength, out shaderLog); + byte[] log = new byte[2048]; + getShaderInfoLog(Handle, (uint)log.Length, out logLength, log); if (logLength > 0) { Dispose(); - throw new OpenGLException(shaderLog); + throw new OpenGLException(Encoding.UTF8.GetString(log)); } OpenGLErrorException.CheckGLErrorStatus(); } diff --git a/src/SlatedGameToolkit.Framework/Graphics/Shaders/GLShader.cs b/src/SlatedGameToolkit.Framework/Graphics/Shaders/GLShader.cs index 7d71c49..03d17e9 100644 --- a/src/SlatedGameToolkit.Framework/Graphics/Shaders/GLShader.cs +++ b/src/SlatedGameToolkit.Framework/Graphics/Shaders/GLShader.cs @@ -2,6 +2,7 @@ using System; using System.Runtime.InteropServices; using SDL2; using SlatedGameToolkit.Framework.Exceptions; +using SlatedGameToolkit.Framework.Graphics; using SlatedGameToolkit.Framework.Graphics.Programs; using SlatedGameToolkit.Framework.Graphics.Window; @@ -9,35 +10,23 @@ namespace SlatedGameToolkit.Framework.Graphics.Shaders { public abstract class GLShader : IDisposable { private WindowContext context; - public UIntPtr Handle { get; private protected set; } + public virtual UIntPtr Handle { get; protected set; } protected GLCreateShader createShader; protected GLShaderSource shaderSource; protected GLCompileShader compileShader; - protected GLGetShaderLogInfo getShaderLogInfo; - protected GLGetAttribLocation getAttribLocation; + protected GLGetShaderInfoLog getShaderInfoLog; private GLDeleteShader deleteShader; public GLShader() { context = WindowContextsManager.CurrentWindowContext; - createShader = GLFunctionUtils.RetrieveGLDelegate("glCreateShader"); - shaderSource = GLFunctionUtils.RetrieveGLDelegate("glShaderSource"); - compileShader = GLFunctionUtils.RetrieveGLDelegate("glCompileShader"); - getShaderLogInfo = GLFunctionUtils.RetrieveGLDelegate("glGetShaderLogInfo"); - deleteShader = GLFunctionUtils.RetrieveGLDelegate("glDeleteShader"); - getAttribLocation = GLFunctionUtils.RetrieveGLDelegate("glGetAttribLocation"); + createShader = OpenGL.RetrieveGLDelegate("glCreateShader"); + shaderSource = OpenGL.RetrieveGLDelegate("glShaderSource"); + compileShader = OpenGL.RetrieveGLDelegate("glCompileShader"); + getShaderInfoLog = OpenGL.RetrieveGLDelegate("glGetShaderInfoLog"); + deleteShader = OpenGL.RetrieveGLDelegate("glDeleteShader"); } - /// - /// Gets the attribute location. - /// - /// The name of the attribute. - /// The attribute location index. - public int GetAttributeLocation(string attributeName) { - int index = getAttribLocation(Handle, attributeName); - OpenGLErrorException.CheckGLErrorStatus(); - return index; - } /// /// Disposes of the shader at the handle. diff --git a/src/SlatedGameToolkit.Framework/Graphics/Shaders/GLVertexArraySet.cs b/src/SlatedGameToolkit.Framework/Graphics/Shaders/GLVertexArraySet.cs index fc9635c..52001a2 100644 --- a/src/SlatedGameToolkit.Framework/Graphics/Shaders/GLVertexArraySet.cs +++ b/src/SlatedGameToolkit.Framework/Graphics/Shaders/GLVertexArraySet.cs @@ -1,6 +1,7 @@ using System; using System.Runtime.InteropServices; using SlatedGameToolkit.Framework.Exceptions; +using SlatedGameToolkit.Framework.Graphics; namespace SlatedGameToolkit.Framework.Graphics.Shaders { @@ -23,16 +24,16 @@ namespace SlatedGameToolkit.Framework.Graphics.Shaders public GLVertexArraySet(uint amount) { if (amount > int.MaxValue) throw new ArgumentOutOfRangeException("amount"); this.Count = (int)amount; - genBuffers = GLFunctionUtils.RetrieveGLDelegate("glGenBuffers"); - deleteBuffers = GLFunctionUtils.RetrieveGLDelegate("glDeleteBuffers"); - bindBuffer = GLFunctionUtils.RetrieveGLDelegate("glBindBuffer"); - genVertexArrays = GLFunctionUtils.RetrieveGLDelegate("glGenVertexArrays"); - bindVertexArray = GLFunctionUtils.RetrieveGLDelegate("glBindVertexArray"); - deleteVertexArrays = GLFunctionUtils.RetrieveGLDelegate("glDeleteVertexArrays"); - vertexAttribPointer = GLFunctionUtils.RetrieveGLDelegate("glVertexAttribPointer"); - enableVertexAttribArray = GLFunctionUtils.RetrieveGLDelegate("glEnableVertexAttribArray"); - disableVertexAttribArray = GLFunctionUtils.RetrieveGLDelegate("glDisableVertexAttribArray"); - bufferData = GLFunctionUtils.RetrieveGLDelegate("glBufferData"); + genBuffers = OpenGL.RetrieveGLDelegate("glGenBuffers"); + deleteBuffers = OpenGL.RetrieveGLDelegate("glDeleteBuffers"); + bindBuffer = OpenGL.RetrieveGLDelegate("glBindBuffer"); + genVertexArrays = OpenGL.RetrieveGLDelegate("glGenVertexArrays"); + bindVertexArray = OpenGL.RetrieveGLDelegate("glBindVertexArray"); + deleteVertexArrays = OpenGL.RetrieveGLDelegate("glDeleteVertexArrays"); + vertexAttribPointer = OpenGL.RetrieveGLDelegate("glVertexAttribPointer"); + enableVertexAttribArray = OpenGL.RetrieveGLDelegate("glEnableVertexAttribArray"); + disableVertexAttribArray = OpenGL.RetrieveGLDelegate("glDisableVertexAttribArray"); + bufferData = OpenGL.RetrieveGLDelegate("glBufferData"); arrayBufferHandles = new UIntPtr[amount]; vertexArrayHandles = new UIntPtr[amount]; indexArrayHandles = new UIntPtr[amount]; @@ -47,20 +48,20 @@ namespace SlatedGameToolkit.Framework.Graphics.Shaders public void Use(uint index) { bindVertexArray(vertexArrayHandles[index]); OpenGLErrorException.CheckGLErrorStatus(); - bindBuffer(GLEnums.GL_ELEMENT_ARRAY_BUFFER, indexArrayHandles[index]); - bindBuffer(GLEnums.GL_ARRAY_BUFFER, arrayBufferHandles[index]); + bindBuffer(GLEnum.GL_ELEMENT_ARRAY_BUFFER, indexArrayHandles[index]); + bindBuffer(GLEnum.GL_ARRAY_BUFFER, arrayBufferHandles[index]); OpenGLErrorException.CheckGLErrorStatus(); } public unsafe void BufferVertices(uint index, float[] data, bool dynamic) { Use(index); - bufferData(GLEnums.GL_ARRAY_BUFFER, (uint) (sizeof(float) * data.Length), data, dynamic ? GLEnums.GL_DYNAMIC_DRAW : GLEnums.GL_STATIC_DRAW); + bufferData(GLEnum.GL_ARRAY_BUFFER, (uint) (sizeof(float) * data.Length), data, dynamic ? GLEnum.GL_DYNAMIC_DRAW : GLEnum.GL_STATIC_DRAW); OpenGLErrorException.CheckGLErrorStatus(); } public unsafe void BufferIndices(uint index, uint[] data, bool dynamic) { Use(index); - bufferData(GLEnums.GL_ELEMENT_ARRAY_BUFFER, (uint) (sizeof(float) * data.Length), data, dynamic ? GLEnums.GL_DYNAMIC_DRAW : GLEnums.GL_STATIC_DRAW); + bufferData(GLEnum.GL_ELEMENT_ARRAY_BUFFER, (uint) (sizeof(float) * data.Length), data, dynamic ? GLEnum.GL_DYNAMIC_DRAW : GLEnum.GL_STATIC_DRAW); OpenGLErrorException.CheckGLErrorStatus(); } @@ -75,7 +76,7 @@ namespace SlatedGameToolkit.Framework.Graphics.Shaders Use(bufferIndex); foreach (VertexAttributeDefinition definition in definitions) { - vertexAttribPointer(definition.attributeIndex, definition.size, GLEnums.GL_FLOAT, false, definition.stride, definition.offset); + vertexAttribPointer(definition.attributeIndex, definition.size, GLEnum.GL_FLOAT, false, definition.stride, definition.offset); OpenGLErrorException.CheckGLErrorStatus(); if (enableAttributes) enableVertexAttribArray(definition.attributeIndex); } diff --git a/src/SlatedGameToolkit.Framework/Graphics/Shaders/GLVertexShader.cs b/src/SlatedGameToolkit.Framework/Graphics/Shaders/GLVertexShader.cs index 5967752..89e0ef9 100644 --- a/src/SlatedGameToolkit.Framework/Graphics/Shaders/GLVertexShader.cs +++ b/src/SlatedGameToolkit.Framework/Graphics/Shaders/GLVertexShader.cs @@ -1,3 +1,4 @@ +using System.Text; using SlatedGameToolkit.Framework.Exceptions; namespace SlatedGameToolkit.Framework.Graphics.Shaders @@ -9,15 +10,15 @@ namespace SlatedGameToolkit.Framework.Graphics.Shaders /// /// A string representing the GLSL code. public GLVertexShader(string shader) : base() { - Handle = createShader(GLEnums.GL_VERTEX_SHADER); - shaderSource(Handle, 1, shader, null); + Handle = createShader(GLEnum.GL_VERTEX_SHADER); + shaderSource(Handle, 1, new string[] {shader}, null); compileShader(Handle); uint logLength; - string shaderLog; - getShaderLogInfo(Handle, 1024, out logLength, out shaderLog); + byte[] log = new byte[2048]; + getShaderInfoLog(Handle, (uint)log.Length, out logLength, log); if (logLength > 0) { Dispose(); - throw new OpenGLException(shaderLog); + throw new OpenGLException(Encoding.UTF8.GetString(log)); } OpenGLErrorException.CheckGLErrorStatus(); } diff --git a/src/SlatedGameToolkit.Framework/Graphics/Shaders/NormalFragmentShader.cs b/src/SlatedGameToolkit.Framework/Graphics/Shaders/NormalFragmentShader.cs new file mode 100644 index 0000000..3718113 --- /dev/null +++ b/src/SlatedGameToolkit.Framework/Graphics/Shaders/NormalFragmentShader.cs @@ -0,0 +1,21 @@ +using System; +using System.IO; +using System.Reflection; + +namespace SlatedGameToolkit.Framework.Graphics.Shaders +{ + public class NormalFragmentShader : GLShader + { + private GLFragmentShader shader; + public NormalFragmentShader() { + using (Stream stream = Assembly.GetExecutingAssembly().GetManifestResourceStream("SlatedGameToolkit.Framework.Resources.default.frag")) + { + using (StreamReader reader = new StreamReader(stream)) + { + shader = new GLFragmentShader(reader.ReadToEnd()); + } + } + this.Handle = shader.Handle; + } + } +} \ No newline at end of file diff --git a/src/SlatedGameToolkit.Framework/Graphics/Shaders/NormalVertexShader.cs b/src/SlatedGameToolkit.Framework/Graphics/Shaders/NormalVertexShader.cs new file mode 100644 index 0000000..aa071a5 --- /dev/null +++ b/src/SlatedGameToolkit.Framework/Graphics/Shaders/NormalVertexShader.cs @@ -0,0 +1,20 @@ +using System; +using System.IO; +using System.Reflection; + +namespace SlatedGameToolkit.Framework.Graphics.Shaders { + public class NormalVertexShader : GLShader + { + private GLVertexShader shader; + + public NormalVertexShader() { + using (Stream stream = Assembly.GetExecutingAssembly().GetManifestResourceStream("SlatedGameToolkit.Framework.Resources.default.vert")) + { + using (StreamReader reader = new StreamReader(stream)) { + shader = new GLVertexShader(reader.ReadToEnd()); + } + } + Handle = shader.Handle; + } + } +} \ No newline at end of file diff --git a/src/SlatedGameToolkit.Framework/Graphics/Textures/GLTexture2DSet.cs b/src/SlatedGameToolkit.Framework/Graphics/Textures/GLTexture2DSet.cs index 3b5156b..661bf67 100644 --- a/src/SlatedGameToolkit.Framework/Graphics/Textures/GLTexture2DSet.cs +++ b/src/SlatedGameToolkit.Framework/Graphics/Textures/GLTexture2DSet.cs @@ -15,23 +15,23 @@ namespace SlatedGameToolkit.Framework.Graphics private GLDeleteTextures deleteTextures; private GLTexImage2D texImage2D; private GLBindTexture bindTexture; - private GLGenerateMipMap generateMipMap; + private GLGenerateMipmap generateMipmap; private readonly UIntPtr[] handle; public GLTexture2DSet(TextureData[] textureDatas, bool linear = false, bool mipmap = false) { - genTextures = GLFunctionUtils.RetrieveGLDelegate("glGenTextures"); - deleteTextures = GLFunctionUtils.RetrieveGLDelegate("glDeleteTextures"); - bindTexture = GLFunctionUtils.RetrieveGLDelegate("glBindTexture"); - texParameteri = GLFunctionUtils.RetrieveGLDelegate("glTexParameteri"); - texParameterfv = GLFunctionUtils.RetrieveGLDelegate("glTexParameterfv"); - texImage2D = GLFunctionUtils.RetrieveGLDelegate("glTexImage2D"); - generateMipMap = GLFunctionUtils.RetrieveGLDelegate("glGenerateMipMap"); + genTextures = OpenGL.RetrieveGLDelegate("glGenTextures"); + deleteTextures = OpenGL.RetrieveGLDelegate("glDeleteTextures"); + bindTexture = OpenGL.RetrieveGLDelegate("glBindTexture"); + texParameteri = OpenGL.RetrieveGLDelegate("glTexParameteri"); + texParameterfv = OpenGL.RetrieveGLDelegate("glTexParameterfv"); + texImage2D = OpenGL.RetrieveGLDelegate("glTexImage2D"); + generateMipmap = OpenGL.RetrieveGLDelegate("glGenerateMipmap"); handle = new UIntPtr[textureDatas.Length]; genTextures((uint)textureDatas.Length, handle); for (uint i = 0; i < textureDatas.Length; i++) { Setup(i, textureDatas[i]); } - bindTexture(GLEnums.GL_TEXTURE_2D, UIntPtr.Zero); + bindTexture(GLEnum.GL_TEXTURE_2D, UIntPtr.Zero); OpenGLErrorException.CheckGLErrorStatus(); this.Count = textureDatas.Length; @@ -39,8 +39,10 @@ namespace SlatedGameToolkit.Framework.Graphics private void Setup(uint index, TextureData textureData) { Bind(index); - texImage2D(GLEnums.GL_TEXTURE_2D, 0, (int)GLEnums.GL_RGBA, textureData.width, textureData.height, 0, GLEnums.GL_RGBA, GLEnums.GL_UNSIGNED_BYTE, textureData.data); - generateMipMap(GLEnums.GL_TEXTURE_2D); + texImage2D(GLEnum.GL_TEXTURE_2D, 0, (int)GLEnum.GL_RGBA, textureData.width, textureData.height, 0, GLEnum.GL_RGBA, GLEnum.GL_UNSIGNED_BYTE, textureData.data); + OpenGLErrorException.CheckGLErrorStatus(); + generateMipmap(GLEnum.GL_TEXTURE_2D); + OpenGLErrorException.CheckGLErrorStatus(); } /// @@ -50,12 +52,12 @@ namespace SlatedGameToolkit.Framework.Graphics /// The index of the texture. /// Whether or not to have linear filtering. /// Whether or not to use MipMap - public void SetProperties(uint index, GLEnums min, GLEnums mag) { + public void SetProperties(uint index, GLEnum min, GLEnum mag) { Bind(index); - texParameteri(GLEnums.GL_TEXTURE_2D, GLEnums.GL_TEXTURE_WRAP_S, (int)GLEnums.GL_CLAMP_TO_EDGE); - texParameteri(GLEnums.GL_TEXTURE_2D, GLEnums.GL_TEXTURE_WRAP_T, (int)GLEnums.GL_CLAMP_TO_EDGE); - texParameteri(GLEnums.GL_TEXTURE_2D, GLEnums.GL_TEXTURE_MIN_FILTER, (int) min); - texParameteri(GLEnums.GL_TEXTURE_2D, GLEnums.GL_TEXTURE_MAG_FILTER, (int) mag); + texParameteri(GLEnum.GL_TEXTURE_2D, GLEnum.GL_TEXTURE_WRAP_S, (int)GLEnum.GL_CLAMP_TO_EDGE); + texParameteri(GLEnum.GL_TEXTURE_2D, GLEnum.GL_TEXTURE_WRAP_T, (int)GLEnum.GL_CLAMP_TO_EDGE); + texParameteri(GLEnum.GL_TEXTURE_2D, GLEnum.GL_TEXTURE_MIN_FILTER, (int) min); + texParameteri(GLEnum.GL_TEXTURE_2D, GLEnum.GL_TEXTURE_MAG_FILTER, (int) mag); } /// @@ -63,8 +65,8 @@ namespace SlatedGameToolkit.Framework.Graphics /// /// The index of the texture in this set. public void Bind(uint index) { - bindTexture(GLEnums.GL_TEXTURE_2D, handle[index]); - + bindTexture(GLEnum.GL_TEXTURE_2D, handle[index]); + OpenGLErrorException.CheckGLErrorStatus(); } public void Dispose() diff --git a/src/SlatedGameToolkit.Framework/Graphics/Textures/Texture.cs b/src/SlatedGameToolkit.Framework/Graphics/Textures/Texture.cs index e42d524..5979a84 100644 --- a/src/SlatedGameToolkit.Framework/Graphics/Textures/Texture.cs +++ b/src/SlatedGameToolkit.Framework/Graphics/Textures/Texture.cs @@ -1,3 +1,4 @@ +using SlatedGameToolkit.Framework.Exceptions; using SlatedGameToolkit.Framework.Loader; namespace SlatedGameToolkit.Framework.Graphics.Textures @@ -15,6 +16,7 @@ namespace SlatedGameToolkit.Framework.Graphics.Textures public Texture(TextureData textureData, bool linear = true, bool mipmap = true) { TextureData[] textureDatas = new TextureData[] {textureData}; this.glTexture2DSet = new GLTexture2DSet(textureDatas, linear, mipmap); + OpenGLErrorException.CheckGLErrorStatus(); this.index = 0; } diff --git a/src/SlatedGameToolkit.Framework/Graphics/Window/WindowContext.cs b/src/SlatedGameToolkit.Framework/Graphics/Window/WindowContext.cs index e203383..88e3ec0 100644 --- a/src/SlatedGameToolkit.Framework/Graphics/Window/WindowContext.cs +++ b/src/SlatedGameToolkit.Framework/Graphics/Window/WindowContext.cs @@ -92,18 +92,18 @@ namespace SlatedGameToolkit.Framework.Graphics.Window WindowContext actual = WindowContextsManager.CurrentWindowContext; bool diff = false; if (actual != this) { - WindowContextsManager.CurrentWindowContext = this; + MakeCurrent(); diff = true; } bool vSync = SDL.SDL_GL_GetSwapInterval() != 0; - if (diff) WindowContextsManager.CurrentWindowContext = actual; + if (diff) actual.MakeCurrent(); return vSync; } set { WindowContext actual = WindowContextsManager.CurrentWindowContext; bool diff = false; if (actual != this) { - WindowContextsManager.CurrentWindowContext = this; + MakeCurrent(); diff = true; } if (SDL.SDL_GL_SetSwapInterval(value ? -1 : 0) < 0) { @@ -111,7 +111,7 @@ namespace SlatedGameToolkit.Framework.Graphics.Window throw new OptionalSDLException(); } } - if (diff) WindowContextsManager.CurrentWindowContext = actual; + if (diff) actual.MakeCurrent(); } } @@ -271,10 +271,10 @@ namespace SlatedGameToolkit.Framework.Graphics.Window throw new FrameworkSDLException(); } - clear = GLFunctionUtils.RetrieveGLDelegate("glClear"); - clearColour = GLFunctionUtils.RetrieveGLDelegate("glClearColor"); - viewport = GLFunctionUtils.RetrieveGLDelegate("glViewport"); - getError = GLFunctionUtils.RetrieveGLDelegate("glGetError"); + clear = OpenGL.RetrieveGLDelegate("glClear"); + clearColour = OpenGL.RetrieveGLDelegate("glClearColor"); + viewport = OpenGL.RetrieveGLDelegate("glViewport"); + getError = OpenGL.RetrieveGLDelegate("glGetError"); if (specialRegions) { if (SDL.SDL_SetWindowHitTest(windowHandle, SpecialRegionHit, IntPtr.Zero) < 0) { @@ -283,7 +283,7 @@ namespace SlatedGameToolkit.Framework.Graphics.Window } WindowContextsManager.RegisterWindow(this); - WindowContextsManager.CurrentWindowContext = this; + MakeCurrent(); Vector2 drawable = GetDrawableDimensions(); viewport(0, 0, (int)drawable.X, (int)drawable.Y); @@ -336,9 +336,10 @@ namespace SlatedGameToolkit.Framework.Graphics.Window /// There can only be one window context active at any given time. /// public void MakeCurrent() { - - SDL.SDL_GL_MakeCurrent(windowHandle, glContextHandle); - WindowContextsManager.CurrentWindowContext = this; + if (WindowContextsManager.CurrentWindowContext != this) { + SDL.SDL_GL_MakeCurrent(windowHandle, glContextHandle); + WindowContextsManager.current = this; + } } /// diff --git a/src/SlatedGameToolkit.Framework/Graphics/Window/WindowContextsManager.cs b/src/SlatedGameToolkit.Framework/Graphics/Window/WindowContextsManager.cs index 291bafb..93e2b77 100644 --- a/src/SlatedGameToolkit.Framework/Graphics/Window/WindowContextsManager.cs +++ b/src/SlatedGameToolkit.Framework/Graphics/Window/WindowContextsManager.cs @@ -3,7 +3,7 @@ using System.Collections.Generic; namespace SlatedGameToolkit.Framework.Graphics.Window { public static class WindowContextsManager { - private static WindowContext current; + internal static WindowContext current; /// /// The current window context. @@ -13,13 +13,6 @@ namespace SlatedGameToolkit.Framework.Graphics.Window get { return current; } - - set { - if (current != value) { - current = value; - value.MakeCurrent(); - } - } } private static Dictionary existingWindows = new Dictionary(); diff --git a/src/SlatedGameToolkit.Framework/Loader/AssetManager.cs b/src/SlatedGameToolkit.Framework/Loader/AssetManager.cs index 4702514..fef2a15 100644 --- a/src/SlatedGameToolkit.Framework/Loader/AssetManager.cs +++ b/src/SlatedGameToolkit.Framework/Loader/AssetManager.cs @@ -7,7 +7,7 @@ namespace SlatedGameToolkit.Framework.Loader { public class AssetManager : IDisposable { private readonly object assetThreadLock = new object(); private Thread thread; - private ConcurrentQueue loadables; + private ConcurrentQueue> loadables; private volatile bool load; public bool Complete { get { @@ -17,7 +17,7 @@ namespace SlatedGameToolkit.Framework.Loader { private ConcurrentDictionary assets; public ConcurrentDictionary PathModifiers {get; private set; } public AssetManager() { - this.loadables = new ConcurrentQueue(); + this.loadables = new ConcurrentQueue>(); this.assets = new ConcurrentDictionary(); this.PathModifiers = new ConcurrentDictionary(); thread = new Thread(Process); @@ -32,14 +32,14 @@ namespace SlatedGameToolkit.Framework.Loader { /// The file name of the loadable is the one the loadable is saved under. /// /// The loadable. - public void QueueLoad(ILoadable loadable) { + public void QueueLoad(ILoadable loadable) { loadables.Enqueue(loadable); } private void Process() { lock (assetThreadLock) { - ILoadable loadable; + ILoadable loadable; while (load) { while (loadables.TryDequeue(out loadable)) { Load(loadable); @@ -53,7 +53,7 @@ namespace SlatedGameToolkit.Framework.Loader { /// Loads the loadable and stores it under the filename given to the loadable. /// /// The loadable to load. - public void Load(ILoadable loadable) { + public void Load(ILoadable loadable) { string name = loadable.FileName; assets[name] = loadable.Load(PathModifiers[loadable.GetType()]); } diff --git a/src/SlatedGameToolkit.Framework/Loader/ILoadable.cs b/src/SlatedGameToolkit.Framework/Loader/ILoadable.cs index df1e7c6..99521ef 100644 --- a/src/SlatedGameToolkit.Framework/Loader/ILoadable.cs +++ b/src/SlatedGameToolkit.Framework/Loader/ILoadable.cs @@ -11,7 +11,7 @@ namespace SlatedGameToolkit.Framework.Loader /// A loader is to load an asset the game uses. /// It is created whenever the asset needs to be loaded with a string representing the path to the file to actually load. /// - public interface ILoadable + public interface ILoadable where UseableT : IAssetUseable { /// /// The name of the file to load. @@ -26,6 +26,6 @@ namespace SlatedGameToolkit.Framework.Loader /// Modifies the path. May be null. Default is null. /// The loadable type. /// The loadable type. - IAssetUseable Load(PathModifier pathModifier = null); + UseableT Load(PathModifier pathModifier = null); } } \ No newline at end of file diff --git a/src/SlatedGameToolkit.Framework/Loader/TextureLoader.cs b/src/SlatedGameToolkit.Framework/Loader/TextureLoader.cs index 37eb439..4c97591 100644 --- a/src/SlatedGameToolkit.Framework/Loader/TextureLoader.cs +++ b/src/SlatedGameToolkit.Framework/Loader/TextureLoader.cs @@ -7,22 +7,22 @@ using SlatedGameToolkit.Framework.Loader; namespace SlatedGameToolkit.Framework.Loader { - public class TextureLoader : ILoadable + public class TextureLoader : ILoadable { public string FileName {get; private set; } public TextureLoader(string path) { this.FileName = path; } - public IAssetUseable Load(PathModifier pathModifier = null) + public Texture Load(PathModifier pathModifier = null) { - IntPtr ptr = SDL_image.IMG_Load(pathModifier(FileName)); - if (ptr == null) throw new FrameworkSDLException(); + IntPtr ptr = SDL_image.IMG_Load(pathModifier == null ? FileName : pathModifier(FileName)); + if (ptr.Equals(IntPtr.Zero)) throw new FrameworkSDLException(); SDL.SDL_Surface surface = Marshal.PtrToStructure(ptr); TextureData textureData = new TextureData(); - byte[] data = new byte[surface.pitch * surface.h]; - Marshal.Copy(surface.pixels, data, 0, data.Length); - IAssetUseable useable = new Texture(textureData); + textureData.data = new byte[surface.pitch * surface.h]; + Marshal.Copy(surface.pixels, textureData.data, 0, textureData.data.Length); + Texture useable = new Texture(textureData); return useable; } } diff --git a/src/SlatedGameToolkit.Framework/Resources/default.frag b/src/SlatedGameToolkit.Framework/Resources/default.frag new file mode 100644 index 0000000..f7ad5c1 --- /dev/null +++ b/src/SlatedGameToolkit.Framework/Resources/default.frag @@ -0,0 +1,11 @@ +#version 330 core +out vec4 outputColor; + +in vec2 texCoord; +in vec4 color; +uniform sampler2D texture0; + +void main() +{ + outputColor = texture(texture0, texCoord) * color; +} \ No newline at end of file diff --git a/src/SlatedGameToolkit.Framework/Resources/default.vert b/src/SlatedGameToolkit.Framework/Resources/default.vert new file mode 100644 index 0000000..d98073a --- /dev/null +++ b/src/SlatedGameToolkit.Framework/Resources/default.vert @@ -0,0 +1,17 @@ +#version 330 core +in vec3 aPosition; +in vec4 aColor; +in vec2 aTexCoord; +uniform mat4 models; +uniform mat4 view; +uniform mat4 projection; +out vec2 texCoord; +out vec4 color; + +void main() +{ + texCoord = aTexCoord; + color = aColor; + + gl_Position = projection * view * models * vec4(aPosition, 1.0f); +} \ No newline at end of file diff --git a/src/SlatedGameToolkit.Framework/SlatedGameToolkit.Framework.csproj b/src/SlatedGameToolkit.Framework/SlatedGameToolkit.Framework.csproj index b926bd5..ba806ab 100644 --- a/src/SlatedGameToolkit.Framework/SlatedGameToolkit.Framework.csproj +++ b/src/SlatedGameToolkit.Framework/SlatedGameToolkit.Framework.csproj @@ -10,4 +10,7 @@ + + + diff --git a/src/SlatedGameToolkit.Framework/StateSystem/Manager.cs b/src/SlatedGameToolkit.Framework/StateSystem/StateManager.cs similarity index 85% rename from src/SlatedGameToolkit.Framework/StateSystem/Manager.cs rename to src/SlatedGameToolkit.Framework/StateSystem/StateManager.cs index 15ea52c..befd9b3 100644 --- a/src/SlatedGameToolkit.Framework/StateSystem/Manager.cs +++ b/src/SlatedGameToolkit.Framework/StateSystem/StateManager.cs @@ -2,13 +2,14 @@ using System; using System.Collections.Generic; using System.Drawing; using System.Threading; +using SlatedGameToolkit.Framework.Exceptions; using SlatedGameToolkit.Framework.Graphics; using SlatedGameToolkit.Framework.Graphics.Window; using SlatedGameToolkit.Framework.StateSystem.States; namespace SlatedGameToolkit.Framework.StateSystem { - public sealed class Manager : IDisposable { + public sealed class StateManager : IDisposable { public Thread thread; public Color backgroundColour; private IState currentState; @@ -22,14 +23,17 @@ namespace SlatedGameToolkit.Framework.StateSystem /// /// The name of the initial state. /// The initial set of game states to be added. - internal Manager(IState initialState) { + internal StateManager() { backgroundColour = Color.Orange; + this.states = new Dictionary(); + } + + internal void Initialize(IState initialState) { if (initialState == null) throw new ArgumentNullException("initialState"); thread = Thread.CurrentThread; - this.states = new Dictionary(); - addState(initialState); currentState = initialState; + addState(initialState); } internal void update(double delta) { @@ -44,8 +48,10 @@ namespace SlatedGameToolkit.Framework.StateSystem internal void render(double delta) { WindowContext windowContext = WindowContextsManager.CurrentWindowContext; - windowContext.clearColour(backgroundColour.R / 254f, backgroundColour.G / 254f, backgroundColour.B / 254f); - windowContext.clear(GLEnums.GL_COLOR_BUFFER_BIT | GLEnums.GL_DEPTH_STENCIL); + windowContext.clearColour((float)backgroundColour.R / byte.MaxValue, (float)backgroundColour.G / byte.MaxValue, (float)backgroundColour.B / byte.MaxValue); + OpenGLErrorException.CheckGLErrorStatus(); + windowContext.clear(GLEnum.GL_COLOR_BUFFER_BIT | GLEnum.GL_DEPTH_BUFFER_BIT); + OpenGLErrorException.CheckGLErrorStatus(); currentState.Render(delta); windowContext.SwapBuffer(); } @@ -87,8 +93,8 @@ namespace SlatedGameToolkit.Framework.StateSystem public bool addState(IState state) { if (thread != Thread.CurrentThread) throw new ThreadStateException("Cannot add a state from a different thread."); try { - GameEngine.Logger.Debug("Adding state: " + state.getName()); - this.states.Add(state.getName(), state); + GameEngine.Logger.Debug("Adding state: " + state.getName()); + this.states.Add(state.getName(), state); } catch (ArgumentException) { return false; } @@ -108,7 +114,12 @@ namespace SlatedGameToolkit.Framework.StateSystem if (states[name] == currentState) return false; IState state = states[name]; GameEngine.Logger.Debug("Removing state: " + name); - state.Deinitialize(); + try { + state.Deinitialize(); + } catch (Exception e) { + GameEngine.Logger.Error(e.ToString()); + GameEngine.Logger.Error("Failed to deinitialize state: " + state.getName()); + } return states.Remove(name); } diff --git a/src/SlatedGameToolkit.Framework/StateSystem/States/IState.cs b/src/SlatedGameToolkit.Framework/StateSystem/States/IState.cs index f6f3260..0727d37 100644 --- a/src/SlatedGameToolkit.Framework/StateSystem/States/IState.cs +++ b/src/SlatedGameToolkit.Framework/StateSystem/States/IState.cs @@ -44,7 +44,7 @@ namespace SlatedGameToolkit.Framework.StateSystem.States /// Should be used to set up this state. /// /// The manager that made this call. - void Initialize(Manager manager); + void Initialize(StateManager manager); /// /// The name of this state. diff --git a/src/SlatedGameToolkit.Tools/System/CommandMap.cs b/src/SlatedGameToolkit.Tools/CommandSystem/CommandMap.cs similarity index 95% rename from src/SlatedGameToolkit.Tools/System/CommandMap.cs rename to src/SlatedGameToolkit.Tools/CommandSystem/CommandMap.cs index f62a3da..839aa52 100644 --- a/src/SlatedGameToolkit.Tools/System/CommandMap.cs +++ b/src/SlatedGameToolkit.Tools/CommandSystem/CommandMap.cs @@ -1,9 +1,9 @@ using System; using System.Collections; using System.Collections.Generic; -using SlatedGameToolkit.Tools.System.Interaction; +using SlatedGameToolkit.Tools.CommandSystem.Interaction; -namespace SlatedGameToolkit.Tools.System +namespace SlatedGameToolkit.Tools.CommandSystem { public class CommandMap : ICollection, IDisposable { Dictionary invocations; diff --git a/src/SlatedGameToolkit.Tools/System/CommandProcessor.cs b/src/SlatedGameToolkit.Tools/CommandSystem/CommandProcessor.cs similarity index 60% rename from src/SlatedGameToolkit.Tools/System/CommandProcessor.cs rename to src/SlatedGameToolkit.Tools/CommandSystem/CommandProcessor.cs index 733375f..224a676 100644 --- a/src/SlatedGameToolkit.Tools/System/CommandProcessor.cs +++ b/src/SlatedGameToolkit.Tools/CommandSystem/CommandProcessor.cs @@ -1,22 +1,19 @@ using System; -using SlatedGameToolkit.Tools.System.Interaction; +using SlatedGameToolkit.Tools.CommandSystem.Interaction; -namespace SlatedGameToolkit.Tools.System +namespace SlatedGameToolkit.Tools.CommandSystem { public class CommandProcessor { - private string generalHelpMessage; CommandMap commandMap; /// /// The general help is the string that is printed when the input is not understood. /// {input} in the string is replaced with the user input. /// - /// /// - public CommandProcessor(string generalHelp, CommandMap commands) { + public CommandProcessor(CommandMap commands) { this.commandMap = commands; - this.generalHelpMessage = generalHelp; } public void Process(IInteractable interactable) { string message = interactable.Listen(); @@ -27,11 +24,12 @@ namespace SlatedGameToolkit.Tools.System if (invocable != null) { string[] args = new string[splitMessage.Length - 1]; Array.Copy(splitMessage, 1, args, 0, splitMessage.Length - 1); - if (invocable.Execute(interactable, args)) { - return; + if (!invocable.Execute(interactable, args)) { + interactable.Tell(string.Format("The command \"{0}\" was recognized, but arguments were incorrect. Please refer to Please type \"help {0}\" for more information.", invocation)); } + return; } - interactable.Tell(generalHelpMessage.Replace("{input}", invocation)); + interactable.Tell(string.Format("The input \"{0}\" was not understood. Please type \"help\" for more information.", invocation)); } } } \ No newline at end of file diff --git a/src/SlatedGameToolkit.Tools/CommandSystem/Exceptions/FatalUsageException.cs b/src/SlatedGameToolkit.Tools/CommandSystem/Exceptions/FatalUsageException.cs new file mode 100644 index 0000000..228260d --- /dev/null +++ b/src/SlatedGameToolkit.Tools/CommandSystem/Exceptions/FatalUsageException.cs @@ -0,0 +1,14 @@ +using System; +using System.Runtime.Serialization; + +namespace SlatedGameToolkit.Tools.CommandSystem.Exceptions +{ + [Serializable] + public class FatalUsageException : Exception + { + public FatalUsageException() { } + public FatalUsageException(string message) : base(message) { } + public FatalUsageException(string message, Exception inner) : base(message, inner) { } + protected FatalUsageException(SerializationInfo info, StreamingContext context) : base(info, context) { } + } +} \ No newline at end of file diff --git a/src/SlatedGameToolkit.Tools/System/IInvocable.cs b/src/SlatedGameToolkit.Tools/CommandSystem/IInvocable.cs similarity index 93% rename from src/SlatedGameToolkit.Tools/System/IInvocable.cs rename to src/SlatedGameToolkit.Tools/CommandSystem/IInvocable.cs index f27f63c..3e540f0 100644 --- a/src/SlatedGameToolkit.Tools/System/IInvocable.cs +++ b/src/SlatedGameToolkit.Tools/CommandSystem/IInvocable.cs @@ -1,7 +1,7 @@ using System; -using SlatedGameToolkit.Tools.System.Interaction; +using SlatedGameToolkit.Tools.CommandSystem.Interaction; -namespace SlatedGameToolkit.Tools.System +namespace SlatedGameToolkit.Tools.CommandSystem { public interface IInvocable : IDisposable { diff --git a/src/SlatedGameToolkit.Tools/System/Interaction/ConsoleInteraction.cs b/src/SlatedGameToolkit.Tools/CommandSystem/Interaction/ConsoleInteraction.cs similarity index 89% rename from src/SlatedGameToolkit.Tools/System/Interaction/ConsoleInteraction.cs rename to src/SlatedGameToolkit.Tools/CommandSystem/Interaction/ConsoleInteraction.cs index b8f0064..ae97af1 100644 --- a/src/SlatedGameToolkit.Tools/System/Interaction/ConsoleInteraction.cs +++ b/src/SlatedGameToolkit.Tools/CommandSystem/Interaction/ConsoleInteraction.cs @@ -1,6 +1,6 @@ using System; -namespace SlatedGameToolkit.Tools.System.Interaction +namespace SlatedGameToolkit.Tools.CommandSystem.Interaction { public class ConsoleInteraction : IInteractable { diff --git a/src/SlatedGameToolkit.Tools/System/Interaction/IInteractable.cs b/src/SlatedGameToolkit.Tools/CommandSystem/Interaction/IInteractable.cs similarity index 69% rename from src/SlatedGameToolkit.Tools/System/Interaction/IInteractable.cs rename to src/SlatedGameToolkit.Tools/CommandSystem/Interaction/IInteractable.cs index 1530574..1397ddc 100644 --- a/src/SlatedGameToolkit.Tools/System/Interaction/IInteractable.cs +++ b/src/SlatedGameToolkit.Tools/CommandSystem/Interaction/IInteractable.cs @@ -1,4 +1,4 @@ -namespace SlatedGameToolkit.Tools.System.Interaction +namespace SlatedGameToolkit.Tools.CommandSystem.Interaction { public interface IInteractable { diff --git a/src/SlatedGameToolkit.Tools/CommandSystem/Interaction/SingleConsoleInteraction.cs b/src/SlatedGameToolkit.Tools/CommandSystem/Interaction/SingleConsoleInteraction.cs new file mode 100644 index 0000000..e6bec0f --- /dev/null +++ b/src/SlatedGameToolkit.Tools/CommandSystem/Interaction/SingleConsoleInteraction.cs @@ -0,0 +1,30 @@ +using System; +using SlatedGameToolkit.Tools.CommandSystem.Exceptions; + +namespace SlatedGameToolkit.Tools.CommandSystem.Interaction +{ + public class SingleConsoleInteraction : IInteractable + { + private bool interacted; + private string oneTime; + public SingleConsoleInteraction(string oneTime) { + this.oneTime = oneTime; + } + public string Listen() + { + if (interacted) throw new FatalUsageException("Command attempted to request for more information. This generally occurs if the command being ran requires more user input."); + interacted = true; + return oneTime; + } + + public void Separate() + { + Console.WriteLine(); + } + + public void Tell(string message) + { + Console.WriteLine(message); + } + } +} \ No newline at end of file diff --git a/src/SlatedGameToolkit.Tools/Commands/GraphicalPlaygroundCommand.cs b/src/SlatedGameToolkit.Tools/Commands/GraphicalPlaygroundCommand.cs index c6d32ae..83bb3d8 100644 --- a/src/SlatedGameToolkit.Tools/Commands/GraphicalPlaygroundCommand.cs +++ b/src/SlatedGameToolkit.Tools/Commands/GraphicalPlaygroundCommand.cs @@ -1,8 +1,8 @@ using SlatedGameToolkit.Framework; using SlatedGameToolkit.Framework.StateSystem; -using SlatedGameToolkit.Tools.System; -using SlatedGameToolkit.Tools.System.Interaction; -using SlatedGameToolkit.Tools.Utilities.GraphicalPlayground; +using SlatedGameToolkit.Tools.CommandSystem; +using SlatedGameToolkit.Tools.CommandSystem.Interaction; +using SlatedGameToolkit.Tools.Utilities.Playground; namespace SlatedGameToolkit.Tools.Commands { diff --git a/src/SlatedGameToolkit.Tools/Commands/HelpCommand.cs b/src/SlatedGameToolkit.Tools/Commands/HelpCommand.cs index f3a23f5..df4b296 100644 --- a/src/SlatedGameToolkit.Tools/Commands/HelpCommand.cs +++ b/src/SlatedGameToolkit.Tools/Commands/HelpCommand.cs @@ -1,7 +1,7 @@ using System; using System.Text; -using SlatedGameToolkit.Tools.System; -using SlatedGameToolkit.Tools.System.Interaction; +using SlatedGameToolkit.Tools.CommandSystem; +using SlatedGameToolkit.Tools.CommandSystem.Interaction; namespace SlatedGameToolkit.Tools.Commands { diff --git a/src/SlatedGameToolkit.Tools/Commands/ListEmbeddedResourcesCommand.cs b/src/SlatedGameToolkit.Tools/Commands/ListEmbeddedResourcesCommand.cs new file mode 100644 index 0000000..f1958dd --- /dev/null +++ b/src/SlatedGameToolkit.Tools/Commands/ListEmbeddedResourcesCommand.cs @@ -0,0 +1,42 @@ +using System.Reflection; +using SlatedGameToolkit.Framework; +using SlatedGameToolkit.Tools.CommandSystem; +using SlatedGameToolkit.Tools.CommandSystem.Interaction; + +namespace SlatedGameToolkit.Tools.Commands +{ + public class ListEmbeddedResourcesCommand : IInvocable + { + private string[] invokers = new string[] {"GetEmbedded"}; + public void Dispose() + { + } + + public bool Execute(IInteractable interactable, string[] args) + { + Assembly assembly = Assembly.GetAssembly(typeof(GameEngine)); + string[] embeddedFiles = assembly.GetManifestResourceNames(); + interactable.Tell("Loaded embedded files:"); + foreach (string fileName in embeddedFiles) + { + interactable.Tell(fileName); + } + return true; + } + + public string getDescription() + { + return "Returns a list of embedded resources in the given assembly."; + } + + public string[] GetInvokers() + { + return invokers; + } + + public string getUsage(string arg) + { + return "Usage: \"GetEmbedded\" to retrieve a list of embedded files in the framework and tools."; + } + } +} \ No newline at end of file diff --git a/src/SlatedGameToolkit.Tools/Commands/StopCommand.cs b/src/SlatedGameToolkit.Tools/Commands/StopCommand.cs index b189519..d54fd63 100644 --- a/src/SlatedGameToolkit.Tools/Commands/StopCommand.cs +++ b/src/SlatedGameToolkit.Tools/Commands/StopCommand.cs @@ -1,5 +1,5 @@ -using SlatedGameToolkit.Tools.System; -using SlatedGameToolkit.Tools.System.Interaction; +using SlatedGameToolkit.Tools.CommandSystem; +using SlatedGameToolkit.Tools.CommandSystem.Interaction; namespace SlatedGameToolkit.Tools.Commands { diff --git a/src/SlatedGameToolkit.Tools/Program.cs b/src/SlatedGameToolkit.Tools/Program.cs index acc9ac5..4f9a62d 100644 --- a/src/SlatedGameToolkit.Tools/Program.cs +++ b/src/SlatedGameToolkit.Tools/Program.cs @@ -1,36 +1,43 @@ using System; using System.Reflection; +using System.Text; using SlatedGameToolkit.Tools.Commands; -using SlatedGameToolkit.Tools.System; -using SlatedGameToolkit.Tools.System.Interaction; +using SlatedGameToolkit.Tools.CommandSystem; +using SlatedGameToolkit.Tools.CommandSystem.Interaction; namespace SlatedGameToolkit.Tools { class Program { - static private bool running; + static private bool live; static void Main(string[] args) { CommandMap commands = new CommandMap(); commands.Add(new StopCommand()); commands.Add(new HelpCommand(commands)); + commands.Add(new ListEmbeddedResourcesCommand()); commands.Add(new GraphicalPlaygroundCommand()); - CommandProcessor processor = new CommandProcessor("The command \"{input}\" was not understood. Please type \"help\" for more information.", commands); + CommandProcessor processor = new CommandProcessor(commands); AssemblyName name = Assembly.GetExecutingAssembly().GetName(); - ConsoleInteraction consoleInteracter = new ConsoleInteraction("Tools"); - consoleInteracter.Tell(String.Format("{0} Version: {1}", name.Name, name.Version)); - consoleInteracter.Tell("Welcome to SlatedGameToolkit.Tools! These tools are meant for the developers using the SlatedGameToolkit. Type \"help\" for a list of things this tool can currently do."); - running = true; - while (running) { - consoleInteracter.Separate(); - processor.Process(consoleInteracter); + IInteractable interactable = (args.Length > 0 ? (IInteractable) new SingleConsoleInteraction(string.Join(' ', args)) : (IInteractable) new ConsoleInteraction("Tools")); + interactable.Tell(String.Format("{0} Version: {1}", name.Name, name.Version)); + if (args.Length > 0) { + interactable.Tell("Running in one time use mode."); + processor.Process(interactable); + } else { + interactable.Tell("Welcome to SlatedGameToolkit.Tools! These tools are meant for the developers using the SlatedGameToolkit. Type \"help\" for a list of things this tool can currently do."); + live = true; + while (live) { + interactable.Separate(); + processor.Process(interactable); + } } - consoleInteracter.Tell("Exiting tool."); + interactable.Tell("Exiting tool."); commands.Dispose(); } public static void Stop() { - running = false; + live = false; } } } diff --git a/src/SlatedGameToolkit.Tools/Resources/Playground/yhdnbgnc.png b/src/SlatedGameToolkit.Tools/Resources/Playground/yhdnbgnc.png new file mode 100644 index 0000000000000000000000000000000000000000..ca73e80c8014b0f33b76997e9246610d0ffd8d5b GIT binary patch literal 41434 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
  • O6WGNaCJ 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 literal 0 HcmV?d00001 diff --git a/src/SlatedGameToolkit.Tools/SlatedGameToolkit.Tools.csproj b/src/SlatedGameToolkit.Tools/SlatedGameToolkit.Tools.csproj index 327b64e..9c07a29 100644 --- a/src/SlatedGameToolkit.Tools/SlatedGameToolkit.Tools.csproj +++ b/src/SlatedGameToolkit.Tools/SlatedGameToolkit.Tools.csproj @@ -11,4 +11,7 @@ A tool to help with developing a game using SlatedGameToolkit. + + + diff --git a/src/SlatedGameToolkit.Tools/Utilities/GraphicalPlayground/MainState.cs b/src/SlatedGameToolkit.Tools/Utilities/Playground/MainState.cs similarity index 50% rename from src/SlatedGameToolkit.Tools/Utilities/GraphicalPlayground/MainState.cs rename to src/SlatedGameToolkit.Tools/Utilities/Playground/MainState.cs index 94f7123..6b80852 100644 --- a/src/SlatedGameToolkit.Tools/Utilities/GraphicalPlayground/MainState.cs +++ b/src/SlatedGameToolkit.Tools/Utilities/Playground/MainState.cs @@ -1,18 +1,25 @@ using System; +using System.Drawing; +using System.Numerics; +using SlatedGameToolkit.Framework.Exceptions; using SlatedGameToolkit.Framework.Graphics.Programs; using SlatedGameToolkit.Framework.Graphics.Render; using SlatedGameToolkit.Framework.Graphics.Shaders; using SlatedGameToolkit.Framework.Graphics.Textures; using SlatedGameToolkit.Framework.Graphics.Window; +using SlatedGameToolkit.Framework.Loader; using SlatedGameToolkit.Framework.StateSystem; using SlatedGameToolkit.Framework.StateSystem.States; -namespace SlatedGameToolkit.Tools.Utilities.GraphicalPlayground +namespace SlatedGameToolkit.Tools.Utilities.Playground { public class MainState : IState { private WindowContext window; + private GLShaderProgram program; + private Camera2D camera; private Renderer renderer; + private Texture texture; private Sprite2D sprite; public WindowContext CurrentWindow { get { return window;}} @@ -30,6 +37,9 @@ namespace SlatedGameToolkit.Tools.Utilities.GraphicalPlayground public void Deinitialize() { window.Dispose(); + program.Dispose(); + texture.Dispose(); + renderer.Dispose(); } public string getName() @@ -37,14 +47,24 @@ namespace SlatedGameToolkit.Tools.Utilities.GraphicalPlayground return "main state"; } - public void Initialize(Manager manager) + public void Initialize(StateManager manager) { window = new WindowContext("SlatedGameToolkit Playground"); - renderer = new Renderer(); + camera = new Camera2D(); + camera.Width = window.WindowBoundaries.Width; + camera.Height = window.WindowBoundaries.Height; + camera.Position = new Vector2(camera.Width / 2, camera.Height / 2); + program = new GLShaderProgram(new NormalVertexShader(), new NormalFragmentShader()); + renderer = new Renderer(camera, program); + texture = new TextureLoader("Resources/Playground/yhdnbgnc.png").Load(); + sprite = new Sprite2D(texture, Color.White); } public void Render(double delta) { + OpenGLErrorException.CheckGLErrorStatus(); + renderer.Queue(sprite, Matrix4x4.Identity); + renderer.Render(); } public void Update(double delta)