diff --git a/odin/sdl-opengl-rendering/.gitignore b/odin/sdl-opengl-rendering/.gitignore new file mode 100644 index 0000000..a8a0dce --- /dev/null +++ b/odin/sdl-opengl-rendering/.gitignore @@ -0,0 +1 @@ +*.bin diff --git a/odin/sdl-opengl-rendering/flake.lock b/odin/sdl-opengl-rendering/flake.lock new file mode 100644 index 0000000..fcda77b --- /dev/null +++ b/odin/sdl-opengl-rendering/flake.lock @@ -0,0 +1,61 @@ +{ + "nodes": { + "flake-utils": { + "inputs": { + "systems": "systems" + }, + "locked": { + "lastModified": 1731533236, + "narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=", + "owner": "numtide", + "repo": "flake-utils", + "rev": "11707dc2f618dd54ca8739b309ec4fc024de578b", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "flake-utils", + "type": "github" + } + }, + "nixpkgs": { + "locked": { + "lastModified": 1738680400, + "narHash": "sha256-ooLh+XW8jfa+91F1nhf9OF7qhuA/y1ChLx6lXDNeY5U=", + "owner": "nixos", + "repo": "nixpkgs", + "rev": "799ba5bffed04ced7067a91798353d360788b30d", + "type": "github" + }, + "original": { + "owner": "nixos", + "ref": "nixos-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "root": { + "inputs": { + "flake-utils": "flake-utils", + "nixpkgs": "nixpkgs" + } + }, + "systems": { + "locked": { + "lastModified": 1681028828, + "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", + "owner": "nix-systems", + "repo": "default", + "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", + "type": "github" + }, + "original": { + "owner": "nix-systems", + "repo": "default", + "type": "github" + } + } + }, + "root": "root", + "version": 7 +} diff --git a/odin/sdl-opengl-rendering/flake.nix b/odin/sdl-opengl-rendering/flake.nix new file mode 100644 index 0000000..ab66b89 --- /dev/null +++ b/odin/sdl-opengl-rendering/flake.nix @@ -0,0 +1,87 @@ +{ + inputs = { + nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable"; + flake-utils.url = "github:numtide/flake-utils"; + }; + + outputs = + inputs: + { + overlays.default = final: prev: { + # {{{ Odin + odin = prev.odin.overrideAttrs (_: { + version = "unstable-2025-03-28"; + src = final.fetchFromGitHub { + owner = "starlitcanopy"; + repo = "Odin"; + rev = "a1fc243f8df8b510e6de4f5e115fbfc09371cb9d"; + sha256 = "0fb8lk47nb7ln0skjn3lyfi499q3wlnzp6w3qc4wf4s5zj43d6zh"; + }; + }); + # }}} + # {{{ OLS + ols = prev.ols.overrideAttrs (_: { + version = "unstable-2025-03-12"; + src = final.fetchFromGitHub { + owner = "DanielGavin"; + repo = "ols"; + rev = "1e44e3d78ad8a74ef09c7f54a6f6d3f7df517f8e"; + sha256 = "16f7b8ijcaj5m2bdgbbl1q1mzgpgzzazrap2g17hkgy63aqq8qmf"; + }; + + installPhase = '' + runHook preInstall + + install -Dm755 ols odinfmt -t $out/bin/ + cp -r builtin $out/bin/ + wrapProgram $out/bin/ols --set-default ODIN_ROOT ${final.odin}/share + + runHook postInstall + ''; + }); + # }}} + }; + } + // inputs.flake-utils.lib.eachSystem (with inputs.flake-utils.lib.system; [ x86_64-linux ]) ( + system: + let + pkgs = inputs.nixpkgs.legacyPackages.${system}.extend inputs.self.overlays.default; + raylib = pkgs.raylib.override { platform = "SDL"; }; + inherit (pkgs) lib; + in + { + packages = { inherit (pkgs) odin; }; + + # {{{ Shell + devShell = pkgs.mkShell rec { + nativeBuildInputs = [ + pkgs.entr # File change detection + pkgs.odin # Compiler + pkgs.mold # Linker + pkgs.just # Script runner + pkgs.samply # Profiler + pkgs.ols # Language server + pkgs.gdb # Debugger + pkgs.seer # Debugger GUI + pkgs.valgrind # Detect memory leaks + ]; + + buildInputs = [ + pkgs.libGL + pkgs.libxkbcommon + + pkgs.xorg.libXi + pkgs.xorg.libX11 + pkgs.xorg.libXrandr + pkgs.xorg.libXinerama + pkgs.xorg.libXcursor + pkgs.sdl3 + raylib + ]; + + LD_LIBRARY_PATH = with pkgs; lib.makeLibraryPath buildInputs; + }; + # }}} + } + ); +} diff --git a/odin/sdl-opengl-rendering/src/frag.glsl b/odin/sdl-opengl-rendering/src/frag.glsl new file mode 100644 index 0000000..e5daf9f --- /dev/null +++ b/odin/sdl-opengl-rendering/src/frag.glsl @@ -0,0 +1,7 @@ +#version 140 + +out vec4 LFragment; + +void main() { + LFragment = vec4( 1.0, 1.0, 1.0, 1.0 ); +} diff --git a/odin/sdl-opengl-rendering/src/main.odin b/odin/sdl-opengl-rendering/src/main.odin new file mode 100644 index 0000000..1369fa8 --- /dev/null +++ b/odin/sdl-opengl-rendering/src/main.odin @@ -0,0 +1,244 @@ +package visuals + +import "core:c" +import "core:log" +import "vendor:OpenGL" +import "vendor:sdl3" + +State :: struct { + window: ^sdl3.Window, +} + +init :: proc() -> (state: State, ok: bool) { + sdl3.Init(sdl3.InitFlags{.VIDEO}) or_return + sdl3.GL_SetAttribute(.CONTEXT_MAJOR_VERSION, 3) or_return + sdl3.GL_SetAttribute(.CONTEXT_MINOR_VERSION, 1) or_return + sdl3.GL_SetAttribute(.CONTEXT_PROFILE_MASK, c.int(sdl3.GL_CONTEXT_PROFILE_CORE)) or_return + + window := sdl3.CreateWindow("SDL visual experiment", 640, 480, {.FULLSCREEN, .OPENGL}) + (window != nil) or_return + + gl_ctx := sdl3.GL_CreateContext(window) + (gl_ctx != nil) or_return + + // Init GL here + + return {window = window}, true +} + +init_gl :: proc() -> (ok: bool) { + program_id := OpenGL.load_shaders_source(#load("./vert.glsl"), #load("./frag.glsl")) or_return + return true +} + +// //Get vertex attribute location +// gVertexPos2DLocation = glGetAttribLocation( gProgramID, "LVertexPos2D" ); +// if( gVertexPos2DLocation == -1 ) +// { +// SDL_Log( "LVertexPos2D is not a valid glsl program variable!\n" ); +// success = false; +// } +// else +// { +// //Initialize clear color +// glClearColor( 0.f, 0.f, 0.f, 1.f ); +// +// //VBO data +// GLfloat vertexData[] = +// { +// -0.5f, -0.5f, +// 0.5f, -0.5f, +// 0.5f, 0.5f, +// -0.5f, 0.5f +// }; +// +// //IBO data +// GLuint indexData[] = { 0, 1, 2, 3 }; +// +// //Create VBO +// glGenBuffers( 1, &gVBO ); +// glBindBuffer( GL_ARRAY_BUFFER, gVBO ); +// glBufferData( GL_ARRAY_BUFFER, 2 * 4 * sizeof(GLfloat), vertexData, GL_STATIC_DRAW ); +// +// //Create IBO +// glGenBuffers( 1, &gIBO ); +// glBindBuffer( GL_ELEMENT_ARRAY_BUFFER, gIBO ); +// glBufferData( GL_ELEMENT_ARRAY_BUFFER, 4 * sizeof(GLuint), indexData, GL_STATIC_DRAW ); + +// void render() +// { +// //Clear color buffer +// glClear( GL_COLOR_BUFFER_BIT ); +// +// //Render quad +// if( gRenderQuad ) +// { +// //Bind program +// glUseProgram( gProgramID ); +// +// //Enable vertex position +// glEnableVertexAttribArray( gVertexPos2DLocation ); +// +// //Set vertex data +// glBindBuffer( GL_ARRAY_BUFFER, gVBO ); +// glVertexAttribPointer( gVertexPos2DLocation, 2, GL_FLOAT, GL_FALSE, 2 * sizeof(GLfloat), NULL ); +// +// //Set index data and render +// glBindBuffer( GL_ELEMENT_ARRAY_BUFFER, gIBO ); +// glDrawElements( GL_TRIANGLE_FAN, 4, GL_UNSIGNED_INT, NULL ); +// +// //Disable vertex position +// glDisableVertexAttribArray( gVertexPos2DLocation ); +// +// //Unbind program +// glUseProgram( 0 ); +// } +// } + +close :: proc(state: State) { + sdl3.DestroyWindow(state.window) + sdl3.Quit() +} + +// void close() +// { +// //Deallocate program +// glDeleteProgram( gProgramID ); +// +// //Destroy window +// SDL_DestroyWindow( gWindow ); +// gWindow = NULL; +// +// //Quit SDL subsystems +// SDL_Quit(); +// } +// +// void printProgramLog( GLuint program ) +// { +// //Make sure name is shader +// if( glIsProgram( program ) ) +// { +// //Program log length +// int infoLogLength = 0; +// int maxLength = infoLogLength; +// +// //Get info string length +// glGetProgramiv( program, GL_INFO_LOG_LENGTH, &maxLength ); +// +// //Allocate string +// char* infoLog = new char[ maxLength ]; +// +// //Get info log +// glGetProgramInfoLog( program, maxLength, &infoLogLength, infoLog ); +// if( infoLogLength > 0 ) +// { +// //Print Log +// SDL_Log( "%s\n", infoLog ); +// } +// +// //Deallocate string +// delete[] infoLog; +// } +// else +// { +// SDL_Log( "Name %d is not a program\n", program ); +// } +// } +// +// void printShaderLog( GLuint shader ) +// { +// //Make sure name is shader +// if( glIsShader( shader ) ) +// { +// //Shader log length +// int infoLogLength = 0; +// int maxLength = infoLogLength; +// +// //Get info string length +// glGetShaderiv( shader, GL_INFO_LOG_LENGTH, &maxLength ); +// +// //Allocate string +// char* infoLog = new char[ maxLength ]; +// +// //Get info log +// glGetShaderInfoLog( shader, maxLength, &infoLogLength, infoLog ); +// if( infoLogLength > 0 ) +// { +// //Print Log +// SDL_Log( "%s\n", infoLog ); +// } +// +// //Deallocate string +// delete[] infoLog; +// } +// else +// { +// SDL_Log( "Name %d is not a shader\n", shader ); +// } +// } + + +main :: proc() { + context.logger = log.create_console_logger() + log.debug("Starting") + state, ok := init() + log.assertf(ok, "Got SDL error: %v", sdl3.GetError()) + defer close(state) + + quit := false + log.debug("Created context") + + for !quit { + sdl3.StartTextInput(state.window) or_break + event: sdl3.Event + for sdl3.PollEvent(&event) { + #partial switch event.type { + case .QUIT: + quit = true + } + } + + sdl3.GL_SwapWindow(state.window) + sdl3.StopTextInput(state.window) or_break + } +} +// //Event handler +// SDL_Event e; +// +// //Enable text input +// SDL_StartTextInput( gWindow ); +// +// //While application is running +// while( !quit ) +// { +// //Handle events on queue +// while( SDL_PollEvent( &e ) != 0 ) +// { +// //User requests quit +// if( e.type == SDL_EVENT_QUIT ) +// { +// quit = true; +// } +// //Handle keypress +// else if( e.type == SDL_EVENT_TEXT_INPUT ) +// { +// handleKeys( e.text.text[ 0 ] ); +// } +// } +// +// //Render quad +// render(); +// +// //Update screen +// SDL_GL_SwapWindow( gWindow ); +// } +// +// //Disable text input +// SDL_StopTextInput( gWindow ); +// } +// +// //Free resources and close SDL +// close(); +// +// return 0; +// } diff --git a/odin/sdl-opengl-rendering/src/vert.glsl b/odin/sdl-opengl-rendering/src/vert.glsl new file mode 100644 index 0000000..a419f7c --- /dev/null +++ b/odin/sdl-opengl-rendering/src/vert.glsl @@ -0,0 +1,7 @@ +#version 140 + +in vec2 LVertexPos2D; + +void main() { + gl_Position = vec4(LVertexPos2D.x, LVertexPos2D.y, 0, 1 ); +}