diff --git a/Framework/App.cs b/Framework/App.cs index b43f1f5..c56a925 100644 --- a/Framework/App.cs +++ b/Framework/App.cs @@ -265,9 +265,9 @@ public static bool MouseVisible /// /// Runs the Application with the given Module automatically registered. - /// Functionally the same as calling followed by + /// Functionally the same as calling followed by /// - public static void Run(string applicationName, int width, int height, bool fullscreen = false) where T : Module, new() + public static void Run(string applicationName, int width, int height, bool fullscreen = false, Renderers renderer = Renderers.None) where T : Module, new() { Register(); Run(applicationName, width, height, fullscreen); @@ -276,7 +276,7 @@ public static bool MouseVisible /// /// Runs the Application /// - public static void Run(string applicationName, int width, int height, bool fullscreen = false) + public static void Run(string applicationName, int width, int height, bool fullscreen = false, Renderers renderer = Renderers.None) { Debug.Assert(!Running, "Application is already running"); Debug.Assert(!Exiting, "Application is still exiting"); @@ -301,6 +301,7 @@ public static void Run(string applicationName, int width, int height, bool fulls applicationName = name, width = width, height = height, + renderer = renderer, flags = App.flags, onText = Input.OnText, onKey = Input.OnKey, diff --git a/Framework/Graphics/Batcher.cs b/Framework/Graphics/Batcher.cs index ed0b547..fa9b03d 100644 --- a/Framework/Graphics/Batcher.cs +++ b/Framework/Graphics/Batcher.cs @@ -1638,7 +1638,8 @@ public void Text(SpriteFont font, ReadOnlySpan text, Vector2 position, Vec if (last != 0) at.X += font.GetKerning(last, ch.Codepoint); - Image(ch.Subtexture, at + ch.Offset, color); + if (ch.Subtexture.Texture != null) + Image(ch.Subtexture, at + ch.Offset, color); last = ch.Codepoint; at.X += ch.Advance; diff --git a/Framework/Graphics/ShaderDefaults.cs b/Framework/Graphics/ShaderDefaults.cs index ccdd16c..bd18a2b 100644 --- a/Framework/Graphics/ShaderDefaults.cs +++ b/Framework/Graphics/ShaderDefaults.cs @@ -7,37 +7,37 @@ internal static class ShaderDefaults [Renderers.OpenGL] = new() { VertexShader = - "#version 330\n" + - "uniform mat4 u_matrix;\n" + - "layout(location=0) in vec2 a_position;\n" + - "layout(location=1) in vec2 a_tex;\n" + - "layout(location=2) in vec4 a_color;\n" + - "layout(location=3) in vec4 a_type;\n" + - "out vec2 v_tex;\n" + - "out vec4 v_col;\n" + - "out vec4 v_type;\n" + - "void main(void)\n" + - "{\n" + - " gl_Position = u_matrix * vec4(a_position.xy, 0, 1);\n" + - " v_tex = a_tex;\n" + - " v_col = a_color;\n" + - " v_type = a_type;\n" + - "}", - FragmentShader = - "#version 330\n" + - "uniform sampler2D u_texture;\n" + - "in vec2 v_tex;\n" + - "in vec4 v_col;\n" + - "in vec4 v_type;\n" + - "out vec4 o_color;\n" + - "void main(void)\n" + - "{\n" + - " vec4 color = texture(u_texture, v_tex);\n" + - " o_color = \n" + - " v_type.x * color * v_col + \n" + - " v_type.y * color.a * v_col + \n" + - " v_type.z * v_col;\n" + - "}" + @"#version 330 + uniform mat4 u_matrix; + layout(location=0) in vec2 a_position; + layout(location=1) in vec2 a_tex; + layout(location=2) in vec4 a_color; + layout(location=3) in vec4 a_type; + out vec2 v_tex; + out vec4 v_col; + out vec4 v_type; + void main(void) + { + gl_Position = u_matrix * vec4(a_position.xy, 0, 1); + v_tex = a_tex; + v_col = a_color; + v_type = a_type; + }", + FragmentShader = + @"#version 330 + uniform sampler2D u_texture; + in vec2 v_tex; + in vec4 v_col; + in vec4 v_type; + out vec4 o_color; + void main(void) + { + vec4 color = texture(u_texture, v_tex); + o_color = + v_type.x * color * v_col + + v_type.y * color.a * v_col + + v_type.z * v_col; + }" } }; } diff --git a/Framework/Storage/Content.cs b/Framework/Storage/Content.cs new file mode 100644 index 0000000..49f1748 --- /dev/null +++ b/Framework/Storage/Content.cs @@ -0,0 +1,121 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using System.Linq.Expressions; +using System.Text; +using System.Threading.Tasks; + +namespace Foster.Framework.Storage +{ + /// + /// Default Content implementation. + /// Should work well for PC, etc, but can be overridden for custom handling. + /// + public class Content + { + public string CurrentDirectory { get; set; } = ""; + + private class ContentEnumerator : IEnumerator + { + public string[] Locations; + public int Index = -1; + + public string Current + { + get + { + try + { + return Locations[Index]; + } + catch (IndexOutOfRangeException) + { + throw new InvalidOperationException(); + } + } + } + + object IEnumerator.Current => Current; + + public ContentEnumerator(string[] locations) + { + Locations = locations; + } + + public bool MoveNext() + { + Index++; + if (Index >= Locations.Length) + return false; + return true; + } + + public void Reset() => Index = -1; + + public void Dispose() { } + } + + public Content() { } + public Content(string content) : this() + { + CurrentDirectory = content; + } + + #region Directory + public virtual bool FileExists(string relativePath) + { + return File.Exists(CurrentDirectory + relativePath); + } + public virtual bool DirectoryExists(string relativePath) + { + return Directory.Exists(CurrentDirectory + relativePath); + } + public virtual bool Exists(string name) + { + return FileExists(name) || DirectoryExists(name); + } + + public virtual IEnumerator EnumerateFiles(string path, string searchPattern, bool recursive) + { + return new ContentEnumerator( + Directory.GetFiles( + CurrentDirectory + path, + searchPattern, + recursive ? SearchOption.AllDirectories : SearchOption.TopDirectoryOnly + )); + } + + public virtual IEnumerator EnumerateDirectories(string path, string searchPattern, bool recursive) + { + + return new ContentEnumerator( + Directory.GetDirectories( + CurrentDirectory + path, + searchPattern, + recursive ? SearchOption.AllDirectories : SearchOption.TopDirectoryOnly + )); + } + + #endregion + + #region File + + public virtual Stream OpenRead(string relativePath) + { + return File.OpenRead(CurrentDirectory + relativePath); + } + + public virtual byte[] ReadAllBytes(string relativePath) + { + return File.ReadAllBytes(CurrentDirectory + relativePath); + } + + public virtual string ReadAllText(string relativePath) + { + return File.ReadAllText(CurrentDirectory + relativePath); + } + + #endregion + } +} diff --git a/Framework/Storage/UserStorage.cs b/Framework/Storage/UserStorage.cs new file mode 100644 index 0000000..860509f --- /dev/null +++ b/Framework/Storage/UserStorage.cs @@ -0,0 +1,61 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Foster.Framework.Storage +{ + /// + /// A storage location for user data (save files, etc) + /// + public static class UserStorage + { + public static WritableContent Provider { get; set; } = new WritableContent(App.UserPath); + + #region Directory + + public static bool FileExists(string relativePath) + => Provider.FileExists(relativePath); + public static bool DirectoryExists(string relativePath) + => Provider.DirectoryExists(relativePath); + public static bool Exists(string name) + => Provider.Exists(name); + + public static IEnumerator EnumerateFiles(string path, string searchPattern, bool recursive) + => Provider.EnumerateFiles(path, searchPattern, recursive); + public static IEnumerator EnumerateDirectories(string path, string searchPattern, bool recursive) + => Provider.EnumerateDirectories(path, searchPattern, recursive); + + public static void CreateDirectory(string path) + => Provider.CreateDirectory(path); + public static void DeleteDirectory(string path, bool recursive) + => Provider.DeleteDirectory(path, recursive); + public static void DeleteFile(string path) + => Provider.DeleteFile(path); + + #endregion + + #region File + + public static Stream OpenRead(string relativePath) + => Provider.OpenRead(relativePath); + + public static byte[] ReadAllBytes(string relativePath) + => Provider.ReadAllBytes(relativePath); + + public static string ReadAllText(string relativePath) + => Provider.ReadAllText(relativePath); + + + public static Stream OpenWrite(string path) + => Provider.OpenWrite(path); + public static void WriteAllBytes(string path, byte[] bytes) + => Provider.WriteAllBytes(path, bytes); + public static void WriteAllText(string path, string text) + => Provider.WriteAllText(path, text); + + #endregion + + } +} diff --git a/Framework/Storage/WritableContent.cs b/Framework/Storage/WritableContent.cs new file mode 100644 index 0000000..e0eb453 --- /dev/null +++ b/Framework/Storage/WritableContent.cs @@ -0,0 +1,54 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Foster.Framework.Storage +{ + /// + /// A Content that may also be written to. + /// + public class WritableContent : Content + { + public WritableContent() { } + public WritableContent(string currentDirectory) : base(currentDirectory) { } + + #region Directory + public virtual void CreateDirectory(string path) + { + Directory.CreateDirectory(CurrentDirectory + path); + } + + public virtual void DeleteDirectory(string path, bool recursive) + { + Directory.Delete(CurrentDirectory + path, recursive); + } + + public virtual void DeleteFile(string path) + { + File.Delete(CurrentDirectory + path); + } + + #endregion + + #region File + + public virtual Stream OpenWrite(string path) + { + return File.OpenWrite(CurrentDirectory + path); + } + + public virtual void WriteAllBytes(string path, byte[] bytes) + { + File.WriteAllBytes(CurrentDirectory + path, bytes); + } + + public virtual void WriteAllText(string path, string text) + { + File.WriteAllText(CurrentDirectory + path, text); + } + + #endregion + } +} diff --git a/Framework/Utility/Ease.cs b/Framework/Utility/Ease.cs index 449e7ff..1357827 100644 --- a/Framework/Utility/Ease.cs +++ b/Framework/Utility/Ease.cs @@ -5,53 +5,70 @@ namespace Foster.Framework; /// /// Ease Delegates /// -public static class Ease +public struct Ease { public delegate float Easer(float t); - public static readonly Easer Linear = (float t) => t; + #region Ease + public Easer In { get; set; } + public Easer Out { get; set; } + public Easer InOut { get; set; } - public static readonly Easer SineIn = (float t) => -(float)Math.Cos(Calc.HalfPI * t) + 1; - public static readonly Easer SineOut = (float t) => (float)Math.Sin(Calc.HalfPI * t); - public static readonly Easer SineInOut = (float t) => -(MathF.Cos(MathF.PI * t) - 1f) / 2f; + public Ease(Easer @in, Easer? @out = null, Easer? inOut = null) + { + In = @in; + if (@out != null) + Out = @out; + else + Out = Invert(@in); + if (inOut != null) + InOut = inOut; + else + InOut = Follow(In, Out); + } + + public float Apply(float t) + { + if (t <= 0) + return 0; + if (t >= 1) + return 1; + return InOut(t); + } - public static readonly Easer QuadIn = (float t) => t * t; - public static readonly Easer QuadOut = Invert(QuadIn); - public static readonly Easer QuadInOut = Follow(QuadIn, QuadOut); + #endregion - public static readonly Easer CubeIn = (float t) => t * t * t; - public static readonly Easer CubeOut = Invert(CubeIn); - public static readonly Easer CubeInOut = Follow(CubeIn, CubeOut); + #region Easers - public static readonly Easer QuintIn = (float t) => t * t * t * t * t; - public static readonly Easer QuintOut = Invert(QuintIn); - public static readonly Easer QuintInOut = Follow(QuintIn, QuintOut); + public static readonly Easer Linear = t => t; + public static readonly Ease Quad = new(t => t * t); + public static readonly Ease Cube = new(t => t * t * t); + public static readonly Ease Quart = new(t => t * t * t * t); + public static readonly Ease Quint = new(t => t * t * t * t * t); - public static readonly Easer ExpoIn = (float t) => (float)Math.Pow(2, 10 * (t - 1)); - public static readonly Easer ExpoOut = Invert(ExpoIn); - public static readonly Easer ExpoInOut = Follow(ExpoIn, ExpoOut); + public static readonly Ease Sine = new( + (float t) => -(float)Math.Cos(Calc.HalfPI * t) + 1, + (float t) => (float)Math.Sin(Calc.HalfPI * t), + (float t) => -(MathF.Cos(MathF.PI * t) - 1f) / 2f); - public static readonly Easer BackIn = (float t) => t * t * (2.70158f * t - 1.70158f); - public static readonly Easer BackOut = Invert(BackIn); - public static readonly Easer BackInOut = Follow(BackIn, BackOut); + public static readonly Ease Expo = new(t => (float)Math.Pow(2, 10 * (t - 1))); - public static readonly Easer BigBackIn = (float t) => t * t * (4f * t - 3f); - public static readonly Easer BigBackOut = Invert(BigBackIn); - public static readonly Easer BigBackInOut = Follow(BigBackIn, BigBackOut); + public static readonly Ease Back = new(t => t * t * (2.70158f * t - 1.70158f)); + public static readonly Ease BigBack = new(t => t * t * (4f * t - 3f)); - public static readonly Easer ElasticIn = (float t) => + public static readonly Ease Elastic = new( + (float t) => { var ts = t * t; var tc = ts * t; return (33 * tc * ts + -59 * ts * ts + 32 * tc + -5 * ts); - }; - public static readonly Easer ElasticOut = (float t) => + }, + (float t) => { var ts = t * t; var tc = ts * t; return (33 * tc * ts + -106 * ts * ts + 126 * tc + -67 * ts + 15 * t); - }; - public static readonly Easer ElasticInOut = Follow(ElasticIn, ElasticOut); + }); private const float B1 = 1f / 2.75f; private const float B2 = 2f / 2.75f; @@ -60,7 +77,7 @@ public static class Ease private const float B5 = 2.25f / 2.75f; private const float B6 = 2.625f / 2.75f; - public static readonly Easer BounceIn = (float t) => + public static readonly Ease Bounce = new((float t) => { t = 1 - t; if (t < B1) @@ -70,9 +87,8 @@ public static class Ease if (t < B4) return 1 - (7.5625f * (t - B5) * (t - B5) + .9375f); return 1 - (7.5625f * (t - B6) * (t - B6) + .984375f); - }; - - public static readonly Easer BounceOut = (float t) => + }, + (float t) => { if (t < B1) return 7.5625f * t * t; @@ -81,30 +97,7 @@ public static class Ease if (t < B4) return 7.5625f * (t - B5) * (t - B5) + .9375f; return 7.5625f * (t - B6) * (t - B6) + .984375f; - }; - - public static readonly Easer BounceInOut = (float t) => - { - if (t < .5f) - { - t = 1 - t * 2; - if (t < B1) - return (1 - 7.5625f * t * t) / 2; - if (t < B2) - return (1 - (7.5625f * (t - B3) * (t - B3) + .75f)) / 2; - if (t < B4) - return (1 - (7.5625f * (t - B5) * (t - B5) + .9375f)) / 2; - return (1 - (7.5625f * (t - B6) * (t - B6) + .984375f)) / 2; - } - t = t * 2 - 1; - if (t < B1) - return (7.5625f * t * t) / 2 + .5f; - if (t < B2) - return (7.5625f * (t - B3) * (t - B3) + .75f) / 2 + .5f; - if (t < B4) - return (7.5625f * (t - B5) * (t - B5) + .9375f) / 2 + .5f; - return (7.5625f * (t - B6) * (t - B6) + .984375f) / 2 + .5f; - }; + }); public static Easer Invert(Easer easer) { @@ -123,4 +116,53 @@ public static float UpDown(float eased) else return 1 - (eased - .5f) * 2; } + + #endregion + + #region Obsolete Compatibility + + // redirections to ease implementations + [Obsolete("Use Sine.In instead")] + public static readonly Easer SineIn = Sine.In; + [Obsolete("Use Sine.Out instead")] + public static readonly Easer SineOut = Sine.Out; + [Obsolete("Use Sine.InOut instead")] + public static readonly Easer SineInOut = Sine.InOut; + + [Obsolete("Use Expo.In instead")] + public static readonly Easer ExpoIn = Expo.In; + [Obsolete("Use Expo.Out instead")] + public static readonly Easer ExpoOut = Expo.Out; + [Obsolete("Use Expo.InOut instead")] + public static readonly Easer ExpoInOut = Expo.InOut; + + [Obsolete("Use Back.In instead")] + public static readonly Easer BackIn = Back.In; + [Obsolete("Use Back.Out instead")] + public static readonly Easer BackOut = Back.Out; + [Obsolete("Use Back.InOut instead")] + public static readonly Easer BackInOut = Back.InOut; + + [Obsolete("Use BigBack.In instead")] + public static readonly Easer BigBackIn = BigBack.In; + [Obsolete("Use BigBack.Out instead")] + public static readonly Easer BigBackOut = BigBack.Out; + [Obsolete("Use BigBack.InOut instead")] + public static readonly Easer BigBackInOut = BigBack.InOut; + + [Obsolete("Use Elastic.In instead")] + public static readonly Easer ElasticIn = Elastic.In; + [Obsolete("Use Elastic.Out instead")] + public static readonly Easer ElasticOut = Elastic.Out; + [Obsolete("Use Elastic.InOut instead")] + public static readonly Easer ElasticInOut = Elastic.InOut; + + [Obsolete("Use Bounce.In instead")] + public static readonly Easer BounceIn = Bounce.In; + [Obsolete("Use Bounce.Out instead")] + public static readonly Easer BounceOut = Bounce.Out; + [Obsolete("Use Bounce.InOut instead")] + public static readonly Easer BounceInOut = Bounce.InOut; + + #endregion } diff --git a/Platform/libs/lib64/libFosterPlatform.so b/Platform/libs/lib64/libFosterPlatform.so index d5de6d7..05e037a 100755 Binary files a/Platform/libs/lib64/libFosterPlatform.so and b/Platform/libs/lib64/libFosterPlatform.so differ diff --git a/Platform/libs/libarm64/libFosterPlatform.so b/Platform/libs/libarm64/libFosterPlatform.so index 3abba78..eff1dd8 100644 Binary files a/Platform/libs/libarm64/libFosterPlatform.so and b/Platform/libs/libarm64/libFosterPlatform.so differ diff --git a/Platform/libs/osx/libFosterPlatform.dylib b/Platform/libs/osx/libFosterPlatform.dylib index e3a4abd..bb584af 100644 Binary files a/Platform/libs/osx/libFosterPlatform.dylib and b/Platform/libs/osx/libFosterPlatform.dylib differ diff --git a/Platform/libs/x64/FosterPlatform.dll b/Platform/libs/x64/FosterPlatform.dll index cff447c..4b5b758 100644 Binary files a/Platform/libs/x64/FosterPlatform.dll and b/Platform/libs/x64/FosterPlatform.dll differ diff --git a/Platform/src/foster_renderer_opengl.c b/Platform/src/foster_renderer_opengl.c index 4a77e41..f0d0011 100644 --- a/Platform/src/foster_renderer_opengl.c +++ b/Platform/src/foster_renderer_opengl.c @@ -348,7 +348,7 @@ GL_FUNCTIONS #define FOSTER_RECT_EQUAL(a, b) ((a).x == (b).x && (a).y == (b).y && (a).w == (b).w && (a).h == (b).h) -typedef struct FosterTexure_OpenGL +typedef struct FosterTexture_OpenGL { GLuint id; int width; @@ -359,7 +359,7 @@ typedef struct FosterTexure_OpenGL GLenum glType; GLenum glAttachment; FosterTextureSampler sampler; - + // Because Shader uniforms assign textures, it's possible for the user to // dispose of a texture but still have it assigned in a shader. Thus we use // a simple ref counter to determine when it's safe to delete the wrapping @@ -417,9 +417,9 @@ typedef struct FosterMesh_OpenGL typedef struct { // GL function pointers - #define GL_FUNC(name, ret, ...) gl ## name ## Fn gl ## name; +#define GL_FUNC(name, ret, ...) gl ## name ## Fn gl ## name; GL_FUNCTIONS - #undef GL_FUNC +#undef GL_FUNC // GL context void* context; @@ -431,6 +431,8 @@ typedef struct GLuint stateProgram; GLuint stateFrameBuffer; GLuint stateVertexArray; + GLuint stateArrayBuffer; + GLuint stateElementBuffer; int stateFrameBufferWidth; int stateFrameBufferHeight; int stateHasScissor; @@ -449,8 +451,8 @@ typedef struct int max_samples; int max_texture_image_units; int max_texture_size; - } FosterOpenGLState; + static FosterOpenGLState fgl; // debug callback @@ -468,23 +470,23 @@ void APIENTRY FosterMessage_OpenGL(GLenum source, GLenum type, GLuint id, GLenum switch (type) { - case GL_DEBUG_TYPE_ERROR: typeName = "ERROR"; break; - case GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR: typeName = "DEPRECATED BEHAVIOR"; break; - case GL_DEBUG_TYPE_MARKER: typeName = "MARKER"; break; - case GL_DEBUG_TYPE_OTHER: typeName = "OTHER"; break; - case GL_DEBUG_TYPE_PERFORMANCE: typeName = "PEROFRMANCE"; break; - case GL_DEBUG_TYPE_POP_GROUP: typeName = "POP GROUP"; break; - case GL_DEBUG_TYPE_PORTABILITY: typeName = "PORTABILITY"; break; - case GL_DEBUG_TYPE_PUSH_GROUP: typeName = "PUSH GROUP"; break; - case GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR: typeName = "UNDEFINED BEHAVIOR"; break; + case GL_DEBUG_TYPE_ERROR: typeName = "ERROR"; break; + case GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR: typeName = "DEPRECATED BEHAVIOR"; break; + case GL_DEBUG_TYPE_MARKER: typeName = "MARKER"; break; + case GL_DEBUG_TYPE_OTHER: typeName = "OTHER"; break; + case GL_DEBUG_TYPE_PERFORMANCE: typeName = "PEROFRMANCE"; break; + case GL_DEBUG_TYPE_POP_GROUP: typeName = "POP GROUP"; break; + case GL_DEBUG_TYPE_PORTABILITY: typeName = "PORTABILITY"; break; + case GL_DEBUG_TYPE_PUSH_GROUP: typeName = "PUSH GROUP"; break; + case GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR: typeName = "UNDEFINED BEHAVIOR"; break; } switch (severity) { - case GL_DEBUG_SEVERITY_HIGH: severityName = "HIGH"; break; - case GL_DEBUG_SEVERITY_MEDIUM: severityName = "MEDIUM"; break; - case GL_DEBUG_SEVERITY_LOW: severityName = "LOW"; break; - case GL_DEBUG_SEVERITY_NOTIFICATION: severityName = "NOTIFICATION"; break; + case GL_DEBUG_SEVERITY_HIGH: severityName = "HIGH"; break; + case GL_DEBUG_SEVERITY_MEDIUM: severityName = "MEDIUM"; break; + case GL_DEBUG_SEVERITY_LOW: severityName = "LOW"; break; + case GL_DEBUG_SEVERITY_NOTIFICATION: severityName = "NOTIFICATION"; break; } if (type == GL_DEBUG_TYPE_ERROR) @@ -522,12 +524,12 @@ GLenum FosterBlendOpToGL(FosterBlendOp operation) { switch (operation) { - case FOSTER_BLEND_OP_ADD: return GL_FUNC_ADD; - case FOSTER_BLEND_OP_SUBTRACT: return GL_FUNC_SUBTRACT; - case FOSTER_BLEND_OP_REVERSE_SUBTRACT: return GL_FUNC_REVERSE_SUBTRACT; - case FOSTER_BLEND_OP_MIN: return GL_MIN; - case FOSTER_BLEND_OP_MAX: return GL_MAX; - default: return GL_FUNC_ADD; + case FOSTER_BLEND_OP_ADD: return GL_FUNC_ADD; + case FOSTER_BLEND_OP_SUBTRACT: return GL_FUNC_SUBTRACT; + case FOSTER_BLEND_OP_REVERSE_SUBTRACT: return GL_FUNC_REVERSE_SUBTRACT; + case FOSTER_BLEND_OP_MIN: return GL_MIN; + case FOSTER_BLEND_OP_MAX: return GL_MAX; + default: return GL_FUNC_ADD; }; } @@ -535,25 +537,25 @@ GLenum FosterBlendFactorToGL(FosterBlendFactor factor) { switch (factor) { - case FOSTER_BLEND_FACTOR_Zero: return GL_ZERO; - case FOSTER_BLEND_FACTOR_One: return GL_ONE; - case FOSTER_BLEND_FACTOR_SrcColor: return GL_SRC_COLOR; - case FOSTER_BLEND_FACTOR_OneMinusSrcColor: return GL_ONE_MINUS_SRC_COLOR; - case FOSTER_BLEND_FACTOR_DstColor: return GL_DST_COLOR; - case FOSTER_BLEND_FACTOR_OneMinusDstColor: return GL_ONE_MINUS_DST_COLOR; - case FOSTER_BLEND_FACTOR_SrcAlpha: return GL_SRC_ALPHA; - case FOSTER_BLEND_FACTOR_OneMinusSrcAlpha: return GL_ONE_MINUS_SRC_ALPHA; - case FOSTER_BLEND_FACTOR_DstAlpha: return GL_DST_ALPHA; - case FOSTER_BLEND_FACTOR_OneMinusDstAlpha: return GL_ONE_MINUS_DST_ALPHA; - case FOSTER_BLEND_FACTOR_ConstantColor: return GL_CONSTANT_COLOR; - case FOSTER_BLEND_FACTOR_OneMinusConstantColor: return GL_ONE_MINUS_CONSTANT_COLOR; - case FOSTER_BLEND_FACTOR_ConstantAlpha: return GL_CONSTANT_ALPHA; - case FOSTER_BLEND_FACTOR_OneMinusConstantAlpha: return GL_ONE_MINUS_CONSTANT_ALPHA; - case FOSTER_BLEND_FACTOR_SrcAlphaSaturate: return GL_SRC_ALPHA_SATURATE; - case FOSTER_BLEND_FACTOR_Src1Color: return GL_SRC1_COLOR; - case FOSTER_BLEND_FACTOR_OneMinusSrc1Color: return GL_ONE_MINUS_SRC1_COLOR; - case FOSTER_BLEND_FACTOR_Src1Alpha: return GL_SRC1_ALPHA; - case FOSTER_BLEND_FACTOR_OneMinusSrc1Alpha: return GL_ONE_MINUS_SRC1_ALPHA; + case FOSTER_BLEND_FACTOR_Zero: return GL_ZERO; + case FOSTER_BLEND_FACTOR_One: return GL_ONE; + case FOSTER_BLEND_FACTOR_SrcColor: return GL_SRC_COLOR; + case FOSTER_BLEND_FACTOR_OneMinusSrcColor: return GL_ONE_MINUS_SRC_COLOR; + case FOSTER_BLEND_FACTOR_DstColor: return GL_DST_COLOR; + case FOSTER_BLEND_FACTOR_OneMinusDstColor: return GL_ONE_MINUS_DST_COLOR; + case FOSTER_BLEND_FACTOR_SrcAlpha: return GL_SRC_ALPHA; + case FOSTER_BLEND_FACTOR_OneMinusSrcAlpha: return GL_ONE_MINUS_SRC_ALPHA; + case FOSTER_BLEND_FACTOR_DstAlpha: return GL_DST_ALPHA; + case FOSTER_BLEND_FACTOR_OneMinusDstAlpha: return GL_ONE_MINUS_DST_ALPHA; + case FOSTER_BLEND_FACTOR_ConstantColor: return GL_CONSTANT_COLOR; + case FOSTER_BLEND_FACTOR_OneMinusConstantColor: return GL_ONE_MINUS_CONSTANT_COLOR; + case FOSTER_BLEND_FACTOR_ConstantAlpha: return GL_CONSTANT_ALPHA; + case FOSTER_BLEND_FACTOR_OneMinusConstantAlpha: return GL_ONE_MINUS_CONSTANT_ALPHA; + case FOSTER_BLEND_FACTOR_SrcAlphaSaturate: return GL_SRC_ALPHA_SATURATE; + case FOSTER_BLEND_FACTOR_Src1Color: return GL_SRC1_COLOR; + case FOSTER_BLEND_FACTOR_OneMinusSrc1Color: return GL_ONE_MINUS_SRC1_COLOR; + case FOSTER_BLEND_FACTOR_Src1Alpha: return GL_SRC1_ALPHA; + case FOSTER_BLEND_FACTOR_OneMinusSrc1Alpha: return GL_ONE_MINUS_SRC1_ALPHA; }; return GL_ZERO; @@ -563,13 +565,13 @@ FosterUniformType FosterUniformTypeFromGL(GLenum value) { switch (value) { - case GL_FLOAT: return FOSTER_UNIFORM_TYPE_FLOAT; - case GL_FLOAT_VEC2: return FOSTER_UNIFORM_TYPE_FLOAT2; - case GL_FLOAT_VEC3: return FOSTER_UNIFORM_TYPE_FLOAT3; - case GL_FLOAT_VEC4: return FOSTER_UNIFORM_TYPE_FLOAT4; - case GL_FLOAT_MAT3x2: return FOSTER_UNIFORM_TYPE_MAT3X2; - case GL_FLOAT_MAT4: return FOSTER_UNIFORM_TYPE_MAT4X4; - case GL_SAMPLER_2D: return FOSTER_UNIFORM_TYPE_SAMPLER2D; + case GL_FLOAT: return FOSTER_UNIFORM_TYPE_FLOAT; + case GL_FLOAT_VEC2: return FOSTER_UNIFORM_TYPE_FLOAT2; + case GL_FLOAT_VEC3: return FOSTER_UNIFORM_TYPE_FLOAT3; + case GL_FLOAT_VEC4: return FOSTER_UNIFORM_TYPE_FLOAT4; + case GL_FLOAT_MAT3x2: return FOSTER_UNIFORM_TYPE_MAT3X2; + case GL_FLOAT_MAT4: return FOSTER_UNIFORM_TYPE_MAT4X4; + case GL_SAMPLER_2D: return FOSTER_UNIFORM_TYPE_SAMPLER2D; }; return FOSTER_UNIFORM_TYPE_NONE; @@ -578,7 +580,26 @@ FosterUniformType FosterUniformTypeFromGL(GLenum value) GLuint FosterMeshAssignAttributes_OpenGL(GLuint buffer, GLenum bufferType, FosterVertexFormat* format, GLint divisor) { // bind - fgl.glBindBuffer(bufferType, buffer); + switch (bufferType) + { + case GL_ARRAY_BUFFER: + if (fgl.stateArrayBuffer != buffer) + { + fgl.glBindBuffer(bufferType, buffer); + fgl.stateArrayBuffer = buffer; + } + break; + case GL_ELEMENT_ARRAY_BUFFER: + if (fgl.stateElementBuffer != buffer) + { + fgl.glBindBuffer(bufferType, buffer); + fgl.stateElementBuffer = buffer; + } + break; + default: + fgl.glBindBuffer(bufferType, buffer); + break; + } // TODO: disable existing enabled attributes? // ... @@ -717,7 +738,10 @@ void FosterBindTexture(int slot, GLuint id) } if (fgl.stateTextureSlots[slot] != id) + { fgl.glBindTexture(GL_TEXTURE_2D, id); + fgl.stateTextureSlots[slot] = id; + } } // Same as FosterBindTexture, except it the resulting global state doesn't @@ -733,6 +757,7 @@ void FosterEnsureTextureSlotIs(int slot, GLuint id) } fgl.glBindTexture(GL_TEXTURE_2D, id); + fgl.stateTextureSlots[slot] = id; } } @@ -756,7 +781,7 @@ void FosterSetTextureSampler(FosterTexture_OpenGL* tex, FosterTextureSampler sam if (tex->sampler.wrapY != sampler.wrapY) fgl.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, FosterWrapToGL(sampler.wrapY)); - + tex->sampler = sampler; } } @@ -899,15 +924,15 @@ void FosterSetCompare(FosterCompare compare) switch (compare) { - case FOSTER_COMPARE_NONE: break; - case FOSTER_COMPARE_ALWAYS: fgl.glDepthFunc(GL_ALWAYS); break; - case FOSTER_COMPARE_EQUAL: fgl.glDepthFunc(GL_EQUAL); break; - case FOSTER_COMPARE_GREATER: fgl.glDepthFunc(GL_GREATER); break; - case FOSTER_COMPARE_GREATOR_OR_EQUAL: fgl.glDepthFunc(GL_GEQUAL); break; - case FOSTER_COMPARE_LESS: fgl.glDepthFunc(GL_LESS); break; - case FOSTER_COMPARE_LESS_OR_EQUAL: fgl.glDepthFunc(GL_LEQUAL); break; - case FOSTER_COMPARE_NEVER: fgl.glDepthFunc(GL_NEVER); break; - case FOSTER_COMPARE_NOT_EQUAL: fgl.glDepthFunc(GL_NOTEQUAL); break; + case FOSTER_COMPARE_NONE: break; + case FOSTER_COMPARE_ALWAYS: fgl.glDepthFunc(GL_ALWAYS); break; + case FOSTER_COMPARE_EQUAL: fgl.glDepthFunc(GL_EQUAL); break; + case FOSTER_COMPARE_GREATER: fgl.glDepthFunc(GL_GREATER); break; + case FOSTER_COMPARE_GREATOR_OR_EQUAL: fgl.glDepthFunc(GL_GEQUAL); break; + case FOSTER_COMPARE_LESS: fgl.glDepthFunc(GL_LESS); break; + case FOSTER_COMPARE_LESS_OR_EQUAL: fgl.glDepthFunc(GL_LEQUAL); break; + case FOSTER_COMPARE_NEVER: fgl.glDepthFunc(GL_NEVER); break; + case FOSTER_COMPARE_NOT_EQUAL: fgl.glDepthFunc(GL_NOTEQUAL); break; } } } @@ -943,9 +968,9 @@ void FosterSetCull(FosterCull cull) switch (cull) { - case FOSTER_CULL_NONE: break; - case FOSTER_CULL_BACK: fgl.glCullFace(GL_BACK); break; - case FOSTER_CULL_FRONT: fgl.glCullFace(GL_FRONT); break; + case FOSTER_CULL_NONE: break; + case FOSTER_CULL_BACK: fgl.glCullFace(GL_BACK); break; + case FOSTER_CULL_FRONT: fgl.glCullFace(GL_FRONT); break; } } } @@ -1043,7 +1068,7 @@ bool FosterInitialize_OpenGL() // zero out texture state fgl.stateActiveTextureSlot = 0; fgl.glActiveTexture(GL_TEXTURE0); - for (int i = 0; i < FOSTER_MAX_UNIFORM_TEXTURES; i ++) + for (int i = 0; i < FOSTER_MAX_UNIFORM_TEXTURES; i++) fgl.stateTextureSlots[i] = 0; // log @@ -1099,24 +1124,24 @@ FosterTexture* FosterTextureCreate_OpenGL(int width, int height, FosterTextureFo switch (format) { - case FOSTER_TEXTURE_FORMAT_R8: - result.glInternalFormat = GL_RED; - result.glFormat = GL_RED; - result.glType = GL_UNSIGNED_BYTE; - break; - case FOSTER_TEXTURE_FORMAT_R8G8B8A8: - result.glInternalFormat = GL_RGBA; - result.glFormat = GL_RGBA; - result.glType = GL_UNSIGNED_BYTE; - break; - case FOSTER_TEXTURE_FORMAT_DEPTH24_STENCIL8: - result.glInternalFormat = GL_DEPTH24_STENCIL8; - result.glFormat = GL_DEPTH_STENCIL; - result.glType = GL_UNSIGNED_INT_24_8; - break; - default: - FosterLogError("Invalid Texture Format (%i)", format); - return NULL; + case FOSTER_TEXTURE_FORMAT_R8: + result.glInternalFormat = GL_RED; + result.glFormat = GL_RED; + result.glType = GL_UNSIGNED_BYTE; + break; + case FOSTER_TEXTURE_FORMAT_R8G8B8A8: + result.glInternalFormat = GL_RGBA; + result.glFormat = GL_RGBA; + result.glType = GL_UNSIGNED_BYTE; + break; + case FOSTER_TEXTURE_FORMAT_DEPTH24_STENCIL8: + result.glInternalFormat = GL_DEPTH24_STENCIL8; + result.glFormat = GL_DEPTH_STENCIL; + result.glType = GL_UNSIGNED_INT_24_8; + break; + default: + FosterLogError("Invalid Texture Format (%i)", format); + return NULL; } fgl.glGenTextures(1, &result.id); @@ -1168,7 +1193,7 @@ FosterTarget* FosterTargetCreate_OpenGL(int width, int height, FosterTextureForm result.height = height; result.attachmentCount = attachmentCount; result.colorAttachmentCount = 0; - for (int i = 0; i < FOSTER_MAX_TARGET_ATTACHMENTS; i ++) + for (int i = 0; i < FOSTER_MAX_TARGET_ATTACHMENTS; i++) result.attachments[i] = NULL; fgl.glGenFramebuffers(1, &result.id); @@ -1180,7 +1205,7 @@ FosterTarget* FosterTargetCreate_OpenGL(int width, int height, FosterTextureForm if (tex == NULL) { - for (int j = 0; j < i; j ++) + for (int j = 0; j < i; j++) FosterTextureDestroy_OpenGL((FosterTexture*)tex); FosterLogError("Failed to create Target Attachment"); FosterBindFrameBuffer(NULL); @@ -1221,7 +1246,7 @@ void FosterTargetDestroy_OpenGL(FosterTarget* target) { FosterTarget_OpenGL* tar = (FosterTarget_OpenGL*)target; - for (int i = 0; i < FOSTER_MAX_TARGET_ATTACHMENTS; i ++) + for (int i = 0; i < FOSTER_MAX_TARGET_ATTACHMENTS; i++) { if (tar->attachments[i] != NULL) FosterTextureDestroy_OpenGL((FosterTexture*)tar->attachments[i]); @@ -1332,7 +1357,7 @@ FosterShader* FosterShaderCreate_OpenGL(FosterShaderData* data) shader->uniformCount = 0; shader->uniforms = NULL; - for (int i = 0; i < FOSTER_MAX_UNIFORM_TEXTURES; i ++) + for (int i = 0; i < FOSTER_MAX_UNIFORM_TEXTURES; i++) { shader->textures[i] = NULL; shader->samplers[i].filter = FOSTER_TEXTURE_FILTER_LINEAR; @@ -1359,7 +1384,7 @@ FosterShader* FosterShaderCreate_OpenGL(FosterShaderData* data) // get the name & properties GLsizei nameLen; - char nameBuf[256]; + char nameBuf[256]; fgl.glGetActiveUniform(id, i, 255, &nameLen, &uniform->glSize, &uniform->glType, nameBuf); // array names end with "[0]", and we don't want that @@ -1374,7 +1399,7 @@ FosterShader* FosterShaderCreate_OpenGL(FosterShaderData* data) // allocate enough room for the name uniform->name = (char*)SDL_malloc(nameLen + 8); SDL_strlcpy(uniform->name, nameBuf, nameLen + 1); - + // get GL location uniform->glLocation = fgl.glGetUniformLocation(id, uniform->name); @@ -1398,7 +1423,7 @@ void FosterShaderGetUniforms_OpenGL(FosterShader* shader, FosterUniformInfo* out int t = 0; - for (int i = 0; t < max && i < it->uniformCount; i ++) + for (int i = 0; t < max && i < it->uniformCount; i++) { FosterUniform_OpenGL* uniform = it->uniforms + i; @@ -1447,24 +1472,24 @@ void FosterShaderSetUniform_OpenGL(FosterShader* shader, int index, float* value switch (uniform->glType) { - case GL_FLOAT: - fgl.glUniform1fv(uniform->glLocation, (GLint)uniform->glSize, values); - return; - case GL_FLOAT_VEC2: - fgl.glUniform2fv(uniform->glLocation, (GLint)uniform->glSize, values); - return; - case GL_FLOAT_VEC3: - fgl.glUniform3fv(uniform->glLocation, (GLint)uniform->glSize, values); - return; - case GL_FLOAT_VEC4: - fgl.glUniform4fv(uniform->glLocation, (GLint)uniform->glSize, values); - return; - case GL_FLOAT_MAT3x2: - fgl.glUniformMatrix3x2fv(uniform->glLocation, (GLint)uniform->glSize, 0, values); - return; - case GL_FLOAT_MAT4: - fgl.glUniformMatrix4fv(uniform->glLocation, (GLint)uniform->glSize, 0, values); - return; + case GL_FLOAT: + fgl.glUniform1fv(uniform->glLocation, (GLint)uniform->glSize, values); + return; + case GL_FLOAT_VEC2: + fgl.glUniform2fv(uniform->glLocation, (GLint)uniform->glSize, values); + return; + case GL_FLOAT_VEC3: + fgl.glUniform3fv(uniform->glLocation, (GLint)uniform->glSize, values); + return; + case GL_FLOAT_VEC4: + fgl.glUniform4fv(uniform->glLocation, (GLint)uniform->glSize, values); + return; + case GL_FLOAT_MAT3x2: + fgl.glUniformMatrix3x2fv(uniform->glLocation, (GLint)uniform->glSize, 0, values); + return; + case GL_FLOAT_MAT4: + fgl.glUniformMatrix4fv(uniform->glLocation, (GLint)uniform->glSize, 0, values); + return; } FosterLogError("Failed to set uniform '%s', unsupported type '%i'", uniform->name, uniform->glType); @@ -1487,7 +1512,7 @@ void FosterShaderSetTexture_OpenGL(FosterShader* shader, int index, FosterTextur return; } - for (int i = 0; i < uniform->glSize && uniform->samplerIndex + i < FOSTER_MAX_UNIFORM_TEXTURES; i ++) + for (int i = 0; i < uniform->glSize && uniform->samplerIndex + i < FOSTER_MAX_UNIFORM_TEXTURES; i++) { int index = uniform->samplerIndex + i; FosterTextureReturnReference(it->textures[index]); @@ -1512,7 +1537,7 @@ void FosterShaderSetSampler_OpenGL(FosterShader* shader, int index, FosterTextur return; } - for (int i = 0; i < uniform->glSize && uniform->samplerIndex + i < FOSTER_MAX_UNIFORM_TEXTURES; i ++) + for (int i = 0; i < uniform->glSize && uniform->samplerIndex + i < FOSTER_MAX_UNIFORM_TEXTURES; i++) it->samplers[uniform->samplerIndex + i] = values[i]; } @@ -1521,10 +1546,10 @@ void FosterShaderDestroy_OpenGL(FosterShader* shader) FosterShader_OpenGL* it = (FosterShader_OpenGL*)shader; fgl.glDeleteProgram(it->id); - for (int i = 0; i < FOSTER_MAX_UNIFORM_TEXTURES; i ++) + for (int i = 0; i < FOSTER_MAX_UNIFORM_TEXTURES; i++) FosterTextureReturnReference(it->textures[i]); - for (int i = 0; i < it->uniformCount; i ++) + for (int i = 0; i < it->uniformCount; i++) { SDL_free(it->uniforms[i].name); SDL_free(it->uniforms[i].samplerName); @@ -1574,8 +1599,16 @@ void FosterMeshSetVertexData_OpenGL(FosterMesh* mesh, void* data, int dataSize, FosterBindArray(it->id); if (it->vertexBuffer == 0) + { fgl.glGenBuffers(1, &(it->vertexBuffer)); - fgl.glBindBuffer(GL_ARRAY_BUFFER, it->vertexBuffer); + fgl.glBindBuffer(GL_ARRAY_BUFFER, it->vertexBuffer); + fgl.stateArrayBuffer = it->vertexBuffer; + } + else if (fgl.stateArrayBuffer != it->vertexBuffer) + { + fgl.glBindBuffer(GL_ARRAY_BUFFER, it->vertexBuffer); + fgl.stateArrayBuffer = it->vertexBuffer; + } // expand vertex buffer if needed int totalSize = dataDestOffset + dataSize; @@ -1595,17 +1628,17 @@ void FosterMeshSetIndexFormat_OpenGL(FosterMesh* mesh, FosterIndexFormat format) switch (format) { - case FOSTER_INDEX_FORMAT_SIXTEEN: - it->indexFormat = GL_UNSIGNED_SHORT; - it->indexSize = 2; - break; - case FOSTER_INDEX_FORMAT_THIRTY_TWO: - it->indexFormat = GL_UNSIGNED_INT; - it->indexSize = 4; - break; - default: - FosterLogError("Invalid Index Format '%i'", format); - break; + case FOSTER_INDEX_FORMAT_SIXTEEN: + it->indexFormat = GL_UNSIGNED_SHORT; + it->indexSize = 2; + break; + case FOSTER_INDEX_FORMAT_THIRTY_TWO: + it->indexFormat = GL_UNSIGNED_INT; + it->indexSize = 4; + break; + default: + FosterLogError("Invalid Index Format '%i'", format); + break; } } @@ -1615,9 +1648,16 @@ void FosterMeshSetIndexData_OpenGL(FosterMesh* mesh, void* data, int dataSize, i FosterBindArray(it->id); if (it->indexBuffer == 0) + { fgl.glGenBuffers(1, &(it->indexBuffer)); - fgl.glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, it->indexBuffer); - + fgl.glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, it->indexBuffer); + fgl.stateElementBuffer = it->indexBuffer; + } + else if (fgl.stateElementBuffer != it->indexBuffer) + { + fgl.glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, it->indexBuffer); + fgl.stateElementBuffer = it->indexBuffer; + } // expand buffer if needed int totalSize = dataDestOffset + dataSize; if (totalSize > it->indexBufferSize) @@ -1668,7 +1708,7 @@ void FosterDraw_OpenGL(FosterDrawCommand* command) GLuint textureSlots[FOSTER_MAX_UNIFORM_TEXTURES]; // update samplers - for (int i = 0; i < FOSTER_MAX_UNIFORM_TEXTURES; i ++) + for (int i = 0; i < FOSTER_MAX_UNIFORM_TEXTURES; i++) { FosterTexture_OpenGL* tex = shader->textures[i]; if (shader->textures[i] != NULL) @@ -1677,14 +1717,14 @@ void FosterDraw_OpenGL(FosterDrawCommand* command) // bind textures int slot = 0; - for (int i = 0; i < shader->uniformCount; i ++) + for (int i = 0; i < shader->uniformCount; i++) { FosterUniform_OpenGL* uniform = shader->uniforms + i; if (uniform->glType != GL_SAMPLER_2D) continue; // bind textures & update sampler state - for (int n = 0; n < uniform->glSize && slot < FOSTER_MAX_UNIFORM_TEXTURES; n ++) + for (int n = 0; n < uniform->glSize && slot < FOSTER_MAX_UNIFORM_TEXTURES; n++) { FosterTexture_OpenGL* tex = shader->textures[uniform->samplerIndex + n]; @@ -1743,7 +1783,7 @@ void FosterClear_OpenGL(FosterClearCommand* command) fgl.glColorMask(true, true, true, true); fgl.glClearColor(command->color.r / 255.0f, command->color.g / 255.0f, command->color.b / 255.0f, command->color.a / 255.0f); } - + if ((command->mask & FOSTER_CLEAR_MASK_DEPTH) == FOSTER_CLEAR_MASK_DEPTH) { FosterSetDepthMask(1); @@ -1807,4 +1847,3 @@ bool FosterGetDevice_OpenGL(FosterRenderDevice* device) #endif -