diff options
| author | Claudius "keldu" Holeksa <mail@keldu.de> | 2026-01-10 23:20:05 +0100 |
|---|---|---|
| committer | Claudius "keldu" Holeksa <mail@keldu.de> | 2026-01-10 23:20:05 +0100 |
| commit | 1563ca95b44ea3e0f7384bc3109f366ed761668f (patch) | |
| tree | a721eafa2955737a467b6bff7df78569d7f29a4c | |
| parent | 393e58b20ece6c9d24af432299dfab6be2402ba8 (diff) | |
| download | libs-lbm-1563ca95b44ea3e0f7384bc3109f366ed761668f.tar.gz | |
Minor renderer for particles
| -rw-r--r-- | default.nix | 38 | ||||
| -rw-r--r-- | examples/particles_gpu/particles_gpu.cpp | 3 | ||||
| -rw-r--r-- | util/ogl_renderer/.nix/derivation.nix | 38 | ||||
| -rw-r--r-- | util/ogl_renderer/SConstruct | 77 | ||||
| -rw-r--r-- | util/ogl_renderer/c++/SConscript | 26 | ||||
| -rw-r--r-- | util/ogl_renderer/c++/common.hpp | 13 | ||||
| -rw-r--r-- | util/ogl_renderer/c++/lbm_ogl_renderer.cpp | 60 | ||||
| -rw-r--r-- | util/ogl_renderer/c++/program.hpp | 65 | ||||
| -rw-r--r-- | util/ogl_renderer/c++/shader.hpp | 8 | ||||
| -rw-r--r-- | util/ogl_renderer/c++/shaders/basic.hpp | 36 | ||||
| -rw-r--r-- | util/ogl_renderer/c++/state.hpp | 144 |
11 files changed, 503 insertions, 5 deletions
diff --git a/default.nix b/default.nix index 8399267..8422877 100644 --- a/default.nix +++ b/default.nix @@ -1,12 +1,35 @@ -{ pkgs ? import (builtins.fetchTarball{ - url = "https://github.com/NixOS/nixpkgs/archive/nixos-25.11.tar.gz"; -}) {} +{ pkgs ? import <nixpkgs> {} , llvmPackages ? pkgs.llvmPackages_19 , stdenv ? llvmPackages.stdenv , clang-tools ? llvmPackages.clang-tools }: let + glad_opengl_33_core = stdenv.mkDerivation { + pname = "glad_opengl_33_core"; + version = "0.0.1"; + + dontUnpack = true; + + nativeBuildInputs = [ + pkgs.python312Packages.glad2 + ]; + + buildPhase = '' + mkdir -p glad_out + glad \ + --api gl:core=3.3 \ + --out-path glad_out \ + --reproducible \ + c + mkdir -p glad_out/lib + cc -Iglad_out/include -fPIC -shared -o glad_out/lib/libgl.so glad_out/src/gl.c + ''; + + installPhase = '' + mv glad_out $out + ''; + }; adaptive-cpp = pkgs.callPackage .nix/adaptive-cpp.nix { inherit stdenv; @@ -33,7 +56,14 @@ in rec { sycl = pkgs.callPackage ./lib/sycl/.nix/derivation.nix { inherit forstio stdenv clang-tools pname version kel-lbm adaptive-cpp; }; - }; + }; + + util = { + lbm_ogl_renderer = pkgs.callPackage ./util/ogl_renderer/.nix/derivation.nix { + inherit forstio stdenv clang-tools pname version kel-lbm; + kel-glad = glad_opengl_33_core; + }; + }; examples = { cavity_2d_gpu = pkgs.callPackage ./examples/cavity_2d_gpu/.nix/derivation.nix { diff --git a/examples/particles_gpu/particles_gpu.cpp b/examples/particles_gpu/particles_gpu.cpp index 7e0cd5f..9697ac6 100644 --- a/examples/particles_gpu/particles_gpu.cpp +++ b/examples/particles_gpu/particles_gpu.cpp @@ -56,6 +56,7 @@ saw::error_or<void> lbm_main(int argc, char** argv){ acc_i.at({{0u}}) = 0.0; acc_i.at({{1u}}) = -9.81; } + // for(saw::data<sch::UInt64> i{0u}; i < particles.size(); ++i){ auto& part_i = particles.at(i); @@ -83,7 +84,7 @@ saw::error_or<void> lbm_main(int argc, char** argv){ auto pos_rel = pos_i - pos_j; auto vel_pos_rel_dot = saw::math::dot(vel_rel,pos_rel); - if(vel_pos_rel_dot.get() < 0.0){ + if(vel_pos_rel_dot.at({}).get() < 0.0){ } diff --git a/util/ogl_renderer/.nix/derivation.nix b/util/ogl_renderer/.nix/derivation.nix new file mode 100644 index 0000000..d03d6fa --- /dev/null +++ b/util/ogl_renderer/.nix/derivation.nix @@ -0,0 +1,38 @@ +{ lib +, stdenv +, scons +, clang-tools +, pkg-config +, pname +, version +, forstio +, kel-lbm +, libGL +, kel-glad +, sdl3 +}: + +stdenv.mkDerivation { + pname = pname + "-util-ogl-renderer"; + inherit version; + src = ./..; + + nativeBuildInputs = [ + scons + clang-tools + pkg-config + ]; + + buildInputs = [ + forstio.core + forstio.codec + forstio.codec-json + forstio.codec-unit + kel-lbm.core + libGL + kel-glad + sdl3 + ]; + + preferLocalBuild = true; +} diff --git a/util/ogl_renderer/SConstruct b/util/ogl_renderer/SConstruct new file mode 100644 index 0000000..80942e6 --- /dev/null +++ b/util/ogl_renderer/SConstruct @@ -0,0 +1,77 @@ +#!/usr/bin/env python3 + +import sys +import os +import os.path +import glob +import re + + +if sys.version_info < (3,): + def isbasestring(s): + return isinstance(s,basestring) +else: + def isbasestring(s): + return isinstance(s, (str,bytes)) + +def add_kel_source_files(self, sources, filetype, lib_env=None, shared=False, target_post=""): + + if isbasestring(filetype): + dir_path = self.Dir('.').abspath + filetype = sorted(glob.glob(dir_path+"/"+filetype)) + + for path in filetype: + target_name = re.sub( r'(.*?)(\.cpp|\.c\+\+)', r'\1' + target_post, path ) + if shared: + target_name+='.os' + sources.append( self.SharedObject( target=target_name, source=path ) ) + else: + target_name+='.o' + sources.append( self.StaticObject( target=target_name, source=path ) ) + pass + +def isAbsolutePath(key, dirname, env): + assert os.path.isabs(dirname), "%r must have absolute path syntax" % (key,) + +env_vars = Variables( + args=ARGUMENTS +) + +env_vars.Add('prefix', + help='Installation target location of build results and headers', + default='/usr/local/', + validator=isAbsolutePath +) + +env=Environment(ENV=os.environ, variables=env_vars, CPPPATH=[], + CPPDEFINES=['SAW_UNIX'], + CXXFLAGS=[ + '-std=c++20', + '-g', + '-Wall', + '-Wextra' + ], + LIBS=[ + 'forstio-core' + , 'dl' + , 'gl' + , 'SDL3' + ] +); +env.__class__.add_source_files = add_kel_source_files +env.Tool('compilation_db'); +env.cdb = env.CompilationDatabase('compile_commands.json'); + +env.objects = []; +env.sources = []; +env.headers = []; +env.targets = []; + +Export('env') +SConscript('c++/SConscript') + +env.Alias('cdb', env.cdb); +env.Alias('all', [env.targets]); +env.Default('all'); + +env.Alias('install', '$prefix') diff --git a/util/ogl_renderer/c++/SConscript b/util/ogl_renderer/c++/SConscript new file mode 100644 index 0000000..96d33fb --- /dev/null +++ b/util/ogl_renderer/c++/SConscript @@ -0,0 +1,26 @@ +#!/bin/false + +import os +import os.path +import glob + + +Import('env') + +dir_path = Dir('.').abspath + +# Environment for base library +core_env = env.Clone(); + +core_env.sources = sorted(glob.glob(dir_path + "/*.cpp")); +core_env.headers = sorted(glob.glob(dir_path + "/*.hpp")); + +env.sources += core_env.sources; +env.headers += core_env.headers; + +## Static lib +objects = [] +core_env.add_source_files(objects, core_env.sources, shared=False); +env.lbm_ogl_renderer = core_env.Program('#build/kel_lbm_ogl_renderer', [objects]); + +env.Install('$prefix/bin/', env.lbm_ogl_renderer); diff --git a/util/ogl_renderer/c++/common.hpp b/util/ogl_renderer/c++/common.hpp new file mode 100644 index 0000000..8f425e9 --- /dev/null +++ b/util/ogl_renderer/c++/common.hpp @@ -0,0 +1,13 @@ +#pragma once + +#include <glad/gl.h> +#include <SDL3/SDL.h> +#include <SDL3/SDL_opengl.h> + +namespace kel { +namespace lbm { +namespace sch { +using namespace saw::schema; +} +} +} diff --git a/util/ogl_renderer/c++/lbm_ogl_renderer.cpp b/util/ogl_renderer/c++/lbm_ogl_renderer.cpp new file mode 100644 index 0000000..b8b687d --- /dev/null +++ b/util/ogl_renderer/c++/lbm_ogl_renderer.cpp @@ -0,0 +1,60 @@ +#include <kel/lbm/lbm.hpp> + +#include "state.hpp" + +namespace kel { +namespace lbm { +namespace sch { +using namespace saw::schema; +} + +} + +saw::error_or<void> lbm_main(int argc, char** argv){ + auto eo_state = lbm::create_ogl_renderer(); + if(eo_state.is_error()){ + return std::move(eo_state.get_error()); + } + auto& state = *(eo_state.get_value()); + + bool running{true}; + while(running){ + state.clear(); + glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0); + SDL_GL_SwapWindow(state.window); + + SDL_Event event; + while(SDL_PollEvent(&event)){ + switch(event.type){ + case SDL_EVENT_QUIT: + running = false; + break; + case SDL_EVENT_KEY_UP: + if(event.key.key== SDLK_ESCAPE){ + running = false; + } + break; + default: + break; + } + } + } + + return saw::make_void(); +} +} + +int main(int argc, char** argv){ + auto eov = kel::lbm_main(argc, argv); + if(eov.is_error()){ + auto& err = eov.get_error(); + std::cerr<<"[Error] "<<err.get_category(); + auto err_msg = err.get_message(); + if(err_msg.size() > 0u){ + std::cerr<<" - "<<err_msg; + } + std::cerr<<std::endl; + return err.get_id(); + } + return 0; +} diff --git a/util/ogl_renderer/c++/program.hpp b/util/ogl_renderer/c++/program.hpp new file mode 100644 index 0000000..acf3016 --- /dev/null +++ b/util/ogl_renderer/c++/program.hpp @@ -0,0 +1,65 @@ +#pragma once + +#include "common.hpp" + +namespace kel { +namespace lbm { +GLuint create_ogl_program(const char * vertex_data,const char * fragment_data){ + // Create the shaders + GLuint VertexShaderID = glCreateShader(GL_VERTEX_SHADER); + GLuint FragmentShaderID = glCreateShader(GL_FRAGMENT_SHADER); + + GLint Result = GL_FALSE; + int InfoLogLength; + + // Compile Vertex Shader + glShaderSource(VertexShaderID, 1, &vertex_data, NULL); + glCompileShader(VertexShaderID); + + // Check Vertex Shader + glGetShaderiv(VertexShaderID, GL_COMPILE_STATUS, &Result); + glGetShaderiv(VertexShaderID, GL_INFO_LOG_LENGTH, &InfoLogLength); + if ( InfoLogLength > 0 ){ + std::vector<char> VertexShaderErrorMessage(InfoLogLength+1); + glGetShaderInfoLog(VertexShaderID, InfoLogLength, NULL, &VertexShaderErrorMessage[0]); + printf("%s\n", &VertexShaderErrorMessage[0]); + } + + // Compile Fragment Shader + glShaderSource(FragmentShaderID, 1, &fragment_data , NULL); + glCompileShader(FragmentShaderID); + + // Check Fragment Shader + glGetShaderiv(FragmentShaderID, GL_COMPILE_STATUS, &Result); + glGetShaderiv(FragmentShaderID, GL_INFO_LOG_LENGTH, &InfoLogLength); + if ( InfoLogLength > 0 ){ + std::vector<char> FragmentShaderErrorMessage(InfoLogLength+1); + glGetShaderInfoLog(FragmentShaderID, InfoLogLength, NULL, &FragmentShaderErrorMessage[0]); + printf("%s\n", &FragmentShaderErrorMessage[0]); + } + + // Link the program + GLuint ProgramID = glCreateProgram(); + glAttachShader(ProgramID, VertexShaderID); + glAttachShader(ProgramID, FragmentShaderID); + glLinkProgram(ProgramID); + + // Check the program + glGetProgramiv(ProgramID, GL_LINK_STATUS, &Result); + glGetProgramiv(ProgramID, GL_INFO_LOG_LENGTH, &InfoLogLength); + if ( InfoLogLength > 0 ){ + std::vector<char> ProgramErrorMessage(InfoLogLength+1); + glGetProgramInfoLog(ProgramID, InfoLogLength, NULL, &ProgramErrorMessage[0]); + printf("%s\n", &ProgramErrorMessage[0]); + } + + glDetachShader(ProgramID, VertexShaderID); + glDetachShader(ProgramID, FragmentShaderID); + + glDeleteShader(VertexShaderID); + glDeleteShader(FragmentShaderID); + + return ProgramID; +} +} +} diff --git a/util/ogl_renderer/c++/shader.hpp b/util/ogl_renderer/c++/shader.hpp new file mode 100644 index 0000000..6e84f21 --- /dev/null +++ b/util/ogl_renderer/c++/shader.hpp @@ -0,0 +1,8 @@ +#pragma once + +#include "common.hpp" + +namespace kel { +namespace lbm { +} +} diff --git a/util/ogl_renderer/c++/shaders/basic.hpp b/util/ogl_renderer/c++/shaders/basic.hpp new file mode 100644 index 0000000..2b0a65f --- /dev/null +++ b/util/ogl_renderer/c++/shaders/basic.hpp @@ -0,0 +1,36 @@ +#pragma once + +#include <string_view> + +namespace kel { +namespace lbm { +constexpr std::string_view shader_basic_vertex = R"( +#version 330 core + +layout(location=0) in vec2 vertices; +layout(location=1) in vec2 v_uv; + +out vec2 uv; + +void main(){ + uv = v_uv; + gl_Position = vec4(vertices, 0.0, 1.0); +} +)"; + +constexpr std::string_view shader_basic_fragment = R"( +#version 330 core + +in vec2 uv; +out vec4 color; + +uniform sampler2D tex_sampler; + +void main(){ + color = texture(tex_sampler, uv); + color.r = 0.8; +} +)"; + +} +} diff --git a/util/ogl_renderer/c++/state.hpp b/util/ogl_renderer/c++/state.hpp new file mode 100644 index 0000000..c429195 --- /dev/null +++ b/util/ogl_renderer/c++/state.hpp @@ -0,0 +1,144 @@ +#pragma once + +#include "common.hpp" +#include "program.hpp" + +#include "shaders/basic.hpp" + +namespace kel { +namespace lbm { +namespace sch { +using namespace saw::schema; +} + +struct ogl_state final { +public: + SDL_Window* window; + SDL_GLContext context; + int version; + GLuint vao_id; + GLuint vbo_id; + GLuint veo_id; + + GLuint program_id; + GLuint fbo_id; + GLuint tex_id; + + uint64_t w, h; + +public: + ogl_state(): + window{nullptr}, + version{0}, + vao_id{0u}, + vbo_id{0u}, + program_id{0u}, + fbo_id{0u}, + tex_id{0u}, + w{1024}, + h{1024} + { + SDL_Init(SDL_INIT_VIDEO); + } + + ~ogl_state(){ + SDL_GL_DestroyContext(context); + if(window){ + SDL_DestroyWindow(window); + window = nullptr; + } + SDL_Quit(); + } + + void clear(){ + glClear(GL_COLOR_BUFFER_BIT); + } +}; + +saw::error_or<saw::own<ogl_state>> create_ogl_renderer(){ + auto owned_state = saw::heap<ogl_state>(); + + auto& st = *owned_state; + + SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1); + SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION,3); + SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION,3); + SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE); + + st.window = SDL_CreateWindow("Kel'LBM Renderer",st.w,st.h,SDL_WINDOW_OPENGL /*| SDL_WINDOW_HIDDEN*/); + if(not st.window){ + return saw::make_error<saw::err::critical>("Window not created"); + } + st.context = SDL_GL_CreateContext(st.window); + + st.version = gladLoadGL((GLADloadfunc)SDL_GL_GetProcAddress); + glGenVertexArrays(1,&st.vao_id); + glBindVertexArray(st.vao_id); + glClearColor(0.0,0.0,0.0,1.0); + + glGenBuffers(1,&st.vbo_id); + glGenBuffers(1,&st.veo_id); + static constexpr GLfloat vertex_buffer_data[] = { + -1.f, -1.f, 0.f, 0.f, + 1.f, -1.f, 1.f, 0.f, + 1.f, 1.f, 1.f, 1.f, + -1.f, 1.f, 0.f, 1.f + }; + static unsigned int index_buffer_data[] = { + 0, 1, 2, + 2, 3, 0 + }; + glBindBuffer(GL_ARRAY_BUFFER, st.vbo_id); + glBufferData(GL_ARRAY_BUFFER, sizeof(vertex_buffer_data), vertex_buffer_data, GL_STATIC_DRAW); + + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, st.veo_id); + glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(index_buffer_data), index_buffer_data, GL_STATIC_DRAW); + + glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(GLfloat), 0); + glEnableVertexAttribArray(0); + + glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(GLfloat), (void*)(2 * sizeof(GLfloat))); + glEnableVertexAttribArray(1); + + glGenFramebuffers(1,&st.fbo_id); + //glBindFramebuffer(GL_FRAMEBUFFER, st.fbo_id); + + glGenTextures(1,&st.tex_id); + glBindTexture(GL_TEXTURE_2D,st.tex_id); + + glTexImage2D( + GL_TEXTURE_2D, + 0, + GL_RGBA, + 1024, + 1024, + 0, + GL_RGBA, + GL_UNSIGNED_BYTE, + NULL + ); + + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + + glFramebufferTexture2D( + GL_FRAMEBUFFER, + GL_COLOR_ATTACHMENT0, + GL_TEXTURE_2D, + st.tex_id, + 0 + ); + + glViewport(0,0,1024,1024); + st.program_id = create_ogl_program(shader_basic_vertex.data(), shader_basic_fragment.data()); + glUseProgram(st.program_id); + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, st.tex_id); + + glUniform1i(glGetUniformLocation(st.program_id, "tex_sampler"), 0); + st.clear(); + + return owned_state; +} +} +} |
