├── .editorconfig ├── .github └── workflows │ ├── linux.yml │ └── windows.yml ├── .gitignore ├── .gitmodules ├── LICENSE ├── README.md ├── build.docker ├── build.sh ├── demo ├── .gitignore ├── celtest │ ├── .gitignore │ ├── README.md │ ├── dub.sdl │ └── source │ │ ├── app.d │ │ ├── hud.d │ │ └── play.d ├── content │ ├── .gitignore │ ├── audio │ │ └── 50_50.mp3 │ ├── ball.png │ ├── models │ │ ├── fountain3.glb │ │ ├── fox.glb │ │ ├── fox.mtl │ │ ├── fox.obj │ │ ├── fox_texture.png │ │ ├── foxs.glb │ │ ├── nieradam.glb │ │ ├── qsphr.glb │ │ ├── rcube.glb │ │ ├── rcube.mtl │ │ ├── rcube.obj │ │ └── sphr.glb │ ├── paddle.png │ └── shader │ │ ├── ascii.frag │ │ ├── base.vert │ │ ├── basic_lighting.frag │ │ ├── basic_lighting.vert │ │ ├── blinn_phong.frag │ │ ├── blinn_phong.vert │ │ ├── blossom.frag │ │ ├── bokeh.frag │ │ ├── cel_ish.frag │ │ ├── cel_light.frag │ │ ├── chromatic_aberration.frag │ │ ├── coord.frag │ │ ├── cross_stitch.frag │ │ ├── grayscale.frag │ │ ├── jupiter1.frag │ │ ├── jupiter2.frag │ │ ├── present.frag │ │ ├── simple.frag │ │ └── toy │ │ ├── jup3.frag │ │ ├── jupii.frag │ │ └── test.frag ├── multiscene │ ├── .gitignore │ ├── dub.sdl │ └── source │ │ ├── app.d │ │ └── play.d ├── nuidemo │ ├── .gitignore │ ├── dub.sdl │ ├── res │ │ └── SourceSansPro-Regular.ttf │ └── source │ │ ├── app.d │ │ ├── gui_root.d │ │ ├── gui_scene.d │ │ └── style.d ├── pong │ ├── .gitignore │ ├── dub.sdl │ └── source │ │ ├── app.d │ │ ├── comp │ │ ├── ai.d │ │ ├── ball.d │ │ ├── input.d │ │ ├── paddle.d │ │ └── score.d │ │ └── play.d ├── quickshd │ ├── .gitignore │ ├── dub.sdl │ └── source │ │ └── app.d ├── shanpes │ ├── .gitignore │ ├── dub.sdl │ └── source │ │ ├── app.d │ │ ├── comp │ │ ├── ai.d │ │ ├── body.d │ │ └── input.d │ │ └── play.d ├── shddraw │ ├── .gitignore │ ├── dub.sdl │ └── source │ │ ├── app.d │ │ ├── hud.d │ │ └── play.d ├── table │ ├── .gitignore │ ├── Release.mk │ ├── dub.sdl │ └── source │ │ ├── app.d │ │ ├── hud.d │ │ └── play.d └── three │ ├── .gitignore │ ├── dub.sdl │ └── source │ ├── app.d │ ├── hud.d │ └── play.d ├── doc └── README.md ├── dub.sdl ├── dub.selections.json └── source └── re ├── audio ├── audio_manager.d └── package.d ├── content.d ├── core.d ├── ecs ├── component.d ├── entity.d ├── manager.d ├── package.d ├── renderable.d ├── storage.d └── updatable.d ├── gfx ├── color_ext.d ├── effect.d ├── effects │ └── frag.d ├── lighting │ ├── basic.d │ └── blinn_phong.d ├── package.d ├── postprocessor.d ├── raytypes.d ├── render_ext.d ├── shapes │ ├── anim_model.d │ ├── cube.d │ ├── grid.d │ ├── mesh.d │ ├── model.d │ ├── rect.d │ └── sphere.d ├── sprite.d ├── sprite_renderer.d ├── text.d ├── vr.d └── window.d ├── input ├── input.d ├── package.d └── virtual │ ├── axis.d │ ├── base.d │ ├── button.d │ └── joystick.d ├── math ├── bounds.d ├── funcs.d ├── package.d ├── raytypes.d ├── rectangle_ext.d ├── transform.d └── vector_ext.d ├── ng ├── camera │ ├── base.d │ ├── cam2d.d │ ├── cam3d.d │ └── package.d ├── diag │ ├── console.d │ ├── default_inspect_commands.d │ ├── entity_inspect_view.d │ ├── fps_counter.d │ ├── inspector_overlay.d │ ├── package.d │ └── render.d ├── manager.d ├── scene.d ├── scene2d.d ├── scene3d.d └── viewport.d ├── package.d ├── phys ├── collider.d ├── kin2d.d └── rigid3d.d ├── time.d └── util ├── cache.d ├── dlib.d ├── dual_map.d ├── env.d ├── format.d ├── hotreload.d ├── interop.d ├── jar.d ├── logger.d ├── orbit.d ├── reflect.d ├── rng.d ├── test.d ├── tweens ├── ease.d ├── tween.d └── tween_manager.d ├── type.d └── vr_distortion.d /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | end_of_line = lf 5 | indent_size = 4 6 | 7 | [*.d] 8 | dfmt_brace_style = otbs 9 | -------------------------------------------------------------------------------- /.github/workflows/linux.yml: -------------------------------------------------------------------------------- 1 | name: linux 2 | on: [ push, pull_request ] 3 | jobs: 4 | test-linux: 5 | strategy: 6 | matrix: 7 | os: [ ubuntu-latest ] 8 | dc: [ dmd-2.100.2 ] 9 | arch: [ x86_64 ] 10 | runs-on: ${{ matrix.os }} 11 | steps: 12 | - name: Install X11 on Linux 13 | if: matrix.os == 'ubuntu-latest' 14 | # Xorg dev needed on linux 15 | run: sudo apt install -y xorg-dev 16 | - uses: actions/checkout@v2 17 | - name: Install D compiler 18 | uses: dlang-community/setup-dlang@v1 19 | with: 20 | compiler: ${{ matrix.dc }} 21 | - run: cat dub.sdl 22 | - run: dub test --arch=${{ matrix.arch }} 23 | -------------------------------------------------------------------------------- /.github/workflows/windows.yml: -------------------------------------------------------------------------------- 1 | name: windows 2 | on: [ push, pull_request ] 3 | jobs: 4 | test-windows: 5 | runs-on: windows-latest 6 | env: 7 | RAYLIB_PREBUILD_VERSION: 5.0.0-r1 8 | DRAY_VERSION: 5.0.0-r5 9 | steps: 10 | - uses: actions/checkout@v3 11 | - uses: dlang-community/setup-dlang@v2 12 | with: 13 | compiler: ldc 14 | - name: 'Setup Windows Build Environment' 15 | shell: pwsh 16 | run: | 17 | git submodule update --init --recursive 18 | 19 | echo "get raylib prebuild" 20 | curl -L https://github.com/redthing1/dray/releases/download/v$env:RAYLIB_PREBUILD_VERSION/raylib-dev_win64_msvc16.zip.zip --output raylib-dev_win64_msvc16.zip.zip 21 | 7z x raylib-dev_win64_msvc16.zip.zip 22 | 7z x raylib-dev_win64_msvc16.zip 23 | 24 | curl -L https://github.com/glfw/glfw/releases/download/3.4/glfw-3.4.bin.WIN64.zip --output glfw-3.4.bin.WIN64.zip 25 | unzip glfw-3.4.bin.WIN64.zip 26 | move glfw-3.4.bin.WIN64/lib-vc2022/glfw3_mt.lib ./glfw3_mt.lib 27 | 28 | echo "prebuild dray with libs" 29 | dub fetch dray@$env:DRAY_VERSION 30 | $DRAY_PATH = "$env:LOCALAPPDATA/dub/packages/dray/$env:DRAY_VERSION/dray" 31 | echo "using dray path: $DRAY_PATH" 32 | dub list dray@$env:DRAY_VERSION 33 | cp raylib-dev_win64_msvc16/lib/raylib.lib $DRAY_PATH/raylib.lib 34 | cp glfw3_mt.lib $DRAY_PATH/glfw3_mt.lib 35 | dub build dray@$env:DRAY_VERSION 36 | ls $DRAY_PATH 37 | 38 | echo "show files" 39 | dir 40 | - name: 'Build' 41 | run: | 42 | dub build 43 | - name: 'Run Tests' 44 | run: | 45 | dub test 46 | - name: 'Build Demos' 47 | # run dub build in each demo/ folder, but ignore folders that don't have a dub.sdl 48 | # using powershell syntax 49 | shell: powershell 50 | run: | 51 | $demos = Get-ChildItem -Path ./demo -Directory -Recurse 52 | foreach ($demo in $demos) { 53 | if (Test-Path "$($demo.FullName)/dub.sdl") { 54 | pushd $demo.FullName 55 | dub build 56 | popd 57 | } 58 | } 59 | - name: 'Package' 60 | run: | 61 | dir 62 | 7z a rengfx_win_builddir.7z . 63 | - name: 'Upload Artifacts' 64 | uses: actions/upload-artifact@v3 65 | with: 66 | name: rengfx-win-builddir 67 | path: rengfx_win_builddir.7z 68 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | screenshot* 2 | .dub 3 | docs.json 4 | __dummy.html 5 | docs/ 6 | /reng 7 | reng.so 8 | reng.dylib 9 | reng.dll 10 | reng.a 11 | reng.lib 12 | reng-test-* 13 | *.exe 14 | *.o 15 | *.obj 16 | *.lst 17 | 18 | # lol 19 | 20 | .vscode 21 | 22 | # ---> D 23 | # Compiled Object files 24 | *.o 25 | *.obj 26 | 27 | # Compiled Dynamic libraries 28 | *.so 29 | *.dylib 30 | *.dll 31 | 32 | # Compiled Static libraries 33 | *.a 34 | *.lib 35 | 36 | # Executables 37 | *.exe 38 | 39 | # DUB 40 | .dub 41 | docs.json 42 | __dummy.html 43 | docs/ 44 | 45 | # Code coverage 46 | *.lst 47 | 48 | # ---> Linux 49 | *~ 50 | 51 | # temporary files which can be created if a process still has a handle open of a deleted file 52 | .fuse_hidden* 53 | 54 | # KDE directory preferences 55 | .directory 56 | 57 | # Linux trash folder which might appear on any partition or disk 58 | .Trash-* 59 | 60 | # .nfs files are created when an open file is removed but is still being accessed 61 | .nfs* 62 | 63 | # ---> C++ 64 | 65 | # Compiled Object files 66 | *.slo 67 | *.lo 68 | *.o 69 | *.obj 70 | 71 | # Precompiled Headers 72 | *.gch 73 | *.pch 74 | 75 | # Compiled Dynamic libraries 76 | *.so 77 | *.dylib 78 | *.dll 79 | 80 | # Fortran module files 81 | *.mod 82 | *.smod 83 | 84 | # Compiled Static libraries 85 | *.lai 86 | *.la 87 | *.a 88 | *.lib 89 | 90 | # Executables 91 | *.exe 92 | *.out 93 | *.app 94 | 95 | # ---> C 96 | 97 | # Object files 98 | *.o 99 | *.ko 100 | *.obj 101 | *.elf 102 | 103 | # Linker output 104 | *.ilk 105 | *.map 106 | *.exp 107 | 108 | # Precompiled Headers 109 | *.gch 110 | *.pch 111 | 112 | # Libraries 113 | *.lib 114 | *.a 115 | *.la 116 | *.lo 117 | 118 | # Shared objects (inc. Windows DLLs) 119 | *.dll 120 | *.so 121 | *.so.* 122 | *.dylib 123 | 124 | # Executables 125 | *.exe 126 | *.out 127 | *.app 128 | *.i*86 129 | *.x86_64 130 | *.hex 131 | 132 | # Debug files 133 | *.dSYM/ 134 | *.su 135 | *.idb 136 | *.pdb 137 | 138 | # Kernel Module Compile Results 139 | *.mod* 140 | *.cmd 141 | .tmp_versions/ 142 | modules.order 143 | Module.symvers 144 | Mkfile.old 145 | dkms.conf 146 | 147 | # Created by https://www.toptal.com/developers/gitignore/api/macos,windows,linux 148 | # Edit at https://www.toptal.com/developers/gitignore?templates=macos,windows,linux 149 | 150 | ### Linux ### 151 | *~ 152 | 153 | # temporary files which can be created if a process still has a handle open of a deleted file 154 | .fuse_hidden* 155 | 156 | # KDE directory preferences 157 | .directory 158 | 159 | # Linux trash folder which might appear on any partition or disk 160 | .Trash-* 161 | 162 | # .nfs files are created when an open file is removed but is still being accessed 163 | .nfs* 164 | 165 | ### macOS ### 166 | # General 167 | .DS_Store 168 | .AppleDouble 169 | .LSOverride 170 | 171 | # Icon must end with two \r 172 | Icon 173 | 174 | 175 | # Thumbnails 176 | ._* 177 | 178 | # Files that might appear in the root of a volume 179 | .DocumentRevisions-V100 180 | .fseventsd 181 | .Spotlight-V100 182 | .TemporaryItems 183 | .Trashes 184 | .VolumeIcon.icns 185 | .com.apple.timemachine.donotpresent 186 | 187 | # Directories potentially created on remote AFP share 188 | .AppleDB 189 | .AppleDesktop 190 | Network Trash Folder 191 | Temporary Items 192 | .apdisk 193 | 194 | ### macOS Patch ### 195 | # iCloud generated files 196 | *.icloud 197 | 198 | ### Windows ### 199 | # Windows thumbnail cache files 200 | Thumbs.db 201 | Thumbs.db:encryptable 202 | ehthumbs.db 203 | ehthumbs_vista.db 204 | 205 | # Dump file 206 | *.stackdump 207 | 208 | # Folder config file 209 | [Dd]esktop.ini 210 | 211 | # Recycle Bin used on file shares 212 | $RECYCLE.BIN/ 213 | 214 | # Windows Installer files 215 | *.cab 216 | *.msi 217 | *.msix 218 | *.msm 219 | *.msp 220 | 221 | # Windows shortcuts 222 | *.lnk 223 | 224 | # End of https://www.toptal.com/developers/gitignore/api/macos,windows,linux 225 | 226 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bmchtech/rengfx/83cc5fab8062d7d2d6336295083fbf77434a2d59/.gitmodules -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # rengfx 2 | 3 | RE ENGINE FX 4 | 5 | [![DUB Package](https://img.shields.io/dub/v/reng.svg)](https://code.dlang.org/packages/reng) 6 | 7 | lightweight, expressive, extensible multimedia engine 8 | 9 | ![blocks demo gif](https://raw.githubusercontent.com/wiki/redthing1/rengfx/img/rec-2020-07-30_17.17.12.gif) 10 | 11 | ![table demo gif](https://raw.githubusercontent.com/wiki/redthing1/rengfx/img/rengfx_fox.gif) 12 | 13 | ## features 14 | 15 | + only library dependency is [`raylib` (v5.0)](https://github.com/redthing1/raylib) 16 | + engine features 17 | + combined, mixable 2d and 3d graphics support 18 | + cross platform, system-independent graphics 19 | + composable, modular game components and rendering 20 | + virtual input for transparent rebinding and cross platform input 21 | + vector/matrix math hidden behind nice abstractions 22 | + vr support 23 | + modular, data-driven Scene-Entity-Component architecture 24 | + full headless execution support, making unit tests simple 25 | + emphasis on simplicity and readability, avoidance of unnecessary abstraction 26 | + multi scene layering and compositing 27 | + highly extensible with custom components and logic 28 | + everything can be overrided or extended 29 | + fluent debugging 30 | + real time runtime debug console and inspector 31 | + simple and powerful glsl shaders 32 | + bulit-in shaders for stylized lighting and postprocessing 33 | + streamlined shaders api for custom glsl shaders 34 | + wip 35 | + wip: physics support and integration 36 | + wip: tilemaps with tiled 37 | 38 | ## documentation 39 | + full documentation: [api docs](https://reng.dpldocs.info/) 40 | + demo projects: [demos](demo/) 41 | + notes and tips: [doc](doc/) 42 | 43 | ## hacking 44 | 45 | requirements: 46 | + `make` and a C compiler (`gcc`, `clang`) 47 | + `dub` and a D compiler (`dmd`, `gdc`, `ldc`) 48 | 49 | rengfx depends on raylib (via [dray](https://github.com/redthing1/dray) bindings). 50 | by default, `dray` will run a pre-generate script that automatically builds `raylib`. 51 | 52 | build engine: 53 | ```sh 54 | dub test # run tests 55 | dub build # build library 56 | ``` 57 | 58 | open docs locally: 59 | ```sh 60 | dub run -b ddox 61 | ``` 62 | 63 | run demo: 64 | ```sh 65 | cd demo/ 66 | dub run # run demo 67 | ``` 68 | 69 | ## license 70 | 71 | copyright © 2020-2022, redthing1. 72 | 73 | available to use under the [LGPL v3.0](LICENSE). 74 | 75 | libraries: 76 | + [raylib](https://github.com/raysan5/raylib/blob/be7f717a24e72e0bc84389491a063de65c106048/LICENSE), Zlib license 77 | -------------------------------------------------------------------------------- /build.docker: -------------------------------------------------------------------------------- 1 | FROM debian:buster-slim 2 | 3 | # install dependencies 4 | RUN apt-get update && apt-get install -y \ 5 | bash \ 6 | curl wget xz-utils \ 7 | gcc make libc6-dev libcurl4 \ 8 | git libxml2 \ 9 | libgl1-mesa-dev libx11-dev libxrandr-dev libxcursor-dev libxi-dev libxinerama-dev \ 10 | && rm -rf /var/lib/apt/lists/* && apt autoremove -y && apt clean 11 | 12 | 13 | # install dlang 14 | # RUN wget https://downloads.dlang.org/releases/2022/dmd_2.100.2-0_amd64.deb -O /tmp/dlang_dmd.deb && apt install -y /tmp/dlang_dmd.deb 15 | RUN curl -fsS https://dlang.org/install.sh | bash -s install ldc-1.30.0 \ 16 | && echo "source ~/dlang/ldc-1.30.0/activate" >> ~/.bashrc 17 | 18 | # set up main to run bash 19 | CMD ["/bin/bash", "-l"] 20 | -------------------------------------------------------------------------------- /build.sh: -------------------------------------------------------------------------------- 1 | git submodule update --init --recursive && dub build --compiler ldc2 -B release -------------------------------------------------------------------------------- /demo/.gitignore: -------------------------------------------------------------------------------- 1 | dub.selections.json 2 | dist/ 3 | -------------------------------------------------------------------------------- /demo/celtest/.gitignore: -------------------------------------------------------------------------------- 1 | .dub 2 | docs.json 3 | __dummy.html 4 | docs/ 5 | /celtest 6 | celtest.so 7 | celtest.dylib 8 | celtest.dll 9 | celtest.a 10 | celtest.lib 11 | celtest-test-* 12 | *.exe 13 | *.o 14 | *.obj 15 | *.lst 16 | -------------------------------------------------------------------------------- /demo/celtest/README.md: -------------------------------------------------------------------------------- 1 | 2 | # celtest demo 3 | 4 | a demo of 3d shapes with a minimalistic thresholded cel shader 5 | 6 | ## usage 7 | 8 | ``` 9 | ./celtest [-m /path/to/model.glb] 10 | ``` 11 | -------------------------------------------------------------------------------- /demo/celtest/dub.sdl: -------------------------------------------------------------------------------- 1 | name "celtest" 2 | description "celtest 3d demo" 3 | authors "redthing1" 4 | copyright "Copyright © 2020, redthing1" 5 | license "proprietary" 6 | dependency "reng" path="../.." 7 | targetType "executable" 8 | 9 | configuration "default" { 10 | subConfiguration "reng" "lib-minimal" 11 | } 12 | 13 | configuration "lite" { 14 | subConfiguration "reng" "lib-lite" 15 | 16 | versions "lite" 17 | } 18 | -------------------------------------------------------------------------------- /demo/celtest/source/app.d: -------------------------------------------------------------------------------- 1 | module app; 2 | 3 | import std.stdio; 4 | import std.array; 5 | import std.algorithm.iteration : map, filter; 6 | import std.conv; 7 | import std.format; 8 | 9 | import re; 10 | import re.math; 11 | import std.stdio; 12 | import play; 13 | import hud; 14 | import std.getopt; 15 | 16 | static import raylib; 17 | 18 | class Game : Core { 19 | enum DEFAULT_WIDTH = 960; 20 | enum DEFAULT_HEIGHT = 540; 21 | 22 | public static string custom_mdl1_path = null; 23 | public static bool free_look = false; 24 | public static bool vr_enabled = false; 25 | 26 | this(int width, int height) { 27 | // core init here 28 | Core.default_filter_mode = raylib.TextureFilter.TEXTURE_FILTER_ANISOTROPIC_4X; 29 | 30 | super(width, height, vr_enabled ? "celtest [VR]" : "celtest"); 31 | } 32 | 33 | override void initialize() { 34 | // default_resolution = Vector2(width, height); 35 | content.paths ~= ["../content/", "content/"]; 36 | 37 | // load_scenes([new PlayScene(), new HUDScene()]); 38 | load_scenes([new PlayScene()]); 39 | } 40 | } 41 | 42 | int main(string[] args) { 43 | bool verbose; 44 | string resolution_str; 45 | auto help = getopt(args, 46 | "verbose|v", &verbose, 47 | "model|m", &Game.custom_mdl1_path, 48 | "free-cam|f", &Game.free_look, // "vr", &Game.vr_enabled, 49 | "resolution|r", &resolution_str, 50 | ); 51 | 52 | // parse resolution 53 | auto resolution_parsed = resolution_str.split("x").map!(to!int).array; 54 | if (resolution_parsed.length != 2) { 55 | resolution_parsed = [Game.DEFAULT_WIDTH, Game.DEFAULT_HEIGHT]; 56 | if (resolution_str.length > 0) { 57 | // log error 58 | writefln("invalid resolution: %s", resolution_str); 59 | } 60 | } 61 | 62 | if (help.helpWanted) { 63 | defaultGetoptPrinter("Usage: ./a [--model /path/to/model.glb] [-f]", help.options); 64 | return 1; 65 | } 66 | 67 | raylib.SetConfigFlags(raylib.ConfigFlags.FLAG_MSAA_4X_HINT); 68 | import re.util.logger; 69 | 70 | if (verbose) { 71 | raylib.SetTraceLogLevel(raylib.TraceLogLevel.LOG_INFO); 72 | } else { 73 | raylib.SetTraceLogLevel(raylib.TraceLogLevel.LOG_WARNING); 74 | } 75 | 76 | auto game = new Game(resolution_parsed[0], resolution_parsed[1]); 77 | 78 | if (verbose) { 79 | Core.log.verbosity = Verbosity.trace; 80 | } else { 81 | Core.log.verbosity = Verbosity.info; 82 | } 83 | Core.log.trace("starting game"); 84 | 85 | if (Game.vr_enabled) { 86 | // VR device parameters definition 87 | raylib.VrDeviceInfo vr_device_info; 88 | vr_device_info.hResolution = 2160; 89 | vr_device_info.vResolution = 1200; 90 | vr_device_info.hScreenSize = 0.133793f; 91 | vr_device_info.vScreenSize = 0.0669f; 92 | vr_device_info.vScreenCenter = 0.04678f; 93 | vr_device_info.eyeToScreenDistance = 0.041f; 94 | vr_device_info.lensSeparationDistance = 0.07f; 95 | vr_device_info.interpupillaryDistance = 0.07f; 96 | // NOTE: CV1 uses fresnel-hybrid-asymmetric lenses with specific compute shaders 97 | // Following parameters are just an approximation to CV1 distortion stereo rendering 98 | vr_device_info.lensDistortionValues[0] = 1.0f; 99 | vr_device_info.lensDistortionValues[1] = 0.22f; 100 | vr_device_info.lensDistortionValues[2] = 0.24f; 101 | vr_device_info.lensDistortionValues[3] = 0.0f; 102 | vr_device_info.chromaAbCorrection[0] = 0.996f; 103 | vr_device_info.chromaAbCorrection[1] = -0.004f; 104 | vr_device_info.chromaAbCorrection[2] = 1.014f; 105 | vr_device_info.chromaAbCorrection[3] = 0.0f; 106 | 107 | // Game.vr.setup_vr(vr_device_info); 108 | 109 | // assert(0, "VR not implemented yet"); 110 | } 111 | 112 | game.run(); 113 | game.destroy(); // clean up 114 | 115 | return 0; 116 | } 117 | -------------------------------------------------------------------------------- /demo/celtest/source/hud.d: -------------------------------------------------------------------------------- 1 | module hud; 2 | 3 | import re; 4 | import re.gfx; 5 | import re.math; 6 | import std.format; 7 | import re.util.interop; 8 | 9 | static import raylib; 10 | 11 | class HUDScene : Scene2D { 12 | override void on_start() { 13 | clear_color = Colors.BLANK; 14 | // set the tint of this scene's composite 15 | composite_mode.color = Color(255, 255, 255, 160); 16 | 17 | enum pad = 4; 18 | 19 | auto msg = create_entity("msg", Vector2(pad, resolution.y - pad)); 20 | auto hello_text = msg.add_component(new Text(Text.default_font, 21 | "table.", 10, Colors.PURPLE)); 22 | hello_text.set_align(Text.Align.Close, Text.Align.Far); 23 | } 24 | 25 | override void render_hook() { 26 | // draw fps 27 | raylib.DrawText(format("%s", Core.fps).c_str(), 8, 8, 8, Colors.BLACK); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /demo/content/.gitignore: -------------------------------------------------------------------------------- 1 | !models/*.obj 2 | models/lg 3 | -------------------------------------------------------------------------------- /demo/content/audio/50_50.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bmchtech/rengfx/83cc5fab8062d7d2d6336295083fbf77434a2d59/demo/content/audio/50_50.mp3 -------------------------------------------------------------------------------- /demo/content/ball.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bmchtech/rengfx/83cc5fab8062d7d2d6336295083fbf77434a2d59/demo/content/ball.png -------------------------------------------------------------------------------- /demo/content/models/fountain3.glb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bmchtech/rengfx/83cc5fab8062d7d2d6336295083fbf77434a2d59/demo/content/models/fountain3.glb -------------------------------------------------------------------------------- /demo/content/models/fox.glb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bmchtech/rengfx/83cc5fab8062d7d2d6336295083fbf77434a2d59/demo/content/models/fox.glb -------------------------------------------------------------------------------- /demo/content/models/fox.mtl: -------------------------------------------------------------------------------- 1 | # Blender MTL File: 'None' 2 | # Material Count: 1 3 | 4 | newmtl fox_material.001 5 | Ns 96.078443 6 | Ka 1.000000 1.000000 1.000000 7 | Kd 1.000000 1.000000 1.000000 8 | Ks 0.089804 0.089804 0.089804 9 | Ke 0.000000 0.000000 0.000000 10 | Ni 1.000000 11 | d 1.000000 12 | illum 2 13 | map_Kd fox_texture.png 14 | -------------------------------------------------------------------------------- /demo/content/models/fox_texture.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bmchtech/rengfx/83cc5fab8062d7d2d6336295083fbf77434a2d59/demo/content/models/fox_texture.png -------------------------------------------------------------------------------- /demo/content/models/foxs.glb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bmchtech/rengfx/83cc5fab8062d7d2d6336295083fbf77434a2d59/demo/content/models/foxs.glb -------------------------------------------------------------------------------- /demo/content/models/nieradam.glb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bmchtech/rengfx/83cc5fab8062d7d2d6336295083fbf77434a2d59/demo/content/models/nieradam.glb -------------------------------------------------------------------------------- /demo/content/models/qsphr.glb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bmchtech/rengfx/83cc5fab8062d7d2d6336295083fbf77434a2d59/demo/content/models/qsphr.glb -------------------------------------------------------------------------------- /demo/content/models/rcube.glb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bmchtech/rengfx/83cc5fab8062d7d2d6336295083fbf77434a2d59/demo/content/models/rcube.glb -------------------------------------------------------------------------------- /demo/content/models/rcube.mtl: -------------------------------------------------------------------------------- 1 | # Blender MTL File: 'rcube.blend' 2 | # Material Count: 3 3 | 4 | newmtl BluePlastic 5 | Ns 225.000000 6 | Ka 1.000000 1.000000 1.000000 7 | Kd 0.800000 0.800000 0.800000 8 | Ks 0.500000 0.500000 0.500000 9 | Ke 0.000000 0.000000 0.000000 10 | Ni 1.450000 11 | d 1.000000 12 | illum 2 13 | map_Kd . 14 | 15 | newmtl PinkPlastic 16 | Ns 225.000000 17 | Ka 1.000000 1.000000 1.000000 18 | Kd 0.800000 0.800000 0.800000 19 | Ks 0.500000 0.500000 0.500000 20 | Ke 0.000000 0.000000 0.000000 21 | Ni 1.450000 22 | d 1.000000 23 | illum 2 24 | map_Kd . 25 | 26 | newmtl RedPlastic 27 | Ns 225.000000 28 | Ka 1.000000 1.000000 1.000000 29 | Kd 0.800000 0.800000 0.800000 30 | Ks 0.500000 0.500000 0.500000 31 | Ke 0.000000 0.000000 0.000000 32 | Ni 1.450000 33 | d 1.000000 34 | illum 2 35 | map_Kd . 36 | -------------------------------------------------------------------------------- /demo/content/models/sphr.glb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bmchtech/rengfx/83cc5fab8062d7d2d6336295083fbf77434a2d59/demo/content/models/sphr.glb -------------------------------------------------------------------------------- /demo/content/paddle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bmchtech/rengfx/83cc5fab8062d7d2d6336295083fbf77434a2d59/demo/content/paddle.png -------------------------------------------------------------------------------- /demo/content/shader/base.vert: -------------------------------------------------------------------------------- 1 | #version 330 2 | 3 | // Input vertex attributes 4 | in vec3 vertexPosition; 5 | in vec2 vertexTexCoord; 6 | in vec3 vertexNormal; 7 | in vec4 vertexColor; 8 | 9 | // Input uniform values 10 | uniform mat4 mvp; 11 | 12 | // Output vertex attributes (to fragment shader) 13 | out vec2 fragTexCoord; 14 | out vec4 fragColor; 15 | 16 | // NOTE: Add here your custom variables 17 | 18 | void main() { 19 | // Send vertex attributes to fragment shader 20 | fragTexCoord = vertexTexCoord; 21 | fragColor = vertexColor; 22 | 23 | // Calculate final vertex position 24 | gl_Position = mvp * vec4(vertexPosition, 1.0); 25 | } -------------------------------------------------------------------------------- /demo/content/shader/basic_lighting.frag: -------------------------------------------------------------------------------- 1 | #version 330 2 | 3 | // Input vertex attributes (from vertex shader) 4 | in vec3 fragPosition; 5 | in vec2 fragTexCoord; 6 | in vec4 fragColor; 7 | in vec3 fragNormal; 8 | 9 | // Input uniform values 10 | uniform sampler2D texture0; 11 | uniform sampler2D texture1; 12 | uniform vec4 colDiffuse; 13 | 14 | // Output fragment color 15 | out vec4 finalColor; 16 | 17 | // NOTE: Add here your custom variables 18 | 19 | #define MAX_LIGHTS 4 20 | #define LIGHT_DIRECTIONAL 0 21 | #define LIGHT_POINT 1 22 | 23 | struct MaterialProperty { 24 | vec3 color; 25 | int useSampler; 26 | sampler2D sampler; 27 | }; 28 | 29 | struct Light { 30 | int enabled; 31 | int type; 32 | vec3 position; 33 | vec3 target; 34 | vec4 color; 35 | }; 36 | 37 | // Input lighting values 38 | uniform Light lights[MAX_LIGHTS]; 39 | uniform vec3 viewPos; 40 | 41 | // options 42 | uniform vec4 ambient; 43 | uniform float shine; 44 | uniform float light_clamp; 45 | uniform int light_quantize; 46 | 47 | void main() { 48 | // Texel color fetching from texture sampler 49 | vec4 texelColor = 50 | texture(texture0, fragTexCoord) + texture(texture1, fragTexCoord); 51 | vec3 lightDot = vec3(0.0); 52 | vec3 normal = normalize(fragNormal); 53 | vec3 viewD = normalize(viewPos - fragPosition); 54 | vec3 specular = vec3(0.0); 55 | 56 | // NOTE: Implement here your fragment shader code 57 | 58 | for (int i = 0; i < MAX_LIGHTS; i++) { 59 | if (lights[i].enabled == 1) { 60 | vec3 light = vec3(0.0); 61 | 62 | if (lights[i].type == LIGHT_DIRECTIONAL) { 63 | light = -normalize(lights[i].target - lights[i].position); 64 | } 65 | 66 | if (lights[i].type == LIGHT_POINT) { 67 | light = normalize(lights[i].position - fragPosition); 68 | } 69 | 70 | float NdotL = max(dot(normal, light), 0.0); 71 | lightDot += lights[i].color.rgb * NdotL; 72 | 73 | float specCo = 0.0; 74 | if (NdotL > 0.0) 75 | specCo = pow(max(0.0, dot(viewD, reflect(-(light), normal))), shine); 76 | specular += specCo; 77 | } 78 | } 79 | 80 | vec4 lighting_amount = 81 | ((colDiffuse + vec4(specular, 1.0)) * vec4(lightDot, 1.0)); 82 | 83 | // clamp lighting amount 84 | lighting_amount = clamp(lighting_amount, 0.0, light_clamp); 85 | 86 | // if quantization, do an all or nothing 87 | if (light_quantize > 0) { 88 | if (any(lessThan(lighting_amount, vec4(light_clamp)))) { 89 | // below threshold 90 | lighting_amount = vec4(0.0); 91 | } else { 92 | // above threshold 93 | lighting_amount = vec4(light_clamp); 94 | } 95 | } 96 | 97 | // calculate contributions 98 | vec4 col_base = texelColor * colDiffuse; 99 | vec4 ambient_contrib = col_base * (ambient / 1.0); 100 | vec4 lighting_contrib = texelColor * lighting_amount; 101 | 102 | // adjust and mix 103 | // TODO 104 | 105 | // sum contributions 106 | finalColor = ambient_contrib + lighting_contrib; 107 | 108 | // Gamma correction 109 | finalColor = pow(finalColor, vec4(1.0 / 2.2)); 110 | } 111 | -------------------------------------------------------------------------------- /demo/content/shader/basic_lighting.vert: -------------------------------------------------------------------------------- 1 | #version 330 2 | 3 | // Input vertex attributes 4 | in vec3 vertexPosition; 5 | in vec2 vertexTexCoord; 6 | in vec3 vertexNormal; 7 | in vec4 vertexColor; 8 | 9 | // Input uniform values 10 | uniform mat4 mvp; 11 | uniform mat4 matModel; 12 | 13 | // Output vertex attributes (to fragment shader) 14 | out vec3 fragPosition; 15 | out vec2 fragTexCoord; 16 | out vec4 fragColor; 17 | out vec3 fragNormal; 18 | 19 | // NOTE: Add here your custom variables 20 | 21 | void main() { 22 | // Send vertex attributes to fragment shader 23 | fragPosition = vec3(matModel * vec4(vertexPosition, 1.0)); 24 | fragTexCoord = vertexTexCoord; 25 | fragColor = vertexColor; 26 | 27 | mat3 normalMatrix = transpose(inverse(mat3(matModel))); 28 | fragNormal = normalize(normalMatrix * vertexNormal); 29 | 30 | // Calculate final vertex position 31 | gl_Position = mvp * vec4(vertexPosition, 1.0); 32 | } 33 | -------------------------------------------------------------------------------- /demo/content/shader/blinn_phong.frag: -------------------------------------------------------------------------------- 1 | #version 330 2 | 3 | // Input vertex attributes (from vertex shader) 4 | in vec2 fragTexCoord; 5 | in vec3 fragNormal; 6 | in vec4 fragColor; 7 | 8 | // Input uniform values 9 | uniform sampler2D texture0; 10 | uniform vec4 colDiffuse; 11 | 12 | // Output fragment color 13 | out vec4 finalColor; 14 | 15 | // NOTE: Add here your custom variables 16 | 17 | // Light uniform values 18 | uniform vec3 lightAmbientColor = vec3(0.6, 0.3, 0.0); 19 | uniform vec3 lightDiffuseColor = vec3(1.0, 0.5, 0.0); 20 | uniform vec3 lightSpecularColor = vec3(0.0, 1.0, 0.0); 21 | uniform float lightIntensity = 1.0; 22 | uniform float lightSpecIntensity = 1.0; 23 | 24 | // Material uniform values 25 | uniform vec3 matAmbientColor = vec3(1.0, 1.0, 1.0); 26 | uniform vec3 matSpecularColor = vec3(1.0, 1.0, 1.0); 27 | uniform float matGlossiness = 50.0; 28 | 29 | // World uniform values 30 | uniform vec3 lightPosition; 31 | uniform vec3 cameraPosition; 32 | 33 | // Calculate ambient lighting component 34 | vec3 AmbientLighting() { return (matAmbientColor * lightAmbientColor); } 35 | 36 | // Calculate diffuse lighting component 37 | vec3 DiffuseLighting(in vec3 N, in vec3 L) { 38 | // Lambertian reflection calculation 39 | float diffuse = clamp(dot(N, L), 0, 1); 40 | 41 | return (colDiffuse.xyz * lightDiffuseColor * lightIntensity * diffuse); 42 | } 43 | 44 | // Calculate specular lighting component 45 | vec3 SpecularLighting(in vec3 N, in vec3 L, in vec3 V) { 46 | float specular = 0.0; 47 | 48 | // Calculate specular reflection only if the surface is oriented to the light 49 | // source 50 | if (dot(N, L) > 0) { 51 | // Calculate half vector 52 | vec3 H = normalize(L + V); 53 | 54 | // Calculate specular intensity 55 | specular = pow(dot(N, H), 3 + matGlossiness); 56 | } 57 | 58 | return (matSpecularColor * lightSpecularColor * lightSpecIntensity * 59 | specular); 60 | } 61 | 62 | void main() { 63 | // Normalize input vectors 64 | vec3 L = normalize(lightPosition); 65 | vec3 V = normalize(cameraPosition); 66 | vec3 N = normalize(fragNormal); 67 | 68 | // Calculate lighting components 69 | vec3 ambient = AmbientLighting(); 70 | vec3 diffuse = DiffuseLighting(N, L); 71 | vec3 specular = SpecularLighting(N, L, V); 72 | 73 | // Texel color fetching from texture sampler 74 | vec4 texelColor = texture(texture0, fragTexCoord); 75 | 76 | // Calculate final fragment color 77 | finalColor = 78 | vec4(texelColor.rgb * (ambient + diffuse + specular), texelColor.a); 79 | } -------------------------------------------------------------------------------- /demo/content/shader/blinn_phong.vert: -------------------------------------------------------------------------------- 1 | #version 330 2 | 3 | // Input vertex attributes 4 | in vec3 vertexPosition; 5 | in vec2 vertexTexCoord; 6 | in vec3 vertexNormal; 7 | in vec4 vertexColor; 8 | 9 | // Input uniform values 10 | uniform mat4 mvp; 11 | 12 | // Output vertex attributes (to fragment shader) 13 | out vec2 fragTexCoord; 14 | out vec3 fragNormal; 15 | out vec4 fragColor; 16 | 17 | // NOTE: Add here your custom variables 18 | uniform mat4 modelMatrix; 19 | 20 | void main() { 21 | // Send vertex attributes to fragment shader 22 | fragTexCoord = vertexTexCoord; 23 | 24 | // Calculate view vector normal from model 25 | mat3 normalMatrix = transpose(inverse(mat3(modelMatrix))); 26 | fragNormal = normalize(normalMatrix * vertexNormal); 27 | fragColor = vertexColor; 28 | 29 | // Calculate final vertex position 30 | gl_Position = mvp * vec4(vertexPosition, 1.0); 31 | } -------------------------------------------------------------------------------- /demo/content/shader/blossom.frag: -------------------------------------------------------------------------------- 1 | // shader based on 2 | // https://raw.githubusercontent.com/lunasorcery/Blossom/main/blossom/draw.frag 3 | 4 | #version 330 5 | 6 | // Input vertex attributes (from vertex shader) 7 | in vec2 fragTexCoord; 8 | in vec4 fragColor; 9 | 10 | // Input uniform values 11 | uniform sampler2D texture0; 12 | uniform vec4 colDiffuse; 13 | 14 | // Output fragment color 15 | out vec4 finalColor; 16 | 17 | // user vars 18 | uniform vec3 i_resolution; 19 | uniform int i_frame; 20 | uniform float i_time; 21 | 22 | #define T(u) texelFetch(tex, ivec2(pix_coord), 0); 23 | 24 | /* vvv your shader goes here vvv */ 25 | 26 | const float pi = acos(-1.); 27 | 28 | // hash functions adapted from Devour 29 | // https://www.shadertoy.com/view/3llSzM 30 | float seed; 31 | float hash() { 32 | float p = fract((seed++) * .1031); 33 | p += (p * (p + 19.19)) * 3.; 34 | return fract((p + p) * p); 35 | } 36 | vec2 hash2() { return vec2(hash(), hash()); } 37 | 38 | mat2 rotate(float b) { 39 | float c = cos(b); 40 | float s = sin(b); 41 | return mat2(c, -s, s, c); 42 | } 43 | 44 | float sdOctahedron(vec3 p, float r) { 45 | return (dot(abs(p), vec3(1)) - r) / sqrt(3.); 46 | } 47 | 48 | float shape(vec3 p) { 49 | float d = sdOctahedron(p, 1.5); 50 | p.y = abs(p.y) - .15; 51 | d = max(d, -sdOctahedron(p, 1.5)); 52 | return d; 53 | } 54 | 55 | // space-repeating macro 56 | vec3 rep(vec3 a, float b) { return mod(a - b, b + b) - b; } 57 | 58 | // sdf describing the scene geometry 59 | float scene(vec3 p) { 60 | float d = 1e9; 61 | float R = 4.; 62 | 63 | for (int i = 0; i < 3; ++i) { 64 | d = min(d, shape(rep(p + vec3(R, 0, R), R))); 65 | d = min(d, shape(rep(p + vec3(0, R, 0), R))); 66 | p = p.yzx; 67 | } 68 | 69 | return d; 70 | } 71 | 72 | vec4 frag_shader(sampler2D tex, vec2 frag_coord) { 73 | // set up UVs, jittered for antialiasing 74 | vec2 uv = (frag_coord.xy + hash2() - .5) / i_resolution.xy - .5; 75 | uv.x *= i_resolution.z; 76 | 77 | // mess with UVs for a fun wide-angle lens 78 | uv *= 4. + length(uv); 79 | 80 | // create a camera 81 | vec3 cam = vec3(0, 0, -10); 82 | vec3 dir = normalize(vec3(uv, 1)); 83 | 84 | // a bit of diamond-shaped bokeh 85 | vec2 bokehOffset = (hash2() - .5) * rotate(pi * .25); 86 | const float dofScale = .5; 87 | const float focusDistance = 17.; 88 | cam.xy += bokehOffset * dofScale; 89 | dir.xy -= bokehOffset * dofScale / focusDistance; 90 | 91 | // spin the camera 92 | cam.yz *= rotate(atan(1., sqrt(2.))); 93 | dir.yz *= rotate(atan(1., sqrt(2.))); 94 | cam.xz *= rotate(pi * .75); 95 | dir.xz *= rotate(pi * .75); 96 | 97 | // time effects 98 | // walk the camera back 99 | cam += vec3(0, 0, i_time * .5); 100 | // rotate the camera 101 | cam.xz *= rotate(i_time * .5); 102 | dir.yz *= rotate(i_time * .5); 103 | 104 | // background color 105 | vec3 color = vec3(.2 * (length(uv)), .1, .25); 106 | 107 | // raymarcher loop 108 | const float epsilon = .001; 109 | float t = 0.; 110 | float k = 0.; 111 | for (int i = 0; i < 200; ++i) { 112 | k = scene(cam + dir * t); 113 | if (abs(k) < epsilon) 114 | break; 115 | t += k; 116 | } 117 | 118 | // surface shading 119 | if (abs(k) < epsilon) { 120 | vec3 h = cam + dir * t; 121 | vec2 o = vec2(epsilon, 0); 122 | vec3 n = normalize( 123 | vec3(scene(h + o.xyy), scene(h + o.yxy), scene(h + o.yyx)) - k); 124 | 125 | float light = dot(n, normalize(vec3(1, 2, 3))) * .35 + .65; 126 | 127 | float fog = min(1., pow(.9, t - 20.)); 128 | color = mix(color, vec3(light), fog); 129 | } 130 | 131 | vec4 ret_col = vec4(color, 1); 132 | return ret_col; 133 | } 134 | 135 | void main() { 136 | // set up coordinates 137 | ivec2 pix_coord = ivec2(fragTexCoord.xy * i_resolution.xy); 138 | vec2 frag_coord = fragTexCoord.xy * i_resolution.xy; 139 | 140 | // seed the RNG (again taken from Devour) 141 | seed = float(((i_frame * 73856093) ^ int(frag_coord.x) * 19349663 ^ 142 | int(frag_coord.y) * 83492791) % 143 | 38069); 144 | 145 | // draw 146 | vec4 draw_col = frag_shader(texture0, frag_coord); 147 | 148 | finalColor = draw_col; 149 | } -------------------------------------------------------------------------------- /demo/content/shader/bokeh.frag: -------------------------------------------------------------------------------- 1 | #version 330 2 | 3 | // Input vertex attributes (from vertex shader) 4 | in vec2 fragTexCoord; 5 | in vec4 fragColor; 6 | 7 | // Input uniform values 8 | uniform sampler2D texture0; 9 | uniform vec4 colDiffuse; 10 | 11 | // Output fragment color 12 | out vec4 finalColor; 13 | 14 | // user vars 15 | uniform vec3 i_resolution; 16 | uniform int i_frame; 17 | uniform float i_time; 18 | 19 | // shadertoy compat 20 | #define iResolution i_resolution 21 | #define iFrame i_frame 22 | #define iTime i_time 23 | 24 | // #define T(u) texelFetch(tex, ivec2(pix_coord), 0); 25 | 26 | // shader vars 27 | uniform float bokeh_base; // = .005; 28 | uniform float bokeh_maxv; // = 1000.; 29 | uniform float bokeh_focus_dist; // = 5.; 30 | 31 | #define Tf(u) \ 32 | texelFetch(texture0, clamp(ivec2(u), ivec2(0), ivec2(i_resolution.xy) - 1), 0) 33 | 34 | vec4 image_shader(sampler2D tex, vec2 pix_coord) { 35 | vec4 o = vec4(0); 36 | float d = bokeh_base * 37 | (clamp(Tf(pix_coord).w, .0, bokeh_maxv) - bokeh_focus_dist) * 38 | i_resolution.y; 39 | float s = 10., a, n = 0.; 40 | for (float r = 0.; r < 1.; r += 1. / s) { 41 | for (float i = 0.; i < 1.; i += 1. / (s * r), n++) { 42 | a = i * radians(360.); 43 | vec2 s_adj = vec2(cos(a), sin(a)) * r; 44 | vec2 loc = pix_coord + s_adj * d; 45 | o += clamp(Tf(loc), 0., 1.); 46 | } 47 | } 48 | o /= n; 49 | o = pow(o, vec4(1. / 2.2)); // brighten 50 | 51 | return o; 52 | } 53 | 54 | void main() { 55 | vec2 pix_coord = fragTexCoord.xy * i_resolution.xy; 56 | 57 | finalColor = image_shader(texture0, pix_coord); 58 | } -------------------------------------------------------------------------------- /demo/content/shader/cel_ish.frag: -------------------------------------------------------------------------------- 1 | #version 330 2 | 3 | // Input vertex attributes (from vertex shader) 4 | in vec2 fragTexCoord; 5 | in vec4 fragColor; 6 | 7 | uniform sampler2D texture0; 8 | uniform vec4 colDiffuse; 9 | 10 | // Output fragment color 11 | out vec4 finalColor; 12 | 13 | // user vars 14 | uniform float c_threshold; 15 | uniform vec2 c_resolution; 16 | uniform vec4 c_outline_color; 17 | 18 | vec4 sample_at(vec2 pos) { return texture(texture0, pos / c_resolution.xy); } 19 | 20 | float sample_x_at(float x, float y) { return sample_at(vec2(x, y)).x; } 21 | 22 | void main() { 23 | float x = fragTexCoord.x * c_resolution.x; 24 | float y = fragTexCoord.y * c_resolution.y; 25 | 26 | float xValue = -sample_x_at(x - 1.0, y - 1.0) - 27 | 2.0 * sample_x_at(x - 1.0, y) - sample_x_at(x - 1.0, y + 1.0) + 28 | sample_x_at(x + 1.0, y - 1.0) + 2.0 * sample_x_at(x + 1.0, y) + 29 | sample_x_at(x + 1.0, y + 1.0); 30 | 31 | float yValue = sample_x_at(x - 1.0, y - 1.0) + 2.0 * sample_x_at(x, y - 1.0) + 32 | sample_x_at(x + 1.0, y - 1.0) - sample_x_at(x - 1.0, y + 1.0) - 33 | 2.0 * sample_x_at(x, y + 1.0) - sample_x_at(x + 1.0, y + 1.0); 34 | 35 | if (length(vec2(xValue, yValue)) > c_threshold) { 36 | finalColor = c_outline_color; 37 | } else { 38 | vec4 currentPixel = texture(texture0, fragTexCoord); 39 | finalColor = currentPixel; 40 | } 41 | } -------------------------------------------------------------------------------- /demo/content/shader/cel_light.frag: -------------------------------------------------------------------------------- 1 | #version 330 2 | 3 | // Input vertex attributes (from vertex shader) 4 | in vec2 fragTexCoord; 5 | in vec4 fragColor; 6 | 7 | // Input uniform values 8 | uniform sampler2D texture0; 9 | uniform vec4 colDiffuse; 10 | 11 | // Output fragment color 12 | out vec4 finalColor; 13 | 14 | // user vars 15 | uniform vec3 i_resolution; 16 | uniform int i_frame; 17 | uniform float i_time; 18 | 19 | // shadertoy compat 20 | #define iResolution i_resolution 21 | #define iFrame i_frame 22 | #define iTime i_time 23 | 24 | // #define T(u) texelFetch(tex, ivec2(pix_coord), 0); 25 | 26 | // shader vars 27 | uniform float outline_diag; // = 8.; 28 | uniform float outline_div; // = 8.; 29 | uniform float outline_lighten; //= 0.1; 30 | 31 | #define Tf(u) \ 32 | texelFetch(texture0, clamp(ivec2(u), ivec2(0), ivec2(i_resolution.xy) - 1), 0) 33 | 34 | vec4 image_shader(sampler2D tex, vec2 pix_coord) { 35 | vec4 p = Tf(pix_coord); 36 | 37 | float c = dot(abs(Tf(pix_coord + vec2(0., 1.)) - p) + 38 | abs(Tf(pix_coord + vec2(1., 0.)) - p), 39 | vec4(outline_diag)) / 40 | outline_div; 41 | 42 | // clamp outline 43 | float clamped_outline = clamp(c, 0., 1.); 44 | 45 | vec4 col = p; 46 | // blend in the outline 47 | col += (outline_lighten - clamped_outline); 48 | 49 | return col; 50 | } 51 | 52 | void main() { 53 | vec2 pix_coord = fragTexCoord.xy * i_resolution.xy; 54 | 55 | finalColor = image_shader(texture0, pix_coord); 56 | } -------------------------------------------------------------------------------- /demo/content/shader/chromatic_aberration.frag: -------------------------------------------------------------------------------- 1 | #version 330 2 | 3 | // Input vertex attributes (from vertex shader) 4 | in vec2 fragTexCoord; 5 | in vec4 fragColor; 6 | 7 | // Input uniform values 8 | uniform sampler2D texture0; 9 | uniform vec4 colDiffuse; 10 | 11 | // Output fragment color 12 | out vec4 finalColor; 13 | 14 | // user vars 15 | uniform vec3 i_resolution; 16 | uniform int i_frame; 17 | uniform float i_time; 18 | 19 | // shadertoy compat 20 | #define iResolution i_resolution 21 | #define iFrame i_frame 22 | #define iTime i_time 23 | 24 | #define T(u) texelFetch(tex, ivec2(pix_coord), 0); 25 | 26 | // shader vars 27 | uniform vec2 sample_offset; 28 | // vec2 sample_offset = vec2(0.005, 0.02); 29 | // #define bean 0 30 | 31 | vec4 sample(vec2 offset) { 32 | return texture(texture0, fragTexCoord.xy - offset) * fragColor; 33 | } 34 | 35 | vec4 frag_shader(sampler2D tex, vec2 frag_coord) { 36 | // sample colors 37 | vec2 uv = frag_coord / i_resolution.xy; 38 | 39 | // vec4 texel_r = texture(tex, uv - sample_offset) * fragColor; 40 | // vec4 texel_g = texture(tex, uv) * fragColor; 41 | // vec4 texel_b = texture(tex, uv + sample_offset) * fragColor; 42 | 43 | vec4 texel_r = sample(-sample_offset); 44 | vec4 texel_g = sample(vec2(0, 0)); 45 | vec4 texel_b = sample(sample_offset); 46 | 47 | return vec4(texel_r.r, texel_g.g, texel_b.b, texel_g.a); 48 | } 49 | 50 | void main() { 51 | vec2 frag_coord = fragTexCoord.xy * i_resolution.xy; 52 | vec4 draw_col = frag_shader(texture0, frag_coord); 53 | 54 | finalColor = draw_col; 55 | } -------------------------------------------------------------------------------- /demo/content/shader/coord.frag: -------------------------------------------------------------------------------- 1 | #version 330 2 | 3 | // Input vertex attributes (from vertex shader) 4 | in vec2 fragTexCoord; 5 | in vec4 fragColor; 6 | 7 | // Input uniform values 8 | uniform sampler2D texture0; 9 | uniform vec4 colDiffuse; 10 | 11 | // Output fragment color 12 | out vec4 finalColor; 13 | 14 | // user vars 15 | uniform vec3 i_resolution; 16 | uniform int i_frame; 17 | uniform float i_time; 18 | 19 | #define T(u) texelFetch(tex, ivec2(pix_coord), 0); 20 | 21 | vec4 frag_shader(sampler2D tex, ivec2 pix_coord, vec2 frag_coord) { 22 | vec4 base = T(pix_coord); 23 | 24 | vec2 uv = frag_coord / i_resolution.xy; 25 | 26 | float fade = i_time / 0.3; 27 | fade = clamp(fade, 0.0, 1.0); 28 | vec3 col_shade = vec3(1 * uv.x, 1 * uv.y, 1) * fade; 29 | vec4 col = vec4(col_shade.rgb, 1.0); 30 | return col; 31 | } 32 | 33 | void main() { 34 | ivec2 pix_coord = ivec2(fragTexCoord.xy * i_resolution.xy); 35 | vec2 frag_coord = fragTexCoord.xy * i_resolution.xy; 36 | vec4 draw_col = frag_shader(texture0, pix_coord, frag_coord); 37 | 38 | finalColor = draw_col; 39 | } -------------------------------------------------------------------------------- /demo/content/shader/cross_stitch.frag: -------------------------------------------------------------------------------- 1 | #version 330 2 | 3 | // Input vertex attributes (from vertex shader) 4 | in vec2 fragTexCoord; 5 | in vec4 fragColor; 6 | 7 | // Input uniform values 8 | uniform sampler2D texture0; 9 | uniform vec4 colDiffuse; 10 | 11 | // Output fragment color 12 | out vec4 finalColor; 13 | 14 | // user vars 15 | uniform vec3 i_resolution; 16 | uniform int i_frame; 17 | uniform float i_time; 18 | 19 | // shadertoy compat 20 | #define iResolution i_resolution 21 | #define iFrame i_frame 22 | #define iTime i_time 23 | 24 | #define T(u) texelFetch(tex, ivec2(pix_coord), 0); 25 | 26 | // shader vars 27 | // uniform float stitch_mix; 28 | float stitch_mix = 0.1; 29 | vec2 stitch_scale = vec2(0.1, 0.1); 30 | // float stitch_size = 6.0; 31 | float stitch_size = 6.0; 32 | int invert = 0; 33 | 34 | vec4 frag_shader(sampler2D tex, vec2 frag_coord) { 35 | vec2 uv = frag_coord / i_resolution.xy; 36 | 37 | vec4 c = vec4(0.0); 38 | vec2 c_pos = uv * i_resolution.xy * stitch_scale; 39 | vec2 tlPos = floor(c_pos / vec2(stitch_size, stitch_size)); 40 | tlPos *= stitch_size; 41 | 42 | int remX = int(mod(c_pos.x, stitch_size)); 43 | int remY = int(mod(c_pos.y, stitch_size)); 44 | 45 | if (remX == 0 && remY == 0) 46 | tlPos = c_pos; 47 | 48 | vec2 blPos = tlPos; 49 | blPos.y += (stitch_size - 1.0); 50 | 51 | if ((remX == remY) || 52 | (((int(c_pos.x) - int(blPos.x)) == (int(blPos.y) - int(c_pos.y))))) { 53 | if (invert == 1) 54 | c = vec4(0.2, 0.15, 0.05, 1.0); 55 | else 56 | c = texture(tex, tlPos * vec2(1.0 / (i_resolution.x * stitch_scale.x), 57 | 1.0 / (i_resolution.y * stitch_scale.y))) * 58 | 1.4; 59 | } else { 60 | if (invert == 1) 61 | c = texture(tex, tlPos * vec2(1.0 / (i_resolution.x * stitch_scale.x), 62 | 1.0 / (i_resolution.y * stitch_scale.y))) * 63 | 1.4; 64 | else 65 | c = vec4(0.0, 0.0, 0.0, 1.0); 66 | } 67 | 68 | vec4 texelColor = texture(tex, uv) * colDiffuse * fragColor; 69 | vec3 tc = c.rgb; 70 | vec4 fx_col = vec4(tc, 1.0); 71 | 72 | return mix(texelColor, fx_col, stitch_mix); 73 | } 74 | 75 | void main() { 76 | vec2 frag_coord = fragTexCoord.xy * i_resolution.xy; 77 | vec4 draw_col = frag_shader(texture0, frag_coord); 78 | 79 | finalColor = draw_col; 80 | } 81 | -------------------------------------------------------------------------------- /demo/content/shader/grayscale.frag: -------------------------------------------------------------------------------- 1 | #version 330 2 | 3 | // Input vertex attributes (from vertex shader) 4 | in vec2 fragTexCoord; 5 | in vec4 fragColor; 6 | 7 | // Input uniform values 8 | uniform sampler2D texture0; 9 | uniform vec4 colDiffuse; 10 | 11 | // Output fragment color 12 | out vec4 finalColor; 13 | 14 | // NOTE: Add here your custom variables 15 | 16 | void main() { 17 | // Texel color fetching from texture sampler 18 | vec4 texelColor = texture(texture0, fragTexCoord)*colDiffuse*fragColor; 19 | 20 | // Convert texel color to grayscale using NTSC conversion weights 21 | float gray = dot(texelColor.rgb, vec3(0.299, 0.587, 0.114)); 22 | 23 | // Calculate final fragment color 24 | finalColor = vec4(gray, gray, gray, texelColor.a); 25 | } -------------------------------------------------------------------------------- /demo/content/shader/present.frag: -------------------------------------------------------------------------------- 1 | #version 330 2 | 3 | // Input vertex attributes (from vertex shader) 4 | in vec2 fragTexCoord; 5 | in vec4 fragColor; 6 | 7 | // Input uniform values 8 | uniform sampler2D texture0; 9 | uniform vec4 colDiffuse; 10 | 11 | // Output fragment color 12 | out vec4 finalColor; 13 | 14 | // user vars 15 | uniform vec3 i_resolution; 16 | 17 | #define T(u) texelFetch(tex, ivec2(pix_coord), 0); 18 | 19 | vec4 frag_shader(sampler2D tex, ivec2 pix_coord) { 20 | vec4 base = T(pix_coord); 21 | 22 | return base; 23 | } 24 | 25 | void main() { 26 | ivec2 pix_coord = ivec2(fragTexCoord.xy * i_resolution.xy); 27 | vec2 frag_coord = fragTexCoord.xy * i_resolution.xy; 28 | 29 | vec4 draw_col = frag_shader(texture0, pix_coord); 30 | 31 | finalColor = draw_col; 32 | } -------------------------------------------------------------------------------- /demo/content/shader/simple.frag: -------------------------------------------------------------------------------- 1 | #version 330 2 | 3 | // Input vertex attributes (from vertex shader) 4 | in vec2 fragTexCoord; 5 | in vec4 fragColor; 6 | 7 | // Input uniform values 8 | uniform sampler2D texture0; 9 | uniform vec4 colDiffuse; 10 | 11 | // Output fragment color 12 | out vec4 finalColor; 13 | 14 | // user vars 15 | uniform vec3 i_resolution; 16 | uniform int i_frame; 17 | uniform float i_time; 18 | 19 | // shadertoy compat 20 | #define iResolution i_resolution 21 | #define iFrame i_frame 22 | #define iTime i_time 23 | 24 | #define T(u) texelFetch(tex, ivec2(pix_coord), 0); 25 | 26 | vec4 frag_shader(sampler2D tex, vec2 frag_coord) { 27 | // normalized pixel coordinates (from 0 to 1) 28 | vec2 uv = frag_coord / i_resolution.xy; 29 | 30 | // time varying pixel color 31 | vec3 col = 0.5 + 0.5 * cos(i_time + uv.xyx + vec3(0, 2, 4)); 32 | return vec4(col, 1.0); 33 | } 34 | 35 | void main() { 36 | vec2 frag_coord = fragTexCoord.xy * i_resolution.xy; 37 | vec4 draw_col = frag_shader(texture0, frag_coord); 38 | 39 | finalColor = draw_col; 40 | } -------------------------------------------------------------------------------- /demo/content/shader/toy/jupii.frag: -------------------------------------------------------------------------------- 1 | #ifdef GL_ES 2 | precision mediump float; 3 | #endif 4 | 5 | float rand(vec2 co, float seed){ 6 | return fract(sin(dot(co.xy + seed ,vec2(12.9898,78.233))) * 43758.5453); 7 | } 8 | 9 | vec3 makeJupiter(vec2 uv) 10 | { 11 | float time = iTime; 12 | float timeScale = .5; 13 | vec2 zoom = vec2(20.,5.5); 14 | vec2 offset = vec2(2.,1.); 15 | 16 | 17 | vec2 point = uv * zoom + offset; 18 | float p_x = float(point.x); 19 | float p_y = float(point.y); 20 | 21 | // detail levels of swirl bands 22 | float a_x = .2; 23 | float a_y = .3; 24 | 25 | // compute iterations to get fractal waves 26 | for (int i = 1; i < int(10); i++) { 27 | float float_i = float(i); 28 | point.x += a_x * sin(float_i * point.y + time * timeScale); 29 | point.y += a_y * cos(float_i * point.x); 30 | } 31 | 32 | // colors from point positions 33 | float pr = cos(point.x + point.y + 1.3); 34 | float pg = sin(point.x + point.y + 2.0); 35 | float pb = (sin(point.x + point.y + 0.9) + cos(point.x + point.y + 0.9)); 36 | 37 | // linear transform of colors 38 | float r = pr * .40 + .50; 39 | float g = pg * .40 + .36; 40 | float b = pb * .25 + .20; 41 | 42 | // recurve colors intensity 43 | r = pow(r, .98); 44 | g = pow(g, .91); 45 | b = pow(b, .88); 46 | 47 | // create color from rgb 48 | vec3 rgbcol = vec3(r, g, b); 49 | rgbcol += vec3(.1); 50 | 51 | return rgbcol; 52 | } 53 | 54 | void mainImage( out vec4 fragColor, in vec2 fragCoord ) 55 | { 56 | vec2 resolution = iResolution.xy; 57 | vec2 texCoord = gl_FragCoord.xy / resolution.xy; 58 | texCoord = vec2(texCoord.y,texCoord.x); 59 | vec2 position = ( gl_FragCoord.xy / resolution.xy ); 60 | 61 | vec2 center = resolution.xy / 2.; 62 | float dis = distance(center, gl_FragCoord.xy); 63 | float radius = resolution.y / 3.; 64 | vec3 atmosphereColor = vec3(.7, .6, .5); 65 | if (dis < radius) { 66 | // Find planet coordinates 67 | vec2 posOnPlanet = (gl_FragCoord.xy - (center - radius)); 68 | vec2 planetCoord = posOnPlanet / (radius * 2.0); 69 | 70 | // Spherify it 71 | planetCoord = planetCoord * 2.0 - 1.0; 72 | float sphereDis = length(planetCoord); 73 | sphereDis = 1.0 - pow(1.0 - sphereDis, .6); 74 | planetCoord = normalize(planetCoord) * sphereDis; 75 | planetCoord = (planetCoord + 1.0) / 2.0; 76 | 77 | // Calculate light amounts 78 | float light = pow(planetCoord.x, 2.0*(cos(iTime*.1 +1.)+1.5)); 79 | float lightAtmosphere = pow(planetCoord.x, 2.); 80 | 81 | // Apply light 82 | vec3 surfaceColor = makeJupiter(texCoord); 83 | surfaceColor *= light; 84 | 85 | // Atmosphere 86 | float fresnelIntensity = pow(dis / radius, 3.); 87 | vec3 fresnel = mix(surfaceColor, atmosphereColor, fresnelIntensity * lightAtmosphere); 88 | 89 | fragColor = vec4(fresnel.rgb, 1); 90 | fragColor *= texCoord.x * 2.; 91 | } 92 | else { 93 | // Render stars 94 | float starAmount = rand(gl_FragCoord.xy, 0.0); 95 | vec3 background = vec3(0, 0, 0); 96 | if (starAmount < .01) { 97 | float intensity = starAmount * 1000.0 / 4.0; 98 | intensity = clamp(intensity, .1, .3); 99 | background = vec3(intensity); 100 | } 101 | 102 | // Atmosphere on top 103 | float outter = distance(center, gl_FragCoord.xy) / resolution.y; 104 | outter = 1.0 - outter; 105 | outter = clamp(outter, 0.5, 0.8); 106 | outter = (outter - .5) / .3; 107 | outter = pow(outter, 2.8); 108 | //outter *= texCoord.x * 1.5; 109 | 110 | // Add atmosphere on top 111 | fragColor = vec4(background + atmosphereColor * outter, 1); 112 | } 113 | } 114 | -------------------------------------------------------------------------------- /demo/multiscene/.gitignore: -------------------------------------------------------------------------------- 1 | .dub 2 | docs.json 3 | __dummy.html 4 | docs/ 5 | /multiscene 6 | multiscene.so 7 | multiscene.dylib 8 | multiscene.dll 9 | multiscene.a 10 | multiscene.lib 11 | multiscene-test-* 12 | *.exe 13 | *.o 14 | *.obj 15 | *.lst 16 | -------------------------------------------------------------------------------- /demo/multiscene/dub.sdl: -------------------------------------------------------------------------------- 1 | name "multiscene" 2 | description "multiple scenes" 3 | authors "redthing1" 4 | copyright "Copyright © 2020, redthing1" 5 | license "proprietary" 6 | dependency "reng" path="../.." 7 | 8 | subConfiguration "reng" "lib-minimal" -------------------------------------------------------------------------------- /demo/multiscene/source/app.d: -------------------------------------------------------------------------------- 1 | import std.stdio; 2 | 3 | import re; 4 | import re.math; 5 | import std.stdio; 6 | import play; 7 | 8 | class Game : Core { 9 | enum WIDTH = 800; 10 | enum HEIGHT = 400; 11 | 12 | this() { 13 | super(WIDTH, HEIGHT, "multiscene"); 14 | } 15 | 16 | override void initialize() { 17 | default_resolution = Vector2(WIDTH / 4, HEIGHT / 4); 18 | content.paths ~= "../content/"; 19 | 20 | load_scenes([new PlayScene()]); 21 | } 22 | } 23 | 24 | void main() { 25 | auto game = new Game(); // init game 26 | game.run(); 27 | game.destroy(); // clean up 28 | } 29 | -------------------------------------------------------------------------------- /demo/multiscene/source/play.d: -------------------------------------------------------------------------------- 1 | module play; 2 | 3 | static import raylib; 4 | 5 | import re; 6 | import re.gfx; 7 | import re.gfx.shapes.rect; 8 | import re.ng.camera; 9 | import re.math; 10 | import re.phys.kin2d; 11 | 12 | import std.stdio; 13 | 14 | class PlayScene : Scene2D { 15 | override void setup() { 16 | use_default_viewport = false; 17 | super.setup(); 18 | } 19 | 20 | override void on_start() { 21 | auto bg_tween = Tweener.tween(clear_color, Colors.DARKGRAY, 22 | Colors.LIGHTGRAY, 2, &Ease.QuadIn); 23 | bg_tween.start(); 24 | 25 | auto box1 = create_entity("box1", Vector2(20, 20)); 26 | box1.add_component(new ColorRect(Vector2(8, 8), Colors.BLUE)); 27 | auto box1_body = box1.add_component!KinBody2D(); 28 | box1_body.angular_accel = 0.1; 29 | 30 | auto box2 = create_entity("box2", Vector2(60, 40)); 31 | box2.add_component(new ColorRect(Vector2(8, 8), Colors.RED)); 32 | auto box2_body = box2.add_component!KinBody2D(); 33 | box2_body.angular_accel = -0.07; 34 | 35 | // custom viewports with custom cameras 36 | auto half_vp_resolution = Vector2(100, 100); 37 | 38 | // cam following the first box 39 | auto cam1_nt = create_entity("cam1"); 40 | auto cam1 = cam1_nt.add_component(new SceneCamera2D()); 41 | auto left_half_output_bounds = Rectangle( 42 | 0, 0, 43 | Core.window.screen_width / 2, 44 | Core.window.screen_height 45 | ); 46 | auto vp1 = add_viewport(cam1, left_half_output_bounds, half_vp_resolution); 47 | auto follow1 = cam1_nt.add_component(new CameraFollow2D(vp1, box1, 0.05)); 48 | 49 | // cam following the second box 50 | auto cam2_nt = create_entity("cam2"); 51 | auto cam2 = cam2_nt.add_component(new SceneCamera2D()); 52 | auto right_half_output_bounds = Rectangle( 53 | Core.window.screen_width / 2, 0, 54 | Core.window.screen_width / 2, 55 | Core.window.screen_height 56 | ); 57 | auto vp2 = add_viewport(cam2, right_half_output_bounds, half_vp_resolution); 58 | auto follow2 = cam2_nt.add_component(new CameraFollow2D(vp2, box2, 0.05)); 59 | follow2.follow_rotation = true; 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /demo/nuidemo/.gitignore: -------------------------------------------------------------------------------- 1 | .dub 2 | docs.json 3 | __dummy.html 4 | docs/ 5 | /nuidemo 6 | nuidemo.so 7 | nuidemo.dylib 8 | nuidemo.dll 9 | nuidemo.a 10 | nuidemo.lib 11 | nuidemo-test-* 12 | *.exe 13 | *.o 14 | *.obj 15 | *.lst 16 | -------------------------------------------------------------------------------- /demo/nuidemo/dub.sdl: -------------------------------------------------------------------------------- 1 | name "nuidemo" 2 | description "A minimal D application." 3 | authors "no" 4 | copyright "Copyright © 2022, no" 5 | license "proprietary" 6 | dependency "reng" path="../.." 7 | dependency "dray-nuklear" version=">=0.6.0 <0.7.0" 8 | subConfiguration "dray-nuklear" "debug" 9 | -------------------------------------------------------------------------------- /demo/nuidemo/res/SourceSansPro-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bmchtech/rengfx/83cc5fab8062d7d2d6336295083fbf77434a2d59/demo/nuidemo/res/SourceSansPro-Regular.ttf -------------------------------------------------------------------------------- /demo/nuidemo/source/app.d: -------------------------------------------------------------------------------- 1 | module app; 2 | 3 | import std.stdio; 4 | 5 | import re; 6 | import re.math; 7 | import std.stdio; 8 | import gui_scene; 9 | import std.getopt; 10 | 11 | static import raylib; 12 | 13 | class Game : Core { 14 | enum WIDTH = 960; 15 | enum HEIGHT = 540; 16 | 17 | this() { 18 | window_resizable = true; 19 | render_oversample_factor = 2; 20 | default_filter_mode = raylib.TextureFilter.TEXTURE_FILTER_ANISOTROPIC_16X; 21 | sync_render_target_to_window_resolution = true; 22 | 23 | super(WIDTH, HEIGHT, "nuidemo"); 24 | } 25 | 26 | override void initialize() { 27 | // we will use a custom console 28 | this.inspector_overlay.console.reset(); 29 | 30 | content.paths ~= ["./content/", "./res/"]; 31 | load_scenes([new GuiScene()]); 32 | } 33 | } 34 | 35 | int main(string[] args) { 36 | auto game = new Game(); // init game 37 | game.run(); 38 | game.destroy(); // clean up 39 | 40 | return 0; 41 | } 42 | -------------------------------------------------------------------------------- /demo/nuidemo/source/gui_scene.d: -------------------------------------------------------------------------------- 1 | module gui_scene; 2 | 3 | import re; 4 | import re.gfx; 5 | import re.math; 6 | import std.format; 7 | import re.util.interop; 8 | import re.ng.diag.console; 9 | 10 | import gui_root; 11 | 12 | static import raylib; 13 | static import raygui; 14 | 15 | class GuiScene : Scene2D { 16 | size_t yoop_counter = 0; 17 | 18 | override void on_start() { 19 | clear_color = Colors.RAYWHITE; 20 | viewports[0].sync_maximized = true; 21 | 22 | // add gui root 23 | auto ui_root = create_entity("ui_root", Vector2.zero); 24 | ui_root.add_component!GuiRoot(); 25 | 26 | // set up console commands 27 | yoop_counter = 0; 28 | Core.inspector_overlay.console.add_command(ConsoleCommand("yoop", &yoop, "yoop yoop yoop")); 29 | } 30 | 31 | override void on_unload() { 32 | // reset the console 33 | Core.inspector_overlay.console.reset(); 34 | } 35 | 36 | void yoop(string[] args) { 37 | Core.log.info("yoop %s", yoop_counter); 38 | yoop_counter++; 39 | } 40 | 41 | override void update() { 42 | super.update(); 43 | } 44 | 45 | override void render_hook() { 46 | // draw fps in bottom right corner 47 | auto ui_scale = cast(int)(Core.window.dpi_scale * Core.render_oversample_factor); 48 | auto font_size = 16 * ui_scale; 49 | raylib.DrawText( 50 | format("%s", Core.fps).c_str(), 51 | cast(int)(resolution.x - font_size - 30), cast(int)(resolution.y - font_size - 24), 52 | font_size, Colors.WHITE 53 | ); 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /demo/nuidemo/source/style.d: -------------------------------------------------------------------------------- 1 | module style; 2 | 3 | import raylib; 4 | import nuklear; 5 | import raylib_nuklear; 6 | 7 | enum UI_PAD = 8; 8 | 9 | void apply_style(nk_context* ctx) { 10 | 11 | // nk_color[nk_style_colors.NK_COLOR_COUNT] table; 12 | // table[nk_style_colors.NK_COLOR_TEXT] = nk_rgba(190, 190, 190, 255); 13 | // table[nk_style_colors.NK_COLOR_WINDOW] = nk_rgba(30, 33, 40, 215); 14 | // table[nk_style_colors.NK_COLOR_HEADER] = nk_rgba(181, 45, 69, 220); 15 | // table[nk_style_colors.NK_COLOR_BORDER] = nk_rgba(51, 55, 67, 255); 16 | // table[nk_style_colors.NK_COLOR_BUTTON] = nk_rgba(181, 45, 69, 255); 17 | // table[nk_style_colors.NK_COLOR_BUTTON_HOVER] = nk_rgba(190, 50, 70, 255); 18 | // table[nk_style_colors.NK_COLOR_BUTTON_ACTIVE] = nk_rgba(195, 55, 75, 255); 19 | // table[nk_style_colors.NK_COLOR_TOGGLE] = nk_rgba(51, 55, 67, 255); 20 | // table[nk_style_colors.NK_COLOR_TOGGLE_HOVER] = nk_rgba(45, 60, 60, 255); 21 | // table[nk_style_colors.NK_COLOR_TOGGLE_CURSOR] = nk_rgba(181, 45, 69, 255); 22 | // table[nk_style_colors.NK_COLOR_SELECT] = nk_rgba(51, 55, 67, 255); 23 | // table[nk_style_colors.NK_COLOR_SELECT_ACTIVE] = nk_rgba(181, 45, 69, 255); 24 | // table[nk_style_colors.NK_COLOR_SLIDER] = nk_rgba(51, 55, 67, 255); 25 | // table[nk_style_colors.NK_COLOR_SLIDER_CURSOR] = nk_rgba(181, 45, 69, 255); 26 | // table[nk_style_colors.NK_COLOR_SLIDER_CURSOR_HOVER] = nk_rgba(186, 50, 74, 255); 27 | // table[nk_style_colors.NK_COLOR_SLIDER_CURSOR_ACTIVE] = nk_rgba(191, 55, 79, 255); 28 | // table[nk_style_colors.NK_COLOR_PROPERTY] = nk_rgba(51, 55, 67, 255); 29 | // table[nk_style_colors.NK_COLOR_EDIT] = nk_rgba(51, 55, 67, 225); 30 | // table[nk_style_colors.NK_COLOR_EDIT_CURSOR] = nk_rgba(190, 190, 190, 255); 31 | // table[nk_style_colors.NK_COLOR_COMBO] = nk_rgba(51, 55, 67, 255); 32 | // table[nk_style_colors.NK_COLOR_CHART] = nk_rgba(51, 55, 67, 255); 33 | // table[nk_style_colors.NK_COLOR_CHART_COLOR] = nk_rgba(170, 40, 60, 255); 34 | // table[nk_style_colors.NK_COLOR_CHART_COLOR_HIGHLIGHT] = nk_rgba(255, 0, 0, 255); 35 | // table[nk_style_colors.NK_COLOR_SCROLLBAR] = nk_rgba(30, 33, 40, 255); 36 | // table[nk_style_colors.NK_COLOR_SCROLLBAR_CURSOR] = nk_rgba(64, 84, 95, 255); 37 | // table[nk_style_colors.NK_COLOR_SCROLLBAR_CURSOR_HOVER] = nk_rgba(70, 90, 100, 255); 38 | // table[nk_style_colors.NK_COLOR_SCROLLBAR_CURSOR_ACTIVE] = nk_rgba(75, 95, 105, 255); 39 | // table[nk_style_colors.NK_COLOR_TAB_HEADER] = nk_rgba(181, 45, 69, 220); 40 | // nk_style_from_table(ctx, cast(nk_color*) table); 41 | 42 | ctx.style.button.padding.x = UI_PAD; 43 | } 44 | -------------------------------------------------------------------------------- /demo/pong/.gitignore: -------------------------------------------------------------------------------- 1 | .dub 2 | docs.json 3 | __dummy.html 4 | docs/ 5 | /pong 6 | pong.so 7 | pong.dylib 8 | pong.dll 9 | pong.a 10 | pong.lib 11 | pong-test-* 12 | *.exe 13 | *.o 14 | *.obj 15 | *.lst 16 | -------------------------------------------------------------------------------- /demo/pong/dub.sdl: -------------------------------------------------------------------------------- 1 | name "pong" 2 | description "pong demo" 3 | authors "redthing1" 4 | copyright "Copyright © 2020, redthing1" 5 | license "proprietary" 6 | dependency "reng" path="../.." 7 | 8 | subConfiguration "reng" "lib-minimal" -------------------------------------------------------------------------------- /demo/pong/source/app.d: -------------------------------------------------------------------------------- 1 | import std.stdio; 2 | 3 | import re; 4 | import re.math; 5 | import std.stdio; 6 | import play; 7 | 8 | class Game : Core { 9 | enum WIDTH = 480; 10 | enum HEIGHT = 800; 11 | 12 | this() { 13 | super(WIDTH, HEIGHT, "pong"); 14 | } 15 | 16 | override void initialize() { 17 | default_resolution = Vector2(WIDTH / 2, HEIGHT / 2); 18 | content.paths ~= ["../content/", "content/"]; 19 | 20 | load_scenes([new PlayScene()]); 21 | } 22 | } 23 | 24 | void main() { 25 | auto game = new Game(); // init game 26 | game.run(); 27 | game.destroy(); // clean up 28 | } 29 | -------------------------------------------------------------------------------- /demo/pong/source/comp/ai.d: -------------------------------------------------------------------------------- 1 | module comp.ai; 2 | 3 | import re; 4 | import comp.input; 5 | import comp.ball; 6 | 7 | class AiPlayer : Component, Updatable { 8 | private LogicController controller; 9 | private Ball ball; 10 | 11 | this(Ball ball) { 12 | this.ball = ball; 13 | } 14 | 15 | override void setup() { 16 | controller = entity.get_component!LogicController(); 17 | } 18 | 19 | void update() { 20 | controller.zero(); 21 | if (ball.entity.position2.x < entity.position2.x) { 22 | controller.move_logical.logic_value = -1; 23 | } 24 | if (ball.entity.position2.x > entity.position2.x) { 25 | controller.move_logical.logic_value = 1; 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /demo/pong/source/comp/ball.d: -------------------------------------------------------------------------------- 1 | module comp.ball; 2 | 3 | import re; 4 | import re.math; 5 | import re.gfx; 6 | import std.random; 7 | import comp.score; 8 | import comp.paddle; 9 | import std.math; 10 | 11 | class Ball : Component, Updatable { 12 | mixin Reflect; 13 | private enum base_speed = 160; 14 | private float speed = base_speed; 15 | private float speed_up = 20; 16 | private Vector2 direction; 17 | private SpriteRenderer spr_ren; 18 | private Paddle[] paddles; 19 | 20 | alias res = Core.default_resolution; 21 | 22 | override void setup() { 23 | spr_ren = entity.get_component!SpriteRenderer; 24 | respawn(); 25 | } 26 | 27 | void respawn() { 28 | auto x_dir = [-1, 1].choice(Rng.rng); 29 | auto y_dir = [-1, 1].choice(Rng.rng); 30 | direction = Vector2(x_dir, y_dir); 31 | speed = base_speed; 32 | 33 | entity.position2 = Vector2(res.x / 2, res.y / 2); 34 | } 35 | 36 | void bounce_on(Paddle paddle) { 37 | paddles ~= paddle; 38 | } 39 | 40 | void update() { 41 | // update direction 42 | if (entity.position2.x + spr_ren.bounds.width / 2 >= res.x) { 43 | direction = Vector2(-1, direction.y); 44 | } 45 | 46 | if (entity.position2.x - spr_ren.bounds.width / 2 <= 0) { 47 | direction = Vector2(1, direction.y); 48 | } 49 | 50 | foreach (paddle; paddles) { 51 | // check if within paddle Y 52 | if (abs(entity.position2.y - paddle.entity.position2.y) < 5) { 53 | // check paddle X 54 | if (abs(entity.position2.x - paddle.entity.position2.x) < 60) { 55 | direction = Vector2(direction.x, -direction.y); 56 | } 57 | } 58 | } 59 | 60 | if (entity.position2.y + spr_ren.bounds.height / 2 >= res.y) { 61 | // hit the bottom, ENEMY SCORE 62 | Core.primary_scene.get_entity("score").get_component!Scoreboard().add_point_enemy(); 63 | respawn(); 64 | } 65 | 66 | if (entity.position2.y - spr_ren.bounds.height / 2 <= 0) { 67 | // hit the top, PLAYER SCORE 68 | Core.primary_scene.get_entity("score").get_component!Scoreboard().add_point_player(); 69 | respawn(); 70 | } 71 | 72 | entity.position2 = entity.position2 + (direction * speed * Time.delta_time); 73 | 74 | // increase speed 75 | speed += Time.delta_time * speed_up; 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /demo/pong/source/comp/input.d: -------------------------------------------------------------------------------- 1 | module comp.input; 2 | 3 | import re.ecs; 4 | import re.input; 5 | 6 | abstract class InputController : Component { 7 | public VirtualAxis move; 8 | 9 | this() { 10 | move = new VirtualAxis(); 11 | } 12 | 13 | public override void destroy() { 14 | move.unregister(); 15 | } 16 | } 17 | 18 | class PlayerController : InputController { 19 | this() { 20 | move.nodes ~= new VirtualAxis.KeyboardKeys(Keys.KEY_RIGHT, Keys.KEY_LEFT); 21 | } 22 | } 23 | 24 | class LogicController : InputController { 25 | public VirtualAxis.LogicAxis move_logical; 26 | 27 | this() { 28 | move.nodes ~= (move_logical = new VirtualAxis.LogicAxis()); 29 | } 30 | 31 | void zero() { 32 | move_logical.logic_value = 0; 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /demo/pong/source/comp/paddle.d: -------------------------------------------------------------------------------- 1 | module comp.paddle; 2 | 3 | import re; 4 | import re.math; 5 | import re.gfx; 6 | import comp.input; 7 | 8 | class Paddle : Component, Updatable { 9 | public float speed = 160; 10 | private InputController controls; 11 | private SpriteRenderer spr_ren; 12 | 13 | override void setup() { 14 | controls = entity.get_component!InputController; 15 | spr_ren = entity.get_component!SpriteRenderer; 16 | } 17 | 18 | void update() { 19 | if (controls.move.value < 0) { 20 | entity.position2 = entity.position2 + Vector2(-speed * Time.delta_time, 0); 21 | } 22 | 23 | if (controls.move.value > 0) { 24 | entity.position2 = entity.position2 + Vector2(speed * Time.delta_time, 0); 25 | } 26 | 27 | if (entity.position2.x - spr_ren.bounds.width / 2 <= 0) { 28 | entity.position2 = Vector2(spr_ren.bounds.width / 2, entity.position2.y); 29 | } 30 | 31 | if (entity.position2.x + spr_ren.bounds.width / 2 >= entity.scene.resolution.x) { 32 | entity.position2 = Vector2(entity.scene.resolution.x - spr_ren.bounds.width / 2, entity.position2.y); 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /demo/pong/source/comp/score.d: -------------------------------------------------------------------------------- 1 | module comp.score; 2 | 3 | import re.ecs; 4 | import re.gfx.text; 5 | import std.string; 6 | 7 | class Scoreboard : Component { 8 | private Text text; 9 | public int player_score = 0; 10 | public int enemy_score = 0; 11 | 12 | override void setup() { 13 | text = entity.get_component!Text(); 14 | refresh(); 15 | } 16 | 17 | public void add_point_player() { 18 | player_score++; 19 | refresh(); 20 | } 21 | 22 | public void add_point_enemy() { 23 | enemy_score++; 24 | refresh(); 25 | } 26 | 27 | public void refresh() { 28 | text.text = format("%d | %d", player_score, enemy_score); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /demo/pong/source/play.d: -------------------------------------------------------------------------------- 1 | module play; 2 | 3 | import re; 4 | import re.gfx; 5 | import re.math; 6 | import comp.input; 7 | import comp.paddle; 8 | import comp.ball; 9 | import comp.score; 10 | import comp.ai; 11 | 12 | class PlayScene : Scene2D { 13 | override void on_start() { 14 | clear_color = Colors.BLACK; 15 | 16 | auto ball_tex = Core.content.load_texture2d("ball.png").front; 17 | auto paddle_tex = Core.content.load_texture2d("paddle.png").front; 18 | 19 | auto padding = 20; 20 | 21 | auto paddle_sprite = new Sprite(paddle_tex); 22 | 23 | auto ball_nt = create_entity("ball", Vector2(resolution.x / 2, resolution.y / 2)); 24 | ball_nt.add_component(new SpriteRenderer(new Sprite(ball_tex))); 25 | auto ball = ball_nt.add_component!Ball(); 26 | 27 | auto player = create_entity("player", Vector2(resolution.x / 2, resolution.y - padding)); 28 | player.add_component(new SpriteRenderer(paddle_sprite)); 29 | player.add_component!PlayerController(); 30 | player.add_component!Paddle(); 31 | 32 | auto alice = create_entity("alice", Vector2(resolution.x / 2, padding)); 33 | alice.add_component(new SpriteRenderer(paddle_sprite)); 34 | alice.add_component!LogicController(); 35 | alice.add_component!Paddle(); 36 | alice.add_component(new AiPlayer(ball)); 37 | 38 | ball.bounce_on(player.get_component!Paddle()); 39 | ball.bounce_on(alice.get_component!Paddle()); 40 | 41 | auto pong = create_entity("pong", Vector2(padding, resolution.y / 2)); 42 | auto pong_text = pong.add_component(new Text(Text.default_font, "pong", Text.default_size, Colors.WHITE)); 43 | pong_text.set_align(Text.Align.Close, Text.Align.Center); 44 | 45 | auto score = create_entity("score", Vector2(resolution.x - padding, resolution.y / 2)); 46 | auto score_text = score.add_component(new Text(Text.default_font, string.init, Text.default_size, Colors.WHITE)); 47 | score_text.set_align(Text.Align.Far, Text.Align.Center); 48 | score.add_component!Scoreboard(); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /demo/quickshd/.gitignore: -------------------------------------------------------------------------------- 1 | .dub 2 | docs.json 3 | __dummy.html 4 | docs/ 5 | /quickshd 6 | quickshd.so 7 | quickshd.dylib 8 | quickshd.dll 9 | quickshd.a 10 | quickshd.lib 11 | quickshd-test-* 12 | *.exe 13 | *.o 14 | *.obj 15 | *.lst 16 | -------------------------------------------------------------------------------- /demo/quickshd/dub.sdl: -------------------------------------------------------------------------------- 1 | name "quickshd" 2 | description "quickshd 3d demo" 3 | authors "redthing1" 4 | copyright "Copyright © 2020, redthing1" 5 | license "proprietary" 6 | dependency "reng" path="../.." 7 | 8 | subConfiguration "reng" "lib-minimal" -------------------------------------------------------------------------------- /demo/quickshd/source/app.d: -------------------------------------------------------------------------------- 1 | module app; 2 | 3 | import std.stdio; 4 | 5 | import re; 6 | import re.gfx; 7 | import re.gfx.shapes.model; 8 | import re.gfx.shapes.grid; 9 | import re.gfx.shapes.cube; 10 | import re.gfx.effects.frag; 11 | import re.ng.camera; 12 | import re.util.hotreload; 13 | import re.math; 14 | static import raylib; 15 | 16 | class Game : Core { 17 | enum WIDTH = 960; 18 | enum HEIGHT = 540; 19 | 20 | this() { 21 | super(WIDTH, HEIGHT, "quickshd"); 22 | } 23 | 24 | override void initialize() { 25 | default_resolution = Vector2(WIDTH, HEIGHT); 26 | content.paths ~= ["../content/", "content/"]; 27 | 28 | load_scenes([new PlayScene()]); 29 | } 30 | } 31 | 32 | class PlayScene : Scene3D { 33 | SceneCamera3D cam; 34 | Effect postfx1; 35 | Effect cubefx1; 36 | Cube cube1; 37 | 38 | override void on_start() { 39 | clear_color = Colors.LIGHTGRAY; 40 | 41 | cam = (cast(Viewport3D) viewports[0]).cam; 42 | 43 | // set the camera position 44 | cam.entity.position = Vector3(0, 10, 10); 45 | 46 | auto block = create_entity("block", Vector3(0, 0, 0)); 47 | cube1 = block.add_component(new Cube(Vector3(2, 2, 2))); 48 | 49 | // point the camera at the block, then orbit it 50 | cam.look_at(block.position); 51 | cam.entity.add_component(new CameraOrbit(block, 0.5)); 52 | 53 | // enable an example shader on cube 54 | // auto cross_stitch = new Effect(Core.content.load_shader(null, 55 | // "shader/cross_stitch.frag").front, Colors.DARKGREEN); 56 | cubefx1 = new FragEffect(this, new ReloadableShader(null, "shader/cross_stitch.frag")); 57 | cubefx1.color = Colors.DARKGREEN; 58 | 59 | // draw a grid at the origin 60 | auto grid = create_entity("grid"); 61 | grid.add_component(new Grid3D(10, 1)); 62 | 63 | // add postprocessing 64 | postfx1 = new Effect(new ReloadableShader(null, "shader/chromatic_aberration.frag")); 65 | auto postfx1_pp = new PostProcessor(resolution, postfx1); 66 | postprocessors ~= postfx1_pp; 67 | } 68 | 69 | override void update() { 70 | super.update(); 71 | 72 | cubefx1.update(); 73 | cube1.effect = cubefx1; // update shader effect 74 | // cubefx1.set_shader_var_imm("stitch_mix", cast(float) 0.05); 75 | 76 | postfx1.update(); 77 | postfx1.set_shader_var_imm("sample_offset", cast(float[2])[0.005, 0.0]); 78 | } 79 | } 80 | 81 | void main() { 82 | auto game = new Game(); // init game 83 | game.run(); 84 | game.destroy(); // clean up 85 | } 86 | -------------------------------------------------------------------------------- /demo/shanpes/.gitignore: -------------------------------------------------------------------------------- 1 | .dub 2 | docs.json 3 | __dummy.html 4 | docs/ 5 | /shanpes 6 | shanpes.so 7 | shanpes.dylib 8 | shanpes.dll 9 | shanpes.a 10 | shanpes.lib 11 | shanpes-test-* 12 | *.exe 13 | *.o 14 | *.obj 15 | *.lst 16 | -------------------------------------------------------------------------------- /demo/shanpes/dub.sdl: -------------------------------------------------------------------------------- 1 | name "shanpes" 2 | description "shape demo" 3 | authors "redthing1" 4 | copyright "Copyright © 2020, redthing1" 5 | license "proprietary" 6 | dependency "reng" path="../.." 7 | 8 | subConfiguration "reng" "lib-minimal" -------------------------------------------------------------------------------- /demo/shanpes/source/app.d: -------------------------------------------------------------------------------- 1 | import std.stdio; 2 | 3 | import re; 4 | import re.math; 5 | import std.stdio; 6 | import play; 7 | 8 | class Game : Core { 9 | enum WIDTH = 640; 10 | enum HEIGHT = 480; 11 | 12 | this() { 13 | super(WIDTH, HEIGHT, "shanpes"); 14 | } 15 | 16 | override void initialize() { 17 | default_resolution = Vector2(WIDTH / 4, HEIGHT / 4); 18 | content.paths ~= "../content/"; 19 | 20 | load_scenes([new PlayScene()]); 21 | } 22 | } 23 | 24 | void main() { 25 | auto game = new Game(); // init game 26 | game.run(); 27 | game.destroy(); // clean up 28 | } 29 | -------------------------------------------------------------------------------- /demo/shanpes/source/comp/ai.d: -------------------------------------------------------------------------------- 1 | module comp.ai; 2 | 3 | import re.ecs; 4 | import comp.input; 5 | 6 | class AiPlayer : Component, Updatable { 7 | private LogicController controller; 8 | 9 | override void setup() { 10 | controller = entity.get_component!LogicController(); 11 | } 12 | 13 | void update() { 14 | controller.zero(); 15 | 16 | controller.logic_turn.logic_value = 1; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /demo/shanpes/source/comp/body.d: -------------------------------------------------------------------------------- 1 | module comp.body; 2 | 3 | import re; 4 | import re.math; 5 | import comp.input; 6 | import re.phys.kin2d; 7 | 8 | class ShapeBody : KinBody2D { 9 | /// movement speed 10 | enum move_speed = 40; 11 | /// turn speed 12 | enum turn_speed = PI / 2; 13 | private InputController controller; 14 | 15 | override void setup() { 16 | controller = entity.get_component!InputController(); 17 | 18 | drag = Vector2(move_speed / 4, move_speed / 4); 19 | max_velocity = Vector2(move_speed, move_speed); 20 | } 21 | 22 | override void update() { 23 | super.update(); 24 | 25 | velocity = velocity + (controller.move.value * move_speed * Time.delta_time); 26 | angular_velocity = angular_velocity + (controller.turn.value * turn_speed * Time.delta_time); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /demo/shanpes/source/comp/input.d: -------------------------------------------------------------------------------- 1 | module comp.input; 2 | 3 | import re.ecs; 4 | import re.input; 5 | import re.math; 6 | 7 | abstract class InputController : Component { 8 | public VirtualJoystick move; 9 | public VirtualAxis turn; 10 | 11 | this() { 12 | move = new VirtualJoystick(); 13 | turn = new VirtualAxis(); 14 | } 15 | 16 | public override void destroy() { 17 | move.unregister(); 18 | } 19 | } 20 | 21 | class PlayerController : InputController { 22 | this() { 23 | move.nodes ~= new VirtualJoystick.KeyboardKeys(Keys.KEY_LEFT, 24 | Keys.KEY_RIGHT, Keys.KEY_UP, Keys.KEY_DOWN); 25 | turn.nodes ~= new VirtualAxis.KeyboardKeys(Keys.KEY_E, Keys.KEY_Q); 26 | } 27 | } 28 | 29 | class LogicController : InputController { 30 | public VirtualJoystick.LogicJoystick logic_move; 31 | public VirtualAxis.LogicAxis logic_turn; 32 | 33 | this() { 34 | move.nodes ~= (logic_move = new VirtualJoystick.LogicJoystick()); 35 | turn.nodes ~= (logic_turn = new VirtualAxis.LogicAxis()); 36 | } 37 | 38 | void zero() { 39 | logic_move.logic_value = Vector2Zero; 40 | logic_turn.logic_value = 0; 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /demo/shanpes/source/play.d: -------------------------------------------------------------------------------- 1 | module play; 2 | 3 | import re; 4 | import re.gfx; 5 | import re.gfx.shapes.rect; 6 | import re.ng.camera; 7 | import re.ng.scene; 8 | import re.math; 9 | import comp.input; 10 | import comp.body; 11 | import comp.ai; 12 | static import raylib; 13 | 14 | class PlayScene : Scene2D { 15 | Viewport2D viewport; 16 | SceneCamera2D cam; 17 | 18 | override void on_start() { 19 | viewport = cast(Viewport2D) viewports[0]; 20 | cam = (cast(Viewport2D) viewports[0]).cam; 21 | 22 | auto bg_tween = Tweener.tween(clear_color, Colors.DARKGRAY, 23 | Colors.LIGHTGRAY, 2, &Ease.QuadIn); 24 | bg_tween.start(); 25 | 26 | auto player = create_entity("player", Vector2(20, 20)); 27 | player.add_component(new ColorRect(Vector2(8, 8), Colors.BLUE)); 28 | player.add_component!PlayerController(); 29 | player.add_component!ShapeBody(); 30 | 31 | // follow the player 32 | cam.entity.add_component(new CameraFollow2D(viewport, player, 0.05)); 33 | 34 | auto turret = create_entity("turret", Vector2(60, 60)); 35 | turret.add_component(new ColorRect(Vector2(8, 8), Colors.DARKGRAY)); 36 | turret.add_component!LogicController(); 37 | turret.add_component!ShapeBody(); 38 | turret.add_component!AiPlayer(); 39 | 40 | // add some tweens 41 | auto turret_pos2 = Vector3(20, turret.position2.y, 0); 42 | auto turret_tween_left = Tweener.tween(turret.position, turret.position, turret_pos2, 4, &Ease 43 | .QuadInOut); 44 | auto turret_tween_up = Tweener.tween(turret.position, turret_pos2, Vector3(60, 20, 0), 4, &Ease 45 | .SineOut); 46 | turret_tween_left.add_chain(turret_tween_up); 47 | bg_tween.add_chain(turret_tween_left); // run the tween left chain after bg tween 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /demo/shddraw/.gitignore: -------------------------------------------------------------------------------- 1 | .dub 2 | docs.json 3 | __dummy.html 4 | docs/ 5 | /shddraw 6 | shddraw.so 7 | shddraw.dylib 8 | shddraw.dll 9 | shddraw.a 10 | shddraw.lib 11 | shddraw-test-* 12 | *.exe 13 | *.o 14 | *.obj 15 | *.lst 16 | -------------------------------------------------------------------------------- /demo/shddraw/dub.sdl: -------------------------------------------------------------------------------- 1 | name "shddraw" 2 | description "shddraw 3d demo" 3 | authors "redthing1" 4 | copyright "Copyright © 2020, redthing1" 5 | license "proprietary" 6 | dependency "reng" path="../.." 7 | 8 | subConfiguration "reng" "lib-minimal" -------------------------------------------------------------------------------- /demo/shddraw/source/app.d: -------------------------------------------------------------------------------- 1 | module app; 2 | 3 | import std.stdio; 4 | import std.getopt; 5 | 6 | import re; 7 | import re.math; 8 | import play; 9 | import hud; 10 | 11 | static import raylib; 12 | 13 | class Game : Core { 14 | enum WIDTH = 960; 15 | enum HEIGHT = 540; 16 | 17 | public static string custom_drawshd_path = null; 18 | public static string custom_presentshd_path = null; 19 | 20 | this() { 21 | super(WIDTH, HEIGHT, "shader drawing"); 22 | } 23 | 24 | override void initialize() { 25 | default_resolution = Vector2(WIDTH, HEIGHT); 26 | content.paths ~= ["../content/", "content/"]; 27 | 28 | load_scenes([new PlayScene(), new HUDScene()]); 29 | } 30 | } 31 | 32 | int main(string[] args) { 33 | bool verbose; 34 | auto help = getopt(args, "verbose|v", &verbose, "draw-shader|d", &Game.custom_drawshd_path, "present-shader|p", &Game 35 | .custom_presentshd_path); 36 | 37 | if (help.helpWanted) { 38 | defaultGetoptPrinter("Usage: ./a [-d /path/to/draw.frag -p /path/to/present.frag]", help 39 | .options); 40 | return 1; 41 | } 42 | 43 | raylib.SetConfigFlags(raylib.ConfigFlags.FLAG_MSAA_4X_HINT); 44 | if (verbose) { 45 | raylib.SetTraceLogLevel(raylib.TraceLogLevel.LOG_INFO); 46 | } else { 47 | raylib.SetTraceLogLevel(raylib.TraceLogLevel.LOG_WARNING); 48 | } 49 | 50 | auto game = new Game(); // init game 51 | game.run(); 52 | game.destroy(); // clean up 53 | 54 | return 0; 55 | } 56 | -------------------------------------------------------------------------------- /demo/shddraw/source/hud.d: -------------------------------------------------------------------------------- 1 | module hud; 2 | 3 | import re; 4 | import re.gfx; 5 | import re.math; 6 | import std.format; 7 | import re.util.interop; 8 | 9 | static import raylib; 10 | 11 | class HUDScene : Scene2D { 12 | override void on_start() { 13 | clear_color = Colors.BLANK; 14 | // set the tint of this scene's composite 15 | composite_mode.color = Color(255, 255, 255, 160); 16 | 17 | enum pad = 4; 18 | 19 | // auto msg = create_entity("msg", Vector2(pad, resolution.y - pad)); 20 | // auto hello_text = msg.add_component(new Text(Text.default_font, 21 | // "table.", 10, Colors.PURPLE)); 22 | // hello_text.set_align(Text.Align.Close, Text.Align.Far); 23 | } 24 | 25 | override void render_hook() { 26 | // draw fps 27 | raylib.DrawText(format("%s", Core.fps).c_str(), 8, 8, 8, Colors.WHITE); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /demo/shddraw/source/play.d: -------------------------------------------------------------------------------- 1 | module play; 2 | 3 | import std.stdio; 4 | import optional; 5 | 6 | import re; 7 | import re.gfx; 8 | import re.gfx.shapes.model; 9 | import re.gfx.shapes.grid; 10 | import re.gfx.shapes.cube; 11 | import re.gfx.lighting.basic; 12 | import re.gfx.effects.frag; 13 | import re.ng.camera; 14 | import re.math; 15 | import re.util.orbit; 16 | import re.util.hotreload; 17 | import re.audio; 18 | static import raylib; 19 | 20 | import app; 21 | 22 | /// simple 3d demo scene 23 | class PlayScene : Scene3D { 24 | FragEffect shd_draw; 25 | FragEffect shd_present; 26 | PostProcessor draw_p; 27 | PostProcessor present_p; 28 | 29 | override void on_start() { 30 | clear_color = Colors.WHITE; 31 | 32 | // draw shader 33 | auto draw_shd_path = "shader/blossom.frag"; 34 | if (Game.custom_drawshd_path) { 35 | draw_shd_path = Game.custom_drawshd_path; 36 | } 37 | shd_draw = new FragEffect(this, new ReloadableShader(null, draw_shd_path)); 38 | draw_p = new PostProcessor(resolution, shd_draw); 39 | postprocessors ~= draw_p; 40 | 41 | // present shader 42 | auto present_shd_path = "shader/present.frag"; 43 | if (Game.custom_presentshd_path) { 44 | present_shd_path = Game.custom_presentshd_path; 45 | } 46 | shd_present = new FragEffect(this, new ReloadableShader(null, present_shd_path)); 47 | present_p = new PostProcessor(resolution, shd_present); 48 | postprocessors ~= present_p; 49 | 50 | // enable audio 51 | auto audio = new AudioManager(); 52 | add_manager(audio); 53 | audio.play_music(Core.content.load_music("audio/50_50.mp3").front); 54 | } 55 | 56 | override void update() { 57 | super.update(); 58 | 59 | shd_draw.update(); 60 | shd_present.update(); 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /demo/table/.gitignore: -------------------------------------------------------------------------------- 1 | .dub 2 | docs.json 3 | __dummy.html 4 | docs/ 5 | /table 6 | table.so 7 | table.dylib 8 | table.dll 9 | table.a 10 | table.lib 11 | table-test-* 12 | *.exe 13 | *.o 14 | *.obj 15 | *.lst 16 | -------------------------------------------------------------------------------- /demo/table/Release.mk: -------------------------------------------------------------------------------- 1 | BIN = binary 2 | 3 | CONTENT_SRC := ../../content/ 4 | CONTENT_DST := . 5 | 6 | GET_LIBPATHS := ldd $(BIN) | grep "=> /" | awk '{print $$3}' 7 | COPY_LIB := xargs -I '{}' cp -v '{}' . 8 | 9 | .PHONY: all content $(LIBS) 10 | all: $(BIN) content libs 11 | 12 | $(BIN): 13 | dub build -b release --root .. 14 | cp -v ../$(BIN) $(BIN) 15 | strip $(BIN) 16 | 17 | content: 18 | cp -rvn $(CONTENT_SRC) $(CONTENT_DST) 19 | 20 | # copy shared libraries 21 | libs: $(BIN) 22 | $(GET_LIBPATHS) | grep "raylib" | $(COPY_LIB) 23 | 24 | .PHONY: clean 25 | clean: 26 | rm -v $(BIN) 27 | # remove copied content 28 | rm -rv $(CONTENT_DST)/content 29 | # remove shared libs 30 | rm -rv lib* 31 | -------------------------------------------------------------------------------- /demo/table/dub.sdl: -------------------------------------------------------------------------------- 1 | name "table" 2 | description "table 3d demo" 3 | authors "redthing1" 4 | copyright "Copyright © 2020, redthing1" 5 | license "proprietary" 6 | dependency "reng" path="../.." 7 | 8 | subConfiguration "reng" "lib-minimal" 9 | -------------------------------------------------------------------------------- /demo/table/source/app.d: -------------------------------------------------------------------------------- 1 | module app; 2 | 3 | import std.stdio; 4 | 5 | import re; 6 | import re.math; 7 | import std.stdio; 8 | import play; 9 | import hud; 10 | 11 | class Game : Core { 12 | enum WIDTH = 960; 13 | enum HEIGHT = 540; 14 | 15 | this() { 16 | super(WIDTH, HEIGHT, "table"); 17 | } 18 | 19 | override void initialize() { 20 | default_resolution = Vector2(WIDTH, HEIGHT); 21 | content.paths ~= ["../content/", "content/"]; 22 | 23 | load_scenes([new PlayScene(), new HUDScene()]); 24 | } 25 | } 26 | 27 | void main() { 28 | auto game = new Game(); // init game 29 | game.run(); 30 | game.destroy(); // clean up 31 | } 32 | -------------------------------------------------------------------------------- /demo/table/source/hud.d: -------------------------------------------------------------------------------- 1 | module hud; 2 | 3 | import re; 4 | import re.gfx; 5 | import re.math; 6 | 7 | class HUDScene : Scene2D { 8 | override void on_start() { 9 | clear_color = Colors.BLANK; 10 | // set the tint of this scene's composite 11 | composite_mode.color = Color(255, 255, 255, 160); 12 | 13 | enum pad = 4; 14 | 15 | auto msg = create_entity("msg", Vector2(pad, resolution.y - pad)); 16 | auto hello_text = msg.add_component(new Text(Text.default_font, 17 | "table.", 10, Colors.PURPLE)); 18 | hello_text.set_align(Text.Align.Close, Text.Align.Far); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /demo/table/source/play.d: -------------------------------------------------------------------------------- 1 | module play; 2 | 3 | import re; 4 | import re.gfx; 5 | import re.gfx.shapes.model; 6 | import re.gfx.shapes.grid; 7 | import re.gfx.shapes.cube; 8 | import re.ng.camera; 9 | import re.math; 10 | static import raylib; 11 | 12 | /// simple 3d demo scene 13 | class PlayScene : Scene3D { 14 | SceneCamera3D cam; 15 | private PostProcessor glitch_postproc; 16 | private float[2] sample_offset = [0.005, 0]; 17 | 18 | override void on_start() { 19 | clear_color = Colors.LIGHTGRAY; 20 | 21 | cam = (cast(Viewport3D) viewports[0]).cam; 22 | 23 | // load shader effects and add as a postprocessor 24 | auto ascii_shd = new Effect(Core.content.load_shader(null, "shader/ascii.frag").front, Colors.WHITE); 25 | ascii_shd.set_shader_var_imm("i_resolution", cast(float[3])[ 26 | resolution.x, resolution.y, 1.0 27 | ]); 28 | auto ascii_postproc = new PostProcessor(resolution, ascii_shd); 29 | postprocessors ~= ascii_postproc; 30 | 31 | auto chrm_abr = new Effect(Core.content.load_shader(null, 32 | "shader/chromatic_aberration.frag").front, color_alpha_white(0.8)); 33 | chrm_abr.set_shader_var("sample_offset", sample_offset); 34 | glitch_postproc = new PostProcessor(resolution, chrm_abr); 35 | postprocessors ~= glitch_postproc; 36 | 37 | // set the camera position 38 | cam.entity.position = Vector3(10, 10, 10); 39 | 40 | auto fox = create_entity("fox", Vector3(0, 0, 0)); 41 | auto fox_asset = Core.content.load_model("models/fox.obj").front; 42 | auto fox_model = fox.add_component(new Model3D(fox_asset)); 43 | auto cub = fox.add_component(new Cube(Vector3(1, 1, 1), Colors.GREEN)); 44 | cub.offset = Vector3(0, -4, 0); 45 | 46 | // add a camera to look at the fox 47 | cam.entity.add_component(new CameraOrbit(fox, 0.2)); 48 | // cam.entity.add_component(new CameraMouseNavigation(fox)); 49 | 50 | // draw a grid at the origin 51 | auto grid = create_entity("grid"); 52 | grid.add_component(new Grid3D(20, 1)); 53 | } 54 | 55 | override void update() { 56 | super.update(); 57 | 58 | if (Input.is_mouse_pressed(MouseButton.MOUSE_BUTTON_LEFT)) { 59 | if (Input.is_cursor_locked) { 60 | Input.unlock_cursor(); 61 | } else { 62 | Input.lock_cursor(); 63 | } 64 | } 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /demo/three/.gitignore: -------------------------------------------------------------------------------- 1 | .dub 2 | docs.json 3 | __dummy.html 4 | docs/ 5 | /three 6 | three.so 7 | three.dylib 8 | three.dll 9 | three.a 10 | three.lib 11 | three-test-* 12 | *.exe 13 | *.o 14 | *.obj 15 | *.lst 16 | -------------------------------------------------------------------------------- /demo/three/dub.sdl: -------------------------------------------------------------------------------- 1 | name "three" 2 | description "basic 3d demo" 3 | authors "redthing1" 4 | copyright "Copyright © 2020, redthing1" 5 | license "proprietary" 6 | dependency "reng" path="../.." 7 | 8 | subConfiguration "reng" "lib-minimal" -------------------------------------------------------------------------------- /demo/three/source/app.d: -------------------------------------------------------------------------------- 1 | module app; 2 | 3 | import std.stdio; 4 | 5 | import re; 6 | import re.math; 7 | import std.stdio; 8 | import play; 9 | import hud; 10 | 11 | class Game : Core { 12 | enum WIDTH = 640; 13 | enum HEIGHT = 480; 14 | 15 | this() { 16 | super(WIDTH, HEIGHT, "three"); 17 | } 18 | 19 | override void initialize() { 20 | default_resolution = Vector2(WIDTH, HEIGHT); 21 | content.paths ~= ["../content/", "content/"]; 22 | 23 | load_scenes([new PlayScene(), new HUDScene()]); 24 | } 25 | } 26 | 27 | void main() { 28 | auto game = new Game(); // init game 29 | game.run(); 30 | game.destroy(); // clean up 31 | } 32 | -------------------------------------------------------------------------------- /demo/three/source/hud.d: -------------------------------------------------------------------------------- 1 | module hud; 2 | 3 | import re; 4 | import re.gfx; 5 | import re.math; 6 | 7 | class HUDScene : Scene2D { 8 | override void on_start() { 9 | clear_color = Colors.BLANK; 10 | // set the tint of this scene's composite 11 | composite_mode.color = Color(255, 255, 255, 160); 12 | 13 | enum pad = 4; 14 | 15 | auto msg = create_entity("msg", Vector2(pad, resolution.y - pad)); 16 | auto hello_text = msg.add_component(new Text(Text.default_font, 17 | "hello, third dimension!", 10 * cast(int) Core.scale_factor, Colors.PURPLE 18 | )); 19 | hello_text.set_align(Text.Align.Close, Text.Align.Far); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /demo/three/source/play.d: -------------------------------------------------------------------------------- 1 | module play; 2 | 3 | import re; 4 | import re.gfx; 5 | import re.gfx.shapes.cube; 6 | import re.gfx.shapes.grid; 7 | import re.ng.camera; 8 | import re.math; 9 | static import raylib; 10 | 11 | /// simple 3d demo scene 12 | class PlayScene : Scene3D { 13 | SceneCamera3D cam; 14 | private PostProcessor glitch_postproc; 15 | private float[2] sample_offset = [0.01, 0]; 16 | 17 | override void setup() { 18 | super.setup(); 19 | 20 | default_viewport.resolution = Vector2(160, 120); 21 | resolution = default_viewport.resolution; 22 | } 23 | 24 | override void on_start() { 25 | clear_color = Colors.LIGHTGRAY; 26 | cam = default_viewport.cam; 27 | 28 | // // sample of cropping 29 | // default_viewport.crop_bounds = Rectangle(40, 0, resolution.x - 40, resolution.y); 30 | // default_viewport.output_bounds = default_viewport.crop_bounds.scale( 31 | // Core.window.screen_width / resolution.x); 32 | 33 | // load a shader effect and add it as a postprocessor 34 | auto chrm_abr = new Effect(Core.content.load_shader(null, 35 | "shader/chromatic_aberration.frag").front, color_alpha_white(0.8)); 36 | glitch_postproc = new PostProcessor(resolution, chrm_abr); 37 | glitch_postproc.enabled = false; 38 | postprocessors ~= glitch_postproc; 39 | 40 | // set the camera position 41 | cam.entity.position = Vector3(0, 10, 10); 42 | 43 | auto block = create_entity("block", Vector3(0, 0, 0)); 44 | auto cube = block.add_component(new Cube(Vector3(2, 2, 2))); 45 | 46 | // point the camera at the block, then orbit it 47 | cam.look_at(block.position); 48 | cam.entity.add_component(new CameraOrbit(block, 0.5)); 49 | 50 | // enable an example shader on cube 51 | auto cross_stitch = new Effect(Core.content.load_shader(null, 52 | "shader/cross_stitch.frag").front, Colors.PURPLE); 53 | auto mixAmt = 0.05f; 54 | cross_stitch.set_shader_var("mixAmt", mixAmt); 55 | cube.effect = cross_stitch; 56 | 57 | // draw a grid at the origin 58 | auto grid = create_entity("grid"); 59 | grid.add_component(new Grid3D(10, 1)); 60 | } 61 | 62 | override void update() { 63 | super.update(); 64 | 65 | // allow the postprocessor to be toggled with SPACE 66 | if (Input.is_key_pressed(Keys.KEY_SPACE)) { 67 | glitch_postproc.enabled = !glitch_postproc.enabled; 68 | } 69 | 70 | if (glitch_postproc.enabled) { 71 | // make our postprocess effect fluctuate with time 72 | import std.math : sin; 73 | 74 | sample_offset[0] = 0.010 + 0.005 * sin(Time.total_time / 2); 75 | glitch_postproc.effect.set_shader_var("sample_offset", sample_offset); 76 | } 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /doc/README.md: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /dub.sdl: -------------------------------------------------------------------------------- 1 | name "reng" 2 | description "RE NGINE FX (rengfx) game framework/engine" 3 | authors "redthing1" 4 | copyright "Copyright © 2020-2022, redthing1" 5 | license "Apache-2.0 or proprietary" 6 | x:ddoxFilterArgs "--min-protection=Protected" 7 | dependency "witchcraft" version="~>0.1.9" 8 | dependency "colorize" version="~>1.0.5" 9 | dependency "typetips" version="~>0.1.4" 10 | dependency "minlog" version="~>2.0.0" 11 | dependency "dray" version=">=5.0.0-r5 <5.1.0-0" 12 | dependency "optional" version="~>1.3.0" 13 | 14 | configuration "lib-standard" { 15 | targetType "library" 16 | versions "physics" "vr" 17 | } 18 | configuration "lib-minimal" { 19 | targetType "library" 20 | } 21 | configuration "lib-lite" { 22 | targetType "library" 23 | subConfiguration "dray" "drm-gles2" 24 | libs "GL" "EGL" "drm" "gbm" 25 | versions "physics" "vr" "lite" 26 | } 27 | configuration "unittest" { 28 | dependency "silly" version="~>1.1.1" 29 | targetType "library" 30 | versions "physics" 31 | } 32 | configuration "ddox" { 33 | targetType "library" 34 | versions "physics" 35 | } 36 | -------------------------------------------------------------------------------- /dub.selections.json: -------------------------------------------------------------------------------- 1 | { 2 | "fileVersion": 1, 3 | "versions": { 4 | "bolts": "1.3.1", 5 | "colorize": "1.0.5", 6 | "ddmp": "0.0.1-0.dev.3", 7 | "dray": "5.0.0-r5", 8 | "fluent-asserts": "0.13.3", 9 | "libdparse": "0.14.0", 10 | "minlog": "2.0.0", 11 | "optional": "1.3.0", 12 | "silly": "1.1.1", 13 | "stdx-allocator": "2.77.5", 14 | "typetips": "0.1.4", 15 | "witchcraft": "0.1.9" 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /source/re/audio/audio_manager.d: -------------------------------------------------------------------------------- 1 | module re.audio.audio_manager; 2 | 3 | import re.core; 4 | import re.ecs; 5 | import re.ng.manager; 6 | 7 | static import raylib; 8 | 9 | class AudioManager : Manager, Updatable { 10 | this() { 11 | raylib.InitAudioDevice(); 12 | } 13 | 14 | enum Mode { 15 | Idle, 16 | PlayMusic, 17 | } 18 | 19 | public Mode mode; 20 | public raylib.Music music_stream; 21 | 22 | override void setup() { 23 | } 24 | 25 | override void update() { 26 | switch (mode) { 27 | case Mode.PlayMusic: 28 | raylib.UpdateMusicStream(music_stream); 29 | import std.stdio; 30 | 31 | break; 32 | default: 33 | break; 34 | } 35 | } 36 | 37 | public void play_music(raylib.Music music) { 38 | mode = Mode.PlayMusic; 39 | this.music_stream = music; 40 | raylib.PlayMusicStream(music_stream); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /source/re/audio/package.d: -------------------------------------------------------------------------------- 1 | module re.audio; 2 | 3 | public { 4 | import re.audio.audio_manager; 5 | } -------------------------------------------------------------------------------- /source/re/ecs/component.d: -------------------------------------------------------------------------------- 1 | /** ecs component (unit of composable functionality) */ 2 | 3 | module re.ecs.component; 4 | 5 | import re.ecs.entity; 6 | import re.math.transform; 7 | public import re.util.reflect; 8 | 9 | /// the composable unit of functionality 10 | abstract class Component : ReflectableObject { 11 | mixin Reflect; 12 | 13 | /// owner entity 14 | public Entity entity; 15 | 16 | /// initialize the component. entity is already set. 17 | public void setup() { 18 | } 19 | 20 | /// release resources and clean up 21 | public void destroy() { 22 | } 23 | 24 | /// forward to entity.transform 25 | @property public ref Transform transform() { 26 | return entity.transform; 27 | } 28 | } 29 | 30 | /// basic component classification 31 | enum ComponentType { 32 | Base, 33 | Updatable, 34 | Renderable, 35 | UpdatableRenderable 36 | } 37 | 38 | /// reference to a stored component 39 | struct ComponentId { 40 | /// index in storage 41 | size_t index; 42 | /// entity owner index 43 | size_t owner; 44 | /// component classification 45 | ComponentType type; 46 | } 47 | -------------------------------------------------------------------------------- /source/re/ecs/manager.d: -------------------------------------------------------------------------------- 1 | /** internal orhestration of entity/component system */ 2 | 3 | module re.ecs.manager; 4 | 5 | import re.ecs.entity; 6 | import re.ecs.component; 7 | import re.ecs.storage; 8 | import std.algorithm; 9 | import std.array; 10 | 11 | /// manages the entity/component system 12 | class EntityManager { 13 | /// list of all entities 14 | public Entity[] entities; 15 | /// helper to store components in an optimized way 16 | public ComponentStorage storage; 17 | private size_t[] entities_to_remove; 18 | private size_t entity_counter; 19 | 20 | /// sets up the ECS 21 | this() { 22 | storage = new ComponentStorage(this); 23 | } 24 | 25 | /// create a fresh entity 26 | public Entity create_entity() { 27 | auto nt = new Entity(this); 28 | nt.initialize(); 29 | nt.id = entity_counter++; 30 | entities ~= nt; 31 | return nt; 32 | } 33 | 34 | public Entity get_entity(string name) { 35 | auto list = entities.find!(x => x.name == name); 36 | if (list.length == 0) { 37 | assert(0, "no matching entity was found"); 38 | } 39 | return list[0]; 40 | } 41 | 42 | public bool has_entity(string name) { 43 | return entities.any!(x => x.name == name); 44 | } 45 | 46 | /// remove an entity 47 | public void remove_entity(Entity entity) { 48 | entities.remove!(x => x == entity); 49 | // TODO: entity pooling 50 | } 51 | 52 | /// keeps all the ducks in line 53 | public void update() { 54 | entities_to_remove = []; 55 | for (size_t i = 0; i < entities.length; i++) { 56 | auto nt = entities[i]; 57 | if (!nt.alive) { 58 | entities_to_remove ~= i; 59 | } 60 | } 61 | 62 | // remove entities 63 | foreach (to_remove; entities_to_remove) { 64 | entities = remove(entities, to_remove); 65 | } 66 | } 67 | 68 | /// destroy all entities and components and clean up 69 | public void destroy() { 70 | foreach (entity; entities) { 71 | entity.destroy(); 72 | } 73 | } 74 | } 75 | 76 | @("ecs-basic") 77 | unittest { 78 | class Food : Component { 79 | public bool tasty = true; 80 | } 81 | 82 | auto ecs = new EntityManager(); 83 | auto nt = ecs.create_entity(); 84 | auto food = new Food(); 85 | nt.add_component(food); 86 | assert(nt.has_component!Food, "component was not properly added"); 87 | assert(nt.get_component!Food == food, "component cannot be retrieved"); 88 | nt.remove_component!Food(); 89 | assert(!nt.has_component!Food, "component cannot be removed"); 90 | nt.destroy(); 91 | assert(!nt.alive); 92 | } 93 | 94 | @("ecs-destroy") 95 | unittest { 96 | static class Thing1 : Component { 97 | } 98 | 99 | static class Thing2 : Component { 100 | } 101 | 102 | auto ecs = new EntityManager(); 103 | auto nt1 = ecs.create_entity(); 104 | nt1.add_component!Thing1(); 105 | auto nt2 = ecs.create_entity(); 106 | nt2.add_component!Thing2(); 107 | 108 | ecs.destroy(); 109 | } 110 | 111 | @("ecs-test1") 112 | unittest { 113 | class Butter : Component { 114 | public bool tasty = true; 115 | } 116 | 117 | class Jelly : Component { 118 | public int rank = 4; 119 | } 120 | 121 | auto ecs = new EntityManager(); 122 | auto sandwich1 = ecs.create_entity(); 123 | auto sandwich2 = ecs.create_entity(); 124 | auto sandwich3 = ecs.create_entity(); 125 | 126 | sandwich1.add_component(new Butter()); 127 | sandwich1.add_component(new Jelly()); 128 | assert(sandwich1.has_component!Butter); 129 | assert(sandwich1.has_component!Jelly); 130 | 131 | sandwich2.add_component(new Butter()); 132 | assert(sandwich2.has_component!Butter); 133 | 134 | sandwich3.add_component(new Jelly()); 135 | assert(sandwich3.has_component!Jelly); 136 | 137 | sandwich1.remove_component!Butter; 138 | 139 | // make sure everything else is still good 140 | enum msg = "component storage is unstable"; 141 | assert(!sandwich1.has_component!Butter, msg); 142 | assert(sandwich1.has_component!Jelly, msg); 143 | assert(sandwich2.has_component!Butter, msg); 144 | assert(sandwich3.has_component!Jelly, msg); 145 | 146 | ecs.destroy(); 147 | } 148 | 149 | @("ecs-test2") 150 | unittest { 151 | import re.ecs : Renderable, Updatable; 152 | 153 | static class Brush : Component, Renderable { 154 | void render() { 155 | } 156 | 157 | void debug_render() { 158 | } 159 | } 160 | 161 | static class Control : Component { 162 | } 163 | 164 | static class Paint : Component, Updatable { 165 | void update() { 166 | } 167 | } 168 | 169 | auto ecs = new EntityManager(); 170 | 171 | auto a1 = ecs.create_entity(); 172 | a1.add_component!Brush(); // R 173 | a1.add_component!Control(); // B 174 | a1.add_component!Paint(); // U 175 | 176 | ecs.destroy(); 177 | } 178 | -------------------------------------------------------------------------------- /source/re/ecs/package.d: -------------------------------------------------------------------------------- 1 | /** engine entity component system */ 2 | 3 | module re.ecs; 4 | 5 | public { 6 | // main 7 | import re.ecs.manager; 8 | import re.ecs.entity; 9 | import re.ecs.component; 10 | 11 | // types 12 | import re.ecs.renderable; 13 | import re.ecs.updatable; 14 | } 15 | -------------------------------------------------------------------------------- /source/re/ecs/renderable.d: -------------------------------------------------------------------------------- 1 | /** base interface for renderable components */ 2 | 3 | module re.ecs.renderable; 4 | 5 | import re.math; 6 | 7 | /// a component that can be rendered (drawn) 8 | interface Renderable { 9 | void render(); 10 | void debug_render(); 11 | } 12 | 13 | interface Renderable2D : Renderable { 14 | @property Rectangle bounds(); 15 | } 16 | 17 | interface Renderable3D : Renderable { 18 | @property BoundingBox bounds(); 19 | } 20 | -------------------------------------------------------------------------------- /source/re/ecs/updatable.d: -------------------------------------------------------------------------------- 1 | /** base interface for updatable components */ 2 | 3 | module re.ecs.updatable; 4 | 5 | /// a component that can be updated 6 | interface Updatable { 7 | void update(); 8 | } 9 | -------------------------------------------------------------------------------- /source/re/gfx/color_ext.d: -------------------------------------------------------------------------------- 1 | /** color functions and utilities */ 2 | 3 | module re.gfx.color_ext; 4 | 5 | import re.gfx.raytypes; 6 | import re.math; 7 | import std.math; 8 | static import raylib; 9 | 10 | // - color functions 11 | 12 | pragma(inline) { 13 | /// gets a color that is white with a given alpha 14 | public Color color_alpha_white(float alpha) { 15 | return raylib.ColorFromNormalized(raylib.Vector4(1, 1, 1, alpha)); 16 | } 17 | 18 | /// gets a color from floats 19 | public Color color_alpha_white(float r, float g, float b, float a = 1) { 20 | return raylib.ColorFromNormalized(raylib.Vector4(r, g, b, a)); 21 | } 22 | 23 | /// fades a color 24 | public Color color_fade(Color color, float fade) { 25 | return raylib.Fade(color, fade); 26 | } 27 | 28 | /// gets a color from hsv 29 | public Color color_hsv(float h, float s, float v) { 30 | return raylib.ColorFromHSV(h, s, v); 31 | } 32 | 33 | /// gets a color from rgb 34 | public Color color_rgb(float r, float g, float b, float a = 1.0) { 35 | return raylib.ColorFromNormalized(Vector4(r, g, b, a)); 36 | } 37 | 38 | /// gets a color from rgb 39 | public Color color_rgb(ubyte r, ubyte g, ubyte b, ubyte a = 255) { 40 | return Color(r, g, b, 255); 41 | } 42 | 43 | /// gets a color from rgb in a single value 44 | public Color color_rgb(ubyte v) { 45 | return color_rgb(v, v, v); 46 | } 47 | } 48 | 49 | /// color blending algorithm - from https://stackoverflow.com/a/39924008/13240621 50 | public Color color_blend(Color c1, Color c2, float mix) { 51 | // Mix [0..1] 52 | // 0 --> all c1 53 | // 0.5 --> equal mix of c1 and c2 54 | // 1 --> all c2 55 | 56 | // Invert sRGB gamma compression 57 | c1 = inverse_srgb_companding(c1); 58 | c2 = inverse_srgb_companding(c2); 59 | 60 | Color result; 61 | result.r = cast(ubyte)(c1.r * (1 - mix) + c2.r * (mix)); 62 | result.g = cast(ubyte)(c1.g * (1 - mix) + c2.g * (mix)); 63 | result.b = cast(ubyte)(c1.b * (1 - mix) + c2.b * (mix)); 64 | 65 | // Reapply sRGB gamma compression 66 | result = srgb_companding(result); 67 | 68 | return result; 69 | } 70 | 71 | private Color inverse_srgb_companding(Color c) { 72 | // Convert color from 0..255 to 0..1 73 | float r = c.r / 255; 74 | float g = c.g / 255; 75 | float b = c.b / 255; 76 | 77 | // Inverse Red, Green, and Blue 78 | if (r > 0.04045) 79 | r = pow((r + 0.055) / 1.055, 2.4); 80 | else 81 | r = r / 12.92; 82 | if (g > 0.04045) 83 | g = pow((g + 0.055) / 1.055, 2.4); 84 | else 85 | g = g / 12.92; 86 | if (b > 0.04045) 87 | b = pow((b + 0.055) / 1.055, 2.4); 88 | else 89 | b = b / 12.92; 90 | 91 | // Convert 0..1 back into 0..255 92 | Color result; 93 | result.r = cast(ubyte)(r * 255); 94 | result.g = cast(ubyte)(g * 255); 95 | result.b = cast(ubyte)(b * 255); 96 | 97 | return result; 98 | } 99 | 100 | private Color srgb_companding(Color c) { 101 | // Convert color from 0..255 to 0..1 102 | float r = c.r / 255; 103 | float g = c.g / 255; 104 | float b = c.b / 255; 105 | 106 | // Apply companding to Red, Green, and Blue 107 | if (r > 0.0031308) 108 | r = 1.055 * pow(r, 1 / 2.4) - 0.055; 109 | else 110 | r = r * 12.92; 111 | if (g > 0.0031308) 112 | g = 1.055 * pow(g, 1 / 2.4) - 0.055; 113 | else 114 | g = g * 12.92; 115 | if (b > 0.0031308) 116 | b = 1.055 * pow(b, 1 / 2.4) - 0.055; 117 | else 118 | b = b * 12.92; 119 | 120 | // Convert 0..1 back into 0..255 121 | Color result; 122 | result.r = cast(ubyte)(r * 255); 123 | result.g = cast(ubyte)(g * 255); 124 | result.b = cast(ubyte)(b * 255); 125 | 126 | return result; 127 | } 128 | -------------------------------------------------------------------------------- /source/re/gfx/effect.d: -------------------------------------------------------------------------------- 1 | /** shader based graphical effects */ 2 | 3 | module re.gfx.effect; 4 | 5 | import std.string : toStringz; 6 | 7 | import re.gfx.raytypes; 8 | import re.util.hotreload; 9 | static import raylib; 10 | 11 | /// represents an effect 12 | class Effect { 13 | /// the shader program for the effect 14 | Shader shader; 15 | /// the tint color 16 | Color color; 17 | /// a reloadable shader if any 18 | ReloadableShader reloadable_shader; 19 | 20 | this() { 21 | this(Shader.init); 22 | } 23 | 24 | this(Shader shader, Color color = Colors.WHITE) { 25 | this.shader = shader; 26 | this.color = color; 27 | } 28 | 29 | this(ReloadableShader reloadable_shader, Color color = Colors.WHITE) { 30 | this.reloadable_shader = reloadable_shader; 31 | auto shader = reloadable_shader.reload(); 32 | this(shader, color); 33 | } 34 | 35 | public void update() { 36 | // update the effect 37 | // if hot reload is enabled 38 | if (reloadable_shader) { 39 | // check if we need to reload the shader 40 | if (reloadable_shader.changed()) { 41 | // shader changed, we load it again 42 | reload_shader(); 43 | } 44 | } 45 | } 46 | 47 | protected void reload_shader() { 48 | shader = reloadable_shader.reload(); 49 | } 50 | 51 | /// set a uniform value to an immeditae value 52 | public bool set_shader_var_imm(T)(string name, T value) { 53 | T var = value; 54 | return set_shader_var(name, var); 55 | } 56 | 57 | /// set a uniform value to a variable by reference 58 | public bool set_shader_var(T)(string name, ref T value) { 59 | auto loc = get_shader_loc(name); 60 | if (loc < 0) { 61 | // if the shader variable doesn't exist, return false 62 | return false; 63 | } 64 | 65 | // figure out the uniform var type 66 | raylib.ShaderUniformDataType val_type; 67 | alias vartype = raylib.ShaderUniformDataType; 68 | static if (is(T == float)) { 69 | val_type = vartype.SHADER_UNIFORM_FLOAT; 70 | } else static if (is(T == int)) { 71 | val_type = vartype.SHADER_UNIFORM_INT; 72 | } else static if (is(T == float[2])) { 73 | val_type = vartype.SHADER_UNIFORM_VEC2; 74 | } else static if (is(T == float[3])) { 75 | val_type = vartype.SHADER_UNIFORM_VEC3; 76 | } else static if (is(T == float[4])) { 77 | val_type = vartype.SHADER_UNIFORM_VEC4; 78 | } else static if (is(T == int[2])) { 79 | val_type = vartype.SHADER_UNIFORM_IVEC2; 80 | } else static if (is(T == int[3])) { 81 | val_type = vartype.SHADER_UNIFORM_IVEC3; 82 | } else static if (is(T == int[4])) { 83 | val_type = vartype.SHADER_UNIFORM_IVEC4; 84 | } else { 85 | static assert(0, "unrecognized shader value data type"); 86 | } 87 | raylib.SetShaderValue(shader, loc, &value, val_type); 88 | return true; 89 | } 90 | 91 | /// get location of uniform in shader. returns -1 if not found 92 | public int get_shader_loc(string name) { 93 | return raylib.GetShaderLocation(shader, name.toStringz); 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /source/re/gfx/effects/frag.d: -------------------------------------------------------------------------------- 1 | /** fragment shader effects with auto-synced uniforms */ 2 | 3 | module re.gfx.effects.frag; 4 | 5 | import re.ng.scene; 6 | import re.gfx.raytypes; 7 | import re.gfx.effect; 8 | import re.util.hotreload; 9 | static import raylib; 10 | 11 | /// fragment shader effect 12 | class FragEffect : Effect { 13 | enum shader_uni_resolution = "i_resolution"; 14 | enum shader_uni_frame = "i_frame"; 15 | enum shader_uni_time = "i_time"; 16 | // enum shader_uni_mouse = "i_mouse"; 17 | 18 | Scene scene; 19 | int start_frame = 0; 20 | float start_time = 0; 21 | 22 | this(Scene scene, Shader shader) { 23 | super(shader, Colors.WHITE); 24 | setup(scene); 25 | } 26 | 27 | this(Scene scene, ReloadableShader reloadable_shader) { 28 | super(reloadable_shader, Colors.WHITE); 29 | setup(scene); 30 | } 31 | 32 | private void setup(Scene scene) { 33 | this.scene = scene; 34 | 35 | // initialize uniforms 36 | init_time(); 37 | sync_uniforms(); 38 | } 39 | 40 | public void init_time() { 41 | start_frame = Time.frame_count; 42 | start_time = Time.total_time; 43 | } 44 | 45 | public void sync_uniforms() { 46 | this.set_shader_var_imm(shader_uni_resolution, cast(float[3])[ 47 | scene.resolution.x, scene.resolution.y, 1.0 48 | ]); 49 | 50 | } 51 | 52 | protected override void reload_shader() { 53 | super.reload_shader(); 54 | 55 | // if reloading, we need to resync uniforms 56 | sync_uniforms(); 57 | } 58 | 59 | public override void update() { 60 | super.update(); 61 | 62 | this.set_shader_var_imm(shader_uni_frame, cast(int)(Time.frame_count - start_frame)); 63 | this.set_shader_var_imm(shader_uni_time, Time.total_time - start_time); 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /source/re/gfx/lighting/blinn_phong.d: -------------------------------------------------------------------------------- 1 | /** blinn-phong lighting (incomplete) */ 2 | 3 | module re.gfx.lighting.blinn_phong; 4 | 5 | import re.core; 6 | import re.ecs; 7 | import re.ng.manager; 8 | import re.ng.scene3d; 9 | import re.gfx; 10 | import re.math; 11 | import std.algorithm; 12 | import std.container.array; 13 | static import raylib; 14 | 15 | /// acts as a manager for Light3D components 16 | class BlinnPhongLightManager : Manager, Updatable { 17 | private Entity[] obj_entities; 18 | 19 | override void update() { 20 | 21 | } 22 | 23 | public void register_entity(Entity nt) { 24 | obj_entities ~= nt; 25 | } 26 | 27 | public void unregister_entity(Entity nt) { 28 | obj_entities.remove!(x => x == nt); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /source/re/gfx/package.d: -------------------------------------------------------------------------------- 1 | /** graphics and renderables */ 2 | 3 | module re.gfx; 4 | 5 | public { 6 | // windowing 7 | import re.gfx.window; 8 | 9 | // renderables 10 | import re.gfx.sprite; 11 | import re.gfx.sprite_renderer; 12 | import re.gfx.text; 13 | 14 | // color 15 | import re.gfx.color_ext; 16 | 17 | // shaders 18 | import re.gfx.effect; 19 | 20 | // rendering 21 | import re.gfx.postprocessor; 22 | import re.gfx.render_ext; 23 | 24 | // raylib 25 | import re.gfx.raytypes; 26 | } 27 | -------------------------------------------------------------------------------- /source/re/gfx/postprocessor.d: -------------------------------------------------------------------------------- 1 | /** postprocessing effects on any render target */ 2 | 3 | module re.gfx.postprocessor; 4 | 5 | import re.gfx; 6 | import re.math; 7 | static import raylib; 8 | 9 | /// applies an effect to a rendertarget 10 | class PostProcessor { 11 | /// the effect to apply to the input buffer 12 | public Effect effect; 13 | /// the render target buffer of this postprocessor 14 | public RenderTarget buffer; 15 | /// whether to enable this postprocessor 16 | public bool enabled = true; 17 | 18 | this(Vector2 resolution, Effect effect) { 19 | this.effect = effect; 20 | 21 | // create render target 22 | buffer = RenderExt.create_render_target(cast(int) resolution.x, cast(int) resolution.y); 23 | } 24 | 25 | /// process the source and render to internal buffer 26 | public void process(RenderTarget source) { 27 | RenderExt.draw_render_target_from(source, buffer, effect); 28 | } 29 | 30 | /// release resources 31 | public void destroy() { 32 | RenderExt.destroy_render_target(buffer); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /source/re/gfx/raytypes.d: -------------------------------------------------------------------------------- 1 | /** raylib types */ 2 | 3 | module re.gfx.raytypes; 4 | 5 | static import raylib; 6 | 7 | // - raylib types 8 | 9 | public { 10 | import raylib : Color, Colors, Image, Texture2D; 11 | import raylib : Mesh, Model, Shader; 12 | 13 | alias RenderTarget = raylib.RenderTexture2D; 14 | } 15 | -------------------------------------------------------------------------------- /source/re/gfx/render_ext.d: -------------------------------------------------------------------------------- 1 | /** rendering extensions and utilities */ 2 | 3 | module re.gfx.render_ext; 4 | 5 | import re.math; 6 | import re.gfx; 7 | static import raylib; 8 | 9 | /// renderer utilities 10 | static class RenderExt { 11 | /// draws a render target to the specified destination rect, tinted with color 12 | public static void draw_render_target(RenderTarget target, Rectangle dest_rect, Color color) { 13 | raylib.DrawTexturePro(target.texture, Rectangle(0, 0, 14 | target.texture.width, -target.texture.height), dest_rect, Vector2(0, 0), 0, color); 15 | } 16 | 17 | /// draws a render target to the specified destination rect, tinted with color, supporting a subregion of the input texture 18 | public static void draw_render_target_crop(RenderTarget target, Rectangle source_rect, Rectangle dest_rect, Color color) { 19 | if (source_rect == RectangleZero) { 20 | // default to full texture 21 | source_rect = Rectangle(0, 0, target.texture.width, target.texture.height); 22 | } 23 | // negate height to ensure orientation is correct 24 | source_rect.height = -source_rect.height; 25 | // import std.stdio; 26 | // writefln("target texture size: (%s, %s)", target.texture.width, target.texture.height); 27 | // writefln("source rect: (%s, %s, %s, %s)", source_rect.x, source_rect.y, source_rect.width, source_rect.height); 28 | // writefln("dest rect: (%s, %s, %s, %s)", dest_rect.x, dest_rect.y, dest_rect.width, dest_rect.height); 29 | raylib.DrawTexturePro(target.texture, source_rect, dest_rect, Vector2(0, 0), 0, color); 30 | } 31 | 32 | /// draws a render target on another with an effect 33 | public static void draw_render_target_from(RenderTarget source, RenderTarget dest, Effect effect) { 34 | auto dest_rect = Rectangle(0, 0, dest.texture.width, dest.texture.height); 35 | // start drawing on dest 36 | raylib.BeginTextureMode(dest); 37 | // with shader 38 | raylib.BeginShaderMode(effect.shader); 39 | // blit our render target 40 | draw_render_target(source, dest_rect, effect.color); 41 | raylib.EndShaderMode(); 42 | raylib.EndTextureMode(); 43 | } 44 | 45 | /// draws a render target on another with an effect 46 | public static void draw_render_target_from(RenderTarget source, RenderTarget dest) { 47 | auto dest_rect = Rectangle(0, 0, dest.texture.width, dest.texture.height); 48 | // start drawing on dest 49 | raylib.BeginTextureMode(dest); 50 | // blit our render target 51 | draw_render_target(source, dest_rect, Colors.WHITE); 52 | raylib.EndTextureMode(); 53 | } 54 | 55 | /// create render target with a given size 56 | public static RenderTarget create_render_target(int width, int height) { 57 | return raylib.LoadRenderTexture(width, height); 58 | } 59 | 60 | /// destroy render target 61 | public static void destroy_render_target(RenderTarget target) { 62 | raylib.UnloadRenderTexture(target); 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /source/re/gfx/shapes/anim_model.d: -------------------------------------------------------------------------------- 1 | /** animated 3d models */ 2 | 3 | module re.gfx.shapes.anim_model; 4 | 5 | import re.ecs; 6 | import re.gfx; 7 | import re.math; 8 | import re.ng.diag; 9 | import re.math; 10 | import re.gfx.shapes.model; 11 | static import raylib; 12 | 13 | /// represents an animated 3d model 14 | class AnimModel3D : Model3D, Updatable { 15 | mixin Reflect; 16 | 17 | public raylib.ModelAnimation[] anims; 18 | public int anim_ix = 0; 19 | public int anim_frame; 20 | public bool anim_playing = false; 21 | 22 | this(Model model, raylib.ModelAnimation[] anims) { 23 | super(model); 24 | this.anims = anims; 25 | } 26 | 27 | public void play_anim(int anim_index) { 28 | anim_ix = anim_index; 29 | anim_frame = 0; 30 | anim_playing = true; 31 | } 32 | 33 | public void update() { 34 | if (!anim_playing) return; 35 | 36 | auto curr_anim = &anims[anim_ix]; 37 | if (anim_frame <= curr_anim.frameCount) { 38 | raylib.UpdateModelAnimation(this.model, *curr_anim, anim_frame); 39 | anim_frame++; 40 | } else { 41 | anim_playing = false; 42 | } 43 | } 44 | 45 | public override void render() { 46 | super.render(); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /source/re/gfx/shapes/cube.d: -------------------------------------------------------------------------------- 1 | /** 3d rectangular prism */ 2 | 3 | module re.gfx.shapes.cube; 4 | 5 | import re.gfx; 6 | import re.ecs.component; 7 | import re.gfx.shapes.mesh; 8 | import re.math; 9 | static import raylib; 10 | 11 | /// represents a 3d rectangular prism (we abbreviate as cube) 12 | class Cube : RenderableMesh { 13 | mixin Reflect; 14 | private Vector3 _size; 15 | 16 | this(Vector3 size, Color color = Colors.WHITE) { 17 | effect.color = color; 18 | _size = size; 19 | } 20 | 21 | /// get rectangular prism dimensions 22 | @property Vector3 size() { 23 | return _size; 24 | } 25 | 26 | protected override Mesh gen_mesh() { 27 | return raylib.GenMeshCube(_size.x, _size.y, _size.z); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /source/re/gfx/shapes/grid.d: -------------------------------------------------------------------------------- 1 | /** 3d grid centered at origin */ 2 | 3 | module re.gfx.shapes.grid; 4 | 5 | import re.ecs; 6 | import re.gfx; 7 | import re.math; 8 | import re.ng.diag; 9 | static import raylib; 10 | 11 | /// represents a 3d grid at the origin 12 | class Grid3D : Component, Renderable3D { 13 | /// grid slices 14 | public int slices; 15 | /// grid line spacing 16 | public float spacing; 17 | 18 | this(int slices, float spacing) { 19 | this.slices = slices; 20 | this.spacing = spacing; 21 | } 22 | 23 | @property BoundingBox bounds() { 24 | auto edge = (slices / 2) * spacing; 25 | return BoundingBox(Vector3(-edge, 0, -edge), Vector3(edge, 0, edge)); 26 | } 27 | 28 | public void render() { 29 | raylib.DrawGrid(slices, spacing); 30 | } 31 | 32 | public void debug_render() { 33 | // DebugRender.default_debug_render(this); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /source/re/gfx/shapes/mesh.d: -------------------------------------------------------------------------------- 1 | /** renderable mesh, used for procedural meches only. model3d is used for 3d models */ 2 | 3 | module re.gfx.shapes.mesh; 4 | 5 | import re.ecs; 6 | import re.gfx; 7 | import re.math; 8 | import re.ng.diag; 9 | static import raylib; 10 | 11 | /// renders a model given a mesh. should only be used for procedural meshes; use Model3D for models instead 12 | abstract class RenderableMesh : Component, Renderable3D { 13 | mixin Reflect; 14 | /// effect 15 | private Effect _effect; 16 | private Mesh _mesh; 17 | private Model _model; 18 | public Vector3 offset = Vector3.zero; 19 | 20 | this() { 21 | // set default effect 22 | _effect = new Effect(); 23 | } 24 | 25 | override void setup() { 26 | gen_model(); 27 | } 28 | 29 | @property BoundingBox bounds() { 30 | return Bounds.calculate(raylib.GetMeshBoundingBox(_mesh), entity.transform); 31 | } 32 | 33 | /// gets the effect 34 | @property ref Effect effect() { 35 | return _effect; 36 | } 37 | 38 | /// sets the effect 39 | @property Effect effect(Effect value) { 40 | _effect = value; 41 | _model.materials[0].shader = _effect.shader; 42 | return value; 43 | } 44 | 45 | /// gets the model 46 | @property ref Model model() { 47 | return _model; 48 | } 49 | 50 | /// create the mesh 51 | protected abstract Mesh gen_mesh(); 52 | 53 | /// generate the model (from the mesh) 54 | protected void gen_model() { 55 | _mesh = gen_mesh(); 56 | _model = raylib.LoadModelFromMesh(_mesh); 57 | } 58 | 59 | public void render() { 60 | raylib.DrawModelEx(_model, transform.position + offset, transform.axis_angle.axis, 61 | transform.axis_angle.angle * C_RAD2DEG, transform.scale, effect.color); 62 | } 63 | 64 | public void debug_render() { 65 | DebugRender.default_debug_render(this, _model); 66 | } 67 | 68 | override void destroy() { 69 | // freeing the model also frees the mesh 70 | raylib.UnloadModel(_model); 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /source/re/gfx/shapes/model.d: -------------------------------------------------------------------------------- 1 | /** renderable 3d model */ 2 | 3 | module re.gfx.shapes.model; 4 | 5 | import re.ecs; 6 | import re.gfx; 7 | import re.math; 8 | import re.ng.diag; 9 | import re.math; 10 | static import raylib; 11 | 12 | /// represents a 3d model 13 | class Model3D : Component, Renderable3D { 14 | mixin Reflect; 15 | /// the model 16 | public Model model; 17 | private Effect _effect; 18 | public Vector3 offset = Vector3.zero; 19 | 20 | this(Model model) { 21 | this.model = model; 22 | // default effect 23 | _effect = new Effect(); 24 | } 25 | 26 | /// gets the effect 27 | @property ref Effect effect() { 28 | return _effect; 29 | } 30 | 31 | /// sets the effect 32 | @property Effect effect(Effect value) { 33 | _effect = value; 34 | // set shader for each material 35 | for (int i = 0; i < model.materialCount; i++) { 36 | model.materials[i].shader = _effect.shader; 37 | } 38 | return value; 39 | } 40 | 41 | @property BoundingBox bounds() { 42 | return Bounds.calculate(raylib.GetMeshBoundingBox(model.meshes[0]), entity.transform); 43 | } 44 | 45 | public void render() { 46 | raylib.DrawModelEx(model, transform.position + offset, transform.axis_angle.axis, 47 | transform.axis_angle.angle * C_RAD2DEG, transform.scale, effect.color); 48 | } 49 | 50 | public void debug_render() { 51 | DebugRender.default_debug_render(this, model); 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /source/re/gfx/shapes/rect.d: -------------------------------------------------------------------------------- 1 | /** color filled rectangle */ 2 | 3 | module re.gfx.shapes.rect; 4 | 5 | import re.ecs; 6 | import re.gfx; 7 | import re.math; 8 | import re.ng.diag; 9 | static import raylib; 10 | 11 | /// a color-filled rectangle 12 | class ColorRect : Component, Renderable2D { 13 | mixin Reflect; 14 | /// rectangle dimensions 15 | public Vector2 size; 16 | /// fill color 17 | public Color color; 18 | 19 | this(Vector2 size, raylib.Color color, bool fill = true) { 20 | this.size = size; 21 | this.color = color; 22 | } 23 | 24 | @property Rectangle bounds() { 25 | return Bounds.calculate(entity.transform, size / 2, size.x, size.y); 26 | } 27 | 28 | void render() { 29 | raylib.DrawRectanglePro(Rectangle(entity.position2.x, 30 | entity.position2.y, size.x, size.y), size / 2, 31 | entity.transform.rotation_z * C_RAD2DEG, color); 32 | } 33 | 34 | void debug_render() { 35 | DebugRender.default_debug_render(this); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /source/re/gfx/shapes/sphere.d: -------------------------------------------------------------------------------- 1 | /** 3d sphere */ 2 | 3 | module re.gfx.shapes.sphere; 4 | 5 | import re.gfx; 6 | import re.ecs.component; 7 | import re.gfx.shapes.mesh; 8 | import re.math; 9 | static import raylib; 10 | 11 | /// represents a 3d rectangular prism (we abbreviate as cube) 12 | class Sphere : RenderableMesh { 13 | mixin Reflect; 14 | private float _radius; 15 | private int _rings; 16 | private int _slices; 17 | 18 | this(float radius, int rings, int slices, Color color = Colors.WHITE) { 19 | effect.color = color; 20 | _radius = radius; 21 | _rings = rings; 22 | _slices = slices; 23 | } 24 | 25 | /// get rectangular prism dimensions 26 | @property float radius() { 27 | return _radius; 28 | } 29 | 30 | protected override Mesh gen_mesh() { 31 | return raylib.GenMeshSphere(_radius, _rings, _slices); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /source/re/gfx/sprite.d: -------------------------------------------------------------------------------- 1 | /** basc sprites as texture regions */ 2 | 3 | module re.gfx.sprite; 4 | 5 | import re.gfx; 6 | import re.math; 7 | 8 | /// represents a drawable texture region 9 | class Sprite { 10 | /// raw texture data 11 | public Texture2D texture; 12 | /// texture region 13 | public Rectangle src_rect; 14 | /// origin point (for rotation and position) 15 | public Vector2 origin; 16 | 17 | /// creates a sprite given a region and origin 18 | this(Texture2D texture, Rectangle src_rect, Vector2 origin) { 19 | this.texture = texture; 20 | this.src_rect = src_rect; 21 | this.origin = origin; 22 | } 23 | 24 | /// creates a sprite given a region 25 | this(Texture2D texture, Rectangle src_rect) { 26 | this(texture, src_rect, Vector2(src_rect.width / 2, src_rect.height / 2)); 27 | } 28 | 29 | /// creates a sprite 30 | this(Texture2D texture) { 31 | this(texture, Rectangle(0, 0, texture.width, texture.height)); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /source/re/gfx/sprite_renderer.d: -------------------------------------------------------------------------------- 1 | /** handles rendering of sprite data, highly extensible */ 2 | 3 | module re.gfx.sprite_renderer; 4 | 5 | import re.gfx; 6 | import re.ecs; 7 | import re.math; 8 | import re.ng.diag; 9 | static import raylib; 10 | 11 | class SpriteRenderer : Component, Renderable2D { 12 | /// the sprite 13 | public Sprite sprite; 14 | /// color tint 15 | public Color color = Colors.WHITE; 16 | 17 | this(Sprite sprite) { 18 | this.sprite = sprite; 19 | } 20 | 21 | @property Rectangle bounds() { 22 | return Bounds.calculate(entity.transform, sprite.origin, sprite.src_rect.width, sprite.src_rect.height); 23 | } 24 | 25 | public void render() { 26 | // draw the sprite 27 | auto dest_rect = Rectangle(entity.position2.x, entity.position2.y, 28 | sprite.src_rect.width, sprite.src_rect.height); 29 | raylib.DrawTexturePro(sprite.texture, sprite.src_rect, dest_rect, 30 | sprite.origin, entity.transform.rotation_z * C_RAD2DEG, color); 31 | } 32 | 33 | public void debug_render() { 34 | DebugRender.default_debug_render(this); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /source/re/gfx/text.d: -------------------------------------------------------------------------------- 1 | /** renderable text */ 2 | 3 | module re.gfx.text; 4 | 5 | import re.ecs; 6 | import re.math; 7 | import re.ng.diag; 8 | import std.string; 9 | static import raylib; 10 | 11 | /// renderable text 12 | class Text : Component, Renderable2D { 13 | private string _text; 14 | /// text font 15 | public raylib.Font font; 16 | /// font size 17 | public int size; 18 | /// text color 19 | public raylib.Color color; 20 | private Vector2 _text_size; 21 | private Vector2 _origin; 22 | /// default font size 23 | public enum default_size = 10; 24 | private Align _horiz_align; 25 | private Align _vert_align; 26 | 27 | /// alignment style 28 | public enum Align { 29 | /// left or top 30 | Close, 31 | /// center 32 | Center, 33 | /// right or bottom 34 | Far 35 | } 36 | 37 | /// create a new text 38 | this(raylib.Font font, string text, int size, raylib.Color color) { 39 | import re.core : Core; 40 | 41 | this._text = text; 42 | this.font = font; 43 | this.size = size; 44 | this.color = color; 45 | update_dimens(); 46 | 47 | raylib.SetTextureFilter(font.texture, Core.default_filter_mode); 48 | } 49 | 50 | /// gets text string 51 | @property string text() { 52 | return _text; 53 | } 54 | 55 | /// sets text string 56 | @property string text(string value) { 57 | _text = value; 58 | update_dimens(); 59 | return value; 60 | } 61 | 62 | @property Rectangle bounds() { 63 | return Bounds.calculate(entity.transform, _origin, _text_size.x, _text_size.y); 64 | } 65 | 66 | @property private int spacing() { 67 | return size / default_size; 68 | } 69 | 70 | /// default font (builtin to raylib) 71 | @property public static raylib.Font default_font() { 72 | return raylib.GetFontDefault(); 73 | } 74 | 75 | private void update_dimens() { 76 | _text_size = raylib.MeasureTextEx(font, toStringz(_text), size, spacing); 77 | // calculate origin 78 | auto ori_x = 0.0; 79 | auto ori_y = 0.0; 80 | if (_horiz_align == Align.Close) 81 | ori_x = 0; 82 | else if (_horiz_align == Align.Center) 83 | ori_x = _text_size.x / 2; 84 | else 85 | ori_x = _text_size.x; 86 | if (_vert_align == Align.Close) 87 | ori_y = 0; 88 | else if (_vert_align == Align.Center) 89 | ori_y = _text_size.y / 2; 90 | else 91 | ori_y = _text_size.y; 92 | _origin = Vector2(ori_x, ori_y); 93 | } 94 | 95 | public void set_align(Align horiz, Align vert) { 96 | _horiz_align = horiz; 97 | _vert_align = vert; 98 | update_dimens(); 99 | } 100 | 101 | void render() { 102 | raylib.DrawTextEx(font, toStringz(_text), entity.position2 - _origin, size, spacing, color); 103 | } 104 | 105 | void debug_render() { 106 | DebugRender.default_debug_render(this); 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /source/re/gfx/vr.d: -------------------------------------------------------------------------------- 1 | module re.gfx.vr; 2 | 3 | import re; 4 | import re.math; 5 | import re.gfx; 6 | import std.format; 7 | 8 | static import raylib; 9 | 10 | version (vr) class VRSupport { 11 | /// whether VR is enabled 12 | public bool enabled = false; 13 | public raylib.VrStereoConfig config; 14 | public raylib.Shader distortion_shader; 15 | 16 | /// set up VR rendering 17 | public void setup_vr(raylib.VrDeviceInfo device_info) { 18 | enabled = true; 19 | 20 | config = raylib.LoadVrStereoConfig(device_info); 21 | 22 | Core.log.info(format("initializing vr stereo config for device: %s", device_info)); 23 | 24 | // // update render resolution 25 | // resolution = Vector2(device_info.hResolution, device_info.vResolution); 26 | 27 | import re.util.vr_distortion; 28 | import re.util.interop : c_str; 29 | 30 | // set up distortion shader 31 | version (lite) { 32 | distortion_shader = raylib.LoadShaderFromMemory( 33 | null, VR_DISTORTION_SHADER_GL100.c_str); 34 | } else { 35 | distortion_shader = raylib.LoadShaderFromMemory( 36 | null, VR_DISTORTION_SHADER_GL330.c_str); 37 | } 38 | 39 | // set shader vars 40 | alias vartype = raylib.ShaderUniformDataType; 41 | // Update distortion shader with lens and distortion-scale parameters 42 | raylib.SetShaderValue(distortion_shader, raylib.GetShaderLocation(distortion_shader, "leftLensCenter"), 43 | cast(float*) config.leftLensCenter, vartype.SHADER_UNIFORM_VEC2); 44 | raylib.SetShaderValue(distortion_shader, raylib.GetShaderLocation(distortion_shader, "rightLensCenter"), 45 | cast(float*) config.rightLensCenter, vartype.SHADER_UNIFORM_VEC2); 46 | raylib.SetShaderValue(distortion_shader, raylib.GetShaderLocation(distortion_shader, "leftScreenCenter"), 47 | cast(float*) config.leftScreenCenter, vartype.SHADER_UNIFORM_VEC2); 48 | raylib.SetShaderValue(distortion_shader, raylib.GetShaderLocation(distortion_shader, "rightScreenCenter"), 49 | cast(float*) config.rightScreenCenter, vartype.SHADER_UNIFORM_VEC2); 50 | 51 | raylib.SetShaderValue(distortion_shader, raylib.GetShaderLocation(distortion_shader, "scale"), 52 | cast(float*) config.scale, vartype.SHADER_UNIFORM_VEC2); 53 | raylib.SetShaderValue(distortion_shader, raylib.GetShaderLocation(distortion_shader, "scaleIn"), 54 | cast(float*) config.scaleIn, vartype.SHADER_UNIFORM_VEC2); 55 | raylib.SetShaderValue(distortion_shader, raylib.GetShaderLocation(distortion_shader, "deviceWarpParam"), 56 | cast(float*) device_info.lensDistortionValues, vartype.SHADER_UNIFORM_VEC4); 57 | raylib.SetShaderValue(distortion_shader, raylib.GetShaderLocation(distortion_shader, "chromaAbParam"), 58 | cast(float*) device_info.chromaAbCorrection, vartype.SHADER_UNIFORM_VEC4); 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /source/re/gfx/window.d: -------------------------------------------------------------------------------- 1 | /** system window */ 2 | 3 | module re.gfx.window; 4 | 5 | import re.core; 6 | import re.math; 7 | import std.string; 8 | import std.algorithm.comparison : max; 9 | static import raylib; 10 | 11 | class Window { 12 | /// whether window has been created 13 | private bool _created = false; 14 | /// flags 15 | private uint _window_flags; 16 | /// properties 17 | private bool _resizable; 18 | 19 | /// initializes the window 20 | public void initialize(int width, int height) { 21 | // set config flags 22 | // tell raylib we're hidpi aware 23 | _window_flags |= raylib.ConfigFlags.FLAG_WINDOW_HIGHDPI; 24 | raylib.SetConfigFlags(_window_flags); 25 | 26 | // create the window 27 | raylib.InitWindow(width, height, ""); 28 | _created = true; // window has been created 29 | 30 | // set options 31 | raylib.SetTargetFPS(Core.target_fps); 32 | } 33 | 34 | public void set_resizable(bool resizable) { 35 | _resizable = resizable; 36 | if (_created) { 37 | // fail, you can't set resizable after window creation 38 | Core.log.error("Window.set_resizable: cannot set resizable after window creation"); 39 | assert(false); 40 | } 41 | if (resizable) { 42 | _window_flags |= raylib.ConfigFlags.FLAG_WINDOW_RESIZABLE; 43 | } else { 44 | _window_flags &= ~raylib.ConfigFlags.FLAG_WINDOW_RESIZABLE; 45 | } 46 | } 47 | 48 | public void destroy() { 49 | raylib.CloseWindow(); 50 | } 51 | 52 | public @property float dpi_scale() { 53 | auto scale_dpi_vec = raylib.GetWindowScaleDPI(); 54 | return max(scale_dpi_vec.x, scale_dpi_vec.y); 55 | } 56 | 57 | public @property int screen_width() { 58 | return raylib.GetScreenWidth(); 59 | } 60 | 61 | public @property int screen_height() { 62 | return raylib.GetScreenHeight(); 63 | } 64 | 65 | public @property Rectangle screen_bounds() { 66 | return Rectangle(0, 0, screen_width, screen_height); 67 | } 68 | 69 | public @property int render_width() { 70 | return raylib.GetRenderWidth(); 71 | } 72 | 73 | public @property int render_height() { 74 | return raylib.GetRenderHeight(); 75 | } 76 | 77 | public @property Rectangle render_bounds() { 78 | return Rectangle(0, 0, render_width, render_height); 79 | } 80 | 81 | public @property bool is_minimized() { 82 | return raylib.IsWindowMinimized(); 83 | } 84 | 85 | public @property bool is_maximized() { 86 | return raylib.IsWindowMaximized(); 87 | } 88 | 89 | public @property bool is_focused() { 90 | return raylib.IsWindowFocused(); 91 | } 92 | 93 | public @property bool is_resized() { 94 | return raylib.IsWindowResized(); 95 | } 96 | 97 | public void set_icon(raylib.Image image) { 98 | raylib.SetWindowIcon(image); 99 | } 100 | 101 | public void resize(int width, int height) { 102 | raylib.SetWindowSize(width, height); 103 | } 104 | 105 | public void set_position(int x, int y) { 106 | raylib.SetWindowPosition(x, y); 107 | } 108 | 109 | public void set_title(string title) { 110 | raylib.SetWindowTitle(title.toStringz); 111 | } 112 | 113 | public void toggle_fullscreen() { 114 | raylib.ToggleFullscreen(); 115 | } 116 | 117 | public void toggle_borderless_windowed() { 118 | raylib.ToggleBorderlessWindowed(); 119 | } 120 | 121 | public void maximize() { 122 | raylib.MaximizeWindow(); 123 | } 124 | 125 | public void minimize() { 126 | raylib.MinimizeWindow(); 127 | } 128 | 129 | public void restore() { 130 | raylib.RestoreWindow(); 131 | } 132 | 133 | public void set_focused() { 134 | raylib.SetWindowFocused(); 135 | } 136 | } 137 | -------------------------------------------------------------------------------- /source/re/input/input.d: -------------------------------------------------------------------------------- 1 | /** provides global access to input */ 2 | 3 | module re.input.input; 4 | 5 | import re.core; 6 | import re.math; 7 | import re.input; 8 | static import raylib; 9 | 10 | alias Keys = raylib.KeyboardKey; 11 | alias MouseButton = raylib.MouseButton; 12 | alias GamepadButtons = raylib.GamepadButton; 13 | alias Axes = raylib.GamepadAxis; 14 | 15 | /// input helper 16 | public static class Input { 17 | /// global virtual input list 18 | public static VirtualInput[] virtual_inputs; 19 | private static Vector2 last_mouse_position; 20 | public static Vector2 mouse_delta; 21 | 22 | static this() { 23 | } 24 | 25 | public static void update() { 26 | // update core input pipeline 27 | immutable auto current_mouse_pos = mouse_position(); 28 | mouse_delta = current_mouse_pos - last_mouse_position; 29 | last_mouse_position = current_mouse_pos; 30 | // update virtual inputs 31 | debug { 32 | // skip virtual input update if console open 33 | if (Core.inspector_overlay.console.open) { 34 | return; 35 | } 36 | } 37 | foreach (input; virtual_inputs) { 38 | input.update(); 39 | } 40 | } 41 | 42 | // - keyboard 43 | 44 | /// if a key was pressed this frame 45 | public static bool is_key_pressed(Keys key) { 46 | return raylib.IsKeyPressed(key); 47 | } 48 | 49 | /// if a key is currently down 50 | public static bool is_key_down(Keys key) { 51 | return raylib.IsKeyDown(key); 52 | } 53 | 54 | /// if a key was released this frame 55 | public static bool is_key_released(Keys key) { 56 | return raylib.IsKeyReleased(key); 57 | } 58 | 59 | /// if a key is currently up 60 | public static bool is_key_up(Keys key) { 61 | return raylib.IsKeyUp(key); 62 | } 63 | 64 | // - gamepad 65 | // TODO 66 | 67 | // - mouse 68 | 69 | /// if the mouse was pressed this frame 70 | public static bool is_mouse_pressed(MouseButton button) { 71 | return raylib.IsMouseButtonPressed(button); 72 | } 73 | 74 | /// if the mouse is currently down 75 | public static bool is_mouse_down(MouseButton button) { 76 | return raylib.IsMouseButtonDown(button); 77 | } 78 | 79 | /// if the mouse was released this frame 80 | public static bool is_mouse_released(MouseButton button) { 81 | return raylib.IsMouseButtonReleased(button); 82 | } 83 | 84 | /// if the mouse is currently up 85 | public static bool is_mouse_up(MouseButton button) { 86 | return raylib.IsMouseButtonUp(button); 87 | } 88 | 89 | /// the position of the mouse as a vector 90 | @property public static Vector2 mouse_position() { 91 | return raylib.GetMousePosition(); 92 | } 93 | 94 | /// the mouse scroll wheel delta 95 | @property public static float scroll_delta() { 96 | return raylib.GetMouseWheelMove(); 97 | } 98 | 99 | private static bool _cursor_locked = false; 100 | @property public static bool is_cursor_locked() { 101 | return _cursor_locked; 102 | } 103 | 104 | /// lock the cursor 105 | public static void lock_cursor() { 106 | raylib.DisableCursor(); 107 | _cursor_locked = true; 108 | } 109 | 110 | /// unlock the cursor 111 | public static void unlock_cursor() { 112 | raylib.EnableCursor(); 113 | _cursor_locked = false; 114 | } 115 | } 116 | -------------------------------------------------------------------------------- /source/re/input/package.d: -------------------------------------------------------------------------------- 1 | /** engine input features */ 2 | 3 | module re.input; 4 | 5 | public { 6 | import re.input.input; 7 | 8 | // virtual input 9 | import re.input.virtual.base; 10 | import re.input.virtual.button; 11 | import re.input.virtual.axis; 12 | import re.input.virtual.joystick; 13 | } 14 | -------------------------------------------------------------------------------- /source/re/input/virtual/axis.d: -------------------------------------------------------------------------------- 1 | /** virtual axis input */ 2 | 3 | module re.input.virtual.axis; 4 | 5 | import re.input; 6 | import std.algorithm; 7 | 8 | /// a virtual axis 9 | class VirtualAxis : VirtualInput { 10 | /// monitors a pair of inputs to make an axis 11 | static abstract class Node : VirtualInput.Node { 12 | @property public float value(); 13 | } 14 | 15 | /// logic-controllable axis 16 | static class LogicAxis : Node { 17 | public float logic_value = 0; 18 | 19 | @property public override float value() { 20 | return logic_value; 21 | } 22 | } 23 | 24 | /// monitors a pair of keyboard keys 25 | static class KeyboardKeys : Node { 26 | public OverlapBehavior overlap_behavior; 27 | public Keys positive; 28 | public Keys negative; 29 | private float _value; 30 | private bool _turned; 31 | 32 | /// creates a keyboard keys node 33 | this(Keys positive, Keys negative, OverlapBehavior overlap_behavior = OverlapBehavior.init) { 34 | this.positive = positive; 35 | this.negative = negative; 36 | this.overlap_behavior = overlap_behavior; 37 | } 38 | 39 | @property public override float value() { 40 | return _value; 41 | } 42 | 43 | public override void update() { 44 | if (Input.is_key_down(positive)) { 45 | if (Input.is_key_down(negative)) { 46 | switch (overlap_behavior) { 47 | case OverlapBehavior.Cancel: 48 | _value = 0; 49 | break; 50 | case OverlapBehavior.Newer: 51 | if (!_turned) { 52 | _value *= -1; 53 | _turned = true; 54 | } 55 | break; 56 | case OverlapBehavior.Older: 57 | break; // we don't touch the value 58 | default: 59 | assert(0); 60 | } 61 | } else { 62 | _turned = false; 63 | _value = 1; 64 | } 65 | } else if (Input.is_key_down(negative)) { 66 | _turned = false; 67 | _value = -1; 68 | } else { 69 | _turned = false; 70 | _value = 0; 71 | } 72 | } 73 | } 74 | 75 | @property public float value() { 76 | foreach (node; nodes) { 77 | auto val = (cast(Node) node).value; 78 | if (val != 0) { 79 | return val; 80 | } 81 | } 82 | return 0; 83 | } 84 | } 85 | 86 | @("input-axis") 87 | unittest { 88 | auto the_axis = new VirtualAxis(); 89 | the_axis.nodes ~= new VirtualAxis.KeyboardKeys(Keys.KEY_LEFT, Keys.KEY_RIGHT); 90 | 91 | assert(the_axis.nodes.length > 0); 92 | } 93 | -------------------------------------------------------------------------------- /source/re/input/virtual/base.d: -------------------------------------------------------------------------------- 1 | /** base virtual input */ 2 | 3 | module re.input.virtual.base; 4 | 5 | import re.input; 6 | import std.algorithm; 7 | 8 | /// method of resolving conflicting, overlapping inputs 9 | enum OverlapBehavior { 10 | Cancel, 11 | Older, 12 | Newer 13 | } 14 | 15 | /// a virtual input composed of input node units 16 | abstract class VirtualInput { 17 | /// the input node units 18 | public Node[] nodes; 19 | 20 | /// create and register a virtual input 21 | this() { 22 | Input.virtual_inputs ~= this; 23 | } 24 | 25 | /// unregister from virtual input updates 26 | public void unregister() { 27 | Input.virtual_inputs = Input.virtual_inputs.remove!(x => x == this); 28 | } 29 | 30 | /// updates all nodes 31 | public void update() { 32 | foreach (node; nodes) { 33 | node.update(); 34 | } 35 | } 36 | 37 | /// monitors a single unit for input 38 | static abstract class Node { 39 | void update() { 40 | } 41 | } 42 | } 43 | 44 | @("input-base") 45 | unittest { 46 | import re.ecs; 47 | import std.algorithm; 48 | 49 | class InputComponent : Component { 50 | public VirtualButton bonk; 51 | 52 | this() { 53 | bonk = new VirtualButton(); 54 | bonk.nodes ~= new VirtualButton.LogicButton(); 55 | } 56 | 57 | override void destroy() { 58 | bonk.unregister(); 59 | } 60 | } 61 | 62 | auto ecs = new EntityManager(); 63 | auto nt = ecs.create_entity(); 64 | auto controls = new InputComponent(); 65 | nt.add_component(controls); 66 | // make sure input is registered 67 | assert(Input.virtual_inputs.any!(x => x == controls.bonk)); 68 | assert(nt.has_component!InputComponent); 69 | ecs.destroy(); 70 | // make sure entity was deactivated 71 | assert(!nt.alive); 72 | // make sure input is unregistered 73 | assert(!Input.virtual_inputs.any!(x => x == controls.bonk)); 74 | } 75 | -------------------------------------------------------------------------------- /source/re/input/virtual/button.d: -------------------------------------------------------------------------------- 1 | /** virtual button input */ 2 | 3 | module re.input.virtual.button; 4 | 5 | import re.input; 6 | import std.algorithm; 7 | 8 | /// a virtual button 9 | class VirtualButton : VirtualInput { 10 | /// monitors a single button 11 | static abstract class Node : VirtualInput.Node { 12 | @property public bool is_down(); 13 | @property public bool is_up(); 14 | @property public bool is_pressed(); 15 | @property public bool is_released(); 16 | } 17 | 18 | /// logic-controllable button 19 | static class LogicButton : Node { 20 | public bool logic_pressed = false; 21 | 22 | @property public override bool is_down() { return logic_pressed; } 23 | @property public override bool is_up() { return !logic_pressed; } 24 | @property public override bool is_pressed() { return logic_pressed; } 25 | @property public override bool is_released() { return !logic_pressed; } 26 | } 27 | 28 | /// monitors a keyboard key 29 | static class KeyboardKey : Node { 30 | /// the key being monitored 31 | public Keys key; 32 | 33 | /// creates a keyboard key node 34 | this(Keys key) { 35 | this.key = key; 36 | } 37 | 38 | @property public override bool is_down() { 39 | return Input.is_key_down(key); 40 | } 41 | 42 | @property public override bool is_up() { 43 | return Input.is_key_up(key); 44 | } 45 | 46 | @property public override bool is_pressed() { 47 | return Input.is_key_pressed(key); 48 | } 49 | 50 | @property public override bool is_released() { 51 | return Input.is_key_released(key); 52 | } 53 | } 54 | 55 | @property public bool is_down() { 56 | return nodes.any!(x => (cast(Node) x).is_down); 57 | } 58 | 59 | @property public bool is_up() { 60 | return nodes.any!(x => (cast(Node) x).is_up); 61 | } 62 | 63 | @property public bool is_pressed() { 64 | return nodes.any!(x => (cast(Node) x).is_pressed); 65 | } 66 | 67 | @property public bool is_released() { 68 | return nodes.any!(x => (cast(Node) x).is_released); 69 | } 70 | } 71 | 72 | @("input-button") 73 | unittest { 74 | auto the_button = new VirtualButton(); 75 | the_button.nodes ~= new VirtualButton.KeyboardKey(Keys.KEY_E); 76 | 77 | assert(the_button.nodes.length > 0); 78 | } 79 | -------------------------------------------------------------------------------- /source/re/input/virtual/joystick.d: -------------------------------------------------------------------------------- 1 | /** virtual joystick input */ 2 | 3 | module re.input.virtual.joystick; 4 | 5 | import re.input; 6 | import re.math; 7 | import std.algorithm; 8 | 9 | /// a virtual joystick 10 | class VirtualJoystick : VirtualInput { 11 | // monitors four of inputs to make a joystick 12 | static abstract class Node : VirtualInput.Node { 13 | @property public Vector2 value(); 14 | } 15 | 16 | /// logic-controllable joystick 17 | static class LogicJoystick : Node { 18 | public Vector2 logic_value = Vector2Zero; 19 | 20 | @property public override Vector2 value() { 21 | return logic_value; 22 | } 23 | } 24 | 25 | /// monitors a pair of keyboard keys 26 | static class KeyboardKeys : Node { 27 | public OverlapBehavior overlap_behavior; 28 | public Keys left; 29 | public Keys right; 30 | public Keys up; 31 | public Keys down; 32 | private Vector2 _value; 33 | private bool _turned_x; 34 | private bool _turned_y; 35 | 36 | /// creates a keyboard keys node 37 | this(Keys left, Keys right, Keys up, Keys down, 38 | OverlapBehavior overlap_behavior = OverlapBehavior.init) { 39 | this.left = left; 40 | this.right = right; 41 | this.up = up; 42 | this.down = down; 43 | this.overlap_behavior = overlap_behavior; 44 | } 45 | 46 | @property public override Vector2 value() { 47 | return _value; 48 | } 49 | 50 | public override void update() { 51 | // x axis 52 | if (Input.is_key_down(left)) { 53 | if (Input.is_key_down(right)) { 54 | switch (overlap_behavior) { 55 | case OverlapBehavior.Cancel: 56 | _value.x = 0; 57 | break; 58 | case OverlapBehavior.Newer: 59 | if (!_turned_x) { 60 | _value.x *= -1; 61 | _turned_x = true; 62 | } 63 | break; 64 | case OverlapBehavior.Older: 65 | break; // we don't touch the value 66 | default: 67 | assert(0); 68 | } 69 | } else { 70 | _turned_x = false; 71 | _value.x = -1; 72 | } 73 | } else if (Input.is_key_down(right)) { 74 | _turned_x = false; 75 | _value.x = 1; 76 | } else { 77 | _turned_x = false; 78 | _value.x = 0; 79 | } 80 | // y axis 81 | if (Input.is_key_down(up)) { 82 | if (Input.is_key_down(down)) { 83 | switch (overlap_behavior) { 84 | case OverlapBehavior.Cancel: 85 | _value.y = 0; 86 | break; 87 | case OverlapBehavior.Newer: 88 | if (!_turned_y) { 89 | _value.y *= -1; 90 | _turned_y = true; 91 | } 92 | break; 93 | case OverlapBehavior.Older: 94 | break; // we don't touch the value 95 | default: 96 | assert(0); 97 | } 98 | } else { 99 | _turned_y = false; 100 | _value.y = -1; 101 | } 102 | } else if (Input.is_key_down(down)) { 103 | _turned_y = false; 104 | _value.y = 1; 105 | } else { 106 | _turned_y = false; 107 | _value.y = 0; 108 | } 109 | } 110 | } 111 | 112 | @property public Vector2 value() { 113 | foreach (node; nodes) { 114 | auto val = (cast(Node) node).value; 115 | if (val != raymath.Vector2Zero()) { 116 | return val; 117 | } 118 | } 119 | return raymath.Vector2Zero(); 120 | } 121 | } 122 | 123 | @("input-joystick") 124 | unittest { 125 | auto the_joy = new VirtualJoystick(); 126 | the_joy.nodes ~= new VirtualJoystick.KeyboardKeys(Keys.KEY_LEFT, 127 | Keys.KEY_RIGHT, Keys.KEY_UP, Keys.KEY_DOWN); 128 | 129 | assert(the_joy.nodes.length > 0); 130 | } 131 | -------------------------------------------------------------------------------- /source/re/math/bounds.d: -------------------------------------------------------------------------------- 1 | /** 2d/3d bounds and bounding boxes */ 2 | 3 | module re.math.bounds; 4 | 5 | import std.algorithm.comparison; 6 | static import raymath; 7 | import re.math; 8 | 9 | static class Bounds { 10 | /** based on 11 | calculate bounds for an object in 2d space taking into account its transform 12 | https://github.com/prime31/Nez/blob/0e97e68bd9df191fb3b893eb69e54238c30fcc80/Nez.Portable/Utils/Extensions/Bounds.cs#L184 13 | */ 14 | public static Rectangle calculate(ref Transform transform, Vector2 origin, 15 | float width, float height) { 16 | auto rotation = transform.rotation_z; 17 | auto position = transform.position2; 18 | auto scale = transform.scale2; 19 | if (rotation == 0) { 20 | return Rectangle(cast(int)(position.x - (origin.x * scale.x)), 21 | cast(int)(position.y - (origin.y * scale.y)), 22 | cast(int)(width * scale.x), cast(int)(height * scale.y)); 23 | } else { 24 | auto tmp1 = Matrix.init; 25 | 26 | // set the reference point to world reference taking origin into account 27 | auto transform_mat = raymath.MatrixTranslate(-position.x - origin.x, 28 | -position.y - origin.y, 0); 29 | tmp1 = raymath.MatrixScale(scale.x, scale.y, 1); // scale -> 30 | transform_mat = raymath.MatrixMultiply(transform_mat, tmp1); 31 | tmp1 = raymath.MatrixRotateZ(rotation); // rotate -> 32 | transform_mat = raymath.MatrixMultiply(transform_mat, tmp1); 33 | tmp1 = raymath.MatrixTranslate(position.x, position.y, 0); // translate back 34 | transform_mat = raymath.MatrixMultiply(transform_mat, tmp1); 35 | 36 | // get all four corners in world space 37 | auto top_left_w = Vector3(position.x, position.y, 0); 38 | auto top_right_w = Vector3(position.x + width, position.y, 0); 39 | auto btm_left_w = Vector3(position.x, position.y + height, 0); 40 | auto btm_right_w = Vector3(position.x + width, position.y + height, 0); 41 | 42 | // transform the corners into our work space 43 | auto top_left = raymath.Vector3Transform(top_left_w, transform_mat); 44 | auto top_right = raymath.Vector3Transform(top_right_w, transform_mat); 45 | auto btm_left = raymath.Vector3Transform(btm_left_w, transform_mat); 46 | auto btm_right = raymath.Vector3Transform(btm_right_w, transform_mat); 47 | 48 | // find the min and max values so we can concoct our bounding box 49 | auto min_x = cast(int) min(top_left.x, btm_right.x, top_right.x, btm_left.x); 50 | auto max_x = cast(int) max(top_left.x, btm_right.x, top_right.x, btm_left.x); 51 | auto min_y = cast(int) min(top_left.y, btm_right.y, top_right.y, btm_left.y); 52 | auto max_y = cast(int) max(top_left.y, btm_right.y, top_right.y, btm_left.y); 53 | 54 | return Rectangle(min_x, min_y, max_x - min_x, max_y - min_y); 55 | } 56 | } 57 | 58 | /// calculate the new bounding box by applying the transform to the raw bounding box 59 | public static BoundingBox calculate(BoundingBox bounds, ref Transform transform) { 60 | auto t_min = raymath.Vector3Transform(bounds.min, transform.local_to_world_transform); 61 | auto t_max = raymath.Vector3Transform(bounds.max, transform.local_to_world_transform); 62 | return BoundingBox(t_min, t_max); 63 | 64 | // this is a crappy workaround, using dlib 65 | // import re.util.dlib; 66 | 67 | // auto rot_quat = transform.orientation; 68 | // auto dl_rot_quat = convert_quat(rot_quat); 69 | // auto dl_rot_mat = dl_rot_quat.toMatrix4x4(); 70 | // auto t_min = convert_vec3(bounds.min) * dl_rot_mat; 71 | // auto t_max = convert_vec3(bounds.max) * dl_rot_mat; 72 | // auto dl_tf = convert_mat(transform.local_to_world_transform); 73 | // auto t_min = convert_vec3(bounds.min) * dl_tf; 74 | // auto t_max = convert_vec3(bounds.max) * dl_tf; 75 | 76 | // return BoundingBox(convert_vec3(t_min), convert_vec3(t_max)); 77 | // return BoundingBox(bounds.min * 1.1f, bounds.max * 1.1f); 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /source/re/math/funcs.d: -------------------------------------------------------------------------------- 1 | /** math funcs */ 2 | 3 | module re.math.funcs; 4 | 5 | import std.math; 6 | import re.util.rng; 7 | 8 | public static class Distribution { 9 | public static float exponentialDf(float x, float m) { 10 | return exp(-x * m); 11 | } 12 | 13 | /// 14 | /// selects a random value using a normal distribution 15 | /// 16 | /// mean value 17 | /// standard deviation 18 | /// 19 | public static float normalRand(float u, float s) { 20 | // https://en.wikipedia.org/wiki/Box%E2%80%93Muller_transform 21 | // https://stackoverflow.com/a/218600 22 | auto u1 = 1.0f - Rng.next_float(); 23 | auto u2 = 1.0f - Rng.next_float(); 24 | auto dst = sqrt(-2f * log(u1)) * sin(2f * PI * u2); 25 | auto v = u + s * dst; 26 | return v; 27 | } 28 | 29 | public static float[] summarizeDistribution(float[] values) { 30 | import std.algorithm.sorting : sort; 31 | import std.algorithm.iteration : sum; 32 | 33 | auto sorted = values.sort(); 34 | auto val_sum = sorted.sum(); 35 | auto mean = val_sum / sorted.length; 36 | auto min = sorted[0]; 37 | auto max = sorted[sorted.length - 1]; 38 | auto q1 = sorted[cast(int)(sorted.length * 0.25)]; 39 | auto q2 = sorted[cast(int)(sorted.length * 0.50)]; 40 | auto q3 = sorted[cast(int)(sorted.length * 0.75)]; 41 | return [mean, min, q1, q2, q3, max]; 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /source/re/math/package.d: -------------------------------------------------------------------------------- 1 | /** engine math utilities */ 2 | 3 | module re.math; 4 | 5 | public { 6 | import re.math.funcs; 7 | import re.math.transform; 8 | import re.math.bounds; 9 | import re.math.vector_ext; 10 | import re.math.rectangle_ext; 11 | 12 | import std.math; 13 | 14 | /// the mathematical constant pi 15 | enum C_PI = cast(float) std.math.PI; 16 | enum C_2_PI = cast(float) std.math.PI * 2; 17 | enum C_PI_2 = cast(float) std.math.PI / 2; 18 | enum C_PI_4 = cast(float) std.math.PI / 4; 19 | 20 | /// factor to convert radians to degrees 21 | enum C_RAD2DEG = cast(float)(180.0 / PI); 22 | 23 | /// factor to convert degrees to radians 24 | enum C_DEG2RAD = cast(float)(PI / 180.0); 25 | 26 | // raylib 27 | import re.math.raytypes; 28 | } 29 | -------------------------------------------------------------------------------- /source/re/math/raytypes.d: -------------------------------------------------------------------------------- 1 | /** math types */ 2 | 3 | module re.math.raytypes; 4 | 5 | // - raylib types 6 | 7 | public { 8 | import raylib : Vector2, Vector3, Vector4, Matrix, Quaternion; 9 | import raylib : Rectangle; 10 | import raylib : BoundingBox; 11 | static import raymath; 12 | } 13 | 14 | /// represents an angle around a specified axis 15 | struct AxisAngle { 16 | /// the axis of rotation 17 | Vector3 axis; 18 | /// the angle around the axis of rotation 19 | float angle; 20 | } 21 | -------------------------------------------------------------------------------- /source/re/math/rectangle_ext.d: -------------------------------------------------------------------------------- 1 | /** rectangle utilities */ 2 | 3 | module re.math.rectangle_ext; 4 | 5 | import re.math.raytypes; 6 | 7 | // - custom 8 | 9 | Vector2 center(Rectangle r) { 10 | return Vector2(r.x + r.width / 2, r.y + r.height / 2); 11 | } 12 | 13 | Vector2 top_left(Rectangle r) { 14 | return Vector2(r.x, r.y); 15 | } 16 | 17 | Vector2 top_right(Rectangle r) { 18 | return Vector2(r.x + r.width, r.y); 19 | } 20 | 21 | Vector2 bottom_left(Rectangle r) { 22 | return Vector2(r.x, r.y + r.height); 23 | } 24 | 25 | Vector2 bottom_right(Rectangle r) { 26 | return Vector2(r.x + r.width, r.y + r.height); 27 | } 28 | 29 | float left(Rectangle r) { 30 | return r.x; 31 | } 32 | 33 | float right(Rectangle r) { 34 | return r.x + r.width; 35 | } 36 | 37 | float top(Rectangle r) { 38 | return r.y; 39 | } 40 | 41 | float bottom(Rectangle r) { 42 | return r.y + r.height; 43 | } 44 | 45 | Rectangle scale(Rectangle r, float factor) { 46 | return Rectangle(r.x * factor, r.y * factor, r.width * factor, r.height * factor); 47 | } 48 | 49 | Rectangle scale(Rectangle r, Vector2 factor) { 50 | return Rectangle(r.x * factor.x, r.y * factor.y, r.width * factor.x, r.height * factor.y); 51 | } 52 | 53 | Rectangle scale_inplace(Rectangle r, float factor) { 54 | return Rectangle(r.x, r.y, r.width * factor, r.height * factor); 55 | } 56 | 57 | Rectangle scale_inplace(Rectangle r, Vector2 factor) { 58 | return Rectangle(r.x, r.y, r.width * factor.x, r.height * factor.y); 59 | } 60 | 61 | enum RectangleZero = Rectangle(0, 0, 0, 0); 62 | enum RectangleUnit = Rectangle(0, 0, 1, 1); 63 | -------------------------------------------------------------------------------- /source/re/math/vector_ext.d: -------------------------------------------------------------------------------- 1 | /** vector utilities */ 2 | 3 | module re.math.vector_ext; 4 | 5 | import re.math.raytypes; 6 | 7 | // - raylib aliases 8 | alias Normalize = raymath.Vector2Normalize; 9 | alias Normalize = raymath.Vector3Normalize; 10 | 11 | // - custom 12 | 13 | float LengthSquared(Vector2 v) { 14 | return (v.x * v.x) + (v.y * v.y); 15 | } 16 | 17 | 18 | enum Vector2Zero = Vector2(0, 0); 19 | enum Vector2One = Vector2(1, 1); 20 | enum Vector3Zero = Vector3(0, 0, 0); 21 | enum Vector3One = Vector3(1, 1, 1); -------------------------------------------------------------------------------- /source/re/ng/camera/base.d: -------------------------------------------------------------------------------- 1 | /** scene camera */ 2 | 3 | module re.ng.camera.base; 4 | 5 | import re.ecs; 6 | 7 | /// base for SceneCamera2D and SceneCamera3D 8 | abstract class SceneCamera : Component { 9 | mixin Reflect; 10 | 11 | void update() { 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /source/re/ng/camera/cam2d.d: -------------------------------------------------------------------------------- 1 | /** 2d scene camera */ 2 | 3 | module re.ng.camera.cam2d; 4 | 5 | import re.ecs; 6 | import re.time; 7 | import re.math; 8 | import re.ng.camera.base; 9 | import re.ng.viewport; 10 | import re.gfx.raytypes; 11 | import std.math; 12 | static import raylib; 13 | 14 | /// represents a camera for a 2D scene 15 | class SceneCamera2D : SceneCamera { 16 | mixin Reflect; 17 | private raylib.Camera2D _camera; 18 | 19 | this() { 20 | _camera = raylib.Camera2D(); 21 | _camera.offset = Vector2(0, 0); 22 | _camera.target = Vector2(0, 0); 23 | _camera.rotation = 0; 24 | _camera.zoom = 1; 25 | } 26 | 27 | override void setup() { 28 | // defaults 29 | _camera.zoom = 1; 30 | } 31 | 32 | @property ref raylib.Camera2D camera() return { 33 | return _camera; 34 | } 35 | 36 | /// gets the camera offset (displacement from target) 37 | @property Vector2 offset() { 38 | return _camera.offset; 39 | } 40 | 41 | /// sets the camera offset (displacement from target) 42 | @property Vector2 offset(Vector2 value) { 43 | return _camera.offset = value; 44 | } 45 | 46 | override void update() { 47 | super.update(); 48 | 49 | // copy entity to camera transform 50 | _camera.target = entity.transform.position2; 51 | _camera.rotation = entity.transform.rotation_z * C_RAD2DEG; 52 | _camera.zoom = entity.transform.scale.x; 53 | } 54 | } 55 | 56 | class CameraFollow2D : Component, Updatable { 57 | mixin Reflect; 58 | public Viewport viewport; 59 | public Entity target; 60 | public float lerp; 61 | private SceneCamera2D cam; 62 | public bool follow_rotation = false; 63 | 64 | this(Viewport viewport, Entity target, float lerp) { 65 | this.viewport = viewport; 66 | this.target = target; 67 | this.lerp = lerp; 68 | } 69 | 70 | override void setup() { 71 | cam = entity.get_component!SceneCamera2D(); 72 | } 73 | 74 | void update() { 75 | // set offset to half-resolution (so that our target is centered) 76 | cam.offset = Vector2(viewport.resolution.x / 2, viewport.resolution.y / 2); 77 | 78 | if (follow_rotation) { 79 | // // lerp towards target rotation 80 | // auto to_target = target.transform.rotation_z - entity.transform.rotation_z; 81 | // auto rot_scroll = to_target * lerp; 82 | // entity.transform.rotation_z = entity.transform.rotation_z + rot_scroll; 83 | // lock target rotation 84 | entity.transform.rotation_z = target.transform.rotation_z; 85 | } 86 | 87 | // get vector to target 88 | auto to_target = target.position2 - entity.position2; 89 | // lerp towards target 90 | auto scroll = Vector2(to_target.x * lerp, to_target.y * lerp); 91 | entity.position2 = entity.position2 + scroll; 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /source/re/ng/camera/package.d: -------------------------------------------------------------------------------- 1 | /** scene cameras */ 2 | 3 | module re.ng.camera; 4 | 5 | public { 6 | import re.ng.camera.base; 7 | import re.ng.camera.cam2d; 8 | import re.ng.camera.cam3d; 9 | } 10 | -------------------------------------------------------------------------------- /source/re/ng/diag/default_inspect_commands.d: -------------------------------------------------------------------------------- 1 | /** default console commands */ 2 | 3 | module re.ng.diag.default_inspect_commands; 4 | 5 | import std.range; 6 | import std.array; 7 | import std.algorithm; 8 | import std.string; 9 | import std.conv; 10 | 11 | import re.core; 12 | import re.ecs; 13 | 14 | debug static class DefaultEntityInspectorCommands { 15 | alias log = Core.log; 16 | alias scenes = Core.scenes; 17 | alias dbg = Core.inspector_overlay; 18 | alias con = dbg.console; 19 | 20 | static void c_entities(string[] args) { 21 | auto sb = appender!string(); 22 | sb ~= "entity list:\n"; 23 | foreach (i, scene; scenes) { 24 | // print scene header 25 | sb ~= format("▶ Scene[%d]: %s ¬\n", i, typeid(scene).name); 26 | foreach (entity; scene.ecs.entities) { 27 | // get list of components 28 | auto component_types = entity.get_all_components().map!(x => x.classinfo.name); 29 | // sb ~= format(" ▷ %s: pos(%s) components[%d] {%s}\n", entity.name, 30 | // entity.position, entity.components.length, component_types); 31 | sb ~= format(" ▷ %s: pos(%s) components[%s]\n", entity.name, entity.position, component_types 32 | .length); 33 | for (int j = 0; j < component_types.length; j++) { 34 | sb ~= format(" ■ [%s] %s\n", j, component_types[j]); 35 | } 36 | } 37 | } 38 | log.info(sb.data); 39 | } 40 | 41 | private static bool pick_entity(string name, out Entity entity) { 42 | // find entities in all scenes 43 | foreach (scene; scenes) { 44 | if (scene.ecs.has_entity(name)) { 45 | entity = scene.get_entity(name); 46 | log.info("selected entity '%s' in scene %s", 47 | entity.name, typeid(scene).name); 48 | return true; 49 | } 50 | } 51 | 52 | log.err("entity '%s' not found", name); 53 | return false; 54 | } 55 | 56 | private static bool pick_component(string[] args, out Component comp) { 57 | if (args.length < 2) { 58 | log.err("usage: "); 59 | return false; 60 | } 61 | Entity entity; 62 | if (!pick_entity(args[0], entity)) 63 | return false; 64 | auto comp_sel = args[1]; 65 | auto all_comps = entity.get_all_components(); 66 | // check if we can parse component selector as int 67 | try { 68 | auto comp_ix = comp_sel.to!int; 69 | 70 | // select component at this index 71 | comp = all_comps[comp_ix]; 72 | } catch (ConvException) { 73 | // not an int, try to find component by name 74 | auto comp_search = comp_sel.toLower; 75 | // find matching component 76 | auto matches = all_comps 77 | .find!(x => x.classinfo.name.toLower.indexOf(comp_search) > 0); 78 | if (matches.length == 0) { 79 | log.err("no matching component for '%s'", comp_search); 80 | return false; 81 | } 82 | comp = matches.front; 83 | } 84 | 85 | return true; 86 | } 87 | 88 | static void c_dump(string[] args) { 89 | Component comp; 90 | if (!pick_component(args, comp)) 91 | return; 92 | // dump this component 93 | auto sb = appender!(string); 94 | auto comp_class = comp.getMetaType; 95 | // log.info(format("dumping: %s", comp_class.getName)); 96 | sb ~= format("dump: %s\n", comp_class.getName); 97 | foreach (field; comp_class.getFields) { 98 | sb ~= format(" %s = %s\n", field.getName, field.get(comp)); 99 | } 100 | log.info(sb.data); 101 | } 102 | 103 | static void c_inspect(string[] args) { 104 | if (args.length == 0) { 105 | if (dbg.entity_inspect_view.open) { 106 | // close inspector when run without args 107 | dbg.entity_inspect_view.close(); 108 | } else { 109 | // inspector isn't open, and no arg was given 110 | log.err("usage: inspect "); 111 | } 112 | return; 113 | } 114 | Entity entity; 115 | if (!pick_entity(args[0], entity)) 116 | return; 117 | if (dbg.entity_inspect_view.open) 118 | dbg.entity_inspect_view.close(); // close the existing inspector 119 | 120 | // attach the inspector to this entity 121 | dbg.entity_inspect_view.inspect(entity); 122 | } 123 | } 124 | -------------------------------------------------------------------------------- /source/re/ng/diag/fps_counter.d: -------------------------------------------------------------------------------- 1 | /** 2d renderable fps counter */ 2 | 3 | module re.ng.diag.fps_counter; 4 | 5 | import std.format; 6 | 7 | import re.core; 8 | import re.math; 9 | import re.ecs.component; 10 | import re.ecs.renderable; 11 | import re.gfx.raytypes; 12 | import re.util.interop; 13 | static import raylib; 14 | 15 | class FPSCounter : Component, Renderable2D { 16 | int font_size; 17 | Color color; 18 | 19 | this(int font_size, Color color = Colors.WHITE) { 20 | this.font_size = font_size; 21 | this.color = color; 22 | } 23 | 24 | @property public Rectangle bounds() { 25 | return Rectangle(transform.position2.x, transform.position2.y, 60, 10); 26 | } 27 | 28 | void render() { 29 | auto fps_str = format("%s", Core.fps); 30 | raylib.DrawText(fps_str.c_str(), cast(int) transform.position2.x, 31 | cast(int) transform.position2.y, font_size, color); 32 | } 33 | 34 | void debug_render() { 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /source/re/ng/diag/inspector_overlay.d: -------------------------------------------------------------------------------- 1 | /** provides runtime debugging functionality in an overlay */ 2 | 3 | module re.ng.diag.inspector_overlay; 4 | 5 | import std.format; 6 | 7 | import re.core; 8 | import re.ecs; 9 | import re.input.input; 10 | import re.math; 11 | import re.gfx; 12 | import re.gfx.render_ext; 13 | import re.ng.diag.console; 14 | import re.ng.diag.inspector_overlay; 15 | import re.ng.diag.entity_inspect_view; 16 | static import raylib; 17 | static import raygui; 18 | 19 | /// a robust overlay debugging tool 20 | class InspectorOverlay { 21 | public bool enabled = false; 22 | public enum screen_padding = 12; 23 | public Rectangle ui_bounds; 24 | private enum bg_col = Color(180, 180, 180, 180); 25 | private RenderTarget _render_target; 26 | private enum _render_col = Color(255, 255, 255, 160); 27 | 28 | /// inspector panel 29 | debug { 30 | public static EntityInspectView entity_inspect_view; 31 | } 32 | 33 | /// debug console 34 | public static InspectorConsole console; 35 | 36 | /// sets up debugger 37 | this() { 38 | debug { 39 | entity_inspect_view = new EntityInspectView(); 40 | } 41 | console = new InspectorConsole(); 42 | if (!Core.headless) { 43 | ui_bounds = Rectangle(0, 0, Core.window.screen_width, Core.window.screen_height); 44 | _render_target = RenderExt.create_render_target( 45 | cast(int) ui_bounds.width, cast(int) ui_bounds.height 46 | ); 47 | Core.log.info( 48 | format("debugger info: ui_bounds=%s, resolution=%s", 49 | ui_bounds, Core.default_resolution) 50 | ); 51 | } 52 | } 53 | 54 | public void update() { 55 | if (Input.is_key_pressed(console.key)) { 56 | Core.debug_render = !Core.debug_render; 57 | console.open = !console.open; 58 | } 59 | 60 | if (console.open) 61 | console.update(); 62 | 63 | debug { 64 | if (!Core.headless) { 65 | // auto-resize inspector 66 | entity_inspect_view.width = cast(int)(ui_bounds.width * 0.7); 67 | } 68 | if (entity_inspect_view.open) 69 | entity_inspect_view.update(); 70 | } 71 | } 72 | 73 | public void render() { 74 | raylib.BeginTextureMode(_render_target); 75 | raylib.ClearBackground(Colors.BLANK); 76 | 77 | if (console.open) 78 | console.render(); 79 | 80 | debug { 81 | if (entity_inspect_view.open) 82 | entity_inspect_view.render(); 83 | } 84 | 85 | raylib.EndTextureMode(); 86 | 87 | // draw render target 88 | RenderExt.draw_render_target(_render_target, 89 | Rectangle(0, 0, Core.window.screen_width, Core.window.screen_height), 90 | _render_col 91 | ); 92 | } 93 | 94 | /// clean up 95 | public void destroy() { 96 | debug { 97 | if (entity_inspect_view.open) { 98 | entity_inspect_view.close(); 99 | } 100 | } 101 | RenderExt.destroy_render_target(_render_target); 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /source/re/ng/diag/package.d: -------------------------------------------------------------------------------- 1 | /** engine diagnostics */ 2 | 3 | module re.ng.diag; 4 | 5 | public { 6 | import re.ng.diag.inspector_overlay; 7 | import re.ng.diag.console; 8 | import re.ng.diag.render; 9 | } 10 | -------------------------------------------------------------------------------- /source/re/ng/diag/render.d: -------------------------------------------------------------------------------- 1 | /** debug rendering utilities */ 2 | 3 | module re.ng.diag.render; 4 | 5 | import re.core; 6 | import re.ecs; 7 | import re.math; 8 | import re.gfx; 9 | static import raylib; 10 | 11 | static class DebugRender { 12 | public static Color debug_color = Colors.RED; 13 | public static Color debug_color_mesh = Colors.BLACK; 14 | public static Color debug_color_collider = Colors.GREEN; 15 | 16 | public static void default_debug_render(Renderable2D renderable) { 17 | import re.math.rectangle_ext; 18 | import std.algorithm.comparison: max; 19 | 20 | // draw bounding rectangle 21 | raylib.DrawRectangleLinesEx(renderable.bounds, 1, debug_color); 22 | // // draw center crosshair 23 | // auto center = renderable.bounds.center; 24 | // int crosshair_size = max(cast(int) (Core.default_resolution.x / 200), 1); 25 | // raylib.DrawLineV(center + Vector2(-crosshair_size, 0), center + Vector2(crosshair_size, 0), debug_color); 26 | // raylib.DrawLineV(center + Vector2(0, -crosshair_size), center + Vector2(0, crosshair_size), debug_color); 27 | } 28 | 29 | /// draw 3d bounding box 30 | private static void draw_bounding_box(BoundingBox raw_box, ref Transform transform, Color color) { 31 | import std.math : abs; 32 | 33 | auto box = Bounds.calculate(raw_box, transform); 34 | 35 | auto size = Vector3(abs(box.max.x - box.min.x), 36 | abs(box.max.y - box.min.y), abs(box.max.z - box.min.z)); 37 | 38 | auto center = Vector3(box.min.x + size.x / 2.0f, box.min.y + size.y / 2.0f, 39 | box.min.z + size.z / 2.0f); 40 | 41 | // TODO: we want something that supports transforms 42 | 43 | raylib.DrawCubeWires(center, size.x, size.y, size.z, color); 44 | } 45 | 46 | // public static void default_debug_render(Renderable3D renderable) { 47 | // auto comp = cast(Component) renderable; 48 | // draw_bounding_box(renderable.bounds, comp.transform, debug_color); 49 | // } 50 | 51 | public static void default_debug_render(Renderable3D renderable, Model model) { 52 | import re.phys.collider; 53 | 54 | // default_debug_render(renderable); 55 | auto comp = cast(Component) renderable; 56 | raylib.DrawModelWiresEx(model, comp.transform.position, comp.transform.axis_angle.axis, 57 | comp.transform.axis_angle.angle * C_RAD2DEG, comp.transform.scale, debug_color_mesh); 58 | 59 | // check if renderable has colliders 60 | auto box_colls = comp.entity.get_components!BoxCollider; 61 | foreach (box; box_colls) { 62 | auto raw_bounds = BoundingBox( // min 63 | Vector3(-box.size.x + box.offset.x, 64 | -box.size.y + box.offset.y, -box.size.z + box.offset.z), // max 65 | Vector3(box.size.x + box.offset.x, box.size.y + box.offset.y, 66 | box.size.z + box.offset.z)); 67 | draw_bounding_box(raw_bounds, comp.entity.transform, debug_color_collider); 68 | } 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /source/re/ng/manager.d: -------------------------------------------------------------------------------- 1 | /** global engine systems */ 2 | 3 | module re.ng.manager; 4 | 5 | import re.ng.scene; 6 | 7 | /// represents a global engine system 8 | abstract class Manager { 9 | /// set when attached to a scene 10 | public Scene scene; 11 | 12 | /// performs any setup needed for this manager 13 | void setup() { 14 | } 15 | 16 | /// updates this manager 17 | void update() { 18 | } 19 | 20 | /// frees resources used by this manager 21 | void destroy() { 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /source/re/ng/scene2d.d: -------------------------------------------------------------------------------- 1 | /** scene with 2d rendering */ 2 | 3 | module re.ng.scene2d; 4 | 5 | static import raylib; 6 | import re.ng.camera; 7 | import re; 8 | import re.gfx; 9 | import re.ecs; 10 | import re.math; 11 | 12 | import std.string; 13 | 14 | /// represents a scene rendered in 2d 15 | abstract class Scene2D : Scene { 16 | override void setup() { 17 | super.setup(); 18 | } 19 | 20 | override void create_default_viewport() { 21 | // create a camera entity 22 | auto camera_nt = create_entity("camera"); 23 | auto cam = camera_nt.add_component(new SceneCamera2D()); 24 | 25 | add_viewport(cam, Core.window.screen_bounds, resolution); 26 | } 27 | 28 | Viewport2D default_viewport() { 29 | return cast(Viewport2D) viewports[0]; 30 | } 31 | 32 | Viewport2D add_viewport(SceneCamera2D cam, Rectangle output_bounds, Vector2 resolution) { 33 | auto vp = new Viewport2D(); 34 | vp.cam = cam; 35 | vp.output_bounds = output_bounds; 36 | vp.resolution = resolution; 37 | vp.render_target = RenderExt.create_render_target( 38 | cast(int) vp.resolution.x, cast(int) vp.resolution.y 39 | ); 40 | viewports ~= vp; 41 | return vp; 42 | } 43 | 44 | void render_renderable(Component component) { 45 | auto renderable = cast(Renderable2D) component; 46 | assert(renderable !is null, "renderable was not 2d"); 47 | renderable.render(); 48 | if (Core.debug_render) { 49 | renderable.debug_render(); 50 | } 51 | } 52 | 53 | override void render_scene(Viewport viewport) { 54 | Viewport2D vp = cast(Viewport2D) viewport; 55 | raylib.BeginMode2D(vp.cam.camera); 56 | 57 | // render 2d components 58 | foreach (component; ecs.storage.renderable_components) { 59 | render_renderable(component); 60 | } 61 | foreach (component; ecs.storage.updatable_renderable_components) { 62 | render_renderable(component); 63 | } 64 | 65 | render_hook(); 66 | 67 | raylib.EndMode2D(); 68 | } 69 | 70 | override void update() { 71 | super.update(); 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /source/re/ng/scene3d.d: -------------------------------------------------------------------------------- 1 | /** scene with 3d rendering */ 2 | 3 | module re.ng.scene3d; 4 | 5 | static import raylib; 6 | import re.ng.camera; 7 | import re; 8 | import re.gfx; 9 | import re.ecs; 10 | import re.math; 11 | 12 | import std.string; 13 | 14 | /// represents a scene rendered in 3d 15 | abstract class Scene3D : Scene { 16 | override void setup() { 17 | super.setup(); 18 | } 19 | 20 | override void create_default_viewport() { 21 | // create a camera entity 22 | auto camera_nt = create_entity("camera"); 23 | auto cam = camera_nt.add_component(new SceneCamera3D()); 24 | 25 | add_viewport(cam, Core.window.screen_bounds, resolution); 26 | } 27 | 28 | Viewport3D default_viewport() { 29 | return cast(Viewport3D) viewports[0]; 30 | } 31 | 32 | Viewport3D add_viewport(SceneCamera3D cam, Rectangle output_bounds, Vector2 resolution) { 33 | auto vp = new Viewport3D(); 34 | vp.cam = cam; 35 | vp.output_bounds = output_bounds; 36 | vp.resolution = resolution; 37 | vp.render_target = RenderExt.create_render_target( 38 | cast(int) vp.resolution.x, cast(int) vp.resolution.y 39 | ); 40 | viewports ~= vp; 41 | return vp; 42 | } 43 | 44 | void render_renderable(Component component) { 45 | auto renderable = cast(Renderable3D) component; 46 | assert(renderable !is null, "renderable was not 3d"); 47 | renderable.render(); 48 | if (Core.debug_render) { 49 | renderable.debug_render(); 50 | } 51 | } 52 | 53 | override void render_scene(Viewport viewport) { 54 | version (vr) { 55 | if (Core.vr.enabled) 56 | raylib.BeginVrStereoMode(Core.vr.config); 57 | } 58 | 59 | Viewport3D vp = cast(Viewport3D) viewport; 60 | raylib.BeginMode3D(vp.cam.camera); 61 | 62 | // render 3d components 63 | foreach (component; ecs.storage.renderable_components) { 64 | render_renderable(component); 65 | } 66 | foreach (component; ecs.storage.updatable_renderable_components) { 67 | render_renderable(component); 68 | } 69 | 70 | render_hook(); 71 | 72 | raylib.EndMode3D(); 73 | 74 | version (vr) { 75 | if (Core.vr.enabled) 76 | raylib.EndVrStereoMode(); 77 | } 78 | } 79 | 80 | override void update() { 81 | super.update(); 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /source/re/ng/viewport.d: -------------------------------------------------------------------------------- 1 | module re.ng.viewport; 2 | 3 | static import raylib; 4 | 5 | import re; 6 | import re.math; 7 | import re.gfx; 8 | 9 | import re.ng.camera.cam2d; 10 | import re.ng.camera.cam3d; 11 | 12 | /// represents a view of a scene 13 | abstract class Viewport { 14 | /// the render target's texture 15 | public RenderTarget render_target; 16 | /// the render target's output bounds: the area of the screen it renders to 17 | public Rectangle output_bounds; 18 | /// the render target's crop region: the area of the render target that is rendered to the output bounds 19 | public Rectangle crop_bounds = RectangleZero; // default to full render target 20 | /// the render target's resolution 21 | public Vector2 resolution; 22 | 23 | /// whether to sync the render target to maximum size 24 | public bool sync_maximized; 25 | /// texture filter 26 | public raylib.TextureFilter filter; 27 | 28 | void update() { 29 | 30 | } 31 | 32 | void destroy() { 33 | RenderExt.destroy_render_target(render_target); 34 | } 35 | } 36 | 37 | class Viewport2D : Viewport { 38 | /// the 2d scene camera 39 | public SceneCamera2D cam; 40 | 41 | override void update() { 42 | cam.update(); 43 | } 44 | } 45 | 46 | class Viewport3D : Viewport { 47 | /// the 3d scene camera 48 | public SceneCamera3D cam; 49 | 50 | override void update() { 51 | cam.update(); 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /source/re/package.d: -------------------------------------------------------------------------------- 1 | /** 2 | The main package of RE ENGINE FX. 3 | 4 | Includes Core, Scene, Input, ECS, Tweens, Logger, and some utilities. 5 | 6 | Core is the most important place to start, as it provides access to all key features. 7 | */ 8 | 9 | module re; 10 | 11 | public { 12 | // core engine 13 | import re.core; 14 | import re.ng.scene; 15 | 16 | // input 17 | import re.input; 18 | 19 | // ecs 20 | import re.ecs; 21 | 22 | // util 23 | import re.util.logger; 24 | import re.util.rng; 25 | 26 | // tweens 27 | import re.util.tweens.ease; 28 | import re.util.tweens.tween; 29 | } 30 | -------------------------------------------------------------------------------- /source/re/phys/collider.d: -------------------------------------------------------------------------------- 1 | /** 2d and 3d colliders */ 2 | 3 | module re.phys.collider; 4 | 5 | import re.ecs.component; 6 | import re.math; 7 | 8 | /// a data class that represents a collider 9 | abstract class Collider : Component { 10 | mixin Reflect; 11 | public const(Vector3) offset; 12 | this(Vector3 offset) { 13 | this.offset = offset; 14 | } 15 | } 16 | 17 | /// a rectangular prism collider 18 | class BoxCollider : Collider { 19 | mixin Reflect; 20 | /// the half-size x,y,z dimensions of the collision box 21 | public const(Vector3) size; 22 | 23 | this(Vector3 size, Vector3 offset) { 24 | super(offset); 25 | this.size = size; 26 | } 27 | } 28 | 29 | /// a sphere collider 30 | class SphereCollider : Collider { 31 | mixin Reflect; 32 | /// the radius of the collision box 33 | public const(float) radius; 34 | 35 | this(float radius, Vector3 offset) { 36 | super(offset); 37 | this.radius = radius; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /source/re/phys/kin2d.d: -------------------------------------------------------------------------------- 1 | /** 2d kinematic body */ 2 | 3 | module re.phys.kin2d; 4 | 5 | import re.ecs.component; 6 | import re.ecs.updatable; 7 | import re.math; 8 | import std.math; 9 | import re.time; 10 | 11 | class KinBody2D : Component, Updatable { 12 | mixin Reflect; 13 | public float mass = 1.0f; 14 | public Vector2 velocity = Vector2Zero; 15 | public Vector2 max_velocity = Vector2Zero; 16 | public Vector2 accel = Vector2Zero; 17 | public Vector2 drag = Vector2Zero; 18 | public float max_angular = 0; 19 | public float angular_velocity = 0; 20 | public float angular_accel = 0; 21 | public float angular_drag = 0; 22 | 23 | // - transform 24 | 25 | @property public Vector2 pos() { 26 | return transform.position2; 27 | } 28 | 29 | @property public Vector2 pos(Vector2 value) { 30 | return transform.position2 = value; 31 | } 32 | 33 | @property public float angle() { 34 | return transform.rotation_z; 35 | } 36 | 37 | @property public float angle(float value) { 38 | return transform.rotation_z = value; 39 | } 40 | 41 | @property public float stdAngle() { 42 | return -angle + std.math.PI / 2; 43 | } 44 | 45 | @property public float stdAngle(float value) { 46 | return angle = -(value - std.math.PI / 2); 47 | } 48 | 49 | // misc. getters 50 | @property public Vector2 momentum() { 51 | return mass * velocity; 52 | } 53 | 54 | override void setup() { 55 | } 56 | 57 | void update() { 58 | auto dt = Time.delta_time; 59 | 60 | // apply max velocity 61 | auto vls = velocity.LengthSquared(); 62 | auto mvls = max_velocity.LengthSquared(); 63 | if (mvls > double.epsilon && vls > mvls) { 64 | // convert to unit and rescale 65 | auto unit_vel = velocity.Normalize(); 66 | auto ratio = mvls / vls; 67 | auto reduction_fac = pow(ratio, (1 / 12f)); 68 | // smoothly reduce to max velocity 69 | velocity = velocity * reduction_fac; 70 | } 71 | 72 | // new velocity components 73 | auto nvel_x = velocity.x; 74 | auto nvel_y = velocity.y; 75 | 76 | // apply acceleration 77 | nvel_x += accel.x * dt; 78 | nvel_y += accel.y * dt; 79 | 80 | if (nvel_x > drag.x * dt) { 81 | nvel_x = nvel_x - drag.x * dt; 82 | } 83 | 84 | if (nvel_x < -drag.x * dt) { 85 | nvel_x = nvel_x + drag.x * dt; 86 | } 87 | 88 | if (abs(nvel_x) < drag.x * dt) { 89 | nvel_x = 0; 90 | } 91 | 92 | if (nvel_y > drag.y * dt) { 93 | nvel_y = nvel_y - drag.y * dt; 94 | } 95 | 96 | if (nvel_y < -drag.y * dt) { 97 | nvel_y = nvel_y + drag.y * dt; 98 | } 99 | 100 | if (abs(nvel_y) < drag.y * dt) { 101 | nvel_y = 0; 102 | } 103 | 104 | velocity = Vector2(nvel_x, nvel_y); 105 | 106 | apply_motion(velocity * dt); 107 | 108 | // apply max angular 109 | if (max_angular > 0) { 110 | if (angular_velocity > max_angular) { 111 | angular_velocity = max_angular; 112 | } 113 | 114 | if (angular_velocity < -max_angular) { 115 | angular_velocity = -max_angular; 116 | } 117 | } 118 | 119 | // apply angular accel 120 | angular_velocity += angular_accel * dt; 121 | 122 | // apply angular drag 123 | if (angular_velocity > angular_drag * dt) { 124 | angular_velocity -= angular_drag * dt; 125 | } 126 | 127 | if (angular_velocity < -angular_drag * dt) { 128 | angular_velocity += angular_drag * dt; 129 | } 130 | 131 | transform.rotation_z = transform.rotation_z + (angular_velocity * dt); 132 | } 133 | 134 | protected void apply_motion(Vector2 pos_delta) { 135 | // apply motion to this object. typically this is passed onto a physics body. 136 | transform.position2 = transform.position2 + pos_delta; 137 | } 138 | } 139 | 140 | @("phys-kin2d") 141 | unittest { 142 | import re.ng.scene : Scene2D; 143 | import re.util.test : test_scene; 144 | 145 | class TestScene : Scene2D { 146 | override void on_start() { 147 | auto nt = create_entity("block", Vector2(0, 0)); 148 | // add kin body 149 | auto bod = new KinBody2D(); 150 | bod.accel = Vector2(0, 9.8); 151 | bod.angular_accel = C_PI_2; 152 | nt.add_component(bod); 153 | } 154 | } 155 | 156 | auto test = test_scene(new TestScene()); 157 | test.game.run(); 158 | 159 | // check conditions 160 | assert(test.scene.get_entity("block").get_component!KinBody2D().pos.y > 0, 161 | "KinBody2D did not move"); 162 | assert(test.scene.get_entity("block").get_component!KinBody2D() 163 | .angular_velocity > 0, "KinBody2D did not rotate"); 164 | 165 | test.game.destroy(); 166 | } 167 | -------------------------------------------------------------------------------- /source/re/time.d: -------------------------------------------------------------------------------- 1 | /** game time */ 2 | 3 | module re.time; 4 | static import raylib; 5 | 6 | /// utility class for time calculations 7 | class Time { 8 | /// the time elapsed since the last update (a.k.a. dt) 9 | public static float delta_time = 0; 10 | /// unscaled delta time 11 | public static float raw_delta_time = 0; 12 | /// total elapsed time 13 | public static float total_time = 0; 14 | /// time scale to apply to delta time 15 | public static float time_scale = 1; 16 | /// frame count 17 | public static uint frame_count = 0; 18 | 19 | /// internally used to update the time counters 20 | public static void update(float dt) { 21 | total_time += dt; 22 | delta_time = dt * time_scale; 23 | raw_delta_time = dt; 24 | frame_count++; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /source/re/util/cache.d: -------------------------------------------------------------------------------- 1 | /** caching arbitrary values */ 2 | 3 | module re.util.cache; 4 | 5 | import std.array; 6 | import std.typecons; 7 | 8 | /// represents a cache that associates string keys with items 9 | struct KeyedCache(T) { 10 | private T[string] _cache; 11 | void delegate(T) _free; 12 | 13 | this(void delegate(T) on_free) { 14 | _free = on_free; 15 | } 16 | 17 | /// cache an item 18 | public void put(string key, T value) { 19 | _cache[key] = value; 20 | } 21 | 22 | /// check if an item is cached 23 | public Nullable!T get(string key) { 24 | if (key in _cache) { 25 | return Nullable!T(_cache[key]); 26 | } 27 | return Nullable!T.init; 28 | } 29 | 30 | /// get all items 31 | public T[] get_all() { 32 | return _cache.byValue().array; 33 | } 34 | 35 | /// clear cache 36 | public void drop() { 37 | // run free on each item 38 | if (_free !is null) { 39 | foreach (item; get_all()) { 40 | _free(item); 41 | } 42 | } 43 | _cache.clear(); 44 | } 45 | } 46 | 47 | @("cache-basic") 48 | unittest { 49 | auto num_cache = KeyedCache!int(); 50 | 51 | enum VAL_APPLE = 4; 52 | 53 | num_cache.put("apple", VAL_APPLE); 54 | 55 | immutable auto i1 = num_cache.get("apple"); 56 | assert(!i1.isNull); 57 | assert(i1.get == VAL_APPLE); 58 | assert(num_cache.get_all.length == 1); 59 | 60 | num_cache.drop(); 61 | 62 | immutable auto i2 = num_cache.get("apple"); 63 | assert(i2.isNull); 64 | } 65 | 66 | @("cache-cleanup") 67 | unittest { 68 | auto resources_used = 0; 69 | auto res_cache = KeyedCache!int((x) { resources_used += x; }); 70 | 71 | enum handle = 3; 72 | res_cache.put("handle", handle); 73 | 74 | res_cache.drop(); 75 | assert(res_cache.get_all.length == 0); 76 | 77 | assert(resources_used > 0, "free was not called"); 78 | } 79 | -------------------------------------------------------------------------------- /source/re/util/dlib.d: -------------------------------------------------------------------------------- 1 | module re.util.dlib; 2 | 3 | version (physics_dlib) { 4 | import dl_vec = dlib.math.vector; 5 | import dl_quat = dlib.math.quaternion; 6 | import dl_mat = dlib.math.matrix; 7 | import re.math; 8 | 9 | pragma(inline, true) { 10 | static dl_vec.Vector3f convert_vec3(const(Vector3) vec) { 11 | return dl_vec.Vector3f(vec.x, vec.y, vec.z); 12 | } 13 | 14 | static Vector3 convert_vec3(const(dl_vec.Vector3f) vec) { 15 | return Vector3(vec.x, vec.y, vec.z); 16 | } 17 | 18 | static Quaternion convert_quat(const(dl_quat.Quaternionf) quat) { 19 | auto vec = quat.vectorof[]; 20 | return Quaternion(vec[0], vec[1], vec[2], vec[3]); 21 | } 22 | 23 | static dl_quat.Quaternionf convert_quat(const(Quaternion) quat) { 24 | return dl_quat.Quaternionf(quat.x, quat.y, quat.z, quat.w); 25 | } 26 | 27 | static dl_mat.Matrix4x4f convert_mat(const(Matrix) mat) { 28 | return dl_mat.Matrix4x4f([ 29 | mat.m0, mat.m4, mat.m8, mat.m12, mat.m1, mat.m5, mat.m9, 30 | mat.m13, mat.m2, mat.m6, mat.m10, mat.m14, mat.m3, mat.m7, 31 | mat.m11, mat.m15 32 | ]); 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /source/re/util/dual_map.d: -------------------------------------------------------------------------------- 1 | /** bidirectional map */ 2 | 3 | module re.util.dual_map; 4 | 5 | class DualMap(T1, T2) { 6 | public T2[T1] map1; 7 | public T1[T2] map2; 8 | 9 | public void set(T1 key1, T2 key2) { 10 | map1[key1] = key2; 11 | map2[key2] = key1; 12 | } 13 | 14 | public T2 get(T1 key1) { 15 | return map1[key1]; 16 | } 17 | 18 | public T1 get(T2 key2) { 19 | return map2[key2]; 20 | } 21 | 22 | public bool has(T1 key1) { 23 | return cast(bool)(key1 in map1); 24 | } 25 | 26 | public bool has(T2 key2) { 27 | return cast(bool)(key2 in map2); 28 | } 29 | 30 | public bool remove(T1 key1, T2 key2) { 31 | return map1.remove(key1) && map2.remove(key2); 32 | } 33 | 34 | public bool remove(T1 key1) { 35 | auto key2 = get(key1); 36 | return remove(key1, key2); 37 | } 38 | 39 | public bool remove(T2 key2) { 40 | auto key1 = get(key2); 41 | return remove(key1, key2); 42 | } 43 | 44 | public void clear() { 45 | map1.clear(); 46 | map2.clear(); 47 | } 48 | 49 | @property public size_t count() { 50 | assert(map1.length == map2.length, "maps are out of sync"); 51 | return map1.length; 52 | } 53 | } 54 | 55 | @("util-dualmap") 56 | unittest { 57 | auto dm = new DualMap!(string, int)(); 58 | 59 | dm.set("sunday", 0); 60 | dm.set("monday", 1); 61 | dm.set("tuesday", 2); 62 | 63 | assert(dm.get(1) == "monday"); 64 | assert(dm.get("tuesday") == 2); 65 | assert(dm.count == 3); 66 | 67 | assert(dm.has("monday")); 68 | assert(dm.has(2)); 69 | 70 | assert(dm.remove("tuesday")); 71 | assert(dm.remove(1)); 72 | 73 | assert(dm.count == 1); 74 | assert(!dm.has(1)); 75 | 76 | dm.clear(); 77 | assert(dm.count == 0); 78 | } 79 | -------------------------------------------------------------------------------- /source/re/util/env.d: -------------------------------------------------------------------------------- 1 | module re.util.env; 2 | 3 | import std.conv; 4 | import std.process : environment; 5 | import std.algorithm.searching : canFind; 6 | 7 | static class Environment { 8 | static string get(string name, string default_value = null) { 9 | return environment.get(name, default_value); 10 | } 11 | 12 | static bool get_bool(string name, bool default_value = false) { 13 | auto value = get(name); 14 | static immutable TRUE_VALUES = ["1", "true", "yes"]; 15 | static immutable FALSE_VALUES = ["0", "false", "no"]; 16 | if (TRUE_VALUES.canFind(value)) { 17 | return true; 18 | } else if (FALSE_VALUES.canFind(value)) { 19 | return false; 20 | } else { 21 | return default_value; 22 | } 23 | } 24 | 25 | static int get_int(string name, int default_value = 0) { 26 | auto value = get(name); 27 | if (value is null) { 28 | return default_value; 29 | } 30 | try { 31 | return value.to!int; 32 | } catch (Exception e) { 33 | return default_value; 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /source/re/util/format.d: -------------------------------------------------------------------------------- 1 | /** string formatting */ 2 | 3 | module re.util.format; 4 | 5 | import re.math; 6 | import std.string; 7 | import std.conv : to; 8 | 9 | string format_vec(Vector2 vec, int precision) { 10 | auto spec = "%." ~ to!string(precision) ~ "f"; 11 | auto str = format("%s, %s", spec, spec); 12 | return format(str, vec.x, vec.y); 13 | } 14 | 15 | string format_vec(Vector3 vec, int precision) { 16 | auto spec = "%." ~ to!string(precision) ~ "f"; 17 | auto str = format("%s, %s, %s", spec, spec, spec); 18 | return format(str, vec.x, vec.y, vec.z); 19 | } 20 | 21 | string format_vec(Vector4 vec, int precision) { 22 | auto spec = "%." ~ to!string(precision) ~ "f"; 23 | auto str = format("%s, %s, %s, %s", spec, spec, spec, spec); 24 | return format(str, vec.x, vec.y, vec.z, vec.w); 25 | } 26 | -------------------------------------------------------------------------------- /source/re/util/interop.d: -------------------------------------------------------------------------------- 1 | /** utilities for interop with c */ 2 | 3 | module re.util.interop; 4 | 5 | import std.utf; 6 | 7 | public static char* c_str(string str) { 8 | return str.toUTFz!(char*)(); 9 | } 10 | 11 | @("interop-basic") 12 | unittest { 13 | import core.stdc.string; 14 | import std.conv; 15 | 16 | auto str1 = "hello"; 17 | char* str1c = str1.c_str; 18 | 19 | // check c string 20 | assert(str1c.to!string == str1); 21 | assert(strlen(str1c) == str1.length); 22 | } 23 | -------------------------------------------------------------------------------- /source/re/util/jar.d: -------------------------------------------------------------------------------- 1 | module re.util.jar; 2 | 3 | import std.array; 4 | import std.typecons; 5 | 6 | /// a container for registered types 7 | class Jar { 8 | private Object[][TypeInfo] _instance_registry; 9 | 10 | /// register a type instance 11 | public T register(T)(T instance) { 12 | auto type = typeid(instance); 13 | if (type !in _instance_registry) { 14 | _instance_registry[type] = new Object[0]; 15 | } 16 | _instance_registry[type] ~= instance; 17 | return instance; 18 | } 19 | 20 | /// resolve all matching type instances 21 | public T[] resolve_all(T)() { 22 | auto type = typeid(T); 23 | if (type in _instance_registry) { 24 | Appender!(T[]) resolved; 25 | foreach (item; _instance_registry[type]) { 26 | resolved ~= cast(T) item; 27 | } 28 | return resolved.data; 29 | } 30 | return new T[0]; 31 | } 32 | 33 | /// resolve first matching type instance 34 | public Nullable!T resolve(T)() { 35 | auto items = resolve_all!T(); 36 | if (items.length > 0) { 37 | return Nullable!T(items[0]); 38 | } 39 | return Nullable!T.init; 40 | } 41 | } 42 | 43 | unittest { 44 | class Cookie { 45 | bool delicious = true; 46 | } 47 | 48 | auto jar = new Jar(); 49 | 50 | auto c1 = new Cookie(); 51 | jar.register(c1); 52 | auto r1 = jar.resolve!Cookie(); 53 | assert(!r1.isNull, "resolved item was null"); 54 | assert(r1.get() == c1, "resolved item did not match"); 55 | 56 | auto c2 = new Cookie(); 57 | jar.register(c2); 58 | 59 | const auto cookies = jar.resolve_all!Cookie(); 60 | assert(cookies.length == 2, "mismatch in number of registered items"); 61 | } 62 | -------------------------------------------------------------------------------- /source/re/util/logger.d: -------------------------------------------------------------------------------- 1 | module re.util.logger; 2 | 3 | public import minlog; -------------------------------------------------------------------------------- /source/re/util/orbit.d: -------------------------------------------------------------------------------- 1 | /** component for orbit motion around a point */ 2 | 3 | module re.util.orbit; 4 | 5 | import re.ecs; 6 | import re.math; 7 | import re.time; 8 | 9 | /// orbits an entity around a point 10 | class Orbit : Component, Updatable { 11 | public float angle; 12 | public float speed; 13 | public float radius; 14 | public Vector3 center; 15 | 16 | this(Vector3 center, float radius, float speed, float initial_angle = 0) { 17 | this.angle = initial_angle; 18 | this.radius = radius; 19 | this.speed = speed; 20 | this.center = center; 21 | } 22 | 23 | void update() { 24 | import std.math : sin, cos; 25 | 26 | angle -= speed * Time.delta_time; 27 | entity.position = center + Vector3(cos(angle) * radius, 0, sin(angle) * radius); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /source/re/util/reflect.d: -------------------------------------------------------------------------------- 1 | /** makes the members of a class available for runtime reflection */ 2 | 3 | module re.util.reflect; 4 | 5 | /// in debug mode, allows inspectors to reflect on members 6 | mixin template Reflect() { 7 | debug { 8 | import witchcraft; 9 | 10 | mixin Witchcraft; /// in debug mode, enable reflection 11 | } 12 | } 13 | 14 | /// a base class for all objects that can be reflected upon 15 | abstract class ReflectableObject { 16 | mixin Reflect; 17 | } 18 | -------------------------------------------------------------------------------- /source/re/util/rng.d: -------------------------------------------------------------------------------- 1 | /** random number generator */ 2 | 3 | module re.util.rng; 4 | 5 | import std.random; 6 | 7 | static class Rng { 8 | public static std.random.Random rng; 9 | 10 | static this() { 11 | } 12 | 13 | public static float next_float() { 14 | return (cast(float) next() / cast(float) uint.max); 15 | } 16 | 17 | public static uint next() { 18 | auto val = rng.front; 19 | rng.popFront(); 20 | return val; 21 | } 22 | 23 | public static int next_int() { 24 | return cast(int) next(); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /source/re/util/test.d: -------------------------------------------------------------------------------- 1 | /** utilities for unit-testing core features */ 2 | 3 | module re.util.test; 4 | 5 | import re.core; 6 | import re.ng.scene; 7 | 8 | version (unittest) { 9 | class TestGame : Core { 10 | this() { 11 | headless = true; 12 | super(1280, 720, string.init); 13 | } 14 | 15 | override void initialize() { 16 | // nothing to do here 17 | } 18 | } 19 | 20 | public struct TestGameRunner { 21 | TestGame game; 22 | Scene scene; 23 | } 24 | 25 | public static TestGameRunner test_scene(Scene scene) { 26 | auto game = new TestGame(); 27 | game.load_scenes([scene]); 28 | return TestGameRunner(game, scene); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /source/re/util/tweens/ease.d: -------------------------------------------------------------------------------- 1 | /** tween easings */ 2 | 3 | module re.util.tweens.ease; 4 | 5 | static import easings; 6 | 7 | public alias EaseFunction = float function(float, float, float, float); 8 | 9 | /// ease functions 10 | struct Ease { 11 | pragma(inline, true) { 12 | 13 | // Linear Easing functions 14 | static float LinearNone(float t, float b, float c, float d) { 15 | return easings.EaseLinearNone(t, b, c, d); 16 | } 17 | 18 | static float LinearIn(float t, float b, float c, float d) { 19 | return easings.EaseLinearIn(t, b, c, d); 20 | } 21 | 22 | static float LinearOut(float t, float b, float c, float d) { 23 | return easings.EaseLinearOut(t, b, c, d); 24 | } 25 | 26 | static float LinearInOut(float t, float b, float c, float d) { 27 | return easings.EaseLinearInOut(t, b, c, d); 28 | } 29 | 30 | // Sine Easing functions 31 | static float SineIn(float t, float b, float c, float d) { 32 | return easings.EaseSineIn(t, b, c, d); 33 | } 34 | 35 | static float SineOut(float t, float b, float c, float d) { 36 | return easings.EaseSineOut(t, b, c, d); 37 | } 38 | 39 | static float SineInOut(float t, float b, float c, float d) { 40 | return easings.EaseSineInOut(t, b, c, d); 41 | } 42 | 43 | // Circular Easing functions 44 | static float CircIn(float t, float b, float c, float d) { 45 | return easings.EaseCircIn(t, b, c, d); 46 | } 47 | 48 | static float CircOut(float t, float b, float c, float d) { 49 | return easings.EaseCircOut(t, b, c, d); 50 | } 51 | 52 | static float CircInOut(float t, float b, float c, float d) { 53 | return easings.EaseCircInOut(t, b, c, d); 54 | } 55 | 56 | // Cubic Easing functions 57 | static float CubicIn(float t, float b, float c, float d) { 58 | return easings.EaseCubicIn(t, b, c, d); 59 | } 60 | 61 | static float CubicOut(float t, float b, float c, float d) { 62 | return easings.EaseCubicOut(t, b, c, d); 63 | } 64 | 65 | static float CubicInOut(float t, float b, float c, float d) { 66 | return easings.EaseCubicInOut(t, b, c, d); 67 | } 68 | 69 | // Quadratic Easing functions 70 | static float QuadIn(float t, float b, float c, float d) { 71 | return easings.EaseQuadIn(t, b, c, d); 72 | } 73 | 74 | static float QuadOut(float t, float b, float c, float d) { 75 | return easings.EaseQuadOut(t, b, c, d); 76 | } 77 | 78 | static float QuadInOut(float t, float b, float c, float d) { 79 | return easings.EaseQuadInOut(t, b, c, d); 80 | } 81 | 82 | // Exponential Easing functions 83 | static float ExpoIn(float t, float b, float c, float d) { 84 | return easings.EaseExpoIn(t, b, c, d); 85 | } 86 | 87 | static float ExpoOut(float t, float b, float c, float d) { 88 | return easings.EaseExpoOut(t, b, c, d); 89 | } 90 | 91 | static float ExpoInOut(float t, float b, float c, float d) { 92 | return easings.EaseExpoInOut(t, b, c, d); 93 | } 94 | 95 | // Back Easing functions 96 | static float BackIn(float t, float b, float c, float d) { 97 | return easings.EaseBackIn(t, b, c, d); 98 | } 99 | 100 | static float BackOut(float t, float b, float c, float d) { 101 | return easings.EaseBackOut(t, b, c, d); 102 | } 103 | 104 | static float BackInOut(float t, float b, float c, float d) { 105 | return easings.EaseBackInOut(t, b, c, d); 106 | } 107 | 108 | // Bounce Easing functions 109 | static float BounceOut(float t, float b, float c, float d) { 110 | return easings.EaseBounceOut(t, b, c, d); 111 | } 112 | 113 | static float BounceIn(float t, float b, float c, float d) { 114 | return easings.EaseBounceIn(t, b, c, d); 115 | } 116 | 117 | static float BounceInOut(float t, float b, float c, float d) { 118 | return easings.EaseBounceInOut(t, b, c, d); 119 | } 120 | 121 | // Elastic Easing functions 122 | static float ElasticIn(float t, float b, float c, float d) { 123 | return easings.EaseElasticIn(t, b, c, d); 124 | } 125 | 126 | static float ElasticOut(float t, float b, float c, float d) { 127 | return easings.EaseElasticOut(t, b, c, d); 128 | } 129 | 130 | static float ElasticInOut(float t, float b, float c, float d) { 131 | return easings.EaseElasticInOut(t, b, c, d); 132 | } 133 | } 134 | } 135 | -------------------------------------------------------------------------------- /source/re/util/tweens/tween_manager.d: -------------------------------------------------------------------------------- 1 | /** manager for handling and coordinating all queued tweens */ 2 | 3 | module re.util.tweens.tween_manager; 4 | 5 | import re.ng.manager; 6 | import re.time; 7 | import re.util.tweens.tween; 8 | import std.algorithm; 9 | 10 | /// manages and updates tweens 11 | class TweenManager : Manager { 12 | /// the tweens being managed (internal) 13 | private ITween[] _tweens; 14 | 15 | override void update() { 16 | super.update(); 17 | 18 | // update tweens 19 | ITween[] done; 20 | foreach (tw; _tweens) { 21 | tw.update(Time.delta_time); 22 | if (tw.state == TweenState.Complete) { 23 | done ~= tw; 24 | } 25 | } 26 | foreach (tw; done) { 27 | // remove from our tween list 28 | _tweens = _tweens.remove!(x => x == tw); 29 | // add chains to our list 30 | _tweens ~= tw.get_chain; 31 | // call callback 32 | auto cb = tw.callback; 33 | if (cb !is null) { 34 | cb(tw); 35 | } 36 | } 37 | } 38 | 39 | public void register(ITween tw) { 40 | _tweens ~= tw; 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /source/re/util/type.d: -------------------------------------------------------------------------------- 1 | /** utilities for runtime type reflection */ 2 | 3 | module re.util.type; 4 | 5 | template name_of(alias nameType) { 6 | enum string name_of = __traits(identifier, nameType); 7 | } 8 | 9 | @("util-type") 10 | unittest { 11 | auto test_var = 1; 12 | assert(name_of!test_var == "test_var"); 13 | 14 | class TestClass { 15 | } 16 | 17 | assert(name_of!TestClass == "TestClass"); 18 | } 19 | -------------------------------------------------------------------------------- /source/re/util/vr_distortion.d: -------------------------------------------------------------------------------- 1 | module re.util.vr_distortion; 2 | 3 | version (vr) { 4 | enum VR_DISTORTION_SHADER_GL100 = ` 5 | #version 100 6 | 7 | precision mediump float; 8 | 9 | // Input vertex attributes (from vertex shader) 10 | varying vec2 fragTexCoord; 11 | varying vec4 fragColor; 12 | 13 | // Input uniform values 14 | uniform sampler2D texture0; 15 | uniform vec4 colDiffuse; 16 | 17 | // NOTE: Add here your custom variables 18 | uniform vec2 leftLensCenter; 19 | uniform vec2 rightLensCenter; 20 | uniform vec2 leftScreenCenter; 21 | uniform vec2 rightScreenCenter; 22 | uniform vec2 scale; 23 | uniform vec2 scaleIn; 24 | uniform vec4 deviceWarpParam; 25 | uniform vec4 chromaAbParam; 26 | 27 | void main() 28 | { 29 | // Compute lens distortion 30 | vec2 lensCenter = fragTexCoord.x < 0.5? leftLensCenter : rightLensCenter; 31 | vec2 screenCenter = fragTexCoord.x < 0.5? leftScreenCenter : rightScreenCenter; 32 | vec2 theta = (fragTexCoord - lensCenter)*scaleIn; 33 | float rSq = theta.x*theta.x + theta.y*theta.y; 34 | vec2 theta1 = theta*(deviceWarpParam.x + deviceWarpParam.y*rSq + deviceWarpParam.z*rSq*rSq + deviceWarpParam.w*rSq*rSq*rSq); 35 | vec2 thetaBlue = theta1*(chromaAbParam.z + chromaAbParam.w*rSq); 36 | vec2 tcBlue = lensCenter + scale*thetaBlue; 37 | 38 | if (any(bvec2(clamp(tcBlue, screenCenter - vec2(0.25, 0.5), screenCenter + vec2(0.25, 0.5)) - tcBlue))) 39 | { 40 | // Set black fragment for everything outside the lens border 41 | gl_FragColor = vec4(0.0, 0.0, 0.0, 1.0); 42 | } 43 | else 44 | { 45 | // Compute color chroma aberration 46 | float blue = texture2D(texture0, tcBlue).b; 47 | vec2 tcGreen = lensCenter + scale*theta1; 48 | float green = texture2D(texture0, tcGreen).g; 49 | 50 | vec2 thetaRed = theta1*(chromaAbParam.x + chromaAbParam.y*rSq); 51 | vec2 tcRed = lensCenter + scale*thetaRed; 52 | 53 | float red = texture2D(texture0, tcRed).r; 54 | gl_FragColor = vec4(red, green, blue, 1.0); 55 | } 56 | } 57 | `; 58 | enum VR_DISTORTION_SHADER_GL330 = ` 59 | #version 330 60 | 61 | // Input vertex attributes (from vertex shader) 62 | in vec2 fragTexCoord; 63 | in vec4 fragColor; 64 | 65 | // Input uniform values 66 | uniform sampler2D texture0; 67 | uniform vec4 colDiffuse; 68 | 69 | // Output fragment color 70 | out vec4 finalColor; 71 | 72 | // NOTE: Add here your custom variables 73 | uniform vec2 leftLensCenter = vec2(0.288, 0.5); 74 | uniform vec2 rightLensCenter = vec2(0.712, 0.5); 75 | uniform vec2 leftScreenCenter = vec2(0.25, 0.5); 76 | uniform vec2 rightScreenCenter = vec2(0.75, 0.5); 77 | uniform vec2 scale = vec2(0.25, 0.45); 78 | uniform vec2 scaleIn = vec2(4, 2.2222); 79 | uniform vec4 deviceWarpParam = vec4(1, 0.22, 0.24, 0); 80 | uniform vec4 chromaAbParam = vec4(0.996, -0.004, 1.014, 0.0); 81 | 82 | void main() 83 | { 84 | // Compute lens distortion 85 | vec2 lensCenter = fragTexCoord.x < 0.5? leftLensCenter : rightLensCenter; 86 | vec2 screenCenter = fragTexCoord.x < 0.5? leftScreenCenter : rightScreenCenter; 87 | vec2 theta = (fragTexCoord - lensCenter)*scaleIn; 88 | float rSq = theta.x*theta.x + theta.y*theta.y; 89 | vec2 theta1 = theta*(deviceWarpParam.x + deviceWarpParam.y*rSq + deviceWarpParam.z*rSq*rSq + deviceWarpParam.w*rSq*rSq*rSq); 90 | vec2 thetaBlue = theta1*(chromaAbParam.z + chromaAbParam.w*rSq); 91 | vec2 tcBlue = lensCenter + scale*thetaBlue; 92 | 93 | if (any(bvec2(clamp(tcBlue, screenCenter - vec2(0.25, 0.5), screenCenter + vec2(0.25, 0.5)) - tcBlue))) 94 | { 95 | // Set black fragment for everything outside the lens border 96 | finalColor = vec4(0.0, 0.0, 0.0, 1.0); 97 | } 98 | else 99 | { 100 | // Compute color chroma aberration 101 | float blue = texture(texture0, tcBlue).b; 102 | vec2 tcGreen = lensCenter + scale*theta1; 103 | float green = texture(texture0, tcGreen).g; 104 | 105 | vec2 thetaRed = theta1*(chromaAbParam.x + chromaAbParam.y*rSq); 106 | vec2 tcRed = lensCenter + scale*thetaRed; 107 | 108 | float red = texture(texture0, tcRed).r; 109 | finalColor = vec4(red, green, blue, 1.0); 110 | } 111 | } 112 | 113 | `; 114 | } --------------------------------------------------------------------------------