├── .clangd ├── .github └── workflows │ ├── c-cpp.yml │ ├── cmake-linux-clang.yml │ ├── cmake-linux-gcc.yml │ └── cmake-windows-mingw.yml ├── .gitignore ├── .gitmodules ├── CMakeLists.txt ├── LICENSE.md ├── Makefile ├── MakefileLegacy ├── README.md ├── assets ├── LatoLatin-Regular.ttf ├── MechaGolem.vox ├── Room_1.vox ├── bark.vox ├── black_block.vox ├── bush.vox ├── dirt.vox ├── grass.vox ├── grassNdirt.vox ├── invader.tga ├── iron.vox ├── lamp.vox ├── leaves.vox ├── light.vox ├── metal.vox ├── mirror.vox ├── monu2.vox ├── planks.vox ├── robot.vox ├── scene ├── scene.vox ├── simple.rcss ├── simple.rml ├── stone_brick.vox ├── stone_brick_cracked.vox ├── stone_dirt.vox ├── stone_pack.vox ├── tank.vox ├── tank_body.vox ├── tank_head.vox ├── tank_lf_rb_leg.vox ├── tank_rf_lb_leg.vox ├── white_block.vox └── wood.vox ├── cmake ├── LumConfig.cmake ├── LumConfig.cmake.in └── LumConfigVersion.cmake ├── common ├── forthys.cpp ├── khash.h ├── khash.hpp ├── meshopt.cpp ├── meshopt.hpp ├── ogt_vox.cpp ├── ogt_vox.hpp ├── ogt_voxel_meshify.cpp ├── ogt_voxel_meshify.hpp └── vk_enum_string_helper.h ├── examples ├── CMakeLists.txt ├── README.md ├── cdemo.c ├── cdemo_api_impl.cpp └── demo.cpp ├── external └── README.md ├── include ├── clum.h └── lum.hpp ├── screenshot_v0.3.png ├── screenshot_v0.4.png ├── shaders ├── common │ ├── consts.glsl │ ├── ext.glsl │ └── ubo.glsl ├── diffuse.frag ├── fillStencilGlossy.frag ├── fillStencilSmoke.frag ├── fillStencilSmoke.vert ├── fullscreenTriag.vert ├── glossy.frag ├── grass.frag ├── grass.vert ├── hbao.frag ├── lightmapBlocks.vert ├── lightmapModels.vert ├── map.comp ├── overlay.frag ├── overlay.vert ├── perlin2.comp ├── perlin3.comp ├── radiance.comp ├── rayGenBlocks.frag ├── rayGenBlocks.vert ├── rayGenModels.frag ├── rayGenModels.vert ├── rayGenParticles.frag ├── rayGenParticles.geom ├── rayGenParticles.vert ├── smoke.frag ├── templates │ └── FOLIAGE.vert ├── tonemap.frag ├── unused │ ├── accumulate.comp │ ├── ao_direct.frag │ ├── ao_old.frag │ ├── bitmask.comp │ ├── blockify.comp │ ├── comp.comp │ ├── comp2.comp │ ├── comp3.comp │ ├── conetrace.comp │ ├── copy.comp │ ├── denoise.comp │ ├── dfx.comp │ ├── dfy.comp │ ├── dfz.comp │ ├── doLight_full copy.comp │ ├── glossy_old.frag │ ├── glossy_sssr.comp │ ├── grass_tlist.vert │ ├── map_m2w.comp │ ├── mipmap.comp │ ├── radiance_rtx_only.comp │ ├── raytrace.comp │ ├── updateRadianceCache_full.comp │ └── upscale.comp ├── updateGrass.comp ├── updateWater.comp ├── water.frag └── water.vert └── src ├── containers ├── ankerl_unordered_dense.hpp ├── arena.hpp ├── fixed_map.hpp ├── promised_storage.hpp ├── promised_storage_accessor.hpp ├── safe_types │ ├── README.md │ ├── main.cpp │ ├── safe_base.hpp │ ├── safe_float.hpp │ └── safe_int.hpp ├── shared_vector.hpp ├── size_predictor.hpp └── thread_pool │ ├── thread_pool.hpp │ └── thread_safe_queue.hpp ├── engine ├── README.md ├── ecs.hpp ├── engine.hpp ├── examples │ ├── ecs_bench.cpp │ ├── ecs_example.cpp │ └── engine_example.cpp ├── parallel │ ├── example_parallel.cpp │ ├── parallel.hpp │ └── thread_pool.hpp └── profiler.hpp ├── input ├── README.md ├── input.hpp └── inputHandler.hpp ├── macros └── assert.hpp └── renderer ├── README.md ├── api ├── crenderer.cpp ├── crenderer.h ├── opaque_renderer_members.hpp ├── renderer.cpp └── renderer.hpp └── src ├── ao_lut.cpp ├── ao_lut.hpp ├── internal_render.cpp ├── internal_render.hpp ├── internal_types.hpp ├── load_stuff.cpp ├── render_ui_interface.cpp ├── setup.cpp ├── ui.cpp └── ui.hpp /.clangd: -------------------------------------------------------------------------------- 1 | If: 2 | PathMatch: build/.* 3 | 4 | CompileFlags: 5 | CompilationDatabase: /build 6 | 7 | --- 8 | 9 | If: 10 | PathMatch: examples/build/.* 11 | 12 | CompileFlags: 13 | CompilationDatabase: examples/build/ 14 | 15 | --- 16 | 17 | If: 18 | PathMatch: examples/build_deb/.* 19 | 20 | CompileFlags: 21 | CompilationDatabase: examples/build_deb/ 22 | 23 | --- 24 | 25 | If: 26 | PathMatch: build_default/.* 27 | 28 | CompileFlags: 29 | CompilationDatabase: build_default/ 30 | 31 | --- 32 | 33 | If: 34 | PathMatch: build_gcc/.* 35 | 36 | CompileFlags: 37 | CompilationDatabase: build_gcc/ 38 | 39 | --- 40 | 41 | If: 42 | PathMatch: build_clang/.* 43 | 44 | CompileFlags: 45 | CompilationDatabase: build_clang/ -------------------------------------------------------------------------------- /.github/workflows/c-cpp.yml: -------------------------------------------------------------------------------- 1 | name: Build 2 | 3 | on: 4 | workflow_dispatch: 5 | 6 | env: 7 | VCPKG_BINARY_SOURCES: "clear;x-gha,readwrite" 8 | 9 | jobs: 10 | build: 11 | runs-on: ubuntu-latest 12 | steps: 13 | - name: Export GitHub Actions cache environment variables 14 | uses: actions/github-script@v7 15 | with: 16 | script: | 17 | core.exportVariable('ACTIONS_CACHE_URL', process.env.ACTIONS_CACHE_URL || ''); 18 | core.exportVariable('ACTIONS_RUNTIME_TOKEN', process.env.ACTIONS_RUNTIME_TOKEN || ''); 19 | 20 | - name: Maybe useful update 21 | run: sudo apt update 22 | 23 | - name: Cache APT Packages 24 | uses: awalsh128/cache-apt-pkgs-action@v1.4.2 25 | with: 26 | packages: libxinerama-dev libxcursor-dev xorg-dev libglu1-mesa-dev pkg-config build-essential 27 | execute_install_scripts: true # optional, set to true if you need to run install scripts 28 | 29 | - name: Checkout code 30 | uses: actions/checkout@v4 31 | 32 | - name: Build Lum 33 | run: make only_build_unity 34 | -------------------------------------------------------------------------------- /.github/workflows/cmake-linux-clang.yml: -------------------------------------------------------------------------------- 1 | name: Linux-Clang Build 2 | 3 | on: 4 | workflow_dispatch: 5 | 6 | jobs: 7 | build-linux-clang: 8 | runs-on: ubuntu-latest 9 | 10 | steps: 11 | - name: Checkout repository 12 | uses: actions/checkout@v4 13 | 14 | - name: Download submodules 15 | run: git submodule update --init --recursive 16 | 17 | - name: Maybe useful update 18 | run: sudo apt update 19 | 20 | - name: Cache APT Packages - Clang, Ninja, GLFW-needed ones 21 | uses: awalsh128/cache-apt-pkgs-action@v1.4.2 22 | with: 23 | packages: clang llvm lld clang-tools libclang-dev build-essential ninja-build libxinerama-dev libxcursor-dev xorg-dev libglu1-mesa-dev pkg-config libxkbcommon-dev libwayland-dev 24 | execute_install_scripts: true # run install scripts 25 | 26 | - name: Cache CMake and Build Files 27 | uses: actions/cache@v3 28 | with: 29 | path: | 30 | ~/.cmake/packages 31 | build 32 | key: clang-${{ github.ref }}-${{ github.sha }} 33 | restore-keys: | 34 | clang-${{ github.ref }} 35 | 36 | - name: Install Clang LTO plugin (LLVMgold) 37 | run: | 38 | sudo apt-get install llvm-12-tools # lol 12 is largest supported by default? 39 | 40 | - name: Configure CMake to build examples 41 | run: | 42 | cd examples 43 | mkdir -p build 44 | cd build 45 | cmake -G "Ninja" .. \ 46 | -DCMAKE_BUILD_TYPE=Release \ 47 | -DCMAKE_C_COMPILER=clang \ 48 | -DCMAKE_CXX_COMPILER=clang++ \ 49 | -DCMAKE_LINKER=lld \ 50 | -DCMAKE_EXE_LINKER_FLAGS="-fuse-ld=lld" # just cause i want 51 | 52 | - name: Build 53 | run: | 54 | cd examples/build 55 | cmake --build . --verbose 56 | -------------------------------------------------------------------------------- /.github/workflows/cmake-linux-gcc.yml: -------------------------------------------------------------------------------- 1 | name: Linux-GCC Build 2 | 3 | on: 4 | workflow_dispatch: 5 | 6 | jobs: 7 | build-linux-gcc: 8 | runs-on: ubuntu-latest 9 | 10 | steps: 11 | - name: Checkout repository 12 | uses: actions/checkout@v4 13 | 14 | - name: Download submodules 15 | run: git submodule update --init --recursive 16 | 17 | - name: Maybe useful update 18 | run: sudo apt update 19 | 20 | - name: Cache APT Packages - GCC, Ninja, GLFW-needed ones 21 | uses: awalsh128/cache-apt-pkgs-action@v1.4.2 22 | with: 23 | packages: gcc g++ build-essential ninja-build libxinerama-dev libxcursor-dev xorg-dev libglu1-mesa-dev pkg-config libxkbcommon-dev libwayland-dev 24 | execute_install_scripts: true # run install scripts 25 | 26 | - name: Cache CMake and Build Files 27 | uses: actions/cache@v3 28 | with: 29 | path: | 30 | ~/.cmake/packages 31 | build 32 | key: gcc-${{ github.ref }}-${{ github.sha }} 33 | restore-keys: | 34 | gcc-${{ github.ref }} 35 | 36 | - name: Configure CMake to build examples 37 | run: | 38 | cd examples 39 | mkdir -p build 40 | cd build 41 | cmake -G "Ninja" .. \ 42 | -DCMAKE_BUILD_TYPE=Release \ 43 | -DCMAKE_C_COMPILER=gcc \ 44 | -DCMAKE_CXX_COMPILER=g++ 45 | 46 | - name: Build 47 | run: | 48 | cd examples/build 49 | cmake --build . --verbose 50 | -------------------------------------------------------------------------------- /.github/workflows/cmake-windows-mingw.yml: -------------------------------------------------------------------------------- 1 | name: Windows-MinGW Build 2 | 3 | on: 4 | workflow_dispatch: 5 | 6 | jobs: 7 | build-windows-mingw: 8 | runs-on: windows-latest 9 | 10 | steps: 11 | - name: Checkout repository 12 | uses: actions/checkout@v4 13 | 14 | - name: Download submodules 15 | run: git submodule update --init --recursive 16 | 17 | - name: Install MSYS2 18 | uses: msys2/setup-msys2@v2 19 | with: 20 | msystem: mingw64 21 | install: mingw-w64-x86_64-gcc mingw-w64-x86_64-cmake mingw-w64-x86_64-make ninja 22 | 23 | - name: Cache CMake and Build Files 24 | uses: actions/cache@v3 25 | with: 26 | path: | 27 | ~/.cmake/packages 28 | build 29 | key: mingw-${{ github.ref }}-${{ github.sha }} 30 | restore-keys: | 31 | mingw-${{ github.ref }} 32 | 33 | - name: Configure CMake to build examples 34 | run: | 35 | cd examples 36 | mkdir -p build 37 | cd build 38 | cmake -G "Ninja" .. -DCMAKE_CXX_COMPILER=g++ -DCMAKE_C_COMPILER=gcc -DCMAKE_BUILD_TYPE=Release 39 | 40 | - name: Build 41 | run: | 42 | cd examples/build 43 | cmake --build . --verbose -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | obj 2 | 3 | *.exe 4 | client 5 | 6 | # compiled shaders 7 | shaders/*.spv 8 | shaders/compiled/*.spv 9 | 10 | *.txt 11 | *.md 12 | # for clangd 13 | !compile_flags.txt 14 | !CMakeLists.txt 15 | !README.md 16 | !LICENSE.md 17 | # IDE things 18 | .vscode 19 | .idea 20 | 21 | test.cpp 22 | r.cpp 23 | assets/*.zip 24 | #memory dump from VMA 25 | dump.json 26 | # nvidia nsight imgui file 27 | imgui.ini 28 | # directories with trace data 29 | nv_traces 30 | amd_traces 31 | intel_traces 32 | temp.* 33 | #github release thing 34 | package 35 | package.zip 36 | diag.mmd 37 | *.py 38 | *.orig 39 | build_wrapper_output_directory 40 | vcpkg_installed 41 | *.tar 42 | #pocket vcpkg installation. Not needed anymore, but still gitignored 43 | /lum_vcpkg 44 | 45 | /bin 46 | /lib 47 | /build* 48 | /examples/build* 49 | compile_commands.json 50 | /.cache 51 | /examples/build 52 | /examples/lib 53 | /examples/bin 54 | /examples/CMakeFiles 55 | /.vs 56 | /out/build/x64-Debug 57 | /examples/.cache -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "external/lum-al"] 2 | path = external/lum-al 3 | url = https://github.com/platonvin/lum-al 4 | [submodule "external/freetype"] 5 | path = external/freetype 6 | url = https://github.com/freetype/freetype 7 | [submodule "external/RmlUi"] 8 | path = external/RmlUi 9 | url = https://github.com/mikke89/RmlUi 10 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | **NoMPA License (Not My Problem Anymore License)** *Version 0.0* 2 | 3 | Here’s the code, it’s your problem now (but you can contact me for help) 4 | 5 | 6 | --- 7 | 8 | 9 | ### 1. What You Can Do 10 | 11 | You’re free to use, copy, modify, and distribute this software, including in commercial projects. However: 12 | 13 | - **Don’t sell my code as-is.** You’re welcome to distribute this software unchanged for free, but selling it directly without meaningful additions or improvements is not allowed. 14 | 15 | - **Don’t misrepresent authorship.** If you use this code, you can’t claim that you wrote it entirely by yourself. 16 | 17 | - **Give credit where it’s due.** Please, mention me as a contributor to your project. Its not needed, but I’d appreciate it. ~~~This is primarally because i cannot get a job and maybe mentions will help with it.~~~ 18 | 19 | 20 | --- 21 | 22 | 23 | ### 2. A Warning About Licenses 24 | This software uses or links to other libraries, including **GNU libstdc++ (GPL)** , which may impose additional requirements: 25 | - I’ve made an effort to comply with these licenses, but I can’t guarantee I’ve done everything perfectly. 26 | 27 | - It’s your responsibility to review and ensure compliance with any third-party licenses involved. 28 | 29 | 30 | --- 31 | 32 | 33 | ### 3. Sharing Your Changes 34 | 35 | You’re free to modify this software for your own use or projects. 36 | 37 | - You **don’t have to share your changes** , but if you do, I’d appreciate it. 38 | 39 | 40 | --- 41 | 42 | 43 | ### 4. No Warranty, No Liability 44 | This software is provided **as-is** , with absolutely no guarantees. Specifically: 45 | - **No warranty.** It might work, it might not. If it breaks, that’s on you. 46 | 47 | - **No liability.** If something bad happens because of this software — like broken projects, lost data, or lawsuits — you agree that I’m not responsible. 48 | 49 | 50 | --- 51 | 52 | 53 | ### By using this software, you agree to these terms. 54 | *(End of NoMPA License)* -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # Variables 2 | COMPILER ?= default # Compiler: gcc, clang, msvc, or default (system default) 3 | # BUILD_TYPE ?= Release # Build type: Debug or Release 4 | BUILD_DIR := build_$(COMPILER) # Compiler-specific build directory 5 | 6 | # Detect OS 7 | ifeq ($(OS),Windows_NT) 8 | RM = del /s /q 9 | RMDIR = rmdir /s /q 10 | MKDIR = if not exist "$(BUILD_DIR)" mkdir "$(BUILD_DIR)" 11 | SLASH = \ # Windows uses backslashes for paths 12 | CMAKE_GEN_FLAGS = 13 | else 14 | RM = rm -rf 15 | RMDIR = rm -rf 16 | MKDIR = mkdir -p 17 | SLASH = / # Unix-like systems use forward slashes 18 | CMAKE_GEN_FLAGS = -G "Unix Makefiles" 19 | endif 20 | 21 | # Targets 22 | .PHONY: all configure build clean clean_all 23 | 24 | all: build 25 | 26 | # Configure the build directory 27 | configure: 28 | @echo "Configuring project for $(COMPILER)..." 29 | $(MKDIR) 30 | ifeq ($(COMPILER),gcc) 31 | @cmake -S . -B $(BUILD_DIR) $(CMAKE_GEN_FLAGS) -DCMAKE_C_COMPILER=gcc -DCMAKE_CXX_COMPILER=g++ 32 | else ifeq ($(COMPILER),clang) 33 | @cmake -S . -B $(BUILD_DIR) $(CMAKE_GEN_FLAGS) -DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++ 34 | else ifeq ($(COMPILER),msvc) 35 | @echo "Configuring project for MSVC using Visual Studio generator..." 36 | @cmake -S . -B $(BUILD_DIR) -G "Visual Studio 17 2022" 37 | else 38 | @cmake -S . -B $(BUILD_DIR) $(CMAKE_GEN_FLAGS) 39 | endif 40 | 41 | # Build the project 42 | build: configure 43 | @cmake --build $(BUILD_DIR) 44 | 45 | # Clean the build directory 46 | clean: 47 | @echo "Cleaning build directory $(BUILD_DIR)..." 48 | ifeq ($(OS),Windows_NT) 49 | -$(RM) $(BUILD_DIR)$(SLASH)* && $(RMDIR) $(BUILD_DIR) 50 | else 51 | -$(RM) $(BUILD_DIR) 52 | endif 53 | 54 | # Clean all build directories 55 | clean_all: 56 | @echo "Cleaning all build directories..." 57 | ifeq ($(OS),Windows_NT) 58 | -for /d %d in (build_*) do $(RMDIR) %d 59 | else 60 | -$(RM) build_* 61 | endif 62 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![Build Linux GCC](https://github.com/platonvin/lum/actions/workflows/cmake-linux-gcc.yml/badge.svg)](https://github.com/platonvin/lum/actions/workflows/cmake-linux-gcc.yml) 2 | [![Build Linux Clang](https://github.com/platonvin/lum/actions/workflows/cmake-linux-clang.yml/badge.svg)](https://github.com/platonvin/lum/actions/workflows/cmake-linux-clang.yml) [![Build with Windows MinGW (MSYS2)](https://github.com/platonvin/lum/actions/workflows/cmake-windows-mingw.yml/badge.svg)](https://github.com/platonvin/lum/actions/workflows/cmake-windows-mingw.yml)\ 3 | Note: it takes 30 minutes to build all three, so not every commit is tested to save actions minutes 4 | 5 | # Lum 6 | **Lum** is a voxel\* renderer\*\* built with Vulkan. Currently, it's only available in a form of C99/C++ API, but might (on request) be ported (in form of bindings) into Unity / Unreal / languages that support C interop. Currently, it is also in process of porting to Rust (in pure Rust form). 7 | 8 | \* Note: In Lum, "voxel" refers to a small, grid-aligned cube with a single material. Lum expects you to group voxels into blocks and blocks into world, but also supports non-grid-aligned models at a minor performance cost.\ 9 | \*\* Note: Lum also has ECS ([check it out!](src/engine/README.md)), input, ~~Ui,~~ meshloader (and will have voxel physics engine), but it's stil mostly GPU code - so it's called renderer 10 | 11 | If you have ideas or suggestions for the API, feel free to open an [issue](https://github.com/platonvin/lum/issues)\ 12 | Note: Lum is not production-ready. It is slowly getting polished (via making an actual game), but some core features are missing (e.g. world size control) / unclear (measurement units, blocks<=>voxels) 13 | 14 | ##### Some demo footage 15 | https://github.com/user-attachments/assets/ce7883c4-a706-406f-875c-fbf23d68020d 16 | 17 | #### Some (demo) benchmarks: 18 | * Intel Iris XE (integrated gpu!) - <7 mspf in FullHD 19 | * NVIDIA 1660s - ~1.6 mspf in FullHD 20 | 21 | ## Feature-list 22 | [md file with Lum:Renderer features](FEATURES.md) 23 | 24 | ## Installation 25 | 26 | ### Prerequisites 27 | 28 | - **C++23 Compiler** 29 | - Recommended: [MSYS2 MinGW](https://www.msys2.org/) for Windows, GNU C++ for Linux. 30 | 31 | - See the badges above for compilers that are verified to work. 32 | 33 | - **Note:** MSVC is not currently supported. If you have experience with MSVC and are willing to contribute, help is welcome! 34 | 35 | - **CMake 3.22 or newer** 36 | - Install via your package manager, e.g., `sudo apt-get install cmake` (on Debian-based systems), or [build from source](https://github.com/Kitware/CMake), or [download from official site](https://cmake.org/download/) 37 | 38 | - **Vulkan support** 39 | 40 | - on Linux, GLFW will ask you to install multiple different packages. You can do it in advance : 41 | - for Debian / Ubuntu / Mint:\ 42 | `sudo apt install libxinerama-dev libxcursor-dev xorg-dev libglu1-mesa-dev pkg-config build-essential libxkbcommon-dev libwayland-dev` 43 | - for Fedore / Red Hat:\ 44 | `sudo dnf install wayland-devel libxkbcommon-devel libXcursor-devel libXi-devel libXinerama-devel libXrandr-devel` 45 | - for FreeBSD:\ 46 | `pkg install wayland libxkbcommon evdev-proto xorgproto` 47 | 48 | 49 | 50 | ### Build Instructions 51 | 52 | 1. Clone the repository with it's submodules: 53 | 54 | 55 | ```bash 56 | git clone https://github.com/platonvin/lum.git 57 | cd lum 58 | git submodule update --init --recursive 59 | ``` 60 | 61 | 2. Build using **CMake** : 62 | 63 | ```bash 64 | mkdir build 65 | cd build 66 | cmake .. -DCMAKE_BUILD_TYPE=Release 67 | cmake --build . 68 | ``` 69 | To improve performance for release builds, you can enable **LTO** (Link Time Optimization) : 70 | 71 | ```bash 72 | cmake .. -DCMAKE_BUILD_TYPE=Release -DCMAKE_INTERPROCEDURAL_OPTIMIZATION=ON 73 | cmake --build . 74 | ``` 75 | For faster builds, you can install Ninja and add `-G "Ninja"` to your CMake command 76 | 77 | ```bash 78 | cmake -G "Ninja" .. -DCMAKE_BUILD_TYPE=Release 79 | ``` 80 | Note: this will only build lum library, not demo. For demo, go to [examples](examples/README.md) 81 | 82 | ### Integration 83 | To use Lum as a subproject in your **CMake** project: 84 | See [examples/CMakeLists.txt](https://chatgpt.com/c/examples/CMakeLists.txt) for integration details.\ 85 | If you're using a different build system, include `include/lum.hpp` (or `include/clum.h`) and link against the `lum`, `lumal`, `glfw3`, `freetype`, `brotlidec` and some os-specific libraries*. Note that Lum also builds them (so they can be found in `lum/lib` (or `lum/examples/lib`, if building examples))\ 86 | Note: for C++ api (`lum.hpp`) you will also need glm to be in path (so `#include ` is possible). You can use "pocket" lum's glm installation at `lum/external/lum-al/external/glm` 87 | 88 | * for windows, also link with `gdi32` 89 | * for linux, also link with `dl` 90 | 91 | ## Usage: 92 | - [Lum::Renderer](src/renderer/README.md) 93 | - [Lum::ECSystem](src/engine/README.md) 94 | - [Lum::InpuHandler](src/input/README.md) 95 | 96 | ### Demo controls 97 | - WASD for camera movement 98 | - Arrows for robot movement 99 | - Enter for shooting particles 100 | - 0 to remove block underneath 101 | - 1-9 and F1-F5 to place matching blocks (btw world is saved to a file) 102 | - "<" and ">" to rotate camera 103 | - "Page Up" and "Page Down" to zoom in/out 104 | - Esc to close demo 105 | -------------------------------------------------------------------------------- /assets/LatoLatin-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/platonvin/lum/5a38bb8057cb6195f3bb895e63c5555def3a098e/assets/LatoLatin-Regular.ttf -------------------------------------------------------------------------------- /assets/MechaGolem.vox: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/platonvin/lum/5a38bb8057cb6195f3bb895e63c5555def3a098e/assets/MechaGolem.vox -------------------------------------------------------------------------------- /assets/Room_1.vox: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/platonvin/lum/5a38bb8057cb6195f3bb895e63c5555def3a098e/assets/Room_1.vox -------------------------------------------------------------------------------- /assets/bark.vox: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/platonvin/lum/5a38bb8057cb6195f3bb895e63c5555def3a098e/assets/bark.vox -------------------------------------------------------------------------------- /assets/black_block.vox: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/platonvin/lum/5a38bb8057cb6195f3bb895e63c5555def3a098e/assets/black_block.vox -------------------------------------------------------------------------------- /assets/bush.vox: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/platonvin/lum/5a38bb8057cb6195f3bb895e63c5555def3a098e/assets/bush.vox -------------------------------------------------------------------------------- /assets/dirt.vox: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/platonvin/lum/5a38bb8057cb6195f3bb895e63c5555def3a098e/assets/dirt.vox -------------------------------------------------------------------------------- /assets/grass.vox: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/platonvin/lum/5a38bb8057cb6195f3bb895e63c5555def3a098e/assets/grass.vox -------------------------------------------------------------------------------- /assets/grassNdirt.vox: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/platonvin/lum/5a38bb8057cb6195f3bb895e63c5555def3a098e/assets/grassNdirt.vox -------------------------------------------------------------------------------- /assets/invader.tga: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/platonvin/lum/5a38bb8057cb6195f3bb895e63c5555def3a098e/assets/invader.tga -------------------------------------------------------------------------------- /assets/iron.vox: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/platonvin/lum/5a38bb8057cb6195f3bb895e63c5555def3a098e/assets/iron.vox -------------------------------------------------------------------------------- /assets/lamp.vox: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/platonvin/lum/5a38bb8057cb6195f3bb895e63c5555def3a098e/assets/lamp.vox -------------------------------------------------------------------------------- /assets/leaves.vox: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/platonvin/lum/5a38bb8057cb6195f3bb895e63c5555def3a098e/assets/leaves.vox -------------------------------------------------------------------------------- /assets/light.vox: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/platonvin/lum/5a38bb8057cb6195f3bb895e63c5555def3a098e/assets/light.vox -------------------------------------------------------------------------------- /assets/metal.vox: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/platonvin/lum/5a38bb8057cb6195f3bb895e63c5555def3a098e/assets/metal.vox -------------------------------------------------------------------------------- /assets/mirror.vox: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/platonvin/lum/5a38bb8057cb6195f3bb895e63c5555def3a098e/assets/mirror.vox -------------------------------------------------------------------------------- /assets/monu2.vox: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/platonvin/lum/5a38bb8057cb6195f3bb895e63c5555def3a098e/assets/monu2.vox -------------------------------------------------------------------------------- /assets/planks.vox: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/platonvin/lum/5a38bb8057cb6195f3bb895e63c5555def3a098e/assets/planks.vox -------------------------------------------------------------------------------- /assets/robot.vox: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/platonvin/lum/5a38bb8057cb6195f3bb895e63c5555def3a098e/assets/robot.vox -------------------------------------------------------------------------------- /assets/scene.vox: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/platonvin/lum/5a38bb8057cb6195f3bb895e63c5555def3a098e/assets/scene.vox -------------------------------------------------------------------------------- /assets/simple.rcss: -------------------------------------------------------------------------------- 1 | body { 2 | font-family: LatoLatin; 3 | font-size: 18dp; 4 | color: #050008; 5 | padding: 10dp; 6 | /* background-color: #355C7D; */ 7 | margin: 2dp; 8 | decorator: gradient( horizontal #304e6db4 #355b7d9c ); 9 | border-radius: 10px; /* Rounded edges */ 10 | /* text-align: center; 11 | align-items: center; */ 12 | /* align-content: center; */ 13 | } 14 | 15 | #container { 16 | /* width: 100dp; */ 17 | margin: 40dp; 18 | margin-bottom: 100dp; 19 | /* padding: 10dp; */ 20 | /* background-color: #6C5B7B; */ 21 | /* border: 2px #303030; */ 22 | /* border-radius: 10px; */ 23 | text-align: center; 24 | /* align-items: stretch; */ 25 | } 26 | 27 | h1 { 28 | font-size: 20dp; 29 | color: #1a1a1a; 30 | /* margin-bottom: 40dp; */ 31 | /* margin: 100 dp; */ 32 | padding-top: 2dp; 33 | padding-left: 7dp; 34 | padding-right: 7dp; 35 | /* margin-bottom: 20dp; */ 36 | border: 2px #3b3b3b; 37 | border-radius: 10px; /* Rounded edges */ 38 | align-self: center; 39 | background-color: #6C5B7B; 40 | } 41 | 42 | .parameter { 43 | margin: 10dp; 44 | /* margin-bottom: ; */ 45 | padding: 20dp; 46 | align-self: center; 47 | /* border: 1px #9e119e; */ 48 | /* border-radius: 10px; */ 49 | } 50 | 51 | .parameter p { 52 | display:block; 53 | width: 200dp; 54 | text-align: center; 55 | padding-top: 2dp; 56 | margin-left: 30dp; 57 | margin-top: 5dp; 58 | border: 2px #3b3b3b; 59 | border-radius: 10px; /* Rounded edges */ 60 | /* border-spacing: 10dp; */ 61 | /* background-color: #4b4b4bcc; */ 62 | decorator: gradient( horizontal #a0576b #dd7895 ); 63 | /* decorator: gradient( vertical #D279EE #F8C390 ); */ 64 | } 65 | 66 | .controls { 67 | display:block; 68 | width: 150dp; 69 | text-align: center; 70 | margin: 5dp 5dp; 71 | margin-left: 45dp; 72 | background-color: rgba(224, 224, 224, 0.5); 73 | border: 2px #3b3b3b; 74 | border-radius: 10px; /* Rounded edges */ 75 | padding: 7dp; 76 | padding-top: 8dp; 77 | /* background-color: #7e7e7ecc; */ 78 | decorator: gradient( horizontal #da6470 #ff8793 ); 79 | } 80 | 81 | button { 82 | width: 20dp; 83 | height: 1dp; 84 | margin: 1dp 10dp; 85 | padding: 1dp 10dp; 86 | /* background-color: #858686; */ 87 | decorator: gradient( horizontal #c98a71 #ffc0a7 ); 88 | border: 2dp #3b3b3b; 89 | border-radius: 6px; /* Rounded edges */ 90 | cursor: pointer; 91 | } 92 | 93 | input{ 94 | width: 40dp; 95 | height: 40dp; 96 | /* margin: 1dp 10dp; 97 | padding: 1dp 20dp; */ 98 | /* background-color: #858686; */ 99 | decorator: gradient( horizontal #d62626 #ffc0a7 ); 100 | border: 2dp #3b3b3b; 101 | border-radius: 6px; /* Rounded edges */ 102 | cursor: pointer; 103 | } 104 | 105 | button:hover { 106 | background-color: #c0c0c0; 107 | } 108 | 109 | button:active { 110 | background-color: #a0a0a0; 111 | } 112 | 113 | span { 114 | display: inline-block; 115 | width: 20dp; 116 | text-align: center; 117 | } -------------------------------------------------------------------------------- /assets/simple.rml: -------------------------------------------------------------------------------- 1 | 2 | 3 | Parameter Controls 4 | 5 | 6 | 7 |
8 |

Parameter Controls

9 | 33 |
34 |

thats a button

35 |
36 | 37 | {{upscaling_ratio}} 38 | 39 |
40 |
41 |
42 | 43 |
-------------------------------------------------------------------------------- /assets/stone_brick.vox: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/platonvin/lum/5a38bb8057cb6195f3bb895e63c5555def3a098e/assets/stone_brick.vox -------------------------------------------------------------------------------- /assets/stone_brick_cracked.vox: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/platonvin/lum/5a38bb8057cb6195f3bb895e63c5555def3a098e/assets/stone_brick_cracked.vox -------------------------------------------------------------------------------- /assets/stone_dirt.vox: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/platonvin/lum/5a38bb8057cb6195f3bb895e63c5555def3a098e/assets/stone_dirt.vox -------------------------------------------------------------------------------- /assets/stone_pack.vox: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/platonvin/lum/5a38bb8057cb6195f3bb895e63c5555def3a098e/assets/stone_pack.vox -------------------------------------------------------------------------------- /assets/tank.vox: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/platonvin/lum/5a38bb8057cb6195f3bb895e63c5555def3a098e/assets/tank.vox -------------------------------------------------------------------------------- /assets/tank_body.vox: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/platonvin/lum/5a38bb8057cb6195f3bb895e63c5555def3a098e/assets/tank_body.vox -------------------------------------------------------------------------------- /assets/tank_head.vox: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/platonvin/lum/5a38bb8057cb6195f3bb895e63c5555def3a098e/assets/tank_head.vox -------------------------------------------------------------------------------- /assets/tank_lf_rb_leg.vox: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/platonvin/lum/5a38bb8057cb6195f3bb895e63c5555def3a098e/assets/tank_lf_rb_leg.vox -------------------------------------------------------------------------------- /assets/tank_rf_lb_leg.vox: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/platonvin/lum/5a38bb8057cb6195f3bb895e63c5555def3a098e/assets/tank_rf_lb_leg.vox -------------------------------------------------------------------------------- /assets/white_block.vox: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/platonvin/lum/5a38bb8057cb6195f3bb895e63c5555def3a098e/assets/white_block.vox -------------------------------------------------------------------------------- /assets/wood.vox: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/platonvin/lum/5a38bb8057cb6195f3bb895e63c5555def3a098e/assets/wood.vox -------------------------------------------------------------------------------- /cmake/LumConfig.cmake: -------------------------------------------------------------------------------- 1 | # LumConfig.cmake - configuration for Lum 2 | include(CMakeFindDependencyMacro) 3 | 4 | # Find dependencies 5 | find_dependency(Freetype REQUIRED) 6 | find_dependency(RmlUi REQUIRED) 7 | 8 | # Target configuration 9 | set(LUM_TARGETS lum_static) 10 | 11 | # Add an alias for users to use `lum::lum` instead of manually handling the target 12 | add_library(lum::lum ALIAS lum_static) 13 | 14 | # Include directories 15 | set(LUM_INCLUDE_DIRS 16 | ${CMAKE_CURRENT_LIST_DIR}/../src/ 17 | ${CMAKE_CURRENT_LIST_DIR}/../common 18 | ${CMAKE_CURRENT_LIST_DIR}/../external/lum-al/external 19 | ${CMAKE_CURRENT_LIST_DIR}/../external/lum-al/src 20 | ${CMAKE_CURRENT_LIST_DIR}/../external/RmlUi/Include 21 | ${CMAKE_CURRENT_LIST_DIR}/../external 22 | ) 23 | 24 | # Set variables for CMake config 25 | set(LUM_LIBRARIES lum_static) 26 | 27 | # Export the include directories 28 | set(LUM_INCLUDE_DIRS ${LUM_INCLUDE_DIRS} CACHE INTERNAL "Lum include directories") 29 | set(LUM_LIBRARIES ${LUM_LIBRARIES} CACHE INTERNAL "Lum libraries") 30 | 31 | # Export the target 32 | export(TARGETS lum_static FILE LumTargets.cmake) 33 | -------------------------------------------------------------------------------- /cmake/LumConfig.cmake.in: -------------------------------------------------------------------------------- 1 | @PACKAGE_INIT@ 2 | 3 | include("${CMAKE_CURRENT_LIST_DIR}/LumTargets.cmake") 4 | 5 | set(LUM_INCLUDE_DIRS "@PACKAGE_INCLUDE_INSTALL_DIR@") 6 | set(LUM_LIBRARIES lum::lum_static lum::lum_shared) 7 | -------------------------------------------------------------------------------- /cmake/LumConfigVersion.cmake: -------------------------------------------------------------------------------- 1 | # LumConfigVersion.cmake - version information for Lum 2 | set(PACKAGE_VERSION "0.1.2") 3 | 4 | if(PACKAGE_FIND_VERSION VERSION_LESS PACKAGE_VERSION) 5 | set(PACKAGE_VERSION_COMPATIBLE FALSE) 6 | else() 7 | set(PACKAGE_VERSION_COMPATIBLE TRUE) 8 | endif() 9 | 10 | set(PACKAGE_VERSION_EXACT ${PACKAGE_FIND_VERSION_EXACT}) 11 | -------------------------------------------------------------------------------- /common/ogt_vox.cpp: -------------------------------------------------------------------------------- 1 | //exists to make compiler happy 2 | //TODO: move code here so parsing headers isnt painfully slow 3 | #define VOX_IMPLEMENTATION 4 | #include 5 | #undef VOX_IMPLEMENTATION // for unity builds -------------------------------------------------------------------------------- /examples/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.22) 2 | project(LumTests LANGUAGES C CXX) 3 | 4 | # CMake settings 5 | set(CMAKE_CXX_STANDARD 23) 6 | set(CMAKE_CXX_STANDARD_REQUIRED ON) 7 | set(CMAKE_CXX_EXTENSIONS OFF) 8 | set(CMAKE_COLOR_DIAGNOSTICS ON) 9 | set(CMAKE_EXPORT_COMPILE_COMMANDS ON) 10 | set(BUILD_SHARED_LIBS OFF) # disable shared (dynamic) libraries, so build static 11 | 12 | # Explicitly set the source and binary directories for lum 13 | # Note: it look weird because examples/ folder is inside lum/ folder 14 | # if Lum is your git submodule / just folder, that you can just do 15 | # add_subdirectory(path/to/lum), e.g. add_subdirectory(external/lum) 16 | add_subdirectory(${CMAKE_SOURCE_DIR}/.. ${CMAKE_BINARY_DIR}/lum) 17 | 18 | 19 | if (CMAKE_BUILD_TYPE STREQUAL "Develop") 20 | # lto is too much for development 21 | set(CMAKE_INTERPROCEDURAL_OPTIMIZATION OFF) 22 | # decreases compile speed, but this is absolutely neccessary for lum 23 | set(CMAKE_CXX_FLAGS "-O1") 24 | set(LUM_CUSTOM_FLAGS OFF) 25 | else() 26 | # Enable -flto for higher perfomance. Note that it increases compile times 27 | set(CMAKE_INTERPROCEDURAL_OPTIMIZATION ON) 28 | endif() 29 | 30 | # Add the test executable named test_demo_cpp 31 | add_executable(test_demo_cpp demo.cpp) 32 | add_executable(test_demo_c99 33 | cdemo.c 34 | cdemo_api_impl.cpp # this has to be compiled with c++ compiler 35 | ) 36 | 37 | # Link the test executable with lum (or lum) 38 | # This will automatically propagate include directiory with lum headers 39 | # so target_include_directories is not needed to add lum to include paths 40 | # Note: if you are not using CMake, you still need to add them manually 41 | target_link_libraries(test_demo_cpp PRIVATE lum) 42 | target_link_libraries(test_demo_c99 PRIVATE lum) 43 | 44 | # Tell cmake to but test_demo_cpp.exe into test/bin 45 | set_target_properties(test_demo_cpp test_demo_c99 PROPERTIES 46 | RUNTIME_OUTPUT_DIRECTORY ${CMAKE_SOURCE_DIR}/bin 47 | ) 48 | -------------------------------------------------------------------------------- /examples/README.md: -------------------------------------------------------------------------------- 1 | ## Lum Demos 2 | 3 | Note: run them from `lum/` directory, e.g.\ 4 | `:\some_dir\another\lum> .\examples\bin\test_demo_cpp.exe` 5 | 6 | instruction to build (similar to building Lum standalone)\ 7 | Note: it builds entire Lum from scratch 8 | ```bash 9 | mkdir build 10 | cd build 11 | cmake .. -DCMAKE_BUILD_TYPE=Release 12 | cmake --build . 13 | ``` 14 | -------------------------------------------------------------------------------- /examples/cdemo_api_impl.cpp: -------------------------------------------------------------------------------- 1 | // Note: this has to be compiled with c++ compiler 2 | 3 | #define LUM_C_API_IMPLEMENTATION // to implement c api in this file. Done this way for simplicity 4 | #include -------------------------------------------------------------------------------- /external/README.md: -------------------------------------------------------------------------------- 1 | FreeType is needed for RmlUi 2 | RmlUi is Ui library 3 | Lum-al is Vulkan abstraction layer designed for Lum -------------------------------------------------------------------------------- /include/clum.h: -------------------------------------------------------------------------------- 1 | #include "../src/renderer/api/crenderer.h" -------------------------------------------------------------------------------- /include/lum.hpp: -------------------------------------------------------------------------------- 1 | #include "../src/renderer/api/renderer.hpp" 2 | -------------------------------------------------------------------------------- /screenshot_v0.3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/platonvin/lum/5a38bb8057cb6195f3bb895e63c5555def3a098e/screenshot_v0.3.png -------------------------------------------------------------------------------- /screenshot_v0.4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/platonvin/lum/5a38bb8057cb6195f3bb895e63c5555def3a098e/screenshot_v0.4.png -------------------------------------------------------------------------------- /shaders/common/consts.glsl: -------------------------------------------------------------------------------- 1 | const ivec3 world_size = ivec3(48,48,16); 2 | -------------------------------------------------------------------------------- /shaders/common/ext.glsl: -------------------------------------------------------------------------------- 1 | #extension GL_EXT_control_flow_attributes : enable 2 | // #extension GL_EXT_expect_assume : enable 3 | #extension GL_EXT_scalar_block_layout : enable 4 | 5 | #extension GL_EXT_shader_8bit_storage : enable 6 | #extension GL_EXT_shader_16bit_storage : enable 7 | #extension GL_EXT_shader_explicit_arithmetic_types : enable 8 | #extension GL_EXT_scalar_block_layout : enable 9 | 10 | #extension GL_KHR_shader_subgroup_arithmetic : enable 11 | #extension GL_EXT_shader_explicit_arithmetic_types : enable 12 | #extension GL_EXT_shader_explicit_arithmetic_types_int16 : enable 13 | -------------------------------------------------------------------------------- /shaders/common/ubo.glsl: -------------------------------------------------------------------------------- 1 | layout(binding = 0, set = 0) uniform restrict readonly UniformBufferObject { 2 | mat4 trans_w2s; 3 | vec4 campos; 4 | vec4 camdir; 5 | vec4 horizline_scaled; 6 | vec4 vertiline_scaled; 7 | vec4 globalLightDir; 8 | mat4 lightmap_proj; 9 | vec2 frame_size; 10 | int timeseed; 11 | } ubo; -------------------------------------------------------------------------------- /shaders/fillStencilGlossy.frag: -------------------------------------------------------------------------------- 1 | #version 450 2 | 3 | layout(input_attachment_index = 0, set = 0, binding = 0) uniform usubpassInput matNorm; 4 | layout(set = 0, binding = 1 ) uniform sampler2D voxelPalette; 5 | 6 | //stencils smooth surface pixels bit at 01 from 00 to 01 7 | 8 | struct Material{ 9 | vec3 color; 10 | float emmitance; 11 | vec3 diffuse_light; 12 | float roughness; 13 | }; 14 | int load_mat(){ 15 | int mat = int(round(subpassLoad(matNorm).x)); 16 | return mat; 17 | } 18 | Material GetMat(in int voxel){ 19 | Material mat; 20 | 21 | mat.color.r = texelFetch(voxelPalette, ivec2(0,voxel), 0).r; 22 | mat.color.g = texelFetch(voxelPalette, ivec2(1,voxel), 0).r; 23 | mat.color.b = texelFetch(voxelPalette, ivec2(2,voxel), 0).r; 24 | // mat.transparancy = 1.0 - texelFetch(voxelPalette, ivec2(3,voxel), 0).r; 25 | mat.emmitance = texelFetch(voxelPalette, ivec2(4,voxel), 0).r; 26 | mat.roughness = texelFetch(voxelPalette, ivec2(5,voxel), 0).r; 27 | return mat; 28 | } 29 | void main() 30 | { 31 | float rough = GetMat(load_mat()).roughness; 32 | 33 | if(rough > 0.5){ 34 | discard; 35 | } 36 | } -------------------------------------------------------------------------------- /shaders/fillStencilSmoke.frag: -------------------------------------------------------------------------------- 1 | #version 450 2 | 3 | //just sets stencil values to 10 on rasterization 4 | layout (location = 0) in float end_depth_in; 5 | 6 | layout(location = 0) out float far_depth_out; 7 | layout(location = 1) out float near_depth_out; 8 | 9 | //desired effect of separation achieved through min max blend 10 | #extension GL_GOOGLE_include_directive : require 11 | #include "common\ext.glsl" 12 | 13 | void main() { 14 | if(!gl_FrontFacing){ 15 | gl_FragDepth = end_depth_in - 0.01; 16 | } else { 17 | gl_FragDepth = end_depth_in; 18 | } 19 | 20 | far_depth_out = end_depth_in; 21 | near_depth_out = end_depth_in; 22 | } -------------------------------------------------------------------------------- /shaders/fillStencilSmoke.vert: -------------------------------------------------------------------------------- 1 | #version 450 2 | 3 | // layout (location = 0) out vec2 outUV; 4 | layout (location = 0) out float end_depth; 5 | 6 | #extension GL_GOOGLE_include_directive : require 7 | #include "common\ext.glsl" 8 | #include "common\ubo.glsl" 9 | 10 | layout(push_constant) uniform restrict readonly constants{ 11 | vec4 originSize; 12 | } pco; 13 | 14 | //slow but does not matter in here 15 | const vec3 vertices[] = { 16 | vec3(0,1,1), 17 | vec3(0,1,0), 18 | vec3(0,0,0), 19 | vec3(0,0,0), 20 | vec3(0,0,1), 21 | vec3(0,1,1), 22 | vec3(1,0,0), 23 | vec3(1,1,0), 24 | vec3(1,1,1), 25 | vec3(1,1,1), 26 | vec3(1,0,1), 27 | vec3(1,0,0), 28 | vec3(0,0,0), 29 | vec3(1,0,0), 30 | vec3(1,0,1), 31 | vec3(1,0,1), 32 | vec3(0,0,1), 33 | vec3(0,0,0), 34 | vec3(1,1,1), 35 | vec3(1,1,0), 36 | vec3(0,1,0), 37 | vec3(0,1,0), 38 | vec3(0,1,1), 39 | vec3(1,1,1), 40 | vec3(1,1,0), 41 | vec3(1,0,0), 42 | vec3(0,0,0), 43 | vec3(0,0,0), 44 | vec3(0,1,0), 45 | vec3(1,1,0), 46 | vec3(0,0,1), 47 | vec3(1,0,1), 48 | vec3(1,1,1), 49 | vec3(1,1,1), 50 | vec3(0,1,1), 51 | vec3(0,0,1), 52 | }; 53 | 54 | // const uint elements [] = { 55 | // 3, 2, 6, 7, 4, 2, 0, 56 | // 3, 1, 6, 5, 4, 1, 0 57 | // }; 58 | 59 | vec3 get_vert(int index){ 60 | int tri = index / 3; 61 | int idx = index % 3; 62 | int face = tri / 2; 63 | int top = tri % 2; 64 | 65 | int dir = face % 3; 66 | int pos = face / 3; 67 | 68 | int nz = dir >> 1; 69 | int ny = dir & 1; 70 | int nx = 1 ^ (ny | nz); 71 | 72 | vec3 d = vec3(nx, ny, nz); 73 | float flip = 1 - 2 * pos; 74 | 75 | vec3 n = flip * d; 76 | vec3 u = -d.yzx; 77 | vec3 v = flip * d.zxy; 78 | 79 | float mirror = 0 + 2 * top; 80 | vec3 xyz = n + mirror*(1-2*(idx&1))*u + mirror*(1-2*(idx>>1))*v; 81 | 82 | return xyz; 83 | } 84 | 85 | void main() 86 | { 87 | // end_depth = 10; 88 | // uint index = elements[gl_VertexIndex]; 89 | // vec3 vertex = get_vert(gl_VertexIndex); 90 | vec3 vertex = vertices[gl_VertexIndex]; 91 | 92 | vertex*= pco.originSize.w*1; 93 | 94 | vec4 world_pos = vec4(vertex + pco.originSize.xyz,1); 95 | vec3 clip_coords = (ubo.trans_w2s*world_pos).xyz; 96 | clip_coords.z = 1.0+clip_coords.z; 97 | end_depth = clip_coords.z; 98 | // clip_coords.z = 0; 99 | 100 | gl_Position = vec4(clip_coords, 1); 101 | } -------------------------------------------------------------------------------- /shaders/fullscreenTriag.vert: -------------------------------------------------------------------------------- 1 | #version 450 2 | 3 | // layout (location = 0) out vec2 outUV; 4 | 5 | void main() 6 | { 7 | vec2 outUV = vec2((gl_VertexIndex << 1) & 2, gl_VertexIndex & 2); 8 | gl_Position = vec4(outUV * 2.0f + -1.0f, 0.0f, 1.0f); 9 | } -------------------------------------------------------------------------------- /shaders/grass.frag: -------------------------------------------------------------------------------- 1 | #version 450 2 | layout(early_fragment_tests) in; 3 | 4 | precision highp int; 5 | precision highp float; 6 | 7 | layout(location = 0) lowp flat in uvec4 mat_norm; 8 | 9 | layout(location = 0) lowp out uvec4 outMatNorm; 10 | 11 | void main() { 12 | // outMatNorm.x = fmat; 13 | // outMatNorm.gba = normalize(norm.xyz); 14 | 15 | outMatNorm = uvec4(mat_norm); 16 | } -------------------------------------------------------------------------------- /shaders/hbao.frag: -------------------------------------------------------------------------------- 1 | #version 450 2 | 3 | //simple ssao shader (nvidia calls this hbao) 4 | //uses lut instead of direct computation 5 | 6 | precision highp float; 7 | precision highp int; 8 | 9 | #extension GL_GOOGLE_include_directive : require 10 | #include "common\ext.glsl" 11 | #include "common\ubo.glsl" 12 | #include "common\consts.glsl" 13 | 14 | layout(location = 0) out vec4 frame_color; 15 | 16 | struct AoLut { 17 | vec3 world_shift; 18 | float weight_normalized; //divided by total_weight and multipled by 0.7 19 | vec2 screen_shift; 20 | vec2 padding; 21 | }; 22 | //precomputed sample_count values on cpu 23 | layout(std430, binding = 1, set = 0) uniform restrict readonly LutBuffer { 24 | AoLut lut [8]; 25 | } lut; 26 | layout(input_attachment_index = 0, set = 0, binding = 2) uniform usubpassInput matNorm; 27 | layout(set = 0, binding = 3) uniform sampler2D depthBuffer; 28 | 29 | const float PI = 3.1415926535; 30 | // const ivec3 world_size = ivec3(48,48,16); 31 | 32 | vec3 load_norm(){ 33 | // i16vec3 nenc = i16vec3(subpassLoad(matNorm).gba); 34 | vec3 norm = (((subpassLoad(matNorm).gba)/255.0)*2.0 - 1.0); 35 | return norm; 36 | } 37 | int load_mat(){ 38 | int mat = int(subpassLoad(matNorm).x); 39 | return mat; 40 | } 41 | 42 | // float load_depth(vec2 pixel){ 43 | // vec2 uv = (vec2(pixel)+0.0)/ubo.frame_size; 44 | // float depth_encoded = (texture(depthBuffer, uv).x); 45 | // return (depth_encoded)*1000.0; 46 | // } 47 | float load_depth(vec2 uv){ 48 | // float depth_encoded = (texelFetch(depthBuffer, ivec2(uv), 0).x); 49 | float depth_encoded = (textureLod(depthBuffer, (uv), 0).x); 50 | return (depth_encoded)*1000.0; 51 | } 52 | // vec3 get_shift_from_depth(float depth_diff, vec2 clip_shift){ 53 | // vec3 shift = 54 | // (ubo.horizline_scaled.xyz*clip_shift.x) + 55 | // (ubo.vertiline_scaled.xyz*clip_shift.y) + 56 | // (ubo.camdir.xyz*depth_diff); 57 | // return shift; 58 | // } 59 | const float COLOR_ENCODE_VALUE = 8.0; 60 | vec3 decode_color(vec3 encoded_color){ 61 | return encoded_color*COLOR_ENCODE_VALUE; 62 | } 63 | vec3 encode_color(vec3 color){ 64 | return color/COLOR_ENCODE_VALUE; 65 | } 66 | 67 | mat2 rotate2d(float a) { 68 | float s = sin(a); 69 | float c = cos(a); 70 | mat2 m = mat2(c, s, -s, c); 71 | return m; 72 | } 73 | float square(float x) {return x*x;} 74 | void main() { 75 | // horizline_doublescaled = ubo.horizline_scaled.xyz*2.0; 76 | // vertiline_doublescaled = ubo.vertiline_scaled.xyz*2.0; 77 | 78 | vec3 norm = load_norm(); 79 | vec2 initial_pix = gl_FragCoord.xy / ubo.frame_size; 80 | float initial_depth = load_depth(initial_pix); 81 | const int sample_count = 8; //in theory i should do smth with temporal accumulation 82 | // const float max_radius = 16.0 / 1000.0; 83 | // float angle = 00; 84 | // float angle_bias = sin(radians(0)); 85 | // float radius = 00; 86 | // float normalized_radius = 00; 87 | // float radius_step = max_radius / float(sample_count); 88 | // float norm_radius_step = 1.0 / float(sample_count); 89 | 90 | // vec2 ratio = ubo.frame_size / ubo.frame_size.x; 91 | 92 | float total_ao = 00; 93 | // float total_weight = 00; 94 | 95 | // mat2 rotate = rotate2d(0.69420); 96 | // vec2 screen_rot = vec2(1,0); 97 | 98 | //TODO: i think its possible to compute in screen space to avoid translating into worldspace every iteration 99 | 100 | // [[unroll]] 101 | for(int i=00; i -1.0); 112 | // vec2 clip_shift = (screen_shift)*2.0; 113 | // vec3 relative_pos = get_shift_from_depth(depth_shift, clip_shift); 114 | vec3 relative_pos = 115 | // (ubo.horizline_scaled.xyz*clip_shift.x) + //PRECOMP world_shift 116 | // (ubo.vertiline_scaled.xyz*clip_shift.y) + //PRECOMP world_shift 117 | lut.lut[i].world_shift + 118 | (ubo.camdir.xyz*depth_shift); 119 | 120 | vec3 direction = normalize(relative_pos); 121 | 122 | float ao = max(dot(direction, norm),0); 123 | 124 | // float weight = 1; 125 | float weight = lut.lut[i].weight_normalized; 126 | // weight *= (1.0 - square(normalized_radius)) * 0.7; //PRECOMP weight 127 | weight *= sqrt(clamp(float(8.0+(depth_shift)), 0,8)/8.0); 128 | total_ao += ao*weight; 129 | // total_weight += weight; 130 | } 131 | 132 | // float obfuscation = float(shadowed_count) / float(sample_count); 133 | // float obfuscation = total_ao / total_weight; 134 | float obfuscation = total_ao; 135 | // float obfuscation = total_ao; 136 | // obfuscation = (sqrt(obfuscation)); 137 | // obfuscation = obfuscation*obfuscation; 138 | // obfuscation = clamp((obfuscation), 0.0, 0.7); 139 | // obfuscation *= 0.7; 140 | frame_color = (vec4(encode_color(vec3(0.0)), obfuscation)); 141 | // frame_color = (vec4(encode_color(vec3(obfuscation)), 1)); 142 | // frame_color = (vec4(encode_color(vec3(0.0)), 0)); 143 | } -------------------------------------------------------------------------------- /shaders/lightmapBlocks.vert: -------------------------------------------------------------------------------- 1 | #version 450 2 | 3 | //depth writing for lightmaps visibility 4 | 5 | precision highp int; 6 | precision highp float; 7 | 8 | #extension GL_GOOGLE_include_directive : require 9 | #include "common\ext.glsl" 10 | #include "common\ubo.glsl" 11 | #include "common\consts.glsl" 12 | 13 | layout(location = 0) in lowp uvec3 posIn; 14 | 15 | layout(push_constant) uniform restrict readonly constants{ 16 | i16vec4 shift; 17 | } pco; 18 | 19 | vec3 qtransform( vec4 q, vec3 v ){ 20 | return v + 2.0*cross(cross(v, -q.xyz ) + q.w*v, -q.xyz); 21 | } 22 | 23 | void main() { 24 | vec3 fpos = vec3(posIn); 25 | 26 | vec3 local_pos = fpos; 27 | 28 | vec4 world_pos = vec4(local_pos + vec3(pco.shift.xyz) ,1); 29 | 30 | vec3 clip_coords = (ubo.trans_w2s*world_pos).xyz; //move up 31 | clip_coords.z = 1+clip_coords.z; 32 | 33 | gl_Position = vec4(clip_coords, 1); 34 | } -------------------------------------------------------------------------------- /shaders/lightmapModels.vert: -------------------------------------------------------------------------------- 1 | #version 450 2 | 3 | //depth writing for lightmaps visibility 4 | 5 | precision highp int; 6 | precision highp float; 7 | 8 | layout(location = 0) in lowp uvec3 posIn; 9 | 10 | //no reason to move up in pipeline cause sm load is like ~ 6% in vs 11 | layout(binding = 0, set = 0) uniform restrict readonly UniformBufferObject { 12 | mat4 trans_w2s; 13 | } ubo; 14 | 15 | //quatornions! 16 | layout(push_constant) uniform restrict readonly constants{ 17 | vec4 rot; 18 | vec4 shift; 19 | } pco; 20 | 21 | vec3 qtransform( vec4 q, vec3 v ){ 22 | return v + 2.0*cross(cross(v, -q.xyz ) + q.w*v, -q.xyz); 23 | // return (q*vec4(v,0)).xyz; 24 | } 25 | 26 | void main() { 27 | vec3 fpos = vec3(posIn); 28 | 29 | vec3 local_pos = qtransform(pco.rot, fpos); 30 | // vec3 local_pos = fpos; 31 | 32 | vec4 world_pos = vec4(local_pos + pco.shift.xyz ,1); 33 | 34 | vec3 clip_coords = (ubo.trans_w2s*world_pos).xyz; //move up 35 | clip_coords.z = 1+clip_coords.z; 36 | 37 | gl_Position = vec4(clip_coords, 1); 38 | } -------------------------------------------------------------------------------- /shaders/map.comp: -------------------------------------------------------------------------------- 1 | #version 450 core 2 | 3 | //version with world from model 4 | 5 | #define varp lowp 6 | precision highp int; 7 | precision highp float; 8 | 9 | //TODO: move per-workgroup computations to different shader 10 | //TODO: AND test shared perfomance 11 | layout(push_constant) restrict readonly uniform constants{ 12 | mat4 inverse_trans; //map game voxel world to modelVoxels 13 | ivec4 shift; 14 | } pco; 15 | 16 | layout(set = 0, binding = 0, r16i) uniform restrict readonly iimage3D blocks; 17 | layout(set = 0, binding = 1, r8ui) uniform restrict writeonly uimage3D blockPalette; 18 | 19 | layout(set = 1, binding = 0, r8ui) uniform restrict readonly uimage3D modelVoxels; 20 | 21 | const int BLOCK_PALETTE_SIZE_X = 64; 22 | const int STATIC_BLOCK_COUNT = 15; // 0 + 1..static block count so >=STATIC_BLOCK_COUNT 23 | 24 | ivec3 voxel_in_palette(ivec3 relative_voxel_pos, int block_id) { 25 | int block_x = block_id % BLOCK_PALETTE_SIZE_X; 26 | int block_y = block_id / BLOCK_PALETTE_SIZE_X; 27 | 28 | return relative_voxel_pos + ivec3(16*block_x, 16*block_y,0); 29 | } 30 | 31 | //TODO: balance, X>Y>Z for coherence 32 | layout(local_size_x = 4, local_size_y = 4, local_size_z = 4) in; 33 | void main(void){ 34 | const ivec3 model_size = imageSize(modelVoxels); 35 | const ivec3 shift = pco.shift.xyz; 36 | 37 | ivec3 iabsolute_but_relative_to_shift_voxel = ivec3(gl_GlobalInvocationID.xyz); 38 | vec3 absolute_but_relative_to_shift_voxel = vec3(iabsolute_but_relative_to_shift_voxel) / 1.0; 39 | 40 | vec3 world_voxel = absolute_but_relative_to_shift_voxel + vec3(0.5) + vec3(shift); 41 | ivec3 iworld_voxel = ivec3(world_voxel); 42 | 43 | 44 | //part where actual transformation happens 45 | // vec3 relative_vox = (pco.inverse_trans * vec4(absolute_voxel,1)).xyz; 46 | // ivec3 irelative_vox = ivec3(round(relative_vox.xyz)); 47 | vec3 model_voxel = (pco.inverse_trans * vec4(world_voxel,1)).xyz; 48 | ivec3 imodel_voxel = ivec3(model_voxel); 49 | 50 | ivec3 itarget_block = iworld_voxel/16; 51 | 52 | int target_block_in_palette = int(imageLoad(blocks, itarget_block).r); 53 | 54 | ivec3 target_palette_voxel = voxel_in_palette(iworld_voxel.xyz % 16, target_block_in_palette); 55 | 56 | uvec4 voxel = imageLoad(modelVoxels, imodel_voxel); 57 | 58 | // read only if fails, i hope. 59 | if (target_block_in_palette >= STATIC_BLOCK_COUNT) { 60 | uvec4 voxel = imageLoad(modelVoxels, imodel_voxel); 61 | if (voxel.r != 0) { 62 | imageStore(blockPalette, target_palette_voxel, voxel); // maybe atomic?.. Could use temporal shared block for this 63 | } 64 | } 65 | // if ((voxel.r != 0) && (target_block_in_palette>=STATIC_BLOCK_COUNT)){ 66 | // imageStore(blockPalette, target_palette_voxel, voxel); // maybe atomic?.. Could use temporal shared block for this 67 | // } 68 | } -------------------------------------------------------------------------------- /shaders/overlay.frag: -------------------------------------------------------------------------------- 1 | #version 450 2 | 3 | precision highp float; 4 | precision highp int; 5 | 6 | layout(location = 0) in mediump vec2 fragUV; 7 | layout(location = 1) in mediump vec4 fragColor; 8 | 9 | layout(set = 0, binding = 0) uniform sampler2D ui_elem_texture; 10 | 11 | layout(location = 0) out vec4 outColor; 12 | 13 | const float COLOR_ENCODE_VALUE = 8.0; 14 | vec3 decode_color(vec3 encoded_color){ 15 | return clamp(encoded_color,0,1)*vec3(COLOR_ENCODE_VALUE); 16 | } 17 | vec3 encode_color(vec3 color){ 18 | return clamp(color/vec3(COLOR_ENCODE_VALUE), 0,1); 19 | } 20 | 21 | void main() { 22 | vec2 final_uv = vec2(fragUV.x, fragUV.y); 23 | vec4 sampledColor = texture(ui_elem_texture, final_uv); 24 | 25 | //as stated in rmlui docs 26 | vec4 final_color = sampledColor; 27 | final_color *= fragColor; 28 | 29 | outColor = final_color; 30 | } -------------------------------------------------------------------------------- /shaders/overlay.vert: -------------------------------------------------------------------------------- 1 | #version 450 2 | 3 | layout(location = 0) in vec2 posIn; 4 | layout(location = 1) in vec4 colorIn; 5 | layout(location = 2) in vec2 UvIn; 6 | layout(location = 0) out mediump vec2 fragUV; 7 | layout(location = 1) out mediump vec4 fragColor; 8 | 9 | layout(push_constant) uniform restrict readonly constant { 10 | vec4 shift_size; 11 | mat4 transform; 12 | } pco; 13 | 14 | void main() { 15 | vec2 shift = pco.shift_size.xy; 16 | vec2 scale = pco.shift_size.zw; 17 | 18 | vec4 pos_in_pixels = vec4(posIn + shift, 0, 1); 19 | vec4 pix_pos_transformed = pos_in_pixels; 20 | vec2 pos = pix_pos_transformed.xy / scale; 21 | 22 | vec2 clip_pos = pos.xy*2.0 - 1.0; 23 | 24 | gl_Position = vec4(clip_pos,0,1); 25 | 26 | fragUV = UvIn; 27 | fragColor = colorIn; 28 | } -------------------------------------------------------------------------------- /shaders/rayGenBlocks.frag: -------------------------------------------------------------------------------- 1 | #version 450 2 | layout(early_fragment_tests) in; 3 | 4 | #define varp highp 5 | precision varp int; 6 | precision varp float; 7 | 8 | #extension GL_GOOGLE_include_directive : require 9 | #include "common\ext.glsl" 10 | #include "common\ubo.glsl" 11 | #include "common\consts.glsl" 12 | 13 | layout(binding = 1, set = 0) uniform usampler3D blockPalette; 14 | 15 | layout(location = 0) in vec3 sample_point; 16 | layout(location = 1) in flat uint bunorm; 17 | 18 | layout(location = 0) lowp out uvec4 outMatNorm; 19 | 20 | const int BLOCK_PALETTE_SIZE_X = 64; 21 | ivec3 voxel_in_palette(ivec3 relative_voxel_pos, int block_id) { 22 | int block_x = block_id % BLOCK_PALETTE_SIZE_X; 23 | int block_y = block_id / BLOCK_PALETTE_SIZE_X; 24 | 25 | return relative_voxel_pos + ivec3(16*block_x, 16*block_y, 0); 26 | } 27 | 28 | ivec3 voxel_in_bit_palette(ivec3 relative_voxel_pos, int block_id) { 29 | int block_x = block_id % BLOCK_PALETTE_SIZE_X; 30 | int block_y = block_id / BLOCK_PALETTE_SIZE_X; 31 | 32 | return relative_voxel_pos + ivec3(0+2*block_x, 0+16*block_y,0); 33 | } 34 | 35 | int GetVoxel(in int block_id, ivec3 relative_voxel_pos){ 36 | int voxel; 37 | ivec3 voxel_pos = voxel_in_palette(relative_voxel_pos, block_id); 38 | voxel = int(texelFetch(blockPalette, (voxel_pos), 0).r); 39 | 40 | return (voxel); 41 | } 42 | 43 | void main() { 44 | uint sample_block = unpackUint2x16(bunorm.x).x; 45 | uint normal_encoded = unpackUint2x16(bunorm.x).y; 46 | 47 | uvec3 axis = uvec3( 48 | normal_encoded & 01, 49 | (normal_encoded >> 1) & 01, 50 | (normal_encoded >> 2) & 01 51 | ); 52 | int _sign = 1 - 2 * int((normal_encoded >> 7) & 01); 53 | ivec3 inorm = ivec3(axis) * _sign; 54 | outMatNorm.yzw = uvec3((ivec3(inorm + 1) * 255) / 2); 55 | // outMatNorm.x = 9; 56 | 57 | ivec3 ipos = ivec3(sample_point); 58 | 59 | outMatNorm.x = GetVoxel(int(sample_block), ipos).x; 60 | } -------------------------------------------------------------------------------- /shaders/rayGenBlocks.vert: -------------------------------------------------------------------------------- 1 | #version 450 2 | 3 | #define varp highp 4 | precision varp int; 5 | precision varp float; 6 | 7 | #extension GL_GOOGLE_include_directive : require 8 | #include "common\ext.glsl" 9 | #include "common\ubo.glsl" 10 | #include "common\consts.glsl" 11 | 12 | layout(location = 0) in lowp uvec3 posIn; 13 | layout(location = 0) out vec3 sample_point; 14 | layout(location = 1) out flat uint bunorm; 15 | 16 | layout(binding = 1, set = 0) uniform usampler3D blockPalette; 17 | 18 | layout(scalar, push_constant) restrict readonly uniform constants{ 19 | int16_t block; 20 | i16vec3 shift; 21 | u8vec4 unorm; 22 | } pco; 23 | 24 | vec3 qtransform( vec4 q, vec3 v ){ 25 | return v + 2.0*cross(cross(v, -q.xyz ) + q.w*v, -q.xyz); 26 | } 27 | 28 | void main() { 29 | ivec3 upos = ivec3(posIn); 30 | uint normal_encoded = pco.unorm.x; 31 | uint s = (normal_encoded & (1<<7))>>7; //0 if position 1 if negative 32 | uvec3 axis = uvec3( 33 | ((normal_encoded & (1<<0))>>0), 34 | ((normal_encoded & (1<<1))>>1), 35 | ((normal_encoded & (1<<2))>>2) 36 | ); 37 | ivec3 inorm = ivec3(axis) * (1 - int(s)*2); 38 | vec3 fnorm = (vec3(inorm)); 39 | 40 | ivec3 uworld_pos = ivec3(upos + (pco.shift)); 41 | vec4 fworld_pos = vec4(uworld_pos, 1); 42 | 43 | vec3 clip_coords = (ubo.trans_w2s*fworld_pos).xyz; //move up 44 | clip_coords.z = 1+clip_coords.z; 45 | 46 | gl_Position = vec4(clip_coords, 1); 47 | 48 | sample_point = vec3(upos) - fnorm * 0.5; //for better rounding lol 49 | 50 | uint sample_block = uint(pco.block); 51 | // uvec3 normal_encoded = uvec3(((ivec3(pco.inorm) + 1)*255)/2); 52 | 53 | bunorm = packUint2x16(u16vec2(sample_block, pco.unorm.x)); 54 | } -------------------------------------------------------------------------------- /shaders/rayGenModels.frag: -------------------------------------------------------------------------------- 1 | #version 450 2 | layout(early_fragment_tests) in; 3 | 4 | #define varp highp 5 | precision varp int; 6 | precision varp float; 7 | 8 | #extension GL_GOOGLE_include_directive : require 9 | #include "common\ext.glsl" 10 | #include "common\ubo.glsl" 11 | #include "common\consts.glsl" 12 | 13 | layout(binding = 0, set = 1) uniform usampler3D modelVoxels; 14 | 15 | layout(location = 0) in vec3 sample_point; 16 | layout(location = 1) in flat uint normal_encoded_packed; 17 | // layout(location = 2) in vec3 n; 18 | 19 | layout(location = 0) lowp out uvec4 outMatNorm; 20 | 21 | int GetModelVoxel(ivec3 relative_voxel_pos){ 22 | int voxel; 23 | voxel = int(texelFetch(modelVoxels, (relative_voxel_pos), 0).r); 24 | return (voxel); 25 | } 26 | 27 | void main() { 28 | uvec3 normal_encoded = uvec4( 29 | ((normal_encoded_packed>>0 ) & (255)), 30 | ((normal_encoded_packed>>8 ) & (255)), 31 | ((normal_encoded_packed>>16) & (255)), 32 | 0 33 | ).xyz; 34 | 35 | 36 | outMatNorm.yzw = uvec3(normal_encoded); 37 | // outMatNorm.yzw = uvec3(((n+1.0)/2.0)*255.0); 38 | 39 | ivec3 ipos = ivec3(sample_point); 40 | 41 | outMatNorm.x = GetModelVoxel(ipos).x; 42 | // outMatNorm.x = 9; 43 | } -------------------------------------------------------------------------------- /shaders/rayGenModels.vert: -------------------------------------------------------------------------------- 1 | #version 450 2 | 3 | #define varp highp 4 | precision varp int; 5 | precision varp float; 6 | 7 | #extension GL_GOOGLE_include_directive : require 8 | #include "common\ext.glsl" 9 | #include "common\ubo.glsl" 10 | #include "common\consts.glsl" 11 | 12 | layout(location = 0) in lowp uvec3 posIn; 13 | layout(location = 0) out vec3 sample_point; 14 | layout(location = 1) out flat uint normal_encoded_packed; 15 | // layout(location = 2) out vec3 n; 16 | 17 | layout(scalar, push_constant) uniform restrict readonly constants{ 18 | vec4 rot; 19 | vec4 shift; 20 | vec4 fnormal; //not encoded 21 | } pco; 22 | 23 | vec3 qtransform( vec4 q, vec3 v ){ 24 | return v + 2.0*cross(cross(v, -q.xyz ) + q.w*v, -q.xyz); 25 | // return (q*vec4(v,0)).xyz; 26 | } 27 | 28 | void main() { 29 | vec3 fpos = vec3(posIn); 30 | vec3 fnorm_ms = normalize(vec3(pco.fnormal.xyz)); 31 | 32 | vec3 local_pos = qtransform(pco.rot, fpos); 33 | // vec3 local_pos = fpos; 34 | 35 | vec4 world_pos = vec4(local_pos + pco.shift.xyz ,1); 36 | 37 | vec3 clip_coords = (ubo.trans_w2s*world_pos).xyz; //move up 38 | clip_coords.z = 1+clip_coords.z; 39 | 40 | gl_Position = vec4(clip_coords, 1); 41 | 42 | vec3 fnorm_ws = (qtransform(pco.rot,fnorm_ms)); //move up 43 | // normal_encoded_packed = 44 | // (norm_encoded.x<<0) | 45 | // (norm_encoded.y<<8) | 46 | // (norm_encoded.z<<16) ; 47 | normal_encoded_packed = packUnorm4x8(vec4((fnorm_ws+1.0)/2.0, 0)); 48 | sample_point = fpos - fnorm_ms * 0.5; //for better rounding lol 49 | } -------------------------------------------------------------------------------- /shaders/rayGenParticles.frag: -------------------------------------------------------------------------------- 1 | #version 450 2 | layout(early_fragment_tests) in; 3 | 4 | #define varp highp 5 | 6 | precision varp int; 7 | precision varp float; 8 | 9 | // layout(location = 0) lowp flat in vec3 norm; 10 | layout(location = 0) lowp flat in uvec4 mat_norm; 11 | 12 | layout(location = 0) lowp out uvec4 outMatNorm; 13 | 14 | void main() { 15 | // outMatNorm.x = (float(mat)-127.0)/127.0; 16 | outMatNorm = uvec4(mat_norm); 17 | } -------------------------------------------------------------------------------- /shaders/rayGenParticles.geom: -------------------------------------------------------------------------------- 1 | #version 450 2 | layout (points) in; 3 | layout (triangle_strip, max_vertices = 14) out; 4 | 5 | precision highp float; 6 | 7 | layout(location = 0) in VS_OUT { 8 | // vec2 old_uv; 9 | float size; 10 | flat uint mat; 11 | } gs_in[]; 12 | 13 | // layout(location = 0) out vec2 old_uv; 14 | // layout(location = 0) flat out vec3 norm; 15 | // layout(location = 1) flat out float fmat; 16 | layout(location = 0) lowp flat out uvec4 mat_norm; 17 | 18 | #extension GL_GOOGLE_include_directive : require 19 | #include "common\ext.glsl" 20 | #include "common\ubo.glsl" 21 | 22 | const vec3 cube_strip[14] = { 23 | vec3(-1, +1, +1), // Front - top - left 24 | vec3(+1, +1, +1), // Front - top - right 25 | vec3(-1, -1, +1), // Front - bottom - left 26 | vec3(+1, -1, +1), // Front - bottom - right 27 | vec3(+1, -1, -1), // Back - bottom - right 28 | vec3(+1, +1, +1), // Front - top - right 29 | vec3(+1, +1, -1), // Back - top - right 30 | vec3(-1, +1, +1), // Front - top - left 31 | vec3(-1, +1, -1), // Back - top - left 32 | vec3(-1, -1, +1), // Front - bottom - left 33 | vec3(-1, -1, -1), // Back - bottom - left 34 | vec3(+1, -1, -1), // Back - bottom - right 35 | vec3(-1, +1, -1), // Back - top - left 36 | vec3(+1, +1, -1), // Back - top - right 37 | }; 38 | const vec3 norms[14] = { 39 | vec3(0,0,+1), // Front - top - left 40 | vec3(0,0,+1), // Front - top - right 41 | vec3(0,-1,0), // Front - bottom - left 42 | vec3(+1,0,0), // Front - bottom - right 43 | vec3(+1,0,0), // Back - bottom - right 44 | vec3(0,+1,0), // Front - top - right 45 | vec3(0,+1,0), // Back - top - right 46 | vec3(-1,0,0), // Front - top - left 47 | vec3(-1,0,0), // Back - top - left 48 | vec3(0,-1,0), // Front - bottom - left 49 | vec3(0,0,-1), // Back - bottom - left 50 | vec3(0,0,-1), // Back - bottom - right 51 | //unused: 52 | vec3(1, 1, 1), // Back - top - left 53 | vec3(1, 1, 1), // Back - top - right 54 | }; 55 | 56 | //fuck we need normals so 24 not 14 57 | 58 | void main() { 59 | //so we are given particle center and its old_uv 60 | // clip = mat * pos 61 | // so we need to add\sub mat*0.5 62 | 63 | // mat3 m2w_normals = transpose(inverse(mat3(ubo.trans_m2w))); 64 | 65 | // #pragma unroll 66 | for(int i=0; i<14; i++){ 67 | vec3 shift_in_world = cube_strip[i]*gs_in[0].size; 68 | vec3 shift_on_screen = (ubo.trans_w2s * vec4(shift_in_world,1)).xyz; //todo move out 69 | shift_on_screen.z = +shift_on_screen.z; 70 | // shift_on_screen = -vec3(.1); 71 | 72 | vec3 norm = norms[i]; 73 | 74 | gl_Position = gl_in[0].gl_Position + vec4(shift_on_screen,0); 75 | // gl_Position.z = -.999; 76 | // gl_Position.z = +.999; 77 | 78 | // old_uv = gs_in[0].old_uv; 79 | // old_uv = vec2(0); 80 | uint mat = gs_in[0].mat; 81 | float fmat = (float(mat)-127.0)/127.0; 82 | 83 | vec4 fmat_norm = vec4(fmat, norm); 84 | mat_norm = uvec4(((fmat_norm+1.0)/2.0)*255.0); 85 | 86 | EmitVertex(); 87 | } 88 | 89 | // gl_Position = gl_in[0].gl_Position + vec4(+.5, +.5, 0.1, 0); 90 | // EmitVertex(); 91 | // gl_Position = gl_in[0].gl_Position + vec4(-.5, +.5, 0.1, 0); 92 | // EmitVertex(); 93 | // gl_Position = gl_in[0].gl_Position + vec4(-.5, -.5, 0.1, 0); 94 | // EmitVertex(); 95 | // gl_Position = gl_in[0].gl_Position + vec4(+.5, -.5, 0.1, 0); 96 | // EmitVertex(); 97 | 98 | 99 | EndPrimitive(); 100 | } 101 | 102 | -------------------------------------------------------------------------------- /shaders/rayGenParticles.vert: -------------------------------------------------------------------------------- 1 | #version 450 2 | 3 | precision highp float; 4 | // #extension GL_GOOGLE_include_directive : require 5 | #extension GL_GOOGLE_include_directive : require 6 | #include "common\ext.glsl" 7 | #include "common\ubo.glsl" 8 | #include "common\consts.glsl" 9 | 10 | layout(location = 0) in vec3 posIn; 11 | layout(location = 1) in vec3 velIn; //why not maybe effects 12 | layout(location = 2) in float lifeTimeIn; 13 | layout(location = 3) in uint matIDIn; 14 | 15 | // layout(location = 0) out float depth; 16 | layout(location = 0) out VS_OUT { 17 | // vec2 old_uv; 18 | float size; 19 | flat uint mat; 20 | } vs_out; 21 | 22 | 23 | layout(set = 0, binding = 1, r16i) uniform restrict readonly iimage3D blocks; 24 | layout(set = 0, binding = 2, r8ui) uniform restrict writeonly uimage3D blockPalette; 25 | 26 | const int BLOCK_PALETTE_SIZE_X = 64; 27 | const int STATIC_BLOCK_COUNT = 15; // 0 + 1..static block count so >=STATIC_BLOCK_COUNT 28 | 29 | ivec3 voxel_in_palette(ivec3 relative_voxel_pos, int block_id) { 30 | int block_x = block_id % BLOCK_PALETTE_SIZE_X; 31 | int block_y = block_id / BLOCK_PALETTE_SIZE_X; 32 | 33 | return relative_voxel_pos + ivec3(16*block_x, 16*block_y, 0); 34 | } 35 | 36 | 37 | void main() { 38 | 39 | float deltaTime = 1.0/75.0; 40 | 41 | vec4 world_pos = vec4(posIn,0); 42 | vec4 world_pos_old = vec4(posIn - velIn*deltaTime,0); 43 | 44 | vec3 clip_coords = (ubo.trans_w2s*world_pos).xyz; 45 | clip_coords.z = 1.0+clip_coords.z; 46 | // vec3 clip_coords_old = (ubo.trans_w2s_old*world_pos_old).xyz; 47 | 48 | gl_Position = vec4(clip_coords, 1); 49 | 50 | // mat3 m2w_normals = transpose(inverse(mat3(pco.trans_m2w))); 51 | 52 | // vs_out.old_uv = (clip_coords_old.xy - clip_coords.xy)/2.0; //0..1 53 | vs_out.mat = uint(matIDIn); 54 | float size = lifeTimeIn / 14.0; 55 | vs_out.size = size; 56 | 57 | bool should_map = false; 58 | if(size > .15){ 59 | ivec3 target_voxel_in_world = ivec3(posIn); 60 | ivec3 target_block_in_world = target_voxel_in_world / 16; 61 | 62 | int target_block_id = imageLoad(blocks, target_block_in_world).x; 63 | ivec3 target_voxel_in_palette = voxel_in_palette(target_voxel_in_world % 16, target_block_id); 64 | if(target_block_id>=STATIC_BLOCK_COUNT){ 65 | imageStore(blockPalette, target_voxel_in_palette, uvec4(matIDIn)); 66 | } 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /shaders/templates/FOLIAGE.vert: -------------------------------------------------------------------------------- 1 | #version 450 core 2 | 3 | precision highp float; 4 | precision highp int; 5 | #extension GL_GOOGLE_include_directive : require 6 | #include "..\common\ext.glsl" 7 | #include "..\common\ubo.glsl" 8 | 9 | layout(set = 0, binding = 1) uniform sampler2D state; 10 | 11 | layout(push_constant) uniform restrict readonly PushConstants { 12 | vec4 shift; 13 | int size; 14 | int time; 15 | int x_flip; 16 | int y_flip; 17 | } pco; 18 | 19 | layout(location = 0) lowp flat out uvec4 mat_norm; 20 | 21 | // TODO: spec constants 22 | const int BLOCK_PALETTE_SIZE_X = 64; 23 | const int STATIC_BLOCK_COUNT = 15; // 0 + 1..static block count so >=STATIC_BLOCK_COUNT 24 | const ivec3 WORLD_SIZE = ivec3(48,48,16); 25 | 26 | // local_pos is in [0,1] 27 | // returns sampled WIND global worldspace offset 28 | // you then can multiple it by normalized height to get wind wiggles 29 | vec2 load_offset(vec2 local_pos){ 30 | vec2 offset; 31 | vec2 world_pos = local_pos*16.0 + pco.shift.xy; 32 | vec2 state_pos = world_pos / vec2(WORLD_SIZE.xy*16); 33 | offset = texture(state, state_pos).xy; 34 | return offset; 35 | } 36 | 37 | void main() { 38 | // do smth like this for faster depth testing 39 | // it is literally placing them in different order to place closest to camera first 40 | if(pco.x_flip == 0) my_blade_x = pco.size - blade_x; 41 | if(pco.y_flip != 0) my_blade_y = pco.size - blade_y; 42 | 43 | vec3 normal; 44 | 45 | vec4 world_pos; 46 | world_pos = YOUR COMPUTATIONS; 47 | 48 | // world-space to screen-space 49 | vec3 clip_coords = (ubo.trans_w2s*world_pos).xyz; 50 | clip_coords.z = 1.0+clip_coords.z; 51 | 52 | // common 53 | gl_Position = vec4(clip_coords, 1); 54 | 55 | // Fix for shitty normals like mine 56 | if(dot(ubo.camdir.xyz,normal) > 0) normal = -normal; 57 | 58 | //little coloring. They are hardcoded palette indices 59 | uint mat = (rand01 > (rand(pco.shift.yx) - length(relative_pos - 8.0)/32.0))? uint(9) : uint(10); 60 | vec3 norm = normal; 61 | 62 | // part where attributes are setten 63 | float fmat = (float(mat)-127.0)/127.0; 64 | vec4 fmat_norm = vec4(fmat, norm); 65 | mat_norm = uvec4(((fmat_norm+1.0)/2.0)*255.0); 66 | } -------------------------------------------------------------------------------- /shaders/tonemap.frag: -------------------------------------------------------------------------------- 1 | #version 450 2 | 3 | //simple color -> color map shader just to fix colors lol 4 | 5 | // layout(location = 0) in vec2 non_clip_pos; 6 | layout(location = 0) out vec4 frame_color; 7 | 8 | layout(input_attachment_index = 0, set = 0, binding = 0) uniform subpassInput rendered_frame; 9 | 10 | const float COLOR_ENCODE_VALUE = 8.0; 11 | vec3 decode_color(vec3 encoded_color){ 12 | return encoded_color*COLOR_ENCODE_VALUE; 13 | } 14 | vec3 encode_color(vec3 color){ 15 | return color/COLOR_ENCODE_VALUE; 16 | } 17 | 18 | float luminance(vec3 v){ 19 | return dot(v, vec3(0.2126f, 0.7152f, 0.0722f)); 20 | } 21 | vec3 change_luminance(vec3 c_in, float l_out){ 22 | float l_in = luminance(c_in); 23 | return c_in * (l_out / l_in); 24 | } 25 | vec3 reinhard_extended(vec3 v, float max_white){ 26 | vec3 numerator = v * (1.0f + (v / vec3(max_white * max_white))); 27 | return numerator / (1.0f + v); 28 | } 29 | vec3 reinhard_extended_luminance(vec3 v, float max_white_l){ 30 | float l_old = luminance(v); 31 | float numerator = l_old * (1.0f + (l_old / (max_white_l * max_white_l))); 32 | float l_new = numerator / (1.0f + l_old); 33 | return change_luminance(v, l_new); 34 | } 35 | vec3 uncharted2_tonemap_partial(vec3 x){ 36 | float A = 0.15f; 37 | float B = 0.50f; 38 | float C = 0.10f; 39 | float D = 0.20f; 40 | float E = 0.02f; 41 | float F = 0.30f; 42 | return ((x*(A*x+C*B)+D*E)/(x*(A*x+B)+D*F))-E/F; 43 | } 44 | vec3 uncharted2_filmic(vec3 v){ 45 | float exposure_bias = 2.0f; 46 | vec3 curr = uncharted2_tonemap_partial(v * exposure_bias); 47 | 48 | vec3 W = vec3(11.2f); 49 | vec3 white_scale = vec3(1.0f) / uncharted2_tonemap_partial(W); 50 | return curr * white_scale; 51 | } 52 | vec3 aces_approx(vec3 v){ 53 | v *= 0.6f; 54 | float a = 2.51f; 55 | float b = 0.03f; 56 | float c = 2.43f; 57 | float d = 0.59f; 58 | float e = 0.14f; 59 | return clamp((v*(a*v+b))/(v*(c*v+d)+e), 0.0f, 1.0f); 60 | } 61 | vec3 tonemap(vec3 color){ 62 | // return reinhard_extended(color, 5.0); 63 | return reinhard_extended_luminance(color, 5.0); 64 | // return uncharted2_filmic(color); 65 | // return aces_approx(color); 66 | } 67 | 68 | vec3 adjust_brightness(vec3 color, float value) { 69 | return color + value; 70 | } 71 | vec3 adjust_contrast(vec3 color, float value) { 72 | // return 0.5 + value * (color - 0.5); 73 | return 0.5 + (1.0 + value) * (color - 0.5); 74 | } 75 | vec3 adjust_exposure(vec3 color, float value) { 76 | return (1.0 + value) * color; 77 | } 78 | vec3 adjust_saturation(vec3 color, float value) { 79 | float grayscale = luminance(color); 80 | return mix(vec3(grayscale), color, 1.0 + value); 81 | } 82 | 83 | void main() { 84 | // frame_color = vec4(vec3(0.5), 1); 85 | vec3 color = decode_color(subpassLoad(rendered_frame).xyz); 86 | 87 | color = adjust_saturation(color, .1); 88 | color = adjust_contrast(color, .1); 89 | color = adjust_exposure(color, 0.5); 90 | color = tonemap(color); 91 | 92 | // frame_color = vec4(vec3(non_clip_pos,0), 1); 93 | frame_color = vec4(color,1); 94 | // frame_color = vec4(vec3(color.x), .5); 95 | } -------------------------------------------------------------------------------- /shaders/unused/ao_direct.frag: -------------------------------------------------------------------------------- 1 | #version 450 2 | 3 | //simple ssao shader (nvidia calles this hbao) 4 | //most complicated in terms of instruction count btw 5 | 6 | //when in normalized screen space coords 16 bits is enough 7 | // precision mediump float; 8 | // precision mediump int; 9 | 10 | precision highp float; 11 | precision highp int; 12 | //highp 13 | 14 | // #include "../common\ext.glsl" 15 | #include "../common\ubo.glsl" 16 | 17 | layout(location = 0) out vec4 frame_color; 18 | layout(input_attachment_index = 0, set = 0, binding = 1) uniform usubpassInput matNorm; 19 | layout(set = 0, binding = 2) uniform sampler2D depthBuffer; 20 | 21 | const float PI = 3.1415926535; 22 | const ivec3 world_size = ivec3(48,48,16); 23 | 24 | vec3 load_norm(){ 25 | // i16vec3 nenc = i16vec3(subpassLoad(matNorm).gba); 26 | // vec3 norm = vec3(((nenc*int16_t(2) - i16vec3(255))))/255.0; 27 | vec3 norm = (((subpassLoad(matNorm).gba)/255.0)*2.0 - 1.0); 28 | // vec3 norm = ((ivec3(subpassLoad(matNorm).gba)*2 - 255))/255.0; 29 | return norm; 30 | } 31 | int load_mat(){ 32 | int mat = int(subpassLoad(matNorm).x); 33 | return mat; 34 | } 35 | 36 | // float load_depth(vec2 pixel){ 37 | // vec2 uv = (vec2(pixel)+0.0)/ubo.frame_size; 38 | // float depth_encoded = (texture(depthBuffer, uv).x); 39 | // return (depth_encoded)*1000.0; 40 | // } 41 | float load_depth(vec2 uv){ 42 | // float depth_encoded = (texelFetch(depthBuffer, ivec2(uv), 0).x); 43 | float depth_encoded = (textureLod(depthBuffer, (uv), 0).x); 44 | return (depth_encoded)*1000.0; 45 | } 46 | vec3 horizline_doublescaled; 47 | vec3 vertiline_doublescaled; 48 | vec3 get_shift_from_depth(float depth_diff, vec2 clip_shift){ 49 | vec3 shift = 50 | (ubo.horizline_scaled.xyz*clip_shift.x) + 51 | (ubo.vertiline_scaled.xyz*clip_shift.y) + 52 | (ubo.camdir.xyz*depth_diff); 53 | return shift; 54 | } 55 | vec3 get_shift_from_depth_uv(float depth_diff, vec2 uv){ 56 | vec3 shift = 57 | (horizline_doublescaled*uv.x) + 58 | (vertiline_doublescaled*uv.y) + 59 | (ubo.camdir.xyz*depth_diff); 60 | return shift; 61 | } 62 | const float COLOR_ENCODE_VALUE = 8.0; 63 | vec3 decode_color(vec3 encoded_color){ 64 | return encoded_color*COLOR_ENCODE_VALUE; 65 | } 66 | vec3 encode_color(vec3 color){ 67 | return color/COLOR_ENCODE_VALUE; 68 | } 69 | 70 | mat2 rotate2d(float a) { 71 | float s = sin(a); 72 | float c = cos(a); 73 | mat2 m = mat2(c, s, -s, c); 74 | return m; 75 | } 76 | float square(float x) {return x*x;} 77 | void main() { 78 | horizline_doublescaled = ubo.horizline_scaled.xyz*2.0; 79 | vertiline_doublescaled = ubo.vertiline_scaled.xyz*2.0; 80 | 81 | vec3 norm = load_norm(); 82 | vec2 initial_pix = gl_FragCoord.xy / ubo.frame_size; 83 | float initial_depth = load_depth(initial_pix); 84 | const int sample_count = 8; //in theory i should do smth with temporal accumulation 85 | const float max_radius = 16.0 / 1000.0; 86 | float angle = 00; 87 | float angle_bias = sin(radians(0)); 88 | // float radius = 00; 89 | float normalized_radius = 00; 90 | float radius_step = max_radius / float(sample_count); 91 | float norm_radius_step = 1.0 / float(sample_count); 92 | 93 | vec2 ratio = ubo.frame_size / ubo.frame_size.x; 94 | 95 | float total_ao = 00; 96 | float total_weight = 00; 97 | 98 | mat2 rotate = rotate2d(0.69420); 99 | vec2 screen_rot = vec2(1,0); 100 | 101 | //TODO: i think its possible to compute in screen space to avoid translating into worldspace every iteration 102 | 103 | // [[unroll]] 104 | for(int i=00; iY>Z for coherence 44 | layout(local_size_x = 4, local_size_y = 4, local_size_z = 4) in; 45 | 46 | void main(void){ 47 | vec3 relative_pos = gl_GlobalInvocationID.xyz / 2.0; 48 | ivec3 irelative_pos = ivec3(relative_pos); 49 | 50 | // position of this block in real world grid 51 | vec3 absolute_pos = (PushConstants.trans * vec4(relative_pos*16,1)).xyz; 52 | ivec3 iabsolute_pos = ivec3(absolute_pos/16); 53 | 54 | if(any(lessThan(iabsolute_pos, ivec3(0))) || any(greaterThan(iabsolute_pos, ivec3(7)))) { 55 | return; 56 | } 57 | // int a = atomicExchange(palette_counter, 0); 58 | // atomicExchange(palette_counter, a); 59 | // atomicExchange(palette_counter, 0); 60 | // atomicXor 61 | // atomicCompSwap(palette_counter, 0, ); 62 | // memoryBarrierAtomicCounter(); 63 | 64 | int id = atomicAdd(palette_counter, 1); 65 | imageAtomicExchange(blocks, irelative_pos, id); 66 | // id = (irelative_pos.x + (irelative_pos.y*8) + (irelative_pos.z*8*8) + 2); 67 | // imageAtomicExchange(blocks, irelative_pos, id); 68 | // memoryBarrierBuffer(); 69 | // id = atomicAdd(palette_counter, 1); 70 | // int id = (irelative_pos.x + (irelative_pos.y*8) + (irelative_pos.z*8*8) + 2); 71 | // imageStore(blocks, irelative_pos, ivec4(id)); 72 | return; 73 | //if this is extra "out-of-bounds" block then return 74 | //TODO: for testing, local size is 1 75 | 76 | //we borrow block and replace with BLOCK_IS_BORROWED 77 | int borrowed_block = imageAtomicExchange(blocks, iabsolute_pos, BLOCK_IS_BORROWED); 78 | int new_block; 79 | //block to return back to "blocks" 80 | if(borrowed_block == BLOCK_IS_BORROWED) { 81 | imageAtomicExchange(blocks, iabsolute_pos, borrowed_block); 82 | // return; 83 | } 84 | 85 | imageAtomicExchange(blocks, iabsolute_pos, borrowed_block); 86 | 87 | //if borrowed block already is BLOCK_IS_BORROWED then some core has taken care already, so return 88 | //TODO: if borrowed_block > STATIC return potentially much faster 89 | // if (borrowed_block == BLOCK_IS_BORROWED) {return;} 90 | 91 | //so technically i could copy "nothing" ("air") every frame but it is already there so why whould i care 92 | // if (borrowed_block == 0) { 93 | //"allocate" block in palette and write its num to borrowed_block 94 | //all non-static blocks are zeroed so allocating is just modifying counter :) 95 | // new_block = atomicAdd(palette_counter, 1); 96 | // new_block = atomicAdd(palette_counter, 1); 97 | //end 98 | // } 99 | // else if (borrowed_block <= STATIC_BLOCKS_COUNT) { // but > 0 100 | // //"allocate" block in palette and write its num to borrowed_block 101 | // new_block = atomicAdd(palette_counter, 1); 102 | 103 | // //also submit copying (increment counter) AND copy operation 104 | // int copy_id = atomicAdd(copy_z_counter, 16); //this starts at zero. We hope it will not overflow UB if does 105 | // copy_id /= 16; 106 | // // //we copy static block that is not air 107 | // // //to new allocated block in palette 108 | // copies[copy_id].src = borrowed_block; //TEMP TODO 109 | // copies[copy_id].dst = new_block; 110 | 111 | // //what it means is that after this stage 112 | // //we should copy all the blocks in copy queue to their relevant positions in palette 113 | // //end 114 | // } 115 | // else {/*nothing. Block has already been allocated on palette, so just leave it (return borrowed value back to image)*/} 116 | 117 | 118 | // copy_x=16;//TODO:move away 119 | // copy_y=16;//TODO:move away 120 | // copy_z_counter = 16; //this starts at zero. We hope it will not overflow UB if does 121 | // new_block = (irelative_pos.x + (irelative_pos.y*8) + (irelative_pos.z*8*8) + 2); 122 | //no matter what we have written to our block, we return it 123 | // imageAtomicExchange(blocks, iabsolute_pos, new_block); 124 | // imageStore(blocks, irelative_pos, ivec4(irelative_pos.x + (irelative_pos.y*8) + (irelative_pos.z*8*8) + 2)); 125 | 126 | return; 127 | } -------------------------------------------------------------------------------- /shaders/unused/copy.comp: -------------------------------------------------------------------------------- 1 | #version 450 core 2 | 3 | struct CopyOperation { 4 | int src; 5 | int dst; 6 | }; 7 | 8 | layout(set = 0, binding = 0, rg8ui) uniform uimage3D blocksPalette; 9 | //copy_x and copy_y are set to 16 by vk set buffer 10 | layout(set = 0, binding = 1) readonly buffer copyCounterBuffer { 11 | int copy_x; //16 12 | int copy_y; //16 13 | int copy_z_counter; //16 * copies_count 14 | // TODO: balance this 15 | // static "vector" ("dynamic" array of fixed capacity and atomic size_counter). 16 | // this stage will copy blocks_to_copy[i] from block palette to blocks_to_copy[STATIC_BLOCKS_COUNT+i] 17 | CopyOperation copies[1024]; 18 | }; 19 | 20 | //TODO: balance 21 | //16 by x 22 | //16 by y 23 | //16 * NUMBER OF BLOCKS TO COPY by z 24 | // const int STATIC_BLOCKS_COUNT = 12; 25 | 26 | const int BLOCK_PALETTE_SIZE_X = 16; 27 | ivec3 voxel_in_palette(ivec3 relative_voxel_pos, int block_id) { 28 | int block_x = block_id % BLOCK_PALETTE_SIZE_X; 29 | int block_y = block_id / BLOCK_PALETTE_SIZE_X; 30 | 31 | return relative_voxel_pos + ivec3(0+16*block_x, 0+16*block_y,0); 32 | } 33 | 34 | layout(local_size_x = 16, local_size_y = 16, local_size_z = 1) in; 35 | void main(void){ 36 | vec3 pos = gl_GlobalInvocationID.xyz; 37 | ivec3 ipos = ivec3(pos); 38 | 39 | //if out of bound 40 | //TODO: optimize (probably can be removed if dispatch_size is multiply of local_size) 41 | // if (any(greaterThanEqual(ipos, ivec3(16,16,copy_z_counter)))) return; 42 | 43 | int copy_op_index = ipos.z / 16; 44 | CopyOperation copy = copies[copy_op_index]; 45 | // CopyOperation copy = copies[0]; 46 | // copy.src = 1; 47 | // copy.dst = 2; 48 | 49 | 50 | ivec3 relative_ipos = ipos % 16; 51 | ivec3 src_voxel = voxel_in_palette(ipos, copy.src); 52 | ivec3 dst_voxel = voxel_in_palette(ipos, copy.dst); 53 | // ivec3 dst_voxel = ivec3(1,1,1+16*2); 54 | 55 | imageStore(blocksPalette, dst_voxel, 56 | // imageLoad(blocksPalette, src_voxel) 57 | ivec4(0) 58 | ); 59 | 60 | return; 61 | } -------------------------------------------------------------------------------- /shaders/unused/dfx.comp: -------------------------------------------------------------------------------- 1 | #version 450 core 2 | 3 | // struct CopyOperation { 4 | // int src; 5 | // int dst; 6 | // }; 7 | 8 | layout(set = 0, binding = 0, r8ui) uniform uimage3D blocksPalette; 9 | layout(set = 0, binding = 1, r8ui) uniform uimage3D distancePalette; 10 | //to update only up 11 | // layout(set = 0, binding = 1) readonly buffer copyCounterBuffer { 12 | // int copy_x; //16 13 | // int copy_y; //16 14 | // int copy_z_counter; //16 * copies_count 15 | // CopyOperation copies[1024]; 16 | // }; 17 | 18 | //TODO: balance 19 | //16 by x 20 | //16 by y 21 | //16 * NUMBER OF BLOCKS TO COPY by z 22 | // const int STATIC_BLOCKS_COUNT = 12; 23 | 24 | const int BLOCK_PALETTE_SIZE_X = 64; 25 | ivec3 voxel_in_palette(ivec3 relative_voxel_pos, int block_id) { 26 | int block_x = block_id % BLOCK_PALETTE_SIZE_X; 27 | int block_y = block_id / BLOCK_PALETTE_SIZE_X; 28 | 29 | return relative_voxel_pos + ivec3(0+16*block_x, 0+16*block_y,0); 30 | } 31 | 32 | layout(local_size_x = 16, local_size_y = 16, local_size_z = 1) in; 33 | 34 | //we may pack all passes 35 | const int df_distance = 4; 36 | 37 | bool get_voxel(ivec3 voxel_pos, int block_id){ 38 | //so to prevent weird things needs to assume border IS non-empty 39 | if(any(greaterThanEqual(voxel_pos, ivec3(16)))) return true; 40 | if(any( lessThan (voxel_pos, ivec3(0)))) return true; 41 | 42 | uint voxel = imageLoad(blocksPalette, voxel_in_palette(voxel_pos, block_id)).x; 43 | 44 | if(voxel == 0) return false; 45 | else return true; 46 | } 47 | int get_dist(ivec3 src_pos, ivec3 current_pos){ 48 | ivec3 diff = abs(current_pos - src_pos); 49 | 50 | diff = max(ivec3(0), diff-1); 51 | 52 | return diff.x*diff.x + diff.y*diff.y + diff.z*diff.z; 53 | } 54 | 55 | // int calc_dist(in ivec3 pos){ 56 | 57 | // } 58 | 59 | void main(void){ 60 | vec3 pos = gl_GlobalInvocationID.xyz; 61 | ivec3 ipos = ivec3(pos); 62 | 63 | int block_to_df = ipos.z / 16; 64 | 65 | ivec3 voxel_pos = ipos % 16; 66 | 67 | //for nearby pixel in this direction 68 | // float dist = 999.0; 69 | int size = 8; 70 | // int dist = size*size*3; 71 | int dist = size*size; 72 | 73 | ivec3 corner_1 = voxel_pos - size; 74 | ivec3 corner_2 = voxel_pos + size; 75 | 76 | // corner_1 = clamp(corner_1, ivec3(0), ivec3(15)); 77 | // corner_2 = clamp(corner_2, ivec3(0), ivec3(15)); 78 | 79 | // #pragma optionNV (unroll all) 80 | 81 | for(int x = corner_1.x; x <= corner_2.x; x++){ 82 | bool voxel = get_voxel(ivec3(x,voxel_pos.y,voxel_pos.z), block_to_df); 83 | // int dist = calc_dist(ivec3(x,y,z)); 84 | if (voxel) { 85 | dist = min(dist, get_dist(voxel_pos, ivec3(x,voxel_pos.y,voxel_pos.z))); 86 | } 87 | } 88 | 89 | // final_dist = 10; 90 | imageStore(distancePalette, voxel_in_palette(voxel_pos, block_to_df), uvec4(dist)); 91 | 92 | // for (int z = voxel_pos.z-df_distance; z < voxel_pos.z+df_distance; z++){ 93 | // ivec3 current_pos = voxel_pos + ivec3(0,0,z); 94 | 95 | // bool is_full = get_voxel(current_pos, block_to_df); 96 | 97 | // if(is_full) { 98 | // dist = min(dist, get_dist(voxel_pos, current_pos)); 99 | // //that is the first pass, so we just reme 100 | // } 101 | // } 102 | 103 | //so we make passes in order of ALL_Z => ALL_X => Y (maybe all) 104 | 105 | return; 106 | } -------------------------------------------------------------------------------- /shaders/unused/dfy.comp: -------------------------------------------------------------------------------- 1 | #version 450 core 2 | 3 | // struct CopyOperation { 4 | // int src; 5 | // int dst; 6 | // }; 7 | 8 | layout(set = 0, binding = 0, r8ui) uniform uimage3D blocksPalette; 9 | layout(set = 0, binding = 1, r8ui) uniform uimage3D distancePalette; 10 | //to update only up 11 | // layout(set = 0, binding = 1) readonly buffer copyCounterBuffer { 12 | // int copy_x; //16 13 | // int copy_y; //16 14 | // int copy_z_counter; //16 * copies_count 15 | // CopyOperation copies[1024]; 16 | // }; 17 | 18 | //TODO: balance 19 | //16 by x 20 | //16 by y 21 | //16 * NUMBER OF BLOCKS TO COPY by z 22 | // const int STATIC_BLOCKS_COUNT = 12; 23 | 24 | const int BLOCK_PALETTE_SIZE_X = 64; 25 | ivec3 voxel_in_palette(ivec3 relative_voxel_pos, int block_id) { 26 | int block_x = block_id % BLOCK_PALETTE_SIZE_X; 27 | int block_y = block_id / BLOCK_PALETTE_SIZE_X; 28 | 29 | return relative_voxel_pos + ivec3(0+16*block_x, 0+16*block_y,0); 30 | } 31 | 32 | layout(local_size_x = 16, local_size_y = 16, local_size_z = 1) in; 33 | 34 | //we may pack all passes 35 | const int df_distance = 4; 36 | 37 | bool get_voxel(ivec3 voxel_pos, int block_id){ 38 | //so to prevent weird things needs to assume border IS non-empty 39 | if(any(greaterThanEqual(voxel_pos, ivec3(16)))) return true; 40 | if(any( lessThan (voxel_pos, ivec3(0)))) return true; 41 | 42 | uint voxel = imageLoad(blocksPalette, voxel_in_palette(voxel_pos, block_id)).x; 43 | 44 | if(voxel == 0) return false; 45 | else return true; 46 | } 47 | int get_df_value(ivec3 voxel_pos, int block_id){ 48 | return int(imageLoad(distancePalette, voxel_in_palette(voxel_pos, block_id)).x); 49 | } 50 | int get_dist(ivec3 src_pos, ivec3 current_pos){ 51 | ivec3 diff = abs(current_pos - src_pos); 52 | 53 | diff = max(ivec3(0), diff-1); 54 | 55 | return diff.x*diff.x + diff.y*diff.y + diff.z*diff.z; 56 | } 57 | 58 | // int calc_sq_dist(in ivec3 pos){ 59 | 60 | // } 61 | 62 | void main(void){ 63 | vec3 pos = gl_GlobalInvocationID.xyz; 64 | ivec3 ipos = ivec3(pos); 65 | 66 | int block_to_df = ipos.z / 16; 67 | 68 | ivec3 voxel_pos = ipos % 16; 69 | 70 | int size = 8; 71 | // int dist = size*size*3; 72 | int dist = size*size; 73 | 74 | ivec3 corner_1 = voxel_pos - size; 75 | ivec3 corner_2 = voxel_pos + size; 76 | 77 | 78 | for(int y = corner_1.y; y <= corner_2.y; y++){ 79 | ivec3 current_voxel = ivec3(voxel_pos.x,y,voxel_pos.z); 80 | bool voxel = get_voxel(current_voxel, block_to_df); 81 | int current_df = get_df_value(current_voxel, block_to_df); 82 | int addition_dist = get_dist(voxel_pos, current_voxel); 83 | int total_df = addition_dist + current_df; 84 | 85 | // if (voxel) { 86 | dist = min(dist, total_df); 87 | // } 88 | } 89 | 90 | // final_dist = 10; 91 | imageStore(distancePalette, voxel_in_palette(voxel_pos, block_to_df), uvec4(dist)); 92 | 93 | return; 94 | } -------------------------------------------------------------------------------- /shaders/unused/dfz.comp: -------------------------------------------------------------------------------- 1 | #version 450 core 2 | 3 | // struct CopyOperation { 4 | // int src; 5 | // int dst; 6 | // }; 7 | 8 | layout(set = 0, binding = 0, r8ui) uniform uimage3D blocksPalette; 9 | layout(set = 0, binding = 1, r8ui) uniform uimage3D distancePalette; 10 | //to update only up 11 | // layout(set = 0, binding = 1) readonly buffer copyCounterBuffer { 12 | // int copy_x; //16 13 | // int copy_y; //16 14 | // int copy_z_counter; //16 * copies_count 15 | // CopyOperation copies[1024]; 16 | // }; 17 | 18 | //TODO: balance 19 | //16 by x 20 | //16 by y 21 | //16 * NUMBER OF BLOCKS TO COPY by z 22 | // const int STATIC_BLOCKS_COUNT = 12; 23 | 24 | const int BLOCK_PALETTE_SIZE_X = 64; 25 | ivec3 voxel_in_palette(ivec3 relative_voxel_pos, int block_id) { 26 | int block_x = block_id % BLOCK_PALETTE_SIZE_X; 27 | int block_y = block_id / BLOCK_PALETTE_SIZE_X; 28 | 29 | return relative_voxel_pos + ivec3(0+16*block_x, 0+16*block_y,0); 30 | } 31 | 32 | layout(local_size_x = 16, local_size_y = 16, local_size_z = 1) in; 33 | 34 | //we may pack all passes 35 | const int df_distance = 4; 36 | 37 | bool get_voxel(ivec3 voxel_pos, int block_id){ 38 | //so to prevent weird things needs to assume border IS non-empty 39 | if(any(greaterThanEqual(voxel_pos, ivec3(16)))) return true; 40 | if(any( lessThan (voxel_pos, ivec3(0)))) return true; 41 | 42 | uint voxel = imageLoad(blocksPalette, voxel_in_palette(voxel_pos, block_id)).x; 43 | 44 | if(voxel == 0) return false; 45 | else return true; 46 | } 47 | int get_dist(ivec3 src_pos, ivec3 current_pos){ 48 | ivec3 diff = abs(current_pos - src_pos); 49 | 50 | diff = max(ivec3(0), diff-1); 51 | 52 | return diff.x*diff.x + diff.y*diff.y + diff.z*diff.z; 53 | } 54 | int get_df_value(ivec3 voxel_pos, int block_id){ 55 | return int(imageLoad(distancePalette, voxel_in_palette(voxel_pos, block_id)).x); 56 | } 57 | // int calc_dist(in ivec3 pos){ 58 | 59 | // } 60 | 61 | void main(void){ 62 | vec3 pos = gl_GlobalInvocationID.xyz; 63 | ivec3 ipos = ivec3(pos); 64 | 65 | int block_to_df = ipos.z / 16; 66 | 67 | ivec3 voxel_pos = ipos % 16; 68 | 69 | int size = 8; 70 | // int dist = size*size*3; 71 | int dist = size*size; 72 | 73 | ivec3 corner_1 = voxel_pos - size; 74 | ivec3 corner_2 = voxel_pos + size; 75 | 76 | 77 | for(int z = corner_1.z; z <= corner_2.z; z++){ 78 | ivec3 current_voxel = ivec3(voxel_pos.x,voxel_pos.y,z); 79 | bool voxel = get_voxel(current_voxel, block_to_df); 80 | int current_df = get_df_value(current_voxel, block_to_df); 81 | int addition_dist = get_dist(voxel_pos, current_voxel); 82 | int total_df = addition_dist+current_df; 83 | 84 | // if (voxel) { 85 | dist = min(dist, total_df); 86 | // } 87 | } 88 | 89 | // dist = 100; 90 | imageStore(distancePalette, voxel_in_palette(voxel_pos, block_to_df), uvec4(dist)); 91 | 92 | return; 93 | } -------------------------------------------------------------------------------- /shaders/unused/grass_tlist.vert: -------------------------------------------------------------------------------- 1 | #version 450 core 2 | 3 | precision highp float; 4 | 5 | //triagnle list version 6 | 7 | layout(push_constant) uniform constants{ 8 | vec4 shift; 9 | int size; //total size*size blades 10 | int time; //seed 11 | } pco; 12 | 13 | layout(binding = 0, set = 0) uniform UniformBufferObject { 14 | mat4 trans_w2s; 15 | mat4 trans_w2s_old; 16 | } ubo; 17 | layout(set = 0, binding = 1, r16i) uniform iimage3D blocks; 18 | layout(set = 0, binding = 2, r8ui) uniform uimage3D blockPalette; 19 | 20 | layout(location = 0) flat out vec3 norm; 21 | layout(location = 1) flat out uint mat; 22 | 23 | const int BLOCK_PALETTE_SIZE_X = 64; 24 | const int STATIC_BLOCK_COUNT = 15; // 0 + 1..static block count so >=STATIC_BLOCK_COUNT 25 | const float PI = 3.1415926535; 26 | 27 | ivec3 voxel_in_palette(ivec3 relative_voxel_pos, int block_id) { 28 | int block_x = block_id % BLOCK_PALETTE_SIZE_X; 29 | int block_y = block_id / BLOCK_PALETTE_SIZE_X; 30 | 31 | return relative_voxel_pos + ivec3(16*block_x, 16*block_y, 0); 32 | } 33 | 34 | const int QUADS_IN_BLADE = 4; 35 | const int VTX_IN_BLADE = QUADS_IN_BLADE*6 + 3; 36 | 37 | // vec3 blade_vertices [VTX_IN_BLADE] = { 38 | // vec3(0,0,0+0), vec3(1,0,0+0), vec3(1,0,1+0), 39 | // vec3(0,0,0+0), vec3(0,0,1+0), vec3(1,0,1+0), 40 | // vec3(0,0,0+1), vec3(1,0,0+1), vec3(1,0,1+1), 41 | // vec3(0,0,0+1), vec3(0,0,1+1), vec3(1,0,1+1), 42 | // }; 43 | 44 | // vec3 blade_norms[VTX_IN_BLADE] = { 45 | // vec3(0,0,0), vec3(1,0,0), vec3(0,1,0), 46 | // vec3(0,0,0), vec3(0,0,1), vec3(0,1,0), 47 | // vec3(0,0,0), vec3(1,0,0), vec3(0,1,0), 48 | // vec3(0,0,0), vec3(0,0,1), vec3(0,1,0), 49 | // }; 50 | 51 | float rand(vec2 co){ 52 | return fract(sin(dot(co, vec2(12.9898, 78.233))) * 43758.5453); 53 | } 54 | 55 | vec3 bezier(vec3 p0, vec3 p1, vec3 p2, float t){ 56 | vec3 a = mix(p0, p1, t); 57 | vec3 b = mix(p1, p2, t); 58 | return mix(a, b, t); 59 | } 60 | 61 | vec3 getQuadVertex(int index) { 62 | float x = float(index % 2); 63 | float z = float(index / 2); 64 | 65 | return vec3(x, 0.0, z); 66 | } 67 | 68 | float get_blade_width(float height){ 69 | float max_height = float(QUADS_IN_BLADE); 70 | 71 | // return (max_height+height) / max_height; 72 | // return 1.0; 73 | return (max_height-height) / max_height; 74 | } 75 | 76 | vec3 get_blade_vert(int iindex, out vec3 normal){ 77 | // vec3 vertex = blade_vertices[index]; 78 | 79 | // float index = float(iindex); 80 | int quad_low_corner_height = iindex / 6; 81 | int internal_index = iindex % 6; 82 | int internal_quad_index; 83 | if(internal_index <= 2){ 84 | internal_quad_index = internal_index; 85 | } else { 86 | internal_quad_index = 6-internal_index; 87 | } 88 | vec3 vertex = getQuadVertex(internal_quad_index); 89 | 90 | if((iindex / 3) == (QUADS_IN_BLADE*2 + 1)){ 91 | if((iindex % 3) == 0) vertex = vec3(0,0,0); 92 | if((iindex % 3) == 1) vertex = vec3(1,0,0); 93 | if((iindex % 3) == 2) vertex = vec3(0.5,0,1); 94 | } 95 | 96 | vertex.z += float(quad_low_corner_height); 97 | 98 | // normal = blade_norms[index]; 99 | normal = vec3(0,1,0); 100 | 101 | //narrowing to the end 102 | float width = get_blade_width(vertex.z); 103 | float width_diff = 1.0 - width; 104 | vertex.x = width * vertex.x + width_diff / 2.0; 105 | 106 | //curving 107 | vec3 p0 = vec3(0,0,0); 108 | vec3 p1 = vec3(0,0,QUADS_IN_BLADE); 109 | vec3 p2 = vec3(0,0.7,QUADS_IN_BLADE); 110 | 111 | vec3 b = bezier(p0, p1, p2, vertex.z); 112 | vertex.y = b.y; 113 | 114 | vertex.x *= 3.7; 115 | 116 | vertex.z *= 4.0; // increase height 117 | 118 | return vertex; 119 | } 120 | 121 | //ONLY WORKS WITH BLADE NON TRANSFORMED VERTS 122 | void rotate_blade_vert(float rnd01, inout vec3 vertex, inout vec3 normal){ 123 | float angle = rnd01 * PI * 31.15; 124 | float cos_rot = cos(angle); 125 | float sin_rot = sin(angle); 126 | 127 | float vxNew = vertex.x * cos_rot + vertex.y * sin_rot; 128 | float vyNew = -vertex.x * sin_rot + vertex.y * cos_rot; 129 | 130 | vertex.x = vxNew; 131 | vertex.y = vyNew; 132 | // normal.x = 133 | float nxNew = normal.x * cos_rot + normal.y * sin_rot; 134 | float nyNew = -normal.x * sin_rot + normal.y * cos_rot; 135 | normal.x = nxNew; 136 | normal.y = nyNew; 137 | 138 | return; 139 | } 140 | 141 | // vec3 curve_blade_vert(vec3 vertex, float rnd01){ 142 | // float angle = rnd01 * PI * 31.15; 143 | // float cos_rot = cos(angle); 144 | // float sin_rot = sin(angle); 145 | 146 | // float xNew = vertex.x * cos_rot + vertex.y * sin_rot; 147 | // float Ynew = -vertex.x * sin_rot + vertex.y * cos_rot; 148 | 149 | // return vec3(xNew, Ynew, vertex.z); 150 | // } 151 | 152 | void main() { 153 | // int global_vertex_id = gl_VertexIndex; 154 | // int blade_id = global_vertex_id / VTX_IN_BLADE; 155 | // int blade_vertex_id = global_vertex_id % VTX_IN_BLADE; 156 | 157 | //MEASURED - no difference. As expected. 158 | int blade_id = gl_InstanceIndex; 159 | int blade_vertex_id = gl_VertexIndex; 160 | 161 | 162 | vec3 normal; 163 | vec3 rel2world = get_blade_vert(blade_vertex_id, normal); 164 | // rel2world = rel2world.zxy; 165 | 166 | int blade_x = blade_id % pco.size; 167 | int blade_y = blade_id / pco.size; 168 | vec2 rel2tile_shift = (vec2(blade_x, blade_y) / vec2(pco.size)) * 128.0; //for visibility 169 | // vec3 rel2tile = rel2world + vec3(rel2tile_shift,0); 170 | 171 | float rand_rot = rand(rel2tile_shift); 172 | rotate_blade_vert(rand_rot, rel2world, normal); 173 | 174 | vec3 rel2tile = rel2world + vec3(rel2tile_shift,0); 175 | 176 | vec4 world_pos = vec4(rel2tile,1) + pco.shift; 177 | // vec4 world_pos = vec4(rel2tile,1) + vec4(0,0,16,0); 178 | // world_pos.xy = -world_pos.xy; 179 | // world_pos. 180 | // world_pos.x = mod(world_pos.x, 16.0); 181 | // world_pos.y = mod(world_pos.y, 16.0); 182 | 183 | vec3 clip_coords = (ubo.trans_w2s*world_pos).xyz; 184 | clip_coords.z = -clip_coords.z; 185 | 186 | // // uv_shift = (clip_coords_old.xy - clip_coords.xy)/2.0; //0..1 187 | gl_Position = vec4(clip_coords, 1); 188 | 189 | norm = normal; 190 | // norm = normalize(vec3(1)); 191 | mat = uint(9); 192 | } -------------------------------------------------------------------------------- /shaders/unused/map_m2w.comp: -------------------------------------------------------------------------------- 1 | #version 450 core 2 | 3 | //version with model to world 4 | 5 | precision highp int; 6 | precision highp float; 7 | 8 | //TODO: move per-workgroup computations to different shader 9 | //TODO: AND test shared perfomance 10 | layout(push_constant) uniform constants{ 11 | mat4 trans; //to map modelVoxels to game voxel world 12 | ivec3 model_size; 13 | } PushConstants; 14 | 15 | layout(set = 0, binding = 0, r8ui) readonly uniform uimage3D modelVoxels; 16 | //blocks and blockPalettes are copies 17 | layout(set = 0, binding = 1, r32i) uniform iimage3D blocks; 18 | layout(set = 0, binding = 2, rg8ui) uniform uimage3D blockPalette; 19 | 20 | //should be 2^n 21 | const int BLOCK_PALETTE_SIZE_X = 32; 22 | 23 | ivec3 voxel_in_palette(ivec3 relative_voxel_pos, int block_id) { 24 | int block_x = block_id % BLOCK_PALETTE_SIZE_X; 25 | int block_y = block_id / BLOCK_PALETTE_SIZE_X; 26 | 27 | return relative_voxel_pos + ivec3(16*block_x, 16*block_y,0); 28 | } 29 | //TODO: balance, X>Y>Z for coherence 30 | layout(local_size_x = 4, local_size_y = 4, local_size_z = 4) in; 31 | 32 | //TODO: temp 33 | // const ivec3 model_size = ivec3(16); 34 | void main(void){ 35 | //sqrt(1.5) is for more precise mapping so holes do not appear 36 | vec3 relative_vox = vec3(gl_GlobalInvocationID.xyz) / vec3(2.0); 37 | // if( 38 | // (relative_vox.x < 0.8) 39 | // ||(relative_vox.y < 0.8) 40 | // ||(relative_vox.z < 0.8) 41 | // ||(relative_vox.x > (float(PushConstants.model_size.x)-0.8)) 42 | // ||(relative_vox.y > (float(PushConstants.model_size.y)-0.8)) 43 | // ||(relative_vox.z > (float(PushConstants.model_size.z)-0.8)) //TODO float() on cpu 44 | // ) return; 45 | vec3 size = imageSize(modelVoxels); 46 | // vec3 relative_vox = gl_GlobalInvocationID.xyz / 2; 47 | // vec3 relative_vox = gl_GlobalInvocationID.xyz; 48 | 49 | ivec3 irelative_vox = ivec3(relative_vox); 50 | // ivec3 irelative_block = ivec3(relative_vox)/16; 51 | 52 | 53 | //part where actual transformation happens 54 | // vec3 shrinked_relative_vox = (relative_vox - size/2.0)*((size-1.0)/size) + (size/2.0); 55 | vec3 shrinked_relative_vox = relative_vox; 56 | ivec3 itarget_vox = ivec3((PushConstants.trans * vec4(shrinked_relative_vox,1)).xyz); 57 | ivec3 itarget_block = itarget_vox / 16; 58 | 59 | // vec3 target_block = ((PushConstants.trans * vec4(relative_vox,1)).xyz); 60 | // ivec3 itarget_block = ivec3(target_block); 61 | // ivec3 itarget_block = ivec3(target_block); 62 | // vec3 target_vox = target_block*16.0; 63 | // vec3 target_vox = target_block*16; 64 | // ivec3 itarget_vox = ivec3(target_vox); 65 | 66 | // ivec3 model_size = imageSize(modelVoxels); 67 | //+1 for propper rounding, so 15 results into 1 68 | // ivec3 model_size_in_blocks = (model_size + ivec3(1)) / 16; 69 | //if this is extra "out-of-bounds" voxel 70 | if (any(greaterThanEqual(irelative_vox, imageSize(modelVoxels)))) return; 71 | if (any( lessThan (irelative_vox, ivec3(0)))) return; 72 | 73 | if (any(greaterThanEqual(itarget_block, imageSize(blocks)))) return; 74 | if (any( lessThan (itarget_block, ivec3(0)))) return; 75 | //let it have X->Y->Z order for blocks in palette 76 | //so we have target voxel position, initial one and target block position in the world 77 | //what we have to do is find ID of final block in palette (that this voxel will belong to) 78 | //then we load voxel from its initial position 79 | //then we load it to target block in palette 80 | //then we set target block in world to block in palette ID 81 | //TODO: test different approaches to cast and dot etc... 82 | //WHERE IS MY IDOT 83 | 84 | int target_block_in_palette = int(imageLoad(blocks, itarget_block).r); 85 | // int target_block_in_palette; 86 | // if(itarget_block.z == 0) { 87 | // target_block_in_palette = 1; 88 | // } else 89 | // target_block_in_palette = int(itarget_block.x + (itarget_block.y*8) + (itarget_block.z*8*8) + 2); 90 | // int target_block_in_palette = 2; 91 | 92 | //relative to palette block it will be written to 93 | // ivec3 target_palette_voxel = voxel_in_palette(itarget_vox.xyz % 16, target_block_in_palette); 94 | ivec3 target_palette_voxel = voxel_in_palette(itarget_vox.xyz % 16, target_block_in_palette); 95 | // int a=0; 96 | // if( irelative_vox.x == 0 || 97 | // irelative_vox.y == 0 || 98 | // irelative_vox.z == 0){ 99 | // a=1; 100 | // } 101 | uvec4 voxel = imageLoad(modelVoxels, irelative_vox); 102 | // uint voxel = imageLoad(modelVoxels, irelative_vox).r; 103 | if (voxel.r != 0){ 104 | // if(imageLoad(blockPalette, target_palette_voxel).r == 0){ 105 | imageStore(blockPalette, target_palette_voxel, voxel); // maybe atomic?.. Could use temporal shared block for this 106 | // imageAtomicCompSwap(blockPalette, target_palette_voxel, 0, voxel); 107 | // } 108 | } 109 | } -------------------------------------------------------------------------------- /shaders/unused/mipmap.comp: -------------------------------------------------------------------------------- 1 | #version 450 core 2 | 3 | /* 4 | generates block palette mipmaps every frame 5 | */ 6 | 7 | precision highp int; 8 | precision highp float; 9 | #extension GL_KHR_shader_subgroup_arithmetic : enable 10 | 11 | layout(set = 0, binding = 0, r8ui) uniform uimage3D blockPalette; 12 | 13 | /* 14 | levels of detail depending on depth () 15 | 0000000000000000 11111111 2222 33 4 NaN 16 | 16 8 4 2 1 NaN 17 | */ 18 | 19 | const int BLOCK_PALETTE_SIZE_X = 64; 20 | const int STATIC_BLOCK_COUNT = 15; // 0 + 1..static block count so >=STATIC_BLOCK_COUNT 21 | 22 | // ivec3 voxel_in_palette(ivec3 relative_voxel_pos, int block_id) { 23 | // int block_x = block_id % BLOCK_PALETTE_SIZE_X; 24 | // int block_y = block_id / BLOCK_PALETTE_SIZE_X; 25 | 26 | // return relative_voxel_pos + ivec3(16*block_x, 16*block_y,0); 27 | // } 28 | 29 | int get_lod(int depth){ 30 | // int lod; 31 | 32 | if(depth < 16) return 0; 33 | if(depth < 16+8) return 1; 34 | if(depth < 16+8+4) return 2; 35 | if(depth < 16+8+4+2) return 3; 36 | if(depth < 16+8+4+2+1) return 4; 37 | 38 | return 0; 39 | } 40 | 41 | int get_lod_shift(int lod){ 42 | // iтt lod; 43 | 44 | if(lod == 0) return 0; 45 | if(lod == 1) return 16; 46 | if(lod == 2) return 16+8; 47 | if(lod == 3) return 16+8+4; 48 | if(lod == 4) return 16+8+4+2; 49 | 50 | return 0; 51 | } 52 | 53 | 54 | layout(local_size_x = 8, local_size_y = 8) in; 55 | void main(void){ 56 | ivec3 vox = ivec3(gl_GlobalInvocationID.xyz); 57 | 58 | //should never be 0 cause its origin 59 | int lod = get_lod(vox.z); 60 | int prev_lod = lod-1; 61 | int lod_block_size = 16 / (1 << lod); 62 | int prev_block_size = 16 / (1 << prev_lod); 63 | 64 | //relative to its LOD origin 65 | ivec3 relative_vox = vox; 66 | relative_vox.z -= get_lod_shift(lod); 67 | 68 | //for now we do it as "nearest" 69 | ivec3 relative_src_low_vox = relative_vox*2; 70 | ivec3 src_low_vox = relative_src_low_vox; 71 | src_low_vox.z += get_lod_shift(prev_lod); 72 | // src_vox += lod_block_size / 2; 73 | // src_vox += clamp(lod_block_size / 2, 0, lod-1); 74 | ivec3 dst_vox = vox; 75 | 76 | // int vox_id = int(imageLoad(blockPalette, src_low_vox).x); 77 | int vox_sum = 0; 78 | int w_sum = 0; 79 | for(int dx=0; dx<=1; dx++){ 80 | for(int dy=0; dy<=1; dy++){ 81 | for(int dz=0; dz<=1; dz++){ 82 | ivec3 src_vox = src_low_vox + ivec3(dx,dy,dz); 83 | int vox_id = int(imageLoad(blockPalette, src_vox).x); 84 | 85 | if(vox_id != 0) { 86 | vox_sum = vox_id; 87 | w_sum = 1; 88 | // break; 89 | } 90 | }}} 91 | // ivec3 v00_sr = (src_low_vox + ivec3(0,0,0)); 92 | // ivec3 v10_sr = (src_low_vox + ivec3()); 93 | // ivec3 v01_sr = (src_low_vox + ivec3()); 94 | // ivec3 v11_sr = (src_low_vox + ivec3()); 95 | 96 | // varp vec3 p00_color = (imageLoad(old_frame, p00_pix).rgb); 97 | // varp vec3 p10_color = (imageLoad(old_frame, p10_pix).rgb); 98 | // varp vec3 p01_color = (imageLoad(old_frame, p01_pix).rgb); 99 | // varp vec3 p11_color = (imageLoad(old_frame, p11_pix).rgb); 100 | int final_vox = vox_sum; 101 | imageStore(blockPalette, dst_vox, uvec4(final_vox)); 102 | } -------------------------------------------------------------------------------- /shaders/unused/upscale.comp: -------------------------------------------------------------------------------- 1 | #version 450 2 | 3 | #define varp highp 4 | 5 | precision varp float; 6 | precision varp int; 7 | 8 | #extension GL_KHR_shader_subgroup_arithmetic : enable 9 | #extension GL_KHR_shader_subgroup_shuffle : enable 10 | 11 | layout(set = 0, binding = 0, rgba8) uniform image2D lowres_matNorm; 12 | layout(set = 0, binding = 1, rgba8) uniform image2D highres_matNorm; 13 | layout(set = 0, binding = 2, rgba16) uniform image2D denoised_frame; //in 14 | layout(set = 0, binding = 3, rgba16) uniform image2D upscaled_frame; //out 15 | 16 | ivec2 size; 17 | vec2 ratio; 18 | 19 | vec3 load_lowres_norm(ivec2 pixel){ 20 | vec3 norm = (imageLoad(lowres_matNorm, pixel).gba); 21 | return norm; 22 | } 23 | vec3 load_highres_norm(ivec2 pixel){ 24 | vec3 norm = (imageLoad(highres_matNorm, pixel).gba); 25 | return norm; 26 | } 27 | int load_lowres_mat(ivec2 pixel){ 28 | int mat = int(round(imageLoad(lowres_matNorm, pixel).x*127.0))+127; 29 | return mat; 30 | } 31 | int load_highres_mat(ivec2 pixel){ 32 | int mat = int(round(imageLoad(highres_matNorm, pixel).x*127.0))+127; 33 | return mat; 34 | } 35 | 36 | const float COLOR_ENCODE_VALUE = 5.0; 37 | vec3 decode_color(vec3 encoded_color){ 38 | return clamp(encoded_color,0,1)*vec3(COLOR_ENCODE_VALUE); 39 | } 40 | vec3 encode_color(vec3 color){ 41 | return clamp(color/vec3(COLOR_ENCODE_VALUE), 0,1); 42 | } 43 | 44 | int decode_mat(in float fmat) { 45 | return int(round(fmat*127.0))+127; 46 | } 47 | 48 | //TODO Specialization constants 49 | 50 | layout(local_size_x=8, local_size_y=8) in; 51 | void main(){ 52 | highp ivec2 highres_pix = ivec2(gl_GlobalInvocationID.xy); 53 | highp ivec2 lowres_size = imageSize( lowres_matNorm); 54 | highp ivec2 highres_size = imageSize(highres_matNorm); 55 | 56 | int highres_mat = decode_mat(imageLoad(highres_matNorm, highres_pix).x); 57 | vec3 highres_normal = ((imageLoad(highres_matNorm, highres_pix).yzw)); 58 | 59 | highp ivec2 ll_lowres_pix = ((highres_pix + ivec2(0,0))*lowres_size) / highres_size; 60 | highp ivec2 lh_lowres_pix = ((highres_pix + ivec2(0,1))*lowres_size) / highres_size; 61 | highp ivec2 hl_lowres_pix = ((highres_pix + ivec2(1,0))*lowres_size) / highres_size; 62 | highp ivec2 hh_lowres_pix = ((highres_pix + ivec2(1,1))*lowres_size) / highres_size; 63 | 64 | int ll_lowres_mat = decode_mat(imageLoad(lowres_matNorm, ll_lowres_pix).x); 65 | int lh_lowres_mat = decode_mat(imageLoad(lowres_matNorm, lh_lowres_pix).x); 66 | int hl_lowres_mat = decode_mat(imageLoad(lowres_matNorm, hl_lowres_pix).x); 67 | int hh_lowres_mat = decode_mat(imageLoad(lowres_matNorm, hh_lowres_pix).x); 68 | 69 | 70 | 71 | // float ll_dist = distance((vec2(pix)+.5), (vec2(ll_lowres_pix)+.5)); 72 | // float lh_dist = distance((vec2(pix)+.5), (vec2(lh_lowres_pix)+.5)); 73 | // float hl_dist = distance((vec2(pix)+.5), (vec2(hl_lowres_pix)+.5)); 74 | // float hh_dist = distance((vec2(pix)+.5), (vec2(hh_lowres_pix)+.5)); 75 | 76 | float ll_weight = float(ll_lowres_mat == highres_mat); // * exp(- abs((imageLoad(lowres_Gbuffer, ll_lowres_pix).w - highres_depth)/2000); //); 77 | float lh_weight = float(lh_lowres_mat == highres_mat); // * exp(- abs((imageLoad(lowres_Gbuffer, lh_lowres_pix).w - highres_depth)/2000); //); 78 | float hl_weight = float(hl_lowres_mat == highres_mat); // * exp(- abs((imageLoad(lowres_Gbuffer, hl_lowres_pix).w - highres_depth)/2000); //); 79 | float hh_weight = float(hh_lowres_mat == highres_mat); // * exp(- abs((imageLoad(lowres_Gbuffer, hh_lowres_pix).w - highres_depth)/2000); //); 80 | 81 | // ll_weight *= pow(clamp(dot(highres_normal, ((imageLoad(lowres_matNorm, ll_lowres_pix).yzw))), 0, 1), 1); 82 | // lh_weight *= pow(clamp(dot(highres_normal, ((imageLoad(lowres_matNorm, lh_lowres_pix).yzw))), 0, 1), 1); 83 | // hl_weight *= pow(clamp(dot(highres_normal, ((imageLoad(lowres_matNorm, hl_lowres_pix).yzw))), 0, 1), 1); 84 | // hh_weight *= pow(clamp(dot(highres_normal, ((imageLoad(lowres_matNorm, hh_lowres_pix).yzw))), 0, 1), 1); 85 | 86 | ll_weight *= float((dot(highres_normal, imageLoad(lowres_matNorm, ll_lowres_pix).yzw) > 0.5)); 87 | lh_weight *= float((dot(highres_normal, imageLoad(lowres_matNorm, lh_lowres_pix).yzw) > 0.5)); 88 | hl_weight *= float((dot(highres_normal, imageLoad(lowres_matNorm, hl_lowres_pix).yzw) > 0.5)); 89 | hh_weight *= float((dot(highres_normal, imageLoad(lowres_matNorm, hh_lowres_pix).yzw) > 0.5)); 90 | 91 | // ll_weight *= float((dot(highres_normal, imageLoad(lowres_matNorm, ll_lowres_pix).yzw) > 0.5)); 92 | // lh_weight *= float((dot(highres_normal, imageLoad(lowres_matNorm, lh_lowres_pix).yzw) > 0.5)); 93 | // hl_weight *= float((dot(highres_normal, imageLoad(lowres_matNorm, hl_lowres_pix).yzw) > 0.5)); 94 | // hh_weight *= float((dot(highres_normal, imageLoad(lowres_matNorm, hh_lowres_pix).yzw) > 0.5)); 95 | 96 | vec3 ll_lowres_color = decode_color(imageLoad(denoised_frame, ll_lowres_pix).rgb); 97 | vec3 lh_lowres_color = decode_color(imageLoad(denoised_frame, lh_lowres_pix).rgb); 98 | vec3 hl_lowres_color = decode_color(imageLoad(denoised_frame, hl_lowres_pix).rgb); 99 | vec3 hh_lowres_color = decode_color(imageLoad(denoised_frame, hh_lowres_pix).rgb); 100 | 101 | 102 | ll_weight += 0.0001; //to always have color 103 | 104 | vec3 color = ( 105 | ll_lowres_color * ll_weight+ 106 | lh_lowres_color * lh_weight+ 107 | hl_lowres_color * hl_weight+ 108 | hh_lowres_color * hh_weight 109 | ); 110 | float weight = ( 111 | ll_weight+ 112 | lh_weight+ 113 | hl_weight+ 114 | hh_weight 115 | ); 116 | 117 | 118 | vec3 final_color = color / weight; 119 | final_color = ll_lowres_color; 120 | 121 | imageStore(upscaled_frame, highres_pix, vec4(encode_color(final_color),1)); 122 | // imageStore(upscaled_frame, highres_pix, vec4(encode_color(vec3(highres_pix.x / 1000.0)),1)); 123 | } -------------------------------------------------------------------------------- /shaders/updateGrass.comp: -------------------------------------------------------------------------------- 1 | #version 450 core 2 | 3 | //basically updates grass "marks" texture.. Simple, unefficient and looks cool 4 | #extension GL_GOOGLE_include_directive : require 5 | #include "common\consts.glsl" 6 | 7 | precision highp int; 8 | precision highp float; 9 | 10 | layout(set = 0, binding = 0, rg16f) uniform restrict writeonly image2D state; 11 | layout(set = 0, binding = 1) uniform sampler2D perlin_noise; 12 | 13 | layout(push_constant) uniform restrict readonly constants{ 14 | vec2 windDirection, collisionPoint; 15 | float time; 16 | } pco; 17 | 18 | // const ivec3 world_size = ivec3(48,48,16); 19 | 20 | //honestly it will be like 4 wavegroups i dont think i need to balance it. 21 | layout(local_size_x = 8, local_size_y = 8, local_size_z = 1) in; 22 | void main(){ 23 | ivec2 pix = ivec2(gl_GlobalInvocationID.xy); 24 | vec2 pos = (vec2(pix) + 0.5) /16.0; 25 | 26 | vec2 new_direction = vec2(0); 27 | 28 | vec2 wind_direction = vec2(1, 1); 29 | 30 | vec2 uv_shift = wind_direction * pco.time / 300.0; 31 | vec2 noise_uv = (pos) + uv_shift; 32 | 33 | new_direction.x = textureGrad(perlin_noise, noise_uv, vec2(1.0/96.0,0), vec2(0)).x; 34 | new_direction.y = textureGrad(perlin_noise, noise_uv, vec2(0), vec2(0,1.0/96.0)).x; 35 | 36 | imageStore(state, pix, vec4(new_direction,0,0)); 37 | } -------------------------------------------------------------------------------- /shaders/updateWater.comp: -------------------------------------------------------------------------------- 1 | #version 450 core 2 | 3 | /* 4 | I wanted water that has no visible repetions 5 | this code basically generates several heightmaps used to later lift vertices 6 | multiple heightmaps is like inverse fourier but for different frequancy and amplitude tiles mixed together 7 | its dft cause gpus are fast. 8 | */ 9 | #extension GL_GOOGLE_include_directive : require 10 | #include "common\consts.glsl" 11 | 12 | precision highp int; 13 | precision highp float; 14 | 15 | //32x32x6 16 | layout(set = 0, binding = 0, rgba16f) uniform image2D heighmap; 17 | 18 | layout(push_constant) uniform readonly restrict constants{ 19 | vec2 windDirection; 20 | float time; 21 | } pco; 22 | 23 | // const ivec3 world_size = ivec3(48,48,16); 24 | const float PI = 3.1415926535; 25 | const int lods = 6; 26 | 27 | float calculate_height(vec2 local_pos, float time){ 28 | float height = 0.0; 29 | 30 | // vec2 direction = pco.windDirection; 31 | vec2 direction = vec2(1, 1); 32 | float ampl = 1.0; 33 | for (float freq = 1.0; freq<20.0; freq *= 1.15){ 34 | ampl *= 0.8; 35 | direction.x += 0.1; 36 | direction = normalize(direction); 37 | float T = time; 38 | 39 | height += ampl*sin(T*freq + dot(direction, local_pos)*2*PI); 40 | } 41 | return height; 42 | } 43 | layout(local_size_x = 8, local_size_y = 8, local_size_z = 1) in; 44 | void main(){ 45 | ivec2 pix = ivec2(gl_GlobalInvocationID.xy); 46 | vec2 pos = (vec2(pix) + 0.5) / (48.0*2.0); 47 | 48 | vec4 height; 49 | float time = pco.time / 300.0; 50 | height.x = calculate_height(pos/1.01, time/1.25); 51 | height.y = calculate_height(pos/1.02, time/1.5); 52 | height.z = calculate_height(pos/1.03, time/2.5); 53 | height.w = calculate_height(pos/1.04, time/3.5); 54 | 55 | imageStore(heighmap, pix, vec4(height)); 56 | } -------------------------------------------------------------------------------- /shaders/water.frag: -------------------------------------------------------------------------------- 1 | #version 450 2 | layout(early_fragment_tests) in; 3 | 4 | #define varp highp 5 | precision varp int; 6 | precision varp float; 7 | 8 | // layout(location = 0) lowp flat in uvec4 mat_norm; 9 | layout(location = 0) in vec3 orig; 10 | 11 | layout(location = 0) lowp out uvec4 outMatNorm; 12 | 13 | void main() { 14 | uvec3 normal_encoded; 15 | 16 | //normal to surface 17 | vec3 normal = normalize(cross( 18 | dFdxFine(orig), 19 | dFdyFine(orig) 20 | )); 21 | normal = vec3(0,0,1); 22 | normal_encoded = uvec3(((normal+1.0)/2.0)*255.0); 23 | 24 | outMatNorm = uvec4(30, normal_encoded); 25 | // outMatNorm = mat_norm; 26 | } -------------------------------------------------------------------------------- /shaders/water.vert: -------------------------------------------------------------------------------- 1 | #version 450 core 2 | 3 | precision highp float; 4 | 5 | /* 6 | it is basically cascade heightmap sampling 7 | rendered via instanced triangle strips that represent long thin rectangles 8 | can probably be optimized by few times 9 | */ 10 | #extension GL_GOOGLE_include_directive : require 11 | #include "common\ext.glsl" 12 | #include "common\ubo.glsl" 13 | #include "common\consts.glsl" 14 | 15 | layout(push_constant) uniform readonly restrict constants{ 16 | vec4 shift; 17 | int size; 18 | int time; //that thing that no one can define 19 | } pco; 20 | layout(set = 0, binding = 1) uniform sampler2D state; 21 | 22 | layout(location = 0) out vec3 orig; 23 | 24 | const int BLOCK_PALETTE_SIZE_X = 64; 25 | const int STATIC_BLOCK_COUNT = 15; // 0 + 1..static block count so >=STATIC_BLOCK_COUNT 26 | const float PI = 3.1415926535; 27 | // const ivec3 world_size = ivec3(48,48,16); 28 | const int lods = 6; 29 | 30 | ivec3 voxel_in_palette(ivec3 relative_voxel_pos, int block_id) { 31 | int block_x = block_id % BLOCK_PALETTE_SIZE_X; 32 | int block_y = block_id / BLOCK_PALETTE_SIZE_X; 33 | 34 | return relative_voxel_pos + ivec3(16*block_x, 16*block_y, 0); 35 | } 36 | 37 | const int BLADES_PER_INSTANCE = 1; 38 | const int VERTICES_PER_BLADE = 11; //3 for padding 39 | const int MAX_HEIGHT = 5; 40 | 41 | float rand(vec2 co){ 42 | return fract(sin(dot(co, vec2(12.9898, 78.233))) * 43758.5453); 43 | } 44 | 45 | // waves! sum of sin()'s 46 | float get_height(vec2 globalpos, float time){ 47 | vec2 direction = vec2(sin(pco.time / 100.0), cos(pco.time / 100.0)); 48 | direction = vec2(1,1); 49 | 50 | float total_height = 0; 51 | // yeah ~32 iteration, but this will be offloaded to compute shader+texture soon 52 | // for(float freq=3.0; freq < 100.0; freq *= 1.15){ 53 | // float ampl = 3.0 / freq; 54 | // direction = vec2(sin(pco.time/300.0 + freq), cos(pco.time/300.0 + freq)); 55 | // total_height += sin((time + dot(pos,direction)/10.0)*freq) * ampl; 56 | // } 57 | 58 | total_height += texture(state, globalpos/13.0).x * (13.0)/55.0; 59 | total_height += texture(state, globalpos/31.0).y * (31.0)/55.0; 60 | total_height += texture(state, globalpos/35.0).z * (35.0)/55.0; 61 | total_height += texture(state, globalpos/42.0).w * (42.0)/55.0; 62 | 63 | return total_height / 1.0; 64 | } 65 | #define make_offset(s, off)\ 66 | float s = textureOffset(state, globalpos/13.0, off).x * (13.0)/55.0;\ 67 | s += textureOffset(state, globalpos/31.0, off).y * (31.0)/55.0;\ 68 | s += textureOffset(state, globalpos/35.0, off).z * (35.0)/55.0;\ 69 | s += textureOffset(state, globalpos/42.0, off).w * (42.0)/55.0;\ 70 | 71 | vec3 get_normal(vec2 globalpos, float time){ 72 | vec2 direction = vec2(sin(pco.time / 100.0), cos(pco.time / 100.0)); 73 | direction = vec2(1,1); 74 | 75 | vec2 total_height_d = vec2(0); 76 | // yeah ~32 iteration, but this will be offloaded to compute shader+texture soon 77 | // for(float freq=3.0; freq < 100.0; freq *= 1.15){ 78 | // float ampl = 3.0 / freq; 79 | // direction = vec2(sin(pco.time/300.0 + freq), cos(pco.time/300.0 + freq)); 80 | // total_height_d += ((direction/10.0)*freq)* cos((time + dot(pos,direction)/10.0)*freq) * ampl; 81 | // } 82 | 83 | const vec2 size = vec2(2.0,0.0); 84 | const ivec3 off = ivec3(-1,0,1); 85 | 86 | vec4 wave = texture(state, globalpos/13.0); 87 | // float s11 = wave.x1 88 | make_offset(s01, off.xy); 89 | make_offset(s21, off.zy); 90 | make_offset(s10, off.yx); 91 | make_offset(s12, off.yz); 92 | 93 | vec3 va = normalize(vec3(size.xy,s21-s01)); 94 | vec3 vb = normalize(vec3(size.yx,s12-s10)); 95 | vec3 norm = vec3( cross(va,vb)); 96 | 97 | return norm; 98 | } 99 | 100 | void wave_water_vert(in vec2 pos, vec2 shift, out float height, out vec3 normal){ 101 | float t = pco.time / 300.0; 102 | height = get_height(pos+shift, t); 103 | 104 | //unused 105 | normal = get_normal(pos+shift, t); 106 | 107 | return; 108 | } 109 | 110 | vec3 get_water_vert(int vert_index, int instance_index, vec2 shift, out vec3 normal){ 111 | vec3 vertex; 112 | 113 | //instance is "tape" 114 | int instance_y_shift = instance_index; 115 | int y_shift = (vert_index % 2); 116 | int x_shift = ((vert_index+1) / 2); 117 | vertex.x = (float(x_shift ) / float(pco.size))*16.0;//*4; 118 | vertex.y = (float(y_shift + instance_y_shift) / float(pco.size))*16.0;//*4; 119 | 120 | wave_water_vert(vertex.xy, shift, vertex.z, normal); 121 | 122 | return vertex; 123 | } 124 | 125 | void main() { 126 | int vert_id = gl_VertexIndex; 127 | int instance_id = gl_InstanceIndex; 128 | 129 | vec3 normal; 130 | vec3 rel2world = get_water_vert(vert_id, instance_id, pco.shift.xy, normal); 131 | 132 | vec4 world_pos = vec4(rel2world,1) + pco.shift; 133 | vec3 clip_coords = (ubo.trans_w2s*world_pos).xyz; 134 | clip_coords.z = 1.0+clip_coords.z; 135 | 136 | gl_Position = vec4(clip_coords, 1); 137 | 138 | vec3 norm = normal; 139 | // norm = normalize(vec3(1)); 140 | uint mat = uint(30); 141 | float fmat = (float(mat)-127.0)/127.0; 142 | vec4 fmat_norm = vec4(fmat, norm); 143 | // mat_norm = uvec4(((fmat_norm+1.0)/2.0)*255.0); 144 | 145 | orig = world_pos.xyz; 146 | } -------------------------------------------------------------------------------- /src/containers/arena.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #ifndef __LUM_ARENA_HPP__ 4 | #define __LUM_ARENA_HPP__ 5 | 6 | // extracted from [Circulli Bellum](https://github.com/platonvin/circuli-Bellum) 7 | // simple arena allocator with faster allocation time 8 | 9 | #include 10 | // #include 11 | #include "ankerl_unordered_dense.hpp" 12 | 13 | // ~2x faster than new&delete + possibly more coherent memory 14 | // this really was the bottlneck 15 | 16 | //TODO: test uset against garbage vector 17 | 18 | template 19 | class Arena { 20 | public: 21 | std::vector storage; 22 | ankerl::unordered_dense::set free_indices; 23 | // std::vector free_indices; //TODO: test against me please please please 24 | 25 | Arena(int initial_size) : _size(initial_size){ 26 | storage.resize(initial_size); 27 | free_indices.reserve(initial_size); 28 | for (int i = 0; i < initial_size; i++) { 29 | free_indices.insert(i); 30 | } 31 | } 32 | 33 | ~Arena() { 34 | storage.clear(); 35 | free_indices.clear(); 36 | } 37 | 38 | void clear() { 39 | // freeIndices.clear(); 40 | for (int i = 0; i < storage.size(); i++) { 41 | free_indices.insert(i); 42 | } 43 | } 44 | 45 | // void resize(int new_size) { 46 | // int old_size = storage.size(); 47 | // pl(old_size) 48 | // pl(new_size) 49 | // storage.resize(new_size); 50 | // // free_indices.reserve(new_size); 51 | // for (int i = old_size; i < new_size; i++) { 52 | // free_indices.insert(i); 53 | // } 54 | // } 55 | 56 | Type* allocate() { 57 | if (free_indices.empty()) [[unlikely]] { 58 | assert(false && "arena full\n"); 59 | exit(1); 60 | } 61 | 62 | int index = *free_indices.begin(); 63 | free_indices.erase(index); 64 | //called a constructor, just in case 65 | storage[index] = Type(); 66 | 67 | // return new Type; 68 | return &storage[index]; 69 | } 70 | 71 | void free(Type* obj_ptr) { 72 | // delete obj_ptr; 73 | (*obj_ptr).~Type(); 74 | int idx = (obj_ptr - &storage[0]); 75 | assert(!free_indices.contains(idx)); 76 | free_indices.insert(idx); 77 | 78 | // std::cout << "freed " << (long long)obj_ptr << "\n"; 79 | } 80 | int total_size() {return _size;} 81 | private: 82 | int _size; 83 | }; 84 | #endif // __LUM_ARENA_HPP__ -------------------------------------------------------------------------------- /src/containers/fixed_map.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #ifndef __FIXED_MAP_HPP__ 4 | #define __FIXED_MAP_HPP__ 5 | 6 | // extracted from [Circulli Bellum](https://github.com/platonvin/circuli-Bellum) 7 | // simple arena allocator with faster allocation time 8 | 9 | #include 10 | // #include 11 | 12 | //TODO version with NONE elem as the one that is invalid 13 | //NONE element is the one that is invalid 14 | 15 | //For enums 16 | template 17 | concept IsEnum = std::is_enum_v; 18 | template 19 | concept IsNotEnum = not IsEnum; 20 | 21 | template 22 | constexpr typename std::underlying_type::type to_underlying(EnumType e) noexcept requires IsEnum { 23 | return static_cast::type>(e); 24 | } 25 | 26 | template 27 | constexpr NonEnumType to_underlying(NonEnumType e) noexcept requires IsNotEnum { 28 | return (e); 29 | } 30 | 31 | template 32 | class FixedMap { 33 | //basically exists to allow enum size 34 | static constexpr auto Size = to_underlying(EnumSize); 35 | 36 | public: 37 | FixedMap() { 38 | clear(); 39 | } 40 | 41 | Type& operator[](auto index) { 42 | assert(to_underlying(index) < Size); 43 | presence[to_underlying(index)] = true; 44 | return data[to_underlying(index)]; 45 | } 46 | 47 | const Type& operator[](auto index) const { 48 | assert(to_underlying(index) < Size); 49 | assert(presence[to_underlying(index)] && "accessing non-presented element"); 50 | return data[to_underlying(index)]; 51 | } 52 | 53 | void insert(unsigned int index, const Type& value) { 54 | assert(index < Size); 55 | data[index] = value; 56 | presence[index] = true; 57 | } 58 | 59 | bool contains(unsigned int index) const { 60 | assert(index < Size); 61 | return presence[index]; 62 | } 63 | 64 | Type* find(unsigned int index) { 65 | assert(index < Size); 66 | return presence[index] ? &data[index] : nullptr; 67 | } 68 | 69 | const Type* find(unsigned int index) const { 70 | assert(index < Size); 71 | return presence[index] ? &data[index] : nullptr; 72 | } 73 | 74 | void clear() { 75 | memset(data, 0, sizeof(data)); 76 | for(int i=0; i 2 | #include 3 | #include 4 | 5 | // class that basically helps hiding structs without extra pointer 6 | // i feel like i am extremely stupid and am missing something, but still 7 | // reason to do that is 80k lines saved (my pc cannot handle headers lol) for not including all the rendering stuff 8 | // is intended to be used with OffsetCalculator 9 | 10 | template 11 | class PromisedOpaqueStorage { 12 | private: 13 | alignas(std::max_align_t) std::array data; 14 | 15 | public: 16 | PromisedOpaqueStorage() { std::fill(data.begin(), data.data(), 0); } 17 | 18 | // Take some memory and cast it to some type 19 | template 20 | constexpr T& get() { 21 | static_assert(Offset + sizeof(T) <= Size, "Type exceeds storage bounds"); 22 | return *reinterpret_cast(data + Offset); 23 | } 24 | 25 | // Take some (const) memory and cast it to some (const) type 26 | template 27 | constexpr const T& get() const { 28 | static_assert(Offset + sizeof(T) <= Size, "Type exceeds storage bounds"); 29 | return *reinterpret_cast(data + Offset); 30 | } 31 | }; -------------------------------------------------------------------------------- /src/containers/promised_storage_accessor.hpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | // literally Offset Calculator, mostly for PromisedOpaqueStorage 6 | 7 | template 8 | class OffsetCalculator { 9 | private: 10 | std::tuple typeTuple; 11 | 12 | template 13 | constexpr std::size_t calculateOffset() { 14 | if constexpr (Index == 0) { 15 | return 0; 16 | } else { 17 | return calculateOffset() + sizeof(std::tuple_element_t>); 18 | } 19 | } 20 | 21 | public: 22 | // Get offset of type at index 23 | template 24 | constexpr std::size_t offset() { 25 | return calculateOffset(); 26 | } 27 | 28 | // Get the offset of specified type 29 | template 30 | constexpr std::size_t get() { 31 | constexpr std::size_t index = findIndex(); 32 | static_assert(index < sizeof...(Types), "Type not found in OffsetCalculator"); 33 | return OffsetCalculator::template offset(); 34 | } 35 | 36 | // Find index of type within the param pack 37 | template 38 | constexpr std::size_t findIndex() { 39 | if constexpr (Index < sizeof...(Types)) { 40 | using TypeAtIdx = std::tuple_element_t>; 41 | if constexpr (std::is_same_v) { 42 | return Index; 43 | } else { 44 | return findIndex(); 45 | } 46 | } else { 47 | return sizeof...(Types); // Not found, returns the size of the pack 48 | } 49 | } 50 | 51 | constexpr std::size_t size() { 52 | return (sizeof(Types) + ...); 53 | } 54 | }; 55 | 56 | 57 | // small wrapper to make accessing PromisedOpaqueStorage easier 58 | // constexpr instead of inline ? 59 | namespace TypedStorageHelper { 60 | template 61 | inline T& get(Storage& storage) { 62 | constexpr std::size_t offset = OffsetCalculator::template get(); 63 | return storage.template get(); 64 | } 65 | 66 | template 67 | inline const T& get(const Storage& storage) { 68 | constexpr std::size_t offset = OffsetCalculator::template get(); 69 | return storage.template get(); 70 | } 71 | } -------------------------------------------------------------------------------- /src/containers/safe_types/README.md: -------------------------------------------------------------------------------- 1 | basic types wrapped to check for overflow, zero division, NAN's, nullptr deref, etc. WHen such error found it is reported, as well as file + line + function where it occured. Note: currently, it only tracks location where constructor for SafeType called (so a += SafeInt(1) would print that exact line, but a += b would print lines where a and b declared) 2 | they all compile to same asm when checks are disabled 3 | suggest improvements via issues 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /src/containers/safe_types/main.cpp: -------------------------------------------------------------------------------- 1 | #include "safe_int.hpp" 2 | #include "safe_float.hpp" 3 | 4 | // all casts HAVE to be explicit. I am not sure this can be avoided 5 | int main() { 6 | si32 a(100); 7 | si32 b(200); 8 | 9 | si32 c = a + b; // Addition 10 | a += b; // Compound addition 11 | si64 d = si64(si16(a * b)); // Multiplication 12 | 13 | su32 ua = su32(0); 14 | ua --; // underflow 15 | 16 | si32 e = si32(std::numeric_limits::max()-1) + make_safe(1); // Overflow 17 | 18 | sf32 af = make_safe(3.0f); 19 | sf32 bf = make_safe(2.0f); 20 | sf32 cf = af + bf; // Safe addition 21 | sf32 df = af / make_safe(0.0f); // Safe division 22 | 23 | return 0; 24 | } -------------------------------------------------------------------------------- /src/containers/safe_types/safe_base.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | // comptime (constexpr) flag for enabling or disabling safety checks 8 | #ifndef SAFE_TYPE_DISABLE_CHECKS 9 | #define SAFE_TYPE_CHECKS_ENABLED 1 10 | #else 11 | #define SAFE_TYPE_CHECKS_ENABLED 0 12 | #endif 13 | 14 | 15 | // ANSI colors 16 | #define COLOR_RED "\033[31m" 17 | #define COLOR_CYAN "\033[36m" 18 | #define COLOR_RESET "\033[0m" 19 | 20 | 21 | // Functions to log errors with detailed info 22 | 23 | static void log_error(const std::string_view message) { 24 | std::cerr << COLOR_RED "Error: " << message << COLOR_RESET "\n"; 25 | } 26 | 27 | static void log_error(const std::string_view message, const std::source_location location) { 28 | std::cerr << COLOR_RED "Error: " << message << COLOR_RESET "\n" 29 | << " at " COLOR_CYAN << location.file_name() 30 | << ":" << location.line() 31 | << " in function `" << location.function_name() << "`" COLOR_RESET "\n"; 32 | } 33 | 34 | static void log_error(const std::string_view message, 35 | const std::source_location first_location, 36 | const std::source_location second_location) { 37 | std::cerr << COLOR_RED "Error: " << message << COLOR_RESET "\n" 38 | << " First location: " COLOR_CYAN << first_location.file_name() 39 | << ":" << first_location.line() 40 | << " in function `" << first_location.function_name() << "`" COLOR_RESET "\n" 41 | << " Second location: " COLOR_CYAN << second_location.file_name() 42 | << ":" << second_location.line() 43 | << " in function `" << second_location.function_name() << "`" COLOR_RESET "\n"; 44 | } 45 | 46 | #undef COLOR_RED 47 | #undef COLOR_CYAN 48 | #undef COLOR_RESET -------------------------------------------------------------------------------- /src/containers/safe_types/safe_float.hpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "safe_base.hpp" 6 | 7 | template 8 | requires std::is_floating_point_v 9 | class SafeFloat; 10 | 11 | template 12 | requires std::is_floating_point_v 13 | SafeFloat make_safe(FloatType value, std::source_location loc = std::source_location::current()) { 14 | return SafeFloat(value, loc); 15 | } 16 | 17 | template 18 | requires std::is_floating_point_v 19 | class SafeFloat { 20 | public: 21 | using value_type = FloatType; 22 | 23 | // Default constructor 24 | explicit SafeFloat(value_type v = 0.0, std::source_location loc = std::source_location::current()) 25 | : value(v), creation_location(loc) { 26 | if constexpr (SAFE_TYPE_CHECKS_ENABLED) { 27 | check_invalid_value(value, loc); 28 | } 29 | } 30 | 31 | // Conversion constructor from other floating point types 32 | template 33 | explicit SafeFloat(OtherFloat other, std::source_location loc = std::source_location::current()) 34 | : creation_location(loc) { 35 | if constexpr (SAFE_TYPE_CHECKS_ENABLED) { 36 | if (std::isnan(other) || std::isinf(other)) { 37 | log_error("Invalid floating point value (NaN or Infinity) during conversion: " + std::to_string(other), loc); 38 | } 39 | } 40 | value = static_cast(other); 41 | } 42 | 43 | // Copy constructor 44 | SafeFloat(const SafeFloat&) = default; 45 | 46 | // Arithmetic operators 47 | SafeFloat operator+(const SafeFloat& other) const { 48 | check_invalid_value(value, creation_location); 49 | check_invalid_value(other.value, other.creation_location); 50 | return SafeFloat(value + other.value, creation_location); 51 | } 52 | 53 | SafeFloat operator-(const SafeFloat& other) const { 54 | check_invalid_value(value, creation_location); 55 | check_invalid_value(other.value, other.creation_location); 56 | return SafeFloat(value - other.value, creation_location); 57 | } 58 | 59 | SafeFloat operator*(const SafeFloat& other) const { 60 | check_invalid_value(value, creation_location); 61 | check_invalid_value(other.value, other.creation_location); 62 | return SafeFloat(value * other.value, creation_location); 63 | } 64 | 65 | SafeFloat operator/(const SafeFloat& other) const { 66 | check_invalid_value(value, creation_location); 67 | check_invalid_value(other.value, other.creation_location); 68 | if (other.value == 0.0) { 69 | log_error("Division by zero detected", creation_location, other.creation_location); 70 | } 71 | return SafeFloat(value / other.value, creation_location); 72 | } 73 | 74 | // Compound assignment operators 75 | SafeFloat& operator+=(const SafeFloat& other) { 76 | *this = *this + other; 77 | return *this; 78 | } 79 | 80 | SafeFloat& operator-=(const SafeFloat& other) { 81 | *this = *this - other; 82 | return *this; 83 | } 84 | 85 | SafeFloat& operator*=(const SafeFloat& other) { 86 | *this = *this * other; 87 | return *this; 88 | } 89 | 90 | SafeFloat& operator/=(const SafeFloat& other) { 91 | *this = *this / other; 92 | return *this; 93 | } 94 | 95 | SafeFloat& operator++() { 96 | *this = *this + SafeFloat(1.0); 97 | return *this; 98 | } 99 | SafeFloat& operator++(int) { 100 | *this = *this + SafeFloat(1.0); 101 | return *this; 102 | } 103 | 104 | SafeFloat& operator--() { 105 | *this = *this - SafeFloat(1.0); 106 | return *this; 107 | } 108 | SafeFloat& operator--(int) { 109 | *this = *this - SafeFloat(1.0); 110 | return *this; 111 | } 112 | 113 | // Comparison operators 114 | bool operator==(const SafeFloat& other) const { return value == other.value; } 115 | bool operator!=(const SafeFloat& other) const { return value != other.value; } 116 | bool operator<(const SafeFloat& other) const { return value < other.value; } 117 | bool operator<=(const SafeFloat& other) const { return value <= other.value; } 118 | bool operator>(const SafeFloat& other) const { return value > other.value; } 119 | bool operator>=(const SafeFloat& other) const { return value >= other.value; } 120 | 121 | public: 122 | value_type value; 123 | std::source_location creation_location; 124 | 125 | // Overflow and safety checkers 126 | static void check_invalid_value(value_type v, const std::source_location& loc) { 127 | if constexpr (SAFE_TYPE_CHECKS_ENABLED) { 128 | if (std::isnan(v) || std::isinf(v)) { 129 | log_error("Invalid floating point value (NaN or Infinity) detected: " + std::to_string(v), loc); 130 | } 131 | } 132 | } 133 | }; 134 | 135 | // Typedefs for common floating point types 136 | using sf32 = SafeFloat; 137 | using sf64 = SafeFloat; 138 | using sf80 = SafeFloat; 139 | -------------------------------------------------------------------------------- /src/containers/shared_vector.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #ifndef __LUM_SHARED_VECTOR_HPP__ 3 | #define __LUM_SHARED_VECTOR_HPP__ 4 | 5 | // wrapper for "vectors" of components, each different type and all the same size 6 | // compared to std::vector's, has less overhead for such group usage 7 | // because there is only one allocation and only one size var. Used by ECS 8 | // not indendent to be used without a wrapper (like ecs) 9 | 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | template 17 | class shared_vector { 18 | private: 19 | // Tuple of pointers to memory blocks for each type 20 | std::tuple data_pointers = {}; 21 | 22 | // Number of elements in the container 23 | size_t size_ = 0; 24 | 25 | // Total allocated size (used for capacity management) 26 | size_t capacity_ = 0; 27 | 28 | // Raw allocated memory for all elements 29 | uint8_t* allocated_mem_ptr = nullptr; 30 | 31 | // Calculate the offset of each type in the contiguous memory block 32 | template 33 | constexpr size_t get_type_offset() const { 34 | if constexpr (Index == 0) { 35 | return 0; 36 | } else { 37 | return get_type_offset() + 38 | sizeof(std::tuple_element_t>); 39 | } 40 | } 41 | 42 | // Allocate memory for a given capacity 43 | uint8_t* allocate_memory(size_t capacity) const { 44 | return new uint8_t[capacity * (sizeof(Types) + ...)]; 45 | } 46 | 47 | // Copy data from old memory to new memory during resizing 48 | template 49 | constexpr void copy_data_recursive(uint8_t* new_memory, size_t old_size, size_t new_capacity) const { 50 | using T = std::tuple_element_t>; 51 | T* old_ptr = std::get(data_pointers); 52 | T* new_ptr = reinterpret_cast(new_memory + get_type_offset() * new_capacity); 53 | 54 | for (size_t i = 0; i < old_size; ++i) { 55 | new (new_ptr + i) T(old_ptr[i]); // Placement new to copy construct 56 | old_ptr[i].~T(); // Explicitly call destructor for old objects 57 | } 58 | 59 | if constexpr (Index > 0) { 60 | copy_data_recursive(new_memory, old_size, new_capacity); 61 | } 62 | } 63 | 64 | // Destroy all objects in memory 65 | template 66 | constexpr void destroy_objects_recursive() { 67 | using T = std::tuple_element_t>; 68 | T* ptr = std::get(data_pointers); 69 | 70 | for (size_t i = 0; i < size_; ++i) { 71 | ptr[i].~T(); 72 | } 73 | 74 | if constexpr (Index > 0) { 75 | destroy_objects_recursive(); 76 | } 77 | } 78 | 79 | public: 80 | // Constructor 81 | shared_vector() = default; 82 | 83 | // Destructor 84 | ~shared_vector() { 85 | if (allocated_mem_ptr) { 86 | // destroy_objects_recursive(); 87 | delete[] allocated_mem_ptr; 88 | } 89 | } 90 | 91 | // Resize the container 92 | // Cached here cause im 80 iq, sorry for that 93 | constexpr void resize(size_t new_size) { 94 | if (new_size > capacity_) { 95 | size_t new_capacity = std::max(new_size, capacity_ * 2); 96 | uint8_t* new_memory = allocate_memory(new_capacity); 97 | 98 | if (allocated_mem_ptr) { 99 | copy_data_recursive(new_memory, size_, new_capacity); 100 | // destroy_objects_recursive(); 101 | delete[] allocated_mem_ptr; 102 | } 103 | 104 | allocated_mem_ptr = new_memory; 105 | capacity_ = new_capacity; 106 | 107 | size_t offset = 0; 108 | ((std::get(data_pointers) = 109 | reinterpret_cast(allocated_mem_ptr + offset), 110 | offset += sizeof(Types) * new_capacity), 111 | ...); 112 | } 113 | 114 | size_ = new_size; 115 | } 116 | 117 | // Access specific vector by index 118 | template 119 | constexpr auto* get() { 120 | return std::get(data_pointers); 121 | } 122 | template 123 | constexpr const auto* get() const { 124 | return std::get(data_pointers); 125 | } 126 | 127 | // Access specific vector by type 128 | template 129 | constexpr auto* get() { 130 | return std::get(data_pointers); 131 | } 132 | template 133 | requires std::is_reference::value 134 | constexpr auto* get() { 135 | // return std::get(data_pointers); 136 | return std::get*>(data_pointers); 137 | } 138 | // template 139 | // const auto* get() { 140 | // return std::get(data_pointers); 141 | // } 142 | 143 | // Get the current size 144 | constexpr inline size_t size() const { 145 | return size_; 146 | } 147 | 148 | // Get the current capacity 149 | constexpr inline size_t capacity() const { 150 | return capacity_; 151 | } 152 | }; 153 | 154 | 155 | #endif //__LUM_SHARED_VECTOR_HPP__ -------------------------------------------------------------------------------- /src/containers/size_predictor.hpp: -------------------------------------------------------------------------------- 1 | #include 2 | template 3 | struct TotalSize; 4 | 5 | // Base case for recursion 6 | template <> 7 | struct TotalSize<> { 8 | static constexpr size_t value = 0; 9 | }; 10 | 11 | // Recursive case: add the size of the first type and recurse on the rest 12 | template 13 | struct TotalSize { 14 | static constexpr size_t value = sizeof(First) + TotalSize::value; 15 | }; 16 | 17 | // Helper variable template to make the syntax easier 18 | template 19 | constexpr size_t TotalSize_v = TotalSize::value; 20 | 21 | // Function to print the size (for easy testing) 22 | template 23 | constexpr size_t calculateTotalSize() { 24 | return TotalSize_v; 25 | } -------------------------------------------------------------------------------- /src/containers/thread_pool/thread_safe_queue.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | namespace dp { 10 | /** 11 | * @brief Simple concept for the Lockable and Basic Lockable types as defined by the C++ 12 | * standard. 13 | * @details See https://en.cppreference.com/w/cpp/named_req/Lockable and 14 | * https://en.cppreference.com/w/cpp/named_req/BasicLockable for details. 15 | */ 16 | template 17 | concept is_lockable = requires(Lock&& lock) { 18 | lock.lock(); 19 | lock.unlock(); 20 | { lock.try_lock() } -> std::convertible_to; 21 | }; 22 | 23 | template 24 | requires is_lockable 25 | class thread_safe_queue { 26 | public: 27 | using value_type = T; 28 | using size_type = typename std::deque::size_type; 29 | 30 | thread_safe_queue() = default; 31 | 32 | void push_back(T&& value) { 33 | std::scoped_lock lock(mutex_); 34 | data_.push_back(std::forward(value)); 35 | } 36 | 37 | void push_front(T&& value) { 38 | std::scoped_lock lock(mutex_); 39 | data_.push_front(std::forward(value)); 40 | } 41 | 42 | [[nodiscard]] bool empty() const { 43 | std::scoped_lock lock(mutex_); 44 | return data_.empty(); 45 | } 46 | 47 | size_type clear() { 48 | std::scoped_lock lock(mutex_); 49 | auto size = data_.size(); 50 | data_.clear(); 51 | 52 | return size; 53 | } 54 | 55 | [[nodiscard]] std::optional pop_front() { 56 | std::scoped_lock lock(mutex_); 57 | if (data_.empty()) return std::nullopt; 58 | 59 | auto front = std::move(data_.front()); 60 | data_.pop_front(); 61 | return front; 62 | } 63 | 64 | [[nodiscard]] std::optional pop_back() { 65 | std::scoped_lock lock(mutex_); 66 | if (data_.empty()) return std::nullopt; 67 | 68 | auto back = std::move(data_.back()); 69 | data_.pop_back(); 70 | return back; 71 | } 72 | 73 | [[nodiscard]] std::optional steal() { 74 | std::scoped_lock lock(mutex_); 75 | if (data_.empty()) return std::nullopt; 76 | 77 | auto back = std::move(data_.back()); 78 | data_.pop_back(); 79 | return back; 80 | } 81 | 82 | void rotate_to_front(const T& item) { 83 | std::scoped_lock lock(mutex_); 84 | auto iter = std::find(data_.begin(), data_.end(), item); 85 | 86 | if (iter != data_.end()) { 87 | std::ignore = data_.erase(iter); 88 | } 89 | 90 | data_.push_front(item); 91 | } 92 | 93 | [[nodiscard]] std::optional copy_front_and_rotate_to_back() { 94 | std::scoped_lock lock(mutex_); 95 | 96 | if (data_.empty()) return std::nullopt; 97 | 98 | auto front = data_.front(); 99 | data_.pop_front(); 100 | 101 | data_.push_back(front); 102 | 103 | return front; 104 | } 105 | 106 | private: 107 | std::deque data_{}; 108 | mutable Lock mutex_{}; 109 | }; 110 | } // namespace dp -------------------------------------------------------------------------------- /src/engine/README.md: -------------------------------------------------------------------------------- 1 | # Lum::ECSystem 2 | minimalistic C++ template class implementing an Entity Component System (ECS). Provides an interface for runtime managing (create/destroy) entities and comptime* onStart / onUpdate / onDestroy functions for entities 3 | 4 | *there is no runtime code that would decide which components should be loaded. In other words, compiler knows in compile-time which components to load for each function (to then pass them as arguments to functions). This also means that all the component and systems have to be known in compile-time (via template and constructor args) 5 | 6 | ## Features 7 | 8 | - **Compact Memory** : Update() the ECS to compact memory by removing unused entities. After that, all components are in plain arrays (vectors) 9 | 10 | - **Minimal Runtime Overhead** : No extra runtime code compared to writing everything manually. It also means that library mostly syntax sugar. But every library can be viewed as just syntax sugar. 11 | 12 | ## Usage 13 | 14 | 1. **Define Components** : Components are types (e.g., structs or classes) associated with entities. 15 | 16 | ```cpp 17 | struct ComponentA { int valueA; }; 18 | struct ComponentB { float valueB; }; 19 | ``` 20 | 21 | 2. **Define systems (functions)** : Systems are function that take component&'s. There have to be 2 special system - init and destroy. Order of component& arguments does not matter 22 | 23 | ```cpp 24 | void init (ComponentA& a, ComponentB& b) { 25 | a = {0xA}; 26 | b = {0xB}; 27 | std::cout << " init called\n"; 28 | } 29 | void cleanup (ComponentB& b, ComponentA& a) { 30 | std::cout << " cleanup called\n"; 31 | } 32 | 33 | void funA (ComponentA& a, ComponentB& b) { 34 | std::cout << " funA: ComponentA value: " << a.valueA << ", ComponentB value: " << b.valueB << ";\n"; 35 | } 36 | ``` 37 | 38 | 3. **Initialize ECSystem** : use factory to make ECSystem. Functions init and cleanup have to be presented 39 | 40 | ```cpp 41 | auto ecs = Lum::makeECSystem< 42 | ComponentA, 43 | ComponentB 44 | >(init, cleanup, funA /*any amount of other systems-that-are-functions*/); 45 | ``` 46 | 47 | 4. **Create Entities** : Instantiate entities, each automatically receiving a unique ID. 48 | 49 | ```cpp 50 | auto entityID = ecs.createEntity(); 51 | ``` 52 | 53 | 5. **Retrieve and modify individual entity components** : 54 | 55 | ```cpp 56 | ecs.getEntityComponent(entityID).valueA = 100; 57 | // accessing as not-reference is also an option. No actual difference - just style 58 | ecs.getEntityComponent(entityID).valueB = 100.0f; 59 | ``` 60 | 61 | 6. **Update the ECS** : Call `update()` to remove "dead" entities and invoke every system. Makes destroyed entities handles invalid 62 | 63 | ```cpp 64 | ecs.update(); 65 | ``` 66 | 67 | 7. **Destroy Entities** : Mark entity destroy, making its handle invalid to access *after* next Update() called 68 | 69 | ```cpp 70 | ecs.destroyEntity(entityID); 71 | ``` 72 | 73 | ## Configuration 74 | 75 | - `#define` `ENABLE_THROW` before including ecs.hpp to use throw'ing std::runtime_error. If not defined, assert()'s are used. 76 | 77 | - `#define` `DISABLE_CHECKS` before including ecs.hpp to remove runtime validation -------------------------------------------------------------------------------- /src/engine/engine.hpp: -------------------------------------------------------------------------------- 1 | #include "ecs.hpp" 2 | #include 3 | #include 4 | 5 | /* 6 | Experiments on what i like to use for games 7 | */ 8 | 9 | // base class for all systems 10 | class System { 11 | public: 12 | virtual ~System() = default; 13 | virtual void Start() = 0; 14 | virtual void Update() = 0; 15 | }; 16 | 17 | template 18 | class EntityCollection : public System, public Lum::ECSystem { 19 | public: 20 | // forward constructor args to ECSystem 21 | template 22 | EntityCollection(Args&&... args) : Lum::ECSystem(args...) {} 23 | 24 | void Start() override { 25 | std::cout << "EntityCollection Start\n"; 26 | } 27 | void Update() override { 28 | std::cout << "EntityCollection Update\n"; 29 | this->update(); // call update from ECSystem 30 | } 31 | }; 32 | 33 | class EntitySingle : public System { 34 | public: 35 | static EntitySingle& getInstance() { 36 | static EntitySingle instance; // guaranteed? to be destroyed and instantiated on first use 37 | return instance; 38 | } 39 | 40 | void Start() override { 41 | std::cout << "SingletonEntity Start\n"; 42 | } 43 | 44 | void Update() override { 45 | std::cout << "SingletonEntity Update\n"; 46 | } 47 | 48 | protected: 49 | EntitySingle() = default; // constructor is protected to prevent instantiation 50 | EntitySingle(const EntitySingle&) = delete; // delete copy constructor 51 | EntitySingle& operator=(const EntitySingle&) = delete; // delete copy assignment operator 52 | }; 53 | 54 | // Engine class to manage and update systems 55 | class Engine { 56 | public: 57 | void addSystem(std::shared_ptr system) { 58 | systems.push_back(system); // add system to the list 59 | } 60 | 61 | void start() { 62 | for (auto& system : systems) { 63 | system->Start(); 64 | } 65 | } 66 | 67 | void update() { 68 | for (auto& system : systems) { 69 | system->Update(); 70 | } 71 | } 72 | 73 | private: 74 | // there is rtti involved, but its not that costly for high-level systems and you would do almost the same in code anyways 75 | // no rtti inside collections (i hope so at least) 76 | std::vector> systems; // vector of system pointers 77 | }; -------------------------------------------------------------------------------- /src/engine/examples/ecs_example.cpp: -------------------------------------------------------------------------------- 1 | #define ENABLE_TESTING 2 | #define ENABLE_THROW // used to verify failure 3 | #include "../ecs.hpp" 4 | #include 5 | 6 | struct ComponentA { 7 | int valueA; 8 | }; 9 | 10 | struct ComponentB { 11 | float valueB; 12 | }; 13 | 14 | using ComponentC = float; 15 | using ComponentD = int; 16 | 17 | // Regular functions 18 | void funA(ComponentB& b, ComponentA& a) { 19 | std::cout << " funA: ComponentA value: " << a.valueA << ", ComponentB value: " << b.valueB << ";\n"; 20 | } 21 | 22 | void funB(ComponentA& a) { 23 | std::cout << " funB: ComponentA value: " << a.valueA << ";\n"; 24 | } 25 | 26 | void funC(ComponentC& c) { 27 | std::cout << " funC: ComponentC value: " << c << ";\n"; 28 | } 29 | 30 | // lambda 31 | auto funD = []() { 32 | std::cout << " funD does nothing;\n"; 33 | }; 34 | 35 | int main() { 36 | 37 | using ECManager = Lum::ECManager; 38 | 39 | ECManager manager; 40 | 41 | // Create an ECSystem with a mix of lambdas and functions 42 | Lum::ECSystem system( 43 | manager, // needed for auto type deduction. You can also not use it 44 | funA, 45 | funB, 46 | funC, 47 | funD // lambda function! 48 | ); 49 | 50 | std::cout << "updating empty ecs\n"; 51 | system.update(manager); 52 | std::cout << "updated empty ecs\n\n"; 53 | 54 | auto e1 = manager.createEntity(); 55 | manager.getEntityComponent(e1).valueA = 420; 56 | auto e2 = manager.createEntity(); 57 | manager.getEntityComponent(e2) = 888; 58 | auto e3 = manager.createEntity(); 59 | auto e4 = manager.createEntity(); 60 | manager.getEntityComponent(e3).valueA = 77; 61 | manager.getEntityComponent(e3) = 78; 62 | manager.getEntityComponent(e3) = 79; 63 | 64 | std::cout << "updating ecs with 4 ents\n"; 65 | system.update(manager); 66 | std::cout << "updated ecs with 4 ents\n\n"; 67 | 68 | manager.destroyEntity(e1); 69 | manager.destroyEntity(e3); 70 | 71 | std::cout << "e1/e3 entities destroyed\n"; 72 | 73 | std::cout << "updating ecs with 2 ents\n"; 74 | system.update(manager); 75 | std::cout << "updated ecs with 2 ents\n\n"; 76 | 77 | std::cout << "Successfully finished\n\n"; 78 | 79 | return 0; 80 | } 81 | -------------------------------------------------------------------------------- /src/engine/examples/engine_example.cpp: -------------------------------------------------------------------------------- 1 | #include "../engine.hpp" 2 | 3 | 4 | struct ComponentA { 5 | int valueA; 6 | }; 7 | 8 | struct ComponentB { 9 | float valueB; 10 | }; 11 | typedef float ComponentC; 12 | 13 | void init (ComponentA& a, ComponentB& b, ComponentC& c) { 14 | a = {0xA}; 15 | b = {0xB}; 16 | c = 0xC; 17 | std::cout << " init:" << ";\n"; 18 | } 19 | void cleanup (ComponentA& a, ComponentB& b, ComponentC& c) { 20 | std::cout << " cleanup: ComponentA value: " << a.valueA << ", ComponentB value: " << b.valueB << ", ComponentC value: " << c << ";\n"; 21 | } 22 | 23 | void funA (ComponentA& a, ComponentB& b) { 24 | std::cout << " funA: ComponentA value: " << a.valueA << ", ComponentB value: " << b.valueB << ";\n"; 25 | } 26 | 27 | void funB (ComponentA& a) { 28 | std::cout << " funB: ComponentA value: " << a.valueA << ";\n"; 29 | } 30 | 31 | void funC (ComponentC& c) { 32 | std::cout << " funC: ComponentC value: " << c << ";\n"; 33 | } 34 | 35 | void funD () { 36 | std::cout << " funD does nothing" << ";\n"; 37 | } 38 | 39 | 40 | int main() { 41 | Engine engine; 42 | 43 | // auto ecs = Lum::makeECSystem( 44 | // init, cleanup, funA, funB, funC, funD); 45 | auto secs = EntityCollection, decltype(&init), decltype(&cleanup), decltype(&funA), decltype(&funB), decltype(&funC), decltype(&funD) 50 | >(init, cleanup, funA, funB, funC, funD); 51 | 52 | auto secs_collection = std::make_shared(secs); 53 | 54 | // secs_collection->createEntity(); 55 | // secs_collection->createEntity(); 56 | 57 | // Adding systems to the engine 58 | engine.addSystem(secs_collection); 59 | engine.addSystem(std::shared_ptr(&EntitySingle::getInstance())); // Singleton system 60 | 61 | // Starting and updating all systems 62 | engine.start(); 63 | engine.update(); 64 | engine.update(); 65 | engine.update(); 66 | 67 | return 0; 68 | } -------------------------------------------------------------------------------- /src/engine/parallel/example_parallel.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #define GLM_ENABLE_EXPERIMENTAL 5 | #define GLM_GTX_component_wise 6 | #include "glm/gtx/hash.hpp" 7 | #include 8 | #include "parallel.hpp" 9 | #include 10 | 11 | void test_basic_dispatch() { 12 | ComputeDispatcher dispatcher; 13 | // std::unordered_set positions; 14 | int things [3][3][3]; 15 | 16 | ivec3 size{3, 3, 3}; // This doesn't divide evenly by the thread count in most setups 17 | dispatcher.dispatch(size, [&](ivec3 pos) { 18 | // positions.insert(pos); 19 | things[pos.x][pos.y][pos.z] = 42069; 20 | }); 21 | 22 | for(int x=0; x<3; x++){ 23 | for(int y=0; y<3; y++){ 24 | for(int z=0; z<3; z++){ 25 | assert(things[x][y][z] == 42069 && "Basic Dispatch Test Failed"); 26 | }}} 27 | std::cout << "Basic Dispatch Test Passed.\n"; 28 | } 29 | 30 | void test_large_dispatch() { 31 | ComputeDispatcher dispatcher; 32 | std::atomic counter{0}; 33 | 34 | ivec3 size{100, 100, 10}; 35 | dispatcher.dispatch(size, [&](ivec3) { 36 | counter++; 37 | }); 38 | 39 | // Check if all 100000 positions were processed 40 | assert(counter == size.x * size.y * size.z && "Large Dispatch Test Failed: Expected 100000 invocations."); 41 | std::cout << "Large Dispatch Test Passed.\n"; 42 | } 43 | 44 | void test_single_element() { 45 | ComputeDispatcher dispatcher; 46 | std::atomic counter{0}; 47 | 48 | dispatcher.dispatch({1, 1, 1}, [&](ivec3) { 49 | counter++; 50 | }); 51 | 52 | // Check if only one position was processed 53 | assert(counter == 1 && "Single Element Test Failed: Expected 1 invocation."); 54 | std::cout << "Single Element Test Passed.\n"; 55 | } 56 | 57 | void test_zero_dimension() { 58 | ComputeDispatcher dispatcher; 59 | std::atomic counter{0}; 60 | 61 | dispatcher.dispatch({0, 50, 50}, [&](ivec3) { 62 | counter++; 63 | }); 64 | 65 | // No positions should be processed 66 | assert(counter == 0 && "Zero Dimension Test Failed: Expected 0 invocations."); 67 | std::cout << "Zero Dimension Test Passed.\n"; 68 | } 69 | 70 | void test_uneven_division() { 71 | ComputeDispatcher dispatcher; 72 | ivec3 size = {707,506,510}; 73 | // ivec3 size = {5,5,5}; 74 | // ivec3 size = {5,5,5}; 75 | int* things = new int[glm::compMul(size)]; 76 | std::cout << glm::compMul(size); 77 | // sizeof(things); 78 | 79 | dispatcher.dispatch(size, [&](ivec3 pos) { 80 | // positions.insert(pos); 81 | things[pos.x + size.x*pos.y + size.x*size.y*pos.z] = 69420; 82 | }); 83 | std::cout << glm::compMul(size); 84 | 85 | // for(int x=0; x 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | #include 12 | using glm :: ivec3; 13 | 14 | #include "../../containers/thread_pool/thread_pool.hpp" 15 | 16 | // Concept to check if callable with ivec3 arg 17 | template 18 | concept ComputeFunc = std::invocable; 19 | 20 | class ComputeDispatcher { 21 | public: 22 | explicit ComputeDispatcher(size_t thread_count = 1) 23 | // explicit ComputeDispatcher(size_t thread_count = std::thread::hardware_concurrency()) 24 | : thread_count(thread_count) { 25 | initialize_pool(thread_count); // Initialize the pool on first usage 26 | } 27 | 28 | // Dispatch function with concept constraint on ComputeFunction 29 | void dispatch(const ivec3& size, ComputeFunc auto func) { 30 | // Determine primary dimension for better load balance 31 | int primary_dimension = (size.x >= size.y && size.x >= size.z) ? 0 : 32 | (size.y >= size.x && size.y >= size.z) ? 1 : 2; 33 | int pd = primary_dimension; 34 | 35 | int total_size = (primary_dimension == 0) ? size.x : 36 | (primary_dimension == 1) ? size.y : size.z; 37 | 38 | // Calculate chunk size to divide tasks among threads 39 | int chunk_size = (total_size + thread_count - 1) / thread_count; 40 | 41 | for (size_t i = 0; i < thread_count; ++i) { 42 | int start = i * chunk_size; 43 | int end = std::min(start + chunk_size, total_size); 44 | 45 | // Enqueue tasks for each chunk using the thread pool 46 | pool->enqueue_detach([=, &func]() { 47 | for (int x = (pd == 0 ? start : 0); x < (pd == 0 ? end : size.x); ++x) { 48 | for (int y = (pd == 1 ? start : 0); y < (pd == 1 ? end : size.y); ++y) { 49 | for (int z = (pd == 2 ? start : 0); z < (pd == 2 ? end : size.z); ++z) { 50 | func(ivec3{x, y, z}); 51 | }}} 52 | }); 53 | } 54 | 55 | pool->wait_for_tasks(); // Ensure all tasks are completed before returning 56 | } 57 | 58 | // Dispatch with collecting returned values (in undefined order) 59 | template 60 | void dispatch(const ivec3& size, ComputeFunc auto func, Container& result, ReduceFunc reduce) { 61 | // Determine primary dimension for better load balance 62 | int primary_dimension = (size.x >= size.y && size.x >= size.z) ? 0 : 63 | (size.y >= size.x && size.y >= size.z) ? 1 : 2; 64 | int pd = primary_dimension; 65 | 66 | int total_size = (primary_dimension == 0) ? size.x : 67 | (primary_dimension == 1) ? size.y : size.z; 68 | 69 | // Calculate chunk size to divide tasks among threads 70 | int chunk_size = (total_size + thread_count - 1) / thread_count; 71 | 72 | // Local results for each thread 73 | std::vector local_results(thread_count); 74 | 75 | for (size_t i = 0; i < thread_count; ++i) { 76 | int start = i * chunk_size; 77 | int end = std::min(start + chunk_size, total_size); 78 | 79 | // Enqueue tasks for each chunk using the thread pool 80 | pool->enqueue_detach([=, &func, &local_results]() { 81 | Container local_result; // local result for this thread 82 | for (int x = (pd == 0 ? start : 0); x < (pd == 0 ? end : size.x); ++x) { 83 | for (int y = (pd == 1 ? start : 0); y < (pd == 1 ? end : size.y); ++y) { 84 | for (int z = (pd == 2 ? start : 0); z < (pd == 2 ? end : size.z); ++z) { 85 | // Call the compute function and store the result 86 | auto value = func(ivec3{x, y, z}); 87 | local_result.push_back(value); // Assuming value can be pushed into the container 88 | } 89 | } 90 | } 91 | local_results[i] = local_result; // Store the local result 92 | }); 93 | } 94 | 95 | pool->wait_for_tasks(); // Ensure all tasks are completed before returning 96 | 97 | // Reduce all local results into the final result 98 | for (const auto& local_result : local_results) { 99 | // Assuming you can merge local_result into result 100 | for (const auto& val : local_result) { 101 | result.push_back(val); // Adjust as necessary to fit the container type 102 | } 103 | } 104 | 105 | // Perform final reduction (optional, if you need a single result) 106 | if (!result.empty()) { 107 | auto final_result = result[0]; 108 | for (size_t i = 1; i < result.size(); ++i) { 109 | final_result = reduce(final_result, result[i]); // Use your reduce function 110 | } 111 | result.clear(); // Clear the container for final result 112 | result.push_back(final_result); // Store the final reduced result 113 | } 114 | } 115 | 116 | static inline std::unique_ptr> pool; 117 | private: 118 | // Initialize the static thread pool only once 119 | static void initialize_pool(size_t thread_count) { 120 | static std::once_flag flag; 121 | std::call_once(flag, [=]() { 122 | pool = std::make_unique>(thread_count); 123 | }); 124 | } 125 | 126 | size_t thread_count; 127 | }; 128 | 129 | // int main() { 130 | // ComputeDispatcher dispatcher; 131 | 132 | // dispatcher.dispatch({3, 3, 3}, [](ivec3 pos) { 133 | // std::cout << "Running task at position (" << pos.x << ", " << pos.y << ", " << pos.z << ")\n"; 134 | // }); 135 | 136 | // return 0; 137 | // } 138 | -------------------------------------------------------------------------------- /src/engine/parallel/thread_pool.hpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | using std::atomic, std::mutex; 8 | 9 | class thread_pool { 10 | public: 11 | // Singleton instance for thread pool 12 | static thread_pool& instance() { 13 | static thread_pool instance = {}; // Guaranteed to be destroyed, instantiated on first use 14 | return instance; 15 | } 16 | 17 | // Returns the number of threads in the pool 18 | int thread_count() const { 19 | return num_threads; 20 | } 21 | 22 | // Dispatch a task to the pool, ensuring all threads execute it 23 | void dispatch(const std::function& func) { 24 | while(true) { 25 | if(threads_active == 0) break; 26 | }; 27 | 28 | // NOTE: MUST happen first cause otherwise threads will read invalid one 29 | current_task = func; 30 | 31 | // NOTE: MUST happen before setting flags (so second) 32 | // because otherwise threads are going to decrement already zero counter 33 | threads_active = num_threads; // Set the task counter to the number of threads 34 | 35 | // Set the flags to signal all threads to run the task 36 | // Note: each =true causes one thread to run task (only) one time 37 | for (int i = 0; i < num_threads; i++) { 38 | thread_flags[i] = true; 39 | } 40 | 41 | // Wait for all threads to finish the task 42 | while(true) { 43 | int read_counter = threads_active; 44 | // assert(read_counter >= 0); 45 | if(read_counter == 0) { 46 | break; 47 | } 48 | } 49 | } 50 | 51 | private: 52 | // Constructor: allocate threads and set flags to idle 53 | thread_pool() : threads_active(0), should_stop(false) { 54 | 55 | // num_threads = std::thread::hardware_concurrency() - 4; 56 | num_threads = std::thread::hardware_concurrency()-1; 57 | 58 | if (num_threads <= 0) { 59 | num_threads = 1; 60 | } 61 | 62 | // Allocate arrays for thread management 63 | threads = new std::thread[num_threads]; 64 | thread_flags = new atomic[num_threads]; 65 | 66 | // Initialize flags to false (idle) 67 | for (int i = 0; i < num_threads; i++) { 68 | thread_flags[i] = false; 69 | } 70 | 71 | // Launch worker threads 72 | for (int i = 0; i < num_threads; i++) { 73 | threads[i] = std::thread([this, i]() { 74 | this->worker_thread_fun(i); 75 | }); 76 | } 77 | } 78 | 79 | // Destructor: Clean up resources and join threads 80 | ~thread_pool() { 81 | { 82 | should_stop = true; // Set stop flag to true 83 | 84 | // wait for all threads to return 85 | while(threads_active > 0) {}; 86 | } 87 | 88 | // Join all threads to ensure clean termination 89 | for (int i = 0; i < num_threads; i++) { 90 | if (threads[i].joinable()) { 91 | threads[i].join(); // Ensure each thread completes 92 | } 93 | } 94 | 95 | // Clean up allocated memory 96 | delete[] threads; 97 | delete[] thread_flags; 98 | } 99 | 100 | // Worker thread function that executes tasks 101 | void worker_thread_fun(int thread_id) { 102 | // waiting for actual work to be submitted 103 | while (true) { 104 | 105 | std::function task = {}; 106 | 107 | if (should_stop) break; // If stop flag is set, exit the loop 108 | 109 | if (thread_flags[thread_id]) { 110 | task = current_task; // Get the current task 111 | thread_flags[thread_id] = false; // Reset the flag (task is being processed) 112 | } 113 | 114 | if (task) { 115 | task(thread_id); // Execute the task 116 | 117 | // After task completion, decrement the counter. When it reaches 0, it means that all threads are done with that task 118 | threads_active -= 1; 119 | } 120 | } 121 | } 122 | 123 | std::thread* threads; // Array of worker threads 124 | int num_threads = 0; // Number of threads in the pool 125 | std::function current_task; // The current task to be executed 126 | 127 | alignas(std::hardware_destructive_interference_size) 128 | // Array of flags indicating whether each thread has a task 129 | // each thread flag is like a switch - you set it to true from main thread, and in 130 | atomic* thread_flags; 131 | alignas(std::hardware_destructive_interference_size) 132 | atomic threads_active = 0; // Counter to track the number of remaining tasks 133 | alignas(std::hardware_destructive_interference_size) 134 | atomic should_stop = false; // Flag to signal stopping the pool 135 | }; -------------------------------------------------------------------------------- /src/engine/profiler.hpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | class Profiler { 10 | public: 11 | struct Sample { 12 | std::source_location location; 13 | double avg_time = 0.0; 14 | double last_elapsed = 0.0; 15 | }; 16 | 17 | Profiler(size_t max_samples) : max_samples(max_samples), current_index(0) { 18 | samples.resize(max_samples); 19 | } 20 | 21 | void startSession() { 22 | start_time = Clock::now(); 23 | current_index = 0; // reset index for the new session 24 | } 25 | 26 | void endSession() { } 27 | 28 | void placeTimestamp(const std::source_location& location = std::source_location::current()) { 29 | if (current_index >= max_samples) { 30 | std::cerr << "Profiler: Maximum number of samples reached. Ignoring timestamp.\n"; 31 | return; 32 | } 33 | 34 | auto now = Clock::now(); 35 | double elapsed = std::chrono::duration(now - start_time).count() / 1e6; // ns to ms 36 | 37 | auto& sample = samples[current_index++]; 38 | sample.location = location; 39 | sample.avg_time = mix(sample.avg_time, elapsed, 0.01); 40 | sample.last_elapsed = elapsed; 41 | } 42 | 43 | void printResults() { 44 | double last_timepoint = 0.0; 45 | 46 | for (size_t i = 0; i < current_index; ++i) { 47 | const auto& sample = samples[i]; 48 | double delta = sample.avg_time - last_timepoint; 49 | 50 | std::cout << std::fixed << std::setprecision(3) 51 | << sample.location.file_name() << ":" << sample.location.line() << " : " 52 | << sample.avg_time << " ms : " 53 | << delta << " ms (elapsed: " << sample.last_elapsed << " ms)\n"; 54 | 55 | last_timepoint = sample.avg_time; 56 | } 57 | std::cout << "all results printed;\n"; // clarify no segfault 58 | } 59 | 60 | private: 61 | using Clock = std::chrono::steady_clock; // Highest precision available? 62 | using TimePoint = std::chrono::time_point; 63 | 64 | TimePoint start_time; 65 | size_t max_samples; 66 | size_t current_index; 67 | std::vector samples; 68 | // aint no way i include glm for this 69 | static double mix(double a, double b, double factor) { 70 | return a * (1.0 - factor) + b * factor; 71 | } 72 | }; 73 | 74 | // for convenience 75 | // #define PLACE_TIMESTAMP profiler.placeTimestamp() -------------------------------------------------------------------------------- /src/input/README.md: -------------------------------------------------------------------------------- 1 | ### Lum::InputHandler 2 | 3 | This is a basic input handling system built on top of GLFW. It allows you to define custom actions, specify how these actions are triggered, and map them to (from) various input sources 4 | 5 | #### Features 6 | 7 | - **Indirect Input** : Create your own enum to define actions (e.g., `Jump`, `Shoot`), define callbacks for them and then (re)map keys to actions 8 | 9 | - **Action Types** : Specify how actions are triggered (for now these few. Any ideas?): 10 | - **One-shot** : Triggered once when the input is pressed. 11 | 12 | - **Continuous** : Triggered every frame while the input is held down. 13 | 14 | #### Usage 15 | 16 | 1. **Create an Enum for Actions** : Define your actions using an enumeration. 17 | 18 | ```cpp 19 | enum class GameAction { 20 | Jump, 21 | Shoot, 22 | LAST_ACTION // Always keep this as the last element 23 | }; 24 | ``` 25 | 26 | 2. **Instantiate the InputHandler** : Create an instance of `Lum::InputHandler` with your action enum 27 | 28 | ```cpp 29 | Lum::InputHandler inputHandler; 30 | ``` 31 | 32 | 3. **Setup** : Call the `setup()` initialize GLFW callbacks 33 | 34 | ```cpp 35 | inputHandler.setup(window); 36 | ``` 37 | 38 | 4. **Define Actions** : Bind keys, mouse buttons, and gamepad buttons to actions, set action types, and assign callbacks as needed. 39 | 40 | ```cpp 41 | inputHandler.rebindKey(GameAction::Jump, GLFW_KEY_SPACE); 42 | inputHandler.rebindMouseButton(GameAction::Shoot, GLFW_MOUSE_BUTTON_LEFT); 43 | inputHandler.setActionType(GameAction::Jump, ActionType::OneShot); 44 | inputHandler.setActionType(GameAction::Shoot, ActionType::OneShot); 45 | inputHandler.setActionCallback(GameAction::Jump, [](GameAction action) { 46 | // Handle jump action... 47 | }); 48 | inputHandler.setActionCallback(GameAction::Shoot, [](GameAction action) { 49 | // Handle shoot action... 50 | }); 51 | ``` 52 | 53 | 5. **Poll Updates** : In your main game loop, call `pollUpdates()` to process input 54 | 55 | ```cpp 56 | while (!glfwWindowShouldClose(window)) { 57 | inputHandler.pollUpdates(); 58 | // Other game logic... 59 | } 60 | ``` 61 | 62 | #### TODO 63 | 64 | - `InputState` enum to manage different input contexts (like Esc in StateGame opens menu, but Esc in StateMenu closes menu) -------------------------------------------------------------------------------- /src/input/input.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #ifndef __INPUT_HPP__ 3 | #define __INPUT_HPP__ 4 | 5 | #include "inputHandler.hpp" 6 | 7 | // template 8 | // constexpr auto to_underlying(E e) noexcept { 9 | // return static_cast>(e); 10 | // } 11 | 12 | // template 13 | // constexpr E from_underlying(T value) noexcept { 14 | // return static_cast(value); 15 | // } 16 | 17 | enum class GameAction : char { 18 | MOVE_CAMERA_FORWARD, 19 | MOVE_CAMERA_BACKWARD, 20 | MOVE_CAMERA_LEFT, 21 | MOVE_CAMERA_RIGHT, 22 | TURN_CAMERA_LEFT, 23 | TURN_CAMERA_RIGHT, 24 | INCREASE_ZOOM, 25 | DECREASE_ZOOM, 26 | SHOOT, 27 | MOVE_TANK_FORWARD, 28 | MOVE_TANK_BACKWARD, 29 | TURN_TANK_LEFT, 30 | TURN_TANK_RIGHT, 31 | 32 | //TODO: i guess?.. 33 | SET_BLOCK_1, 34 | SET_BLOCK_2, 35 | SET_BLOCK_3, 36 | SET_BLOCK_4, 37 | SET_BLOCK_5, 38 | SET_BLOCK_6, 39 | SET_BLOCK_7, 40 | SET_BLOCK_8, 41 | SET_BLOCK_9, 42 | SET_BLOCK_0, 43 | SET_BLOCK_F1, 44 | SET_BLOCK_F2, 45 | SET_BLOCK_F3, 46 | SET_BLOCK_F4, 47 | SET_BLOCK_F5, 48 | 49 | LAST_ACTION // MAKE SURE THIS IS THE LAST ITEM 50 | }; 51 | 52 | constexpr GameAction operator+(GameAction lhs, int rhs) { 53 | using underlying = std::underlying_type_t; 54 | return static_cast(static_cast(lhs) + rhs); 55 | } 56 | constexpr GameAction operator-(GameAction lhs, int rhs) { 57 | using underlying = std::underlying_type_t; 58 | return static_cast(static_cast(lhs) - rhs); 59 | } 60 | 61 | constexpr GameAction& operator++(GameAction& action) { 62 | using underlying = std::underlying_type_t; 63 | action = static_cast(static_cast(action) + 1); 64 | return action; 65 | } 66 | constexpr GameAction& operator--(GameAction& action) { 67 | using underlying = std::underlying_type_t; 68 | action = static_cast(static_cast(action) - 1); 69 | return action; 70 | } 71 | 72 | class Input : public Lum::InputHandler{ 73 | public: 74 | Input(){}; 75 | // handler; 76 | }; 77 | 78 | #endif // __INPUT_HPP__ -------------------------------------------------------------------------------- /src/macros/assert.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | // what you are seing down below is a comptime (codegen) system that allows enabling/disabling assert's for debug/release builds 8 | // it also provide ability to see where the function was called FROM when it crashed 9 | 10 | #define ENABLE_SOURCE_LOCATION_ASSERT 11 | 12 | #ifdef DISABLE_CHECKS 13 | #define ASSERT(...) \ 14 | do { \ 15 | } while (false) 16 | #else 17 | #ifdef ENABLE_THROW // default is just abort 18 | #ifdef ENABLE_SOURCE_LOCATION_ASSERT 19 | #define ASSERT(condition, msg) \ 20 | do { \ 21 | if (!(condition)) { \ 22 | throw std::runtime_error("Assertion failed: " #condition ", " msg ", at " << ___loc.file_name() << ":" << ___loc.line() << ":" << ___loc.column()); \ 23 | } \ 24 | } while (false) 25 | #else 26 | #define ASSERT(condition, msg) \ 27 | do { \ 28 | if (!(condition)) { \ 29 | throw std::runtime_error("Assertion failed: " #condition ", " msg ", in " __FILE__ ", line " + std::to_string(__LINE__)); \ 30 | } \ 31 | } while (false) 32 | #endif 33 | #else 34 | #ifdef ENABLE_SOURCE_LOCATION_ASSERT 35 | #define ASSERT(condition, msg) \ 36 | do { \ 37 | if (!(condition)) { \ 38 | std::cerr << "Assertion failed: " #condition ", " msg ", at " << ___loc.file_name() << ":" << ___loc.line() << ":" << ___loc.column() << std::endl; \ 39 | std::abort(); \ 40 | } \ 41 | } while (false) 42 | #else 43 | #define ASSERT(condition, msg) \ 44 | do { \ 45 | if (!(condition)) { \ 46 | std::cerr << "Assertion failed: " #condition ", " msg ", in " << __FILE__ << ", line " << __LINE__ << std::endl; \ 47 | std::abort(); \ 48 | } \ 49 | } while (false) 50 | #endif 51 | #endif 52 | #endif 53 | 54 | #ifdef ENABLE_SOURCE_LOCATION_ASSERT 55 | #define SLOC_DECLARE_DEFARG , std::source_location ___loc = std::source_location::current() 56 | #define SLOC_DECLARE_DEFARG_NOCOMMA std::source_location ___loc = std::source_location::current() 57 | #else 58 | #define SLOC_DECLARE_DEFARG /* no source_location arg */ 59 | #define SLOC_DECLARE_DEFARG_NOCOMMA /* no source_location arg */ 60 | #endif -------------------------------------------------------------------------------- /src/renderer/api/crenderer.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #ifndef LUM_C_BINDINGS_H 3 | #define LUM_C_BINDINGS_H 4 | 5 | /* 6 | C99 API for Lum 7 | it does get updates, but less often (on more stable versions) 8 | also less strictly typed 9 | */ 10 | 11 | #include 12 | #include 13 | #include 14 | 15 | #ifdef __cplusplus 16 | extern "C" { 17 | #endif 18 | 19 | // yypedefs for opaque pointers representing classes in Lum C++ API 20 | typedef void* LumRenderer; 21 | typedef void* LumMeshModel; 22 | typedef int16_t LumMeshBlock; // to avoid pointer 23 | typedef void* LumMeshFoliage; 24 | typedef void* LumMeshLiquid; 25 | typedef void* LumMeshVolumetric; 26 | typedef void* LumMeshUi; 27 | 28 | typedef struct { 29 | int world_size[3]; 30 | int static_block_palette_size; 31 | int maxParticleCount; 32 | 33 | int timestampCount; 34 | int fif; 35 | char vsync; 36 | char fullscreen; 37 | char debug; 38 | char profile; 39 | } LumSettings; 40 | 41 | #define LUM_DEFAULT_SETTINGS ((LumSettings){{48, 48, 16}, 15, 8128, 1024, 2, false, false, false, false}) 42 | // const LumSettings LUM_DEFAULT_SETTINGS = { 43 | // {48, 48, 16}, 44 | // 15, 45 | // 8128, 46 | // 1024, 47 | // 2, 48 | // false, 49 | // false, 50 | // false, 51 | // false, 52 | // }; 53 | 54 | 55 | typedef struct LumMeshTransform { 56 | float rot[4]; // rotation quaternion 57 | float shift[3]; // float because not necessarily snapped to grid 58 | } LumMeshTransform; // to avoid pointer 59 | #define LUM_DEFAULT_MESH_TRANSFORM ((LumMeshTransform){{1,0,0,0}, {0,0,0}}) 60 | // not a comptime expression 61 | // const LumMeshTransform LUM_DEFAULT_MESH_TRANSFORM = { 62 | // {1,0,0,0}, 63 | // {0,0,0}, 64 | // }; 65 | // Function prototypes 66 | LumRenderer lum_create_instance(const LumSettings* settings); 67 | void lum_destroy_instance(LumRenderer instance); 68 | 69 | void lum_demo_load_scene(LumRenderer instance, const char* path); 70 | 71 | void lum_init(LumRenderer instance, const LumSettings* settings); 72 | void lum_load_world(LumRenderer instance, const LumMeshBlock* world_data); 73 | void lum_load_world_file(LumRenderer instance, const char* file); 74 | 75 | void lum_set_world_block(LumRenderer instance, int x, int y, int z, LumMeshBlock block); 76 | LumMeshBlock lum_get_world_block(const LumRenderer instance, int x, int y, int z); 77 | 78 | void lum_load_block(LumRenderer instance, LumMeshBlock block, void* block_data); 79 | void lum_load_block_file(LumRenderer instance, LumMeshBlock block, const char* file); 80 | 81 | void lum_upload_block_palette_to_gpu(LumRenderer instance); 82 | void lum_upload_material_palette_to_gpu(LumRenderer instance); 83 | 84 | LumMeshModel lum_load_mesh_file(LumRenderer instance, const char* file, bool try_extract_palette); 85 | LumMeshModel lum_load_mesh_data(LumRenderer instance, void* mesh_data, int x_size, int y_size, int z_size); 86 | void lum_free_mesh(LumRenderer instance, LumMeshModel model); 87 | 88 | LumMeshFoliage lum_load_foliage(LumRenderer instance, const char* vertex_shader_file, int vertices_per_foliage, int density); 89 | LumMeshVolumetric lum_load_volumetric(LumRenderer instance, float max_density, float density_deviation, const uint8_t color[3]); 90 | LumMeshLiquid lum_load_liquid(LumRenderer instance, int main_mat, int foam_mat); 91 | 92 | void lum_start_frame(LumRenderer instance); 93 | void lum_draw_world(LumRenderer instance); 94 | void lum_draw_particles(LumRenderer instance); 95 | void lum_draw_model(LumRenderer instance, const LumMeshModel mesh, const LumMeshTransform* trans); 96 | void lum_draw_foliage_block(LumRenderer instance, const LumMeshFoliage foliage, const int pos[3]); 97 | void lum_draw_liquid_block(LumRenderer instance, const LumMeshLiquid liquid, const int pos[3]); 98 | void lum_draw_volumetric_block(LumRenderer instance, const LumMeshVolumetric volumetric, const int pos[3]); 99 | 100 | void lum_prepare_frame(LumRenderer instance); 101 | void lum_submit_frame(LumRenderer instance); 102 | 103 | void lum_cleanup(LumRenderer instance); 104 | // GLFWwindow* 105 | void* lum_get_glfw_ptr(const LumRenderer instance); 106 | bool lum_get_should_close(const LumRenderer instance); 107 | bool lum_set_should_close(const LumRenderer instance, bool should_close); 108 | void lum_wait_idle(LumRenderer instance); 109 | 110 | bool lum_is_profiling_enabled(const LumRenderer instance); 111 | int lum_get_current_timestamp_count(const LumRenderer instance); 112 | void lum_get_timestamp_name(const LumRenderer instance, int index, char* out_name, size_t name_length); 113 | double lum_get_timestamp(const LumRenderer instance, int index); 114 | double lum_get_timestamp_difference(const LumRenderer instance, int index_start, int index_end); 115 | 116 | #ifdef __cplusplus 117 | } 118 | #endif 119 | 120 | // define LUM_C_API_IMPLEMENTATION in one of your C++ files 121 | #ifdef LUM_C_API_IMPLEMENTATION 122 | #include "../src/renderer/api/crenderer.cpp" 123 | #undef LUM_C_API_IMPLEMENTATION 124 | #endif 125 | 126 | #endif // LUM_C_BINDINGS_H 127 | -------------------------------------------------------------------------------- /src/renderer/api/opaque_renderer_members.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "renderer/src/internal_render.hpp" 3 | #ifndef __LUM_TRANSPARENT_HPP__ 4 | #define __LUM_TRANSPARENT_HPP__ 5 | 6 | /* 7 | This is transparent version of opaque Lum::Renderer 8 | such division allows small easy to parse headers 9 | saves 80k lines 10 | */ 11 | 12 | 13 | #include "renderer.hpp" 14 | #include 15 | #include "containers/arena.hpp" 16 | 17 | namespace Lum { 18 | 19 | class OpaqueRendererMembers { 20 | public: 21 | OpaqueRendererMembers() noexcept : OpaqueRendererMembers(1024, 1024, 64, 64, 64) {} 22 | OpaqueRendererMembers( 23 | size_t block_palette_size, 24 | size_t mesh_storage_size, 25 | size_t foliage_storage_size, 26 | size_t volumetric_storage_size, 27 | size_t liquid_storage_size) noexcept 28 | : 29 | block_palette(block_palette_size), 30 | mesh_models_storage(mesh_storage_size), 31 | mesh_foliage_storage(foliage_storage_size), 32 | mesh_volumetric_storage(volumetric_storage_size), 33 | mesh_liquid_storage(liquid_storage_size) 34 | // {init();} // i do not like it being that impicit 35 | {} 36 | ~OpaqueRendererMembers() noexcept 37 | // {cleanup();} // i do not like it being that impicit 38 | {} 39 | public: 40 | 41 | LumInternal::LumInternalRenderer render; 42 | Lum::Settings settings; // duplicated? 43 | // Arenas are not only faster to allocate and more coherent memory, but also auto-free is possible 44 | Arena mesh_models_storage; 45 | Arena mesh_foliage_storage; 46 | Arena mesh_volumetric_storage; 47 | Arena mesh_liquid_storage; 48 | 49 | std::vector block_palette; 50 | std::array mat_palette; 51 | }; 52 | 53 | } // namespace Lum 54 | 55 | #endif // __LUM_TRANSPARENT_HPP__ 56 | -------------------------------------------------------------------------------- /src/renderer/src/ao_lut.cpp: -------------------------------------------------------------------------------- 1 | //compute lut for ambient occlusion shader 2 | #include "ao_lut.hpp" 3 | #include 4 | 5 | vec3 get_world_shift_from_clip_shift (vec2 clip_shift, vec3 horizline_scaled, vec3 vertiline_scaled) { 6 | vec3 shift = 7 | (horizline_scaled* clip_shift.x) + 8 | (vertiline_scaled* clip_shift.y); 9 | return shift; 10 | } 11 | 12 | double calculate_total_weight (int sample_count, double max_radius, 13 | dvec2 frame_size, dvec3 horizline_scaled, dvec3 vertiline_scaled) { 14 | double normalized_radius = 0.0; 15 | double norm_radius_step = 1.0 / double (sample_count); 16 | double total_weight = 0.0; 17 | for (int i = 0; i < sample_count; ++i) { 18 | normalized_radius += norm_radius_step; 19 | double weight = (1.0 - normalized_radius* normalized_radius); 20 | total_weight += weight; 21 | } 22 | return total_weight; 23 | } 24 | 25 | template 26 | std::array generateLUT (double max_radius, 27 | dvec2 frame_size, dvec3 horizline_scaled, dvec3 vertiline_scaled) { 28 | std::array lut = {}; 29 | double angle = 0.0; 30 | double normalized_radius = 0.0; 31 | double norm_radius_step = 1.0 / double (sample_count); 32 | double radius_step = max_radius / double (sample_count); 33 | double total_weight = calculate_total_weight (sample_count, max_radius, frame_size, horizline_scaled, vertiline_scaled); 34 | normalized_radius = 0.0; 35 | for (int i = 0; i < sample_count; ++i) { 36 | angle += (6.9 * M_PI) / double (sample_count); // even coverage 37 | normalized_radius += norm_radius_step; 38 | double radius = sqrt (normalized_radius) * max_radius; 39 | dvec2 screen_shift = dvec2 (radius) * (frame_size / dvec2 (frame_size.x)) * dvec2 (sin (angle), cos (angle)); 40 | dvec2 clip_shift = (screen_shift) * 2.0; 41 | dvec3 world_shift = horizline_scaled * clip_shift.x + vertiline_scaled * clip_shift.y; 42 | double weight = (1.0 - normalized_radius* normalized_radius); 43 | double weight_normalized = (weight / total_weight) * 0.7; 44 | lut[i] = { 45 | vec3 (world_shift), 46 | float (weight_normalized), 47 | vec2 (screen_shift), 48 | vec2 (0) //padding 49 | }; 50 | } 51 | return lut; 52 | } 53 | 54 | template class std::array; 55 | template std::array generateLUT<8>(double, dvec2, dvec3, dvec3); -------------------------------------------------------------------------------- /src/renderer/src/ao_lut.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | //compute lookup table for ambient occlusion shader 4 | #include 5 | #include 6 | #ifndef M_PI 7 | #define M_PI 3.14159265358979323846 8 | #endif 9 | 10 | using glm::u8, glm::u16, glm::u16, glm::u32; 11 | using glm::i8, glm::i16, glm::i32; 12 | using glm::f32, glm::f64; 13 | using glm::defaultp; 14 | using glm::quat; 15 | using glm::ivec2,glm::ivec3,glm::ivec4; 16 | using glm::i8vec2,glm::i8vec3,glm::i8vec4; 17 | using glm::i16vec2,glm::i16vec3,glm::i16vec4; 18 | using glm::uvec2,glm::uvec3,glm::uvec4; 19 | using glm::u8vec2,glm::u8vec3,glm::u8vec4; 20 | using glm::u16vec2,glm::u16vec3,glm::u16vec4; 21 | using glm::vec,glm::vec2,glm::vec3,glm::vec4; 22 | using glm::dvec2,glm::dvec3,glm::dvec4; 23 | using glm::mat, glm::mat2, glm::mat3, glm::mat4; 24 | using glm::dmat2, glm::dmat3, glm::dmat4; 25 | 26 | using std::vector; 27 | struct AoLut { 28 | vec3 world_shift; 29 | float weight_normalized; // ((1-r^2)/total_weight)*0.7 30 | vec2 screen_shift; 31 | vec2 padding; 32 | }; 33 | 34 | vec3 get_world_shift_from_clip_shift (vec2 clip_shift, vec3 horizline_scaled, vec3 vertiline_scaled); 35 | 36 | double calculate_total_weight (int sample_count, double max_radius, 37 | dvec2 frame_size, dvec3 horizline_scaled, dvec3 vertiline_scaled); 38 | 39 | // I still have no idea if this is valid C++23 40 | template 41 | std::array generateLUT (double max_radius, 42 | dvec2 frame_size, dvec3 horizline_scaled, dvec3 vertiline_scaled); -------------------------------------------------------------------------------- /src/renderer/src/internal_types.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #define GLM_ENABLE_EXPERIMENTAL 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | namespace LumInternal { 11 | 12 | //Everything is X -> Y -> Z order in arrays (vectors) 13 | const int BLOCK_SIZE = 16; // each block in common world is BLOCK_SIZE x BLOCK_SIZE x BLOCK_SIZE 14 | const int MATERIAL_PALETTE_SIZE = 256; //0 always empty 15 | const int RCACHE_RAYS_PER_PROBE = 32; 16 | const int BLOCK_PALETTE_SIZE_X = 64; 17 | const int BLOCK_PALETTE_SIZE_Y = 64; 18 | const int BLOCK_PALETTE_SIZE = (BLOCK_PALETTE_SIZE_X* BLOCK_PALETTE_SIZE_Y); 19 | 20 | const int MAX_FRAMES_IN_FLIGHT = 2; 21 | 22 | typedef glm::u8 MatID_t; 23 | typedef glm::i16 BlockID_t; 24 | typedef glm::u8 Voxel; 25 | const BlockID_t SPECIAL_BLOCK = 32767; 26 | typedef struct Material { 27 | glm::vec4 color; //color AND transparancy 28 | glm::f32 emmit; //emmits same color as reflect 29 | glm::f32 rough; 30 | } Material; 31 | 32 | 33 | typedef struct Particle { 34 | glm::vec3 pos; 35 | glm::vec3 vel; 36 | glm::f32 lifeTime; 37 | MatID_t matID; 38 | } Particle; 39 | 40 | // separate from Mesh so you can "instance" mesh 41 | typedef struct MeshTransform { 42 | glm::quat rot = glm::quat_identity(); // rotation quaternion 43 | glm::vec3 shift = glm::vec3(0); // float because not necessarily snapped to grid 44 | } MeshTransform; 45 | 46 | typedef Voxel BlockVoxels[BLOCK_SIZE][BLOCK_SIZE][BLOCK_SIZE]; 47 | 48 | typedef struct LumSpecificSettings { 49 | glm::ivec3 world_size = glm::ivec3(48, 48, 16); 50 | int static_block_palette_size = 15; 51 | int maxParticleCount = 8128; 52 | } LumSpecificSettings; 53 | 54 | typedef struct Camera { 55 | glm::dvec3 cameraPos = glm::vec3(60, 0, 194); 56 | glm::dvec3 cameraDir = glm::normalize(glm::vec3(0.61, 1.0, -0.8)); 57 | glm::dmat4 cameraTransform = glm::identity(); 58 | double pixelsInVoxel = 5.0; 59 | glm::dvec2 originViewSize = glm::dvec2(1920, 1080); 60 | glm::dvec2 viewSize = originViewSize / pixelsInVoxel; 61 | glm::dvec3 cameraRayDirPlane = glm::normalize (glm::dvec3 (cameraDir.x, cameraDir.y, 0)); 62 | glm::dvec3 horizline = glm::normalize (cross (cameraRayDirPlane, glm::dvec3 (0, 0, 1))); 63 | glm::dvec3 vertiline = glm::normalize (cross (cameraDir, horizline)); 64 | 65 | void updateCamera(); 66 | } Camera; 67 | 68 | } -------------------------------------------------------------------------------- /src/renderer/src/setup.cpp: -------------------------------------------------------------------------------- 1 | #include "internal_render.hpp" 2 | #include "defines/macros.hpp" 3 | 4 | ring LumInternal::LumInternalRenderer::create_RayTrace_VoxelImages (Voxel* voxels, ivec3 size) { 5 | VkDeviceSize bufferSize = (sizeof (Voxel)) * size.x * size.y * size.z; 6 | ring voxelImages = {}; 7 | lumal.createImageStorages (&voxelImages, 8 | VK_IMAGE_TYPE_3D, 9 | VK_FORMAT_R8_UINT, 10 | VK_IMAGE_USAGE_STORAGE_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT, 11 | VMA_MEMORY_USAGE_AUTO_PREFER_DEVICE, 12 | 0, // no VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT 13 | VK_IMAGE_ASPECT_COLOR_BIT, 14 | Lumal::extent3d(size)); 15 | for (int i = 0; i < MAX_FRAMES_IN_FLIGHT; i++) { 16 | lumal.transitionImageLayoutSingletime (&voxelImages[i], VK_IMAGE_LAYOUT_GENERAL); 17 | } 18 | VkBufferCreateInfo 19 | stagingBufferInfo = {VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO}; 20 | stagingBufferInfo.size = bufferSize; 21 | stagingBufferInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT; 22 | VmaAllocationCreateInfo 23 | stagingAllocInfo = {}; 24 | stagingAllocInfo.usage = VMA_MEMORY_USAGE_AUTO; 25 | stagingAllocInfo.flags = VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT; 26 | stagingAllocInfo.requiredFlags = VK_MEMORY_PROPERTY_HOST_COHERENT_BIT; 27 | VkBuffer stagingBuffer = {}; 28 | VmaAllocation stagingAllocation = {}; 29 | vmaCreateBuffer (lumal.VMAllocator, &stagingBufferInfo, &stagingAllocInfo, &stagingBuffer, &stagingAllocation, NULL); 30 | void* data; 31 | vmaMapMemory (lumal.VMAllocator, stagingAllocation, &data); 32 | memcpy (data, voxels, bufferSize); 33 | vmaUnmapMemory (lumal.VMAllocator, stagingAllocation); 34 | for (i32 i = 0; i < MAX_FRAMES_IN_FLIGHT; i++) { 35 | lumal.copyBufferSingleTime (stagingBuffer, &voxelImages[i], size); 36 | } 37 | vmaDestroyBuffer (lumal.VMAllocator, stagingBuffer, stagingAllocation); 38 | return voxelImages; 39 | } 40 | 41 | void LumInternal::LumInternalRenderer::createSamplers() { 42 | VkSamplerCreateInfo 43 | samplerInfo = {}; 44 | samplerInfo.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO; 45 | samplerInfo.magFilter = VK_FILTER_NEAREST; 46 | samplerInfo.minFilter = VK_FILTER_NEAREST; 47 | samplerInfo.addressModeU = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE; 48 | samplerInfo.addressModeV = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE; 49 | samplerInfo.addressModeW = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE; 50 | samplerInfo.anisotropyEnable = VK_FALSE; 51 | samplerInfo.borderColor = VK_BORDER_COLOR_FLOAT_OPAQUE_BLACK; 52 | samplerInfo.unnormalizedCoordinates = VK_FALSE; 53 | samplerInfo.compareEnable = VK_FALSE; 54 | samplerInfo.compareOp = VK_COMPARE_OP_LESS_OR_EQUAL; 55 | samplerInfo.mipmapMode = VK_SAMPLER_MIPMAP_MODE_NEAREST; 56 | samplerInfo.mipLodBias = 0.0f; 57 | samplerInfo.minLod = 0.0f; 58 | samplerInfo.maxLod = 0.0f; 59 | VK_CHECK (vkCreateSampler (lumal.device, &samplerInfo, NULL, &nearestSampler)); 60 | samplerInfo.magFilter = VK_FILTER_LINEAR; 61 | samplerInfo.minFilter = VK_FILTER_LINEAR; 62 | VK_CHECK (vkCreateSampler (lumal.device, &samplerInfo, NULL, &linearSampler)); 63 | samplerInfo.addressModeU = VK_SAMPLER_ADDRESS_MODE_MIRRORED_REPEAT; 64 | samplerInfo.addressModeV = VK_SAMPLER_ADDRESS_MODE_MIRRORED_REPEAT; 65 | samplerInfo.addressModeW = VK_SAMPLER_ADDRESS_MODE_MIRRORED_REPEAT; 66 | samplerInfo.magFilter = VK_FILTER_LINEAR; 67 | samplerInfo.minFilter = VK_FILTER_LINEAR; 68 | VK_CHECK (vkCreateSampler (lumal.device, &samplerInfo, NULL, &linearSampler_tiled)); 69 | samplerInfo.magFilter = VK_FILTER_NEAREST; 70 | samplerInfo.minFilter = VK_FILTER_NEAREST; 71 | samplerInfo.addressModeU = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE; 72 | samplerInfo.addressModeV = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE; 73 | samplerInfo.addressModeW = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE; 74 | VK_CHECK (vkCreateSampler (lumal.device, &samplerInfo, NULL, &overlaySampler)); 75 | samplerInfo.addressModeU = VK_SAMPLER_ADDRESS_MODE_REPEAT; 76 | samplerInfo.addressModeV = VK_SAMPLER_ADDRESS_MODE_REPEAT; 77 | samplerInfo.addressModeW = VK_SAMPLER_ADDRESS_MODE_REPEAT; 78 | samplerInfo.unnormalizedCoordinates = VK_FALSE; 79 | samplerInfo.magFilter = VK_FILTER_LINEAR; 80 | samplerInfo.minFilter = VK_FILTER_LINEAR; 81 | VK_CHECK (vkCreateSampler (lumal.device, &samplerInfo, NULL, &unnormLinear)); 82 | samplerInfo.addressModeU = VK_SAMPLER_ADDRESS_MODE_REPEAT; 83 | samplerInfo.addressModeV = VK_SAMPLER_ADDRESS_MODE_REPEAT; 84 | samplerInfo.addressModeW = VK_SAMPLER_ADDRESS_MODE_REPEAT; 85 | samplerInfo.unnormalizedCoordinates = VK_FALSE; 86 | samplerInfo.magFilter = VK_FILTER_NEAREST; 87 | samplerInfo.minFilter = VK_FILTER_NEAREST; 88 | VK_CHECK (vkCreateSampler (lumal.device, &samplerInfo, NULL, &unnormNearest)); 89 | samplerInfo.addressModeU = VK_SAMPLER_ADDRESS_MODE_MIRRORED_REPEAT; 90 | samplerInfo.addressModeV = VK_SAMPLER_ADDRESS_MODE_MIRRORED_REPEAT; 91 | samplerInfo.addressModeW = VK_SAMPLER_ADDRESS_MODE_MIRRORED_REPEAT; 92 | samplerInfo.unnormalizedCoordinates = VK_FALSE; 93 | samplerInfo.magFilter = VK_FILTER_NEAREST; 94 | samplerInfo.minFilter = VK_FILTER_NEAREST; 95 | samplerInfo.compareEnable = VK_TRUE; 96 | samplerInfo.borderColor = VK_BORDER_COLOR_FLOAT_OPAQUE_WHITE; 97 | samplerInfo.compareOp = VK_COMPARE_OP_LESS; 98 | VK_CHECK (vkCreateSampler (lumal.device, &samplerInfo, NULL, &shadowSampler)); 99 | } -------------------------------------------------------------------------------- /src/renderer/src/ui.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "internal_render.hpp" 4 | 5 | #include 6 | #include "defines/macros.hpp" 7 | 8 | class SystemInterface_GLFW : public Rml::SystemInterface { 9 | public: 10 | SystemInterface_GLFW(); 11 | ~SystemInterface_GLFW(); 12 | 13 | // Optionally, provide or change the window to be used for setting the mouse cursors and clipboard text. 14 | void SetWindow (GLFWwindow* window); 15 | 16 | // -- Inherited from Rml::SystemInterface -- 17 | 18 | double GetElapsedTime() override; 19 | 20 | void SetMouseCursor (const Rml::String& cursor_name) override; 21 | 22 | void SetClipboardText (const Rml::String& text) override; 23 | void GetClipboardText (Rml::String& text) override; 24 | 25 | bool LogMessage (Rml::Log::Type typ, const Rml::String& message) override { 26 | cout << KBLU << message << KEND "\n"; 27 | return true; 28 | } 29 | 30 | private: 31 | GLFWwindow* window = nullptr; 32 | 33 | GLFWcursor* cursor_pointer = nullptr; 34 | GLFWcursor* cursor_cross = nullptr; 35 | GLFWcursor* cursor_text = nullptr; 36 | GLFWcursor* cursor_move = nullptr; 37 | GLFWcursor* cursor_resize = nullptr; 38 | GLFWcursor* cursor_unavailable = nullptr; 39 | }; 40 | 41 | /** 42 | Optional helper functions for the GLFW plaform. 43 | */ 44 | namespace RmlGLFW { 45 | 46 | // The following optional functions are intended to be called from their respective GLFW callback functions. The functions expect arguments passed 47 | // directly from GLFW, in addition to the RmlUi context to apply the input or sizing event on. The input callbacks return true if the event is 48 | // propagating, i.e. was not handled by the context. 49 | bool ProcessKeyCallback (Rml::Context* context, int key, int action, int mods); 50 | bool ProcessCharCallback (Rml::Context* context, unsigned int codepoint); 51 | bool ProcessCursorEnterCallback (Rml::Context* context, int entered); 52 | bool ProcessCursorPosCallback (Rml::Context* context, GLFWwindow* window, double xpos, double ypos, int mods); 53 | bool ProcessMouseButtonCallback (Rml::Context* context, int button, int action, int mods); 54 | bool ProcessScrollCallback (Rml::Context* context, double yoffset, int mods); 55 | void ProcessFramebufferSizeCallback (Rml::Context* context, int width, int height); 56 | void ProcessContentScaleCallback (Rml::Context* context, float xscale); 57 | 58 | // Converts the GLFW key to RmlUi key. 59 | Rml::Input::KeyIdentifier ConvertKey (int glfw_key); 60 | 61 | // Converts the GLFW key modifiers to RmlUi key modifiers. 62 | int ConvertKeyModifiers (int glfw_mods); 63 | 64 | } // namespace RmlGLFW 65 | 66 | class Ui { 67 | public: 68 | void setup(); 69 | void update(); 70 | void draw(); 71 | void cleanup(); 72 | LumInternal::LumInternalRenderer* renderer; 73 | SystemInterface_GLFW sysInterface; 74 | 75 | Rml::Context* context; 76 | Rml::ElementDocument* document; 77 | 78 | Rml::DataModelHandle my_model; 79 | 80 | bool SetupDataBinding (Rml::Context* context, Rml::DataModelHandle& my_model); 81 | // void set_buttons_callback(Rml::DataModelHandle, Rml::Event&, const Rml::VariantList&); 82 | }; --------------------------------------------------------------------------------