diff --git a/examples/Playground/Program.fs b/examples/Playground/Program.fs index f04603f..1ea0943 100644 --- a/examples/Playground/Program.fs +++ b/examples/Playground/Program.fs @@ -26,13 +26,19 @@ type GameState = Triangles = Primitives.ShadedObject.Default } let private initHandler (config:Config) = - let fragmentPaths = ["Resources/Shaders/fragment.glsl"] - let vertexPaths = ["Resources/Shaders/vertex.glsl"] + let fragmentPaths = ["Resources/Shaders/Texture/fragment.glsl"] + let vertexPaths = ["Resources/Shaders/Texture/vertex.glsl"] let transform = { Transform.Default() with Scale = (1.0f, 1.0f, 1.0f) Rotation = (0.0f, 0.0f, 0.0f) } - match Primitives.ShadedObject.CreateQuad vertexPaths fragmentPaths transform 1.8f 1.0f None with + match Primitives.ShadedObject.CreateQuad + vertexPaths + fragmentPaths + transform + 1.8f + 1.0f + (Types.Texture2D.FromPath "Resources/Textures/smiley.png" |> Some) with | Some primitive -> { config with State.Triangles = primitive } diff --git a/examples/Playground/Resources/Shaders/fragment.glsl b/examples/Playground/Resources/Shaders/Scanlines/fragment.glsl similarity index 100% rename from examples/Playground/Resources/Shaders/fragment.glsl rename to examples/Playground/Resources/Shaders/Scanlines/fragment.glsl diff --git a/examples/Playground/Resources/Shaders/vertex.glsl b/examples/Playground/Resources/Shaders/Scanlines/vertex.glsl similarity index 83% rename from examples/Playground/Resources/Shaders/vertex.glsl rename to examples/Playground/Resources/Shaders/Scanlines/vertex.glsl index 97a4a07..cce78fa 100644 --- a/examples/Playground/Resources/Shaders/vertex.glsl +++ b/examples/Playground/Resources/Shaders/Scanlines/vertex.glsl @@ -1,6 +1,6 @@ #version 330 core -layout (location = 0) in vec3 in_position; // the in_position variable has attribute position 0 +layout (location = 0) in vec3 in_position; // the in_position variable has attribute position 0\ void main() { diff --git a/examples/Playground/Resources/Shaders/Texture/fragment.glsl b/examples/Playground/Resources/Shaders/Texture/fragment.glsl new file mode 100644 index 0000000..8c52f16 --- /dev/null +++ b/examples/Playground/Resources/Shaders/Texture/fragment.glsl @@ -0,0 +1,19 @@ +#version 330 core + +in vec2 out_texture_coordinate; + +out vec4 out_frag_color; + +uniform vec2 in_viewport; +uniform sampler2D in_texture; + +void main() +{ + // out_frag_color = texture(in_texture, TexCoord) + out_frag_color = vec4( + 1.0f - out_texture_coordinate.x, + 1.0f - out_texture_coordinate.y, + 0.0f, + 1.0f + ); +} diff --git a/examples/Playground/Resources/Shaders/Texture/vertex.glsl b/examples/Playground/Resources/Shaders/Texture/vertex.glsl new file mode 100644 index 0000000..4cea338 --- /dev/null +++ b/examples/Playground/Resources/Shaders/Texture/vertex.glsl @@ -0,0 +1,12 @@ +#version 330 core + +layout (location = 0) in vec3 in_position; // the in_position variable has attribute position 0 +layout (location = 1) in vec2 in_tex_coordinate; // the in_tex_coordinate variable has attribute position 1 + +out vec2 out_texture_coordinate; + +void main() +{ + gl_Position = vec4(in_position.x, in_position.y, in_position.z, 1.0); + out_texture_coordinate = in_tex_coordinate; +} diff --git a/src/ThirdParty/SDL2/Image.cs b/src/ThirdParty/SDL2/Image.cs index bf22cf8..a497176 100644 --- a/src/ThirdParty/SDL2/Image.cs +++ b/src/ThirdParty/SDL2/Image.cs @@ -89,13 +89,13 @@ public static SDL.SDL_version IMG_Linked_Version() /* IntPtr refers to an SDL_Surface* */ [DllImport(nativeLibName, EntryPoint = "IMG_Load", CallingConvention = CallingConvention.Cdecl)] - private static extern unsafe IntPtr INTERNAL_IMG_Load( + private static extern unsafe SDL.SDL_Surface* INTERNAL_IMG_Load( byte* file ); - public static unsafe IntPtr IMG_Load(string file) + public static unsafe SDL.SDL_Surface* IMG_Load(string file) { byte* utf8File = SDL.Utf8EncodeHeap(file); - IntPtr handle = INTERNAL_IMG_Load( + SDL.SDL_Surface* handle = INTERNAL_IMG_Load( utf8File ); Marshal.FreeHGlobal((IntPtr)utf8File); diff --git a/src/ThirdParty/SDL2/SDL2Bindings.cs b/src/ThirdParty/SDL2/SDL2Bindings.cs index e5b8014..586f70c 100644 --- a/src/ThirdParty/SDL2/SDL2Bindings.cs +++ b/src/ThirdParty/SDL2/SDL2Bindings.cs @@ -4319,7 +4319,7 @@ uint color /* surface refers to an SDL_Surface* */ [DllImport(DllName, CallingConvention = CallingConvention.Cdecl)] - public static extern void SDL_FreeSurface(IntPtr surface); + public static extern unsafe void SDL_FreeSurface(SDL_Surface* surface); /* surface refers to an SDL_Surface* */ [DllImport(DllName, CallingConvention = CallingConvention.Cdecl)] diff --git a/src/Womb/Backends/OpenGL/Api/Constants/OpenGL1_0.fs b/src/Womb/Backends/OpenGL/Api/Constants/OpenGL1_0.fs index 04220f0..de21bfa 100644 --- a/src/Womb/Backends/OpenGL/Api/Constants/OpenGL1_0.fs +++ b/src/Womb/Backends/OpenGL/Api/Constants/OpenGL1_0.fs @@ -348,4 +348,4 @@ let GL_TEXTURE_WRAP_S = 0x2802u [] let GL_TEXTURE_WRAP_T = 0x2803u [] -let GL_REPEAT = 0x2901u \ No newline at end of file +let GL_REPEAT = 0x2901u diff --git a/src/Womb/Graphics/Primitives.fs b/src/Womb/Graphics/Primitives.fs index 0ea6628..07246ba 100644 --- a/src/Womb/Graphics/Primitives.fs +++ b/src/Womb/Graphics/Primitives.fs @@ -15,7 +15,7 @@ type ShadedObjectContext = EBO: uint; Vertices: array; Indices: array; - Texture: option; } + Texture: uint; } static member Default () = { VAO = 0u @@ -23,38 +23,39 @@ type ShadedObjectContext = EBO = 0u Vertices = Array.empty Indices = Array.empty - Texture = None } + Texture = 0u } - static member From (vertices) (indices) (textureOpt: option) : ShadedObjectContext = + static member From (vertices) (indices) (textureOpt: option) : ShadedObjectContext = let vao = glGenVertexArray() let vbo = glGenBuffer() let ebo = glGenBuffer() let textureId = match textureOpt with - | Some _ -> + | Some texture -> let id = glGenTexture() glBindTexture GL_TEXTURE_2D id - // TODO: Fix the type errors // NOTE: Eventually we will add texturing options for the individual texture, but for now let's set some defaults - // NOTE: set the texture wrapping/filtering options (on the currently bound texture object) - // glTexParameteri GL_TEXTURE_2D GL_TEXTURE_WRAP_S GL_REPEAT - // glTexParameteri GL_TEXTURE_2D GL_TEXTURE_WRAP_T GL_REPEAT - // glTexParameteri GL_TEXTURE_2D GL_TEXTURE_MIN_FILTER GL_LINEAR_MIPMAP_LINEAR - // glTexParameteri GL_TEXTURE_2D GL_TEXTURE_MAG_FILTER GL_LINEAR + glTexParameteri GL_TEXTURE_2D GL_TEXTURE_WRAP_S (int GL_REPEAT) + glTexParameteri GL_TEXTURE_2D GL_TEXTURE_WRAP_T (int GL_REPEAT) + glTexParameteri GL_TEXTURE_2D GL_TEXTURE_MIN_FILTER (int GL_LINEAR_MIPMAP_LINEAR) + glTexParameteri GL_TEXTURE_2D GL_TEXTURE_MAG_FILTER (int GL_LINEAR) - // TODO: Generate the texture and mipmap - // NOTE: glTexImage2d explanation... - // The first argument specifies the texture target; setting this to GL_TEXTURE_2D means this operation will generate a texture on the currently bound texture object at the same target (so any textures bound to targets GL_TEXTURE_1D or GL_TEXTURE_3D will not be affected). - // The second argument specifies the mipmap level for which we want to create a texture for if you want to set each mipmap level manually, but we'll leave it at the base level which is 0. - // The third argument tells OpenGL in what kind of format we want to store the texture. Our image has only RGB values so we'll store the texture with RGB values as well. - // The 4th and 5th argument sets the width and height of the resulting texture. We stored those earlier when loading the image so we'll use the corresponding variables. - // The next argument should always be 0 (some legacy stuff). - // The 7th and 8th argument specify the format and datatype of the source image. We loaded the image with RGB values and stored them as chars (bytes) so we'll pass in the corresponding values. - // The last argument is the actual image data. - // glTexImage2D GL_TEXTURE_2D 0 GL_RGB width height 0 GL_RGB GL_UNSIGNED_BYTE data + glTexImage2D + GL_TEXTURE_2D // specifies the dimensional behavior of a texture: 1D is a line, 2D is a normal flat texture, 3D is a volumetric texture, like when a model is cut open and you want to show the interior of the object: https://stackoverflow.com/a/7329678 + 0 // mipmap level to use (clamped to 0 for now) + (int GL_RGB) // format of output texture + texture.Width // width of resulting texture + texture.Height // height of resulting texture + 0 // always 0, for legacy reasons + GL_RGB // format of source image + GL_UNSIGNED_BYTE // datatype of source image + texture.Data // image data glGenerateMipmap GL_TEXTURE_2D - // TODO: Free the image data from SDL + + match texture.Surface with + | Some surface -> SDL2Bindings.SDL.SDL_FreeSurface(surface) + | None -> () id | None -> 0u @@ -76,7 +77,6 @@ type ShadedObjectContext = indices GL_STATIC_DRAW - // TODO: Adjust the stride parameter if a texture is present glVertexAttribPointer 0u 3u @@ -86,12 +86,25 @@ type ShadedObjectContext = 0 glEnableVertexAttribArray 0u + match textureId with + | 0u -> + // TODO: This is not binding properly. Create a new buffer just for texture coordinates and bind it + glVertexAttribPointer + 1u + 2u + GL_FLOAT + false + 2 + 4 + glEnableVertexAttribArray 1u + | _ -> () + { VAO = vao VBO = vbo EBO = ebo Vertices = vertices Indices = indices - Texture = None } + Texture = textureId } static member UpdateIndices (indices) (context: ShadedObjectContext) : ShadedObjectContext = glBindBuffer @@ -149,6 +162,7 @@ type ShadedObject = fragmentPaths ) with | Some shader -> + // TODO: Construct tex coords let vertices = [| // bottom left -width / 2.0f; -height / 2.0f; 0.0f; @@ -220,8 +234,7 @@ type ShadedObject = let mvp = modelMatrix * viewMatrix * projectionMatrix glUniformMatrix4fv mvpUniform 1 mvp - - // TODO: See if texture data or coords are best passed through uniforms. If so, modify this, otherwise, leave it be + List.map ( fun uniformData -> @@ -298,12 +311,24 @@ type ShadedObject = | 3 -> glUniform3ui location data[0] data[1] data[2] | 4 -> glUniform4ui location data[0] data[1] data[2] data[3] | len -> fail $"Unsupported UVectorUniform length {len} when trying to use shader" + | SamplerUniform(name, data) -> + let location = glGetUniformLocation shader.Id name + glUniform1i location data ) uniforms |> ignore static member Draw<'T> (config:Config<'T>) (viewMatrix:System.Numerics.Matrix4x4) (projectionMatrix:System.Numerics.Matrix4x4) (primitive:ShadedObject) (uniforms) = match primitive with | Quad(context, shader, transform, width, height) -> + let all_uniforms = + match context.Texture with + | 0u -> uniforms + | _ -> [ + SamplerUniform( + "in_texture", + 0 + ) + ] @ uniforms ShadedObject.UseMvpShader config shader @@ -323,12 +348,13 @@ type ShadedObject = config.DisplayConfig.Height |> single ) ); - ] @ uniforms ) + ] @ all_uniforms ) match context.Texture with - | Some texture -> - glBindTexture GL_TEXTURE_2D texture - | None -> () + | 0u -> () + | _ -> + glActiveTexture GL_TEXTURE0 + glBindTexture GL_TEXTURE_2D context.Texture glBindVertexArray context.VAO glBindBuffer GL_ELEMENT_ARRAY_BUFFER diff --git a/src/Womb/Graphics/Types.fs b/src/Womb/Graphics/Types.fs index 7486c67..8b3acff 100644 --- a/src/Womb/Graphics/Types.fs +++ b/src/Womb/Graphics/Types.fs @@ -1,7 +1,12 @@ module Womb.Graphics.Types +#nowarn "9" // unverifiable IL due to NativePtr library usage + +open Microsoft.FSharp.NativeInterop + open SDL2Bindings open Womb.Lib.Types +open Womb.Logging type DisplayConfig = { Width: uint; @@ -48,3 +53,31 @@ type Uniform = | UVector3Uniform of Name:string * Data:UVector3 | UVector4Uniform of Name:string * Data:UVector4 | UVectorUniform of Name:string * Data:array + | SamplerUniform of Name:string * Data:IVector1 + +type Texture2D = + { Surface: option>; + Width: int; + Height: int; + Data: voidptr; + Format: nativeint; } + + static member Default() = + { Surface = None + Width = 0 + Height = 0 + Data = 0n.ToPointer() + Format = 0n } + + static member FromPath path = + let surfacePtr = Image.IMG_Load(path) + if NativePtr.isNullPtr surfacePtr then + fail $"Failed to load surface from path - {path}" + Texture2D.Default() + else + let surface = NativePtr.get surfacePtr 0 + { Surface = Some surfacePtr + Width = surface.w + Height = surface.h + Data = surface.pixels.ToPointer() + Format = surface.format }