├── .gitattributes ├── .github ├── FUNDING.yml └── workflows │ ├── ci.yml │ └── release.yml ├── .gitignore ├── .gitmodules ├── .licensure.yml ├── .luaurc ├── .stylua.toml ├── Cargo.lock ├── Cargo.toml ├── LICENSE ├── README.md ├── all_nodes_test.bjk ├── blackjack_commons ├── Cargo.toml └── src │ ├── lib.rs │ ├── math.rs │ └── utils.rs ├── blackjack_engine ├── Cargo.toml └── src │ ├── engine_tests.rs │ ├── gizmos.rs │ ├── graph.rs │ ├── graph │ └── serialization.rs │ ├── graph_interpreter.rs │ ├── lib.rs │ ├── lua_engine.rs │ ├── lua_engine │ ├── blackjack_utils.lua │ ├── lua_stdlib.rs │ ├── lua_stdlib │ │ ├── lua_core_library.rs │ │ ├── lua_documentation.rs │ │ ├── lua_require_io.rs │ │ └── runtime_types.rs │ ├── node_library.lua │ └── node_params.lua │ ├── mesh.rs │ ├── mesh │ ├── halfedge.rs │ ├── halfedge │ │ ├── channels.rs │ │ ├── compact_mesh.rs │ │ ├── edit_ops.rs │ │ ├── edit_ops │ │ │ └── deprecated.rs │ │ ├── gpu_buffer_generation.rs │ │ ├── halfedge_lua_api.rs │ │ ├── id_types.rs │ │ ├── mappings.rs │ │ ├── mesh_index_impls.rs │ │ ├── primitives.rs │ │ ├── selection.rs │ │ ├── traversals.rs │ │ └── wavefront_obj.rs │ └── heightmap.rs │ ├── prelude.rs │ └── sync.rs ├── blackjack_godot ├── Cargo.toml └── src │ ├── godot_lua_io.rs │ └── lib.rs ├── blackjack_lua ├── lib │ ├── gizmo_helpers.lua │ ├── priority_queue.lua │ ├── table_helpers.lua │ └── vector_math.lua └── run │ ├── .gitignore │ └── core_nodes.lua ├── blackjack_macros ├── Cargo.toml └── src │ ├── blackjack_lua_module.rs │ ├── blackjack_lua_module │ └── fn_attr.rs │ ├── lib.rs │ └── utils.rs ├── blackjack_ui ├── Cargo.toml ├── assets │ ├── bevel_edge_test_case_1.obj │ ├── bevel_edge_test_case_2.mtl │ ├── bevel_edge_test_case_2.obj │ ├── debug │ │ ├── arrow.mtl │ │ ├── arrow.obj │ │ ├── cylinder.obj │ │ └── icosphere.obj │ ├── matcap │ │ ├── 2E763A_78A0B7_B3D1CF_14F209.png │ │ ├── 304FB1_69A1EF_5081DF_5C8CE6.png │ │ ├── 313131_BBBBBB_878787_A3A4A4.png │ │ ├── 326666_66CBC9_C0B8AE_52B3B4.png │ │ ├── 34352A_718184_50605E_6E6761.png │ │ ├── A67362_36150C_5E2E1E_F6C3BF.jpg │ │ ├── CREDITS.txt │ │ ├── E8DEE1_B5A6AA_CCBCC1_C4BBBC.png │ │ └── Perenyi-Midtgaard APLAS20.pdf │ └── test.obj └── src │ ├── app_window.rs │ ├── app_window │ ├── gui_overlay.rs │ └── input.rs │ ├── application.rs │ ├── application │ ├── app_viewport.rs │ ├── application_context.rs │ ├── code_viewer.rs │ ├── gizmo_ui.rs │ ├── graph_editor.rs │ ├── inspector.rs │ ├── root_graph.rs │ ├── root_ui.rs │ ├── serialization.rs │ ├── viewport_3d.rs │ ├── viewport_3d │ │ └── lerp.rs │ └── viewport_split.rs │ ├── cli_args.rs │ ├── color_hex_utils.rs │ ├── custom_widgets.rs │ ├── custom_widgets │ └── smart_dragvalue.rs │ ├── egui_ext.rs │ ├── graph.rs │ ├── graph │ ├── graph_interop.rs │ └── node_graph.rs │ ├── main.rs │ ├── prelude.rs │ ├── render_context.rs │ ├── rendergraph.rs │ └── rendergraph │ ├── common.rs │ ├── edge_wireframe_draw.wgsl │ ├── face_draw.wgsl │ ├── face_overlay_draw.wgsl │ ├── face_routine.rs │ ├── grid_routine.rs │ ├── grid_shader.wgsl │ ├── id_picking_routine.rs │ ├── point_cloud_draw.wgsl │ ├── point_cloud_routine.rs │ ├── rend3_common.wgsl │ ├── rend3_object.wgsl │ ├── rend3_uniforms.wgsl │ ├── rend3_vertex.wgsl │ ├── shader_manager.rs │ ├── utils.wgsl │ ├── viewport_3d_routine.rs │ └── wireframe_routine.rs ├── build_godot_plugin.sh ├── build_ldoc.sh ├── doc ├── images │ ├── cube_primitive.png │ ├── dissolve_edge.png │ ├── drawings.excalidraw │ ├── edge_divide.png │ ├── edge_extrude.excalidraw │ ├── edge_extrude.png │ ├── navigate_edge_loop.png │ ├── split_edge.png │ └── split_vertex.png ├── resources │ ├── blackjack.gif │ ├── blackjack_gif2.gif │ ├── blackjack_gif3.gif │ ├── blackjack_gif4.gif │ ├── blackjack_gif5.gif │ ├── showcase.png │ ├── showcase2.png │ └── showcase3.png └── why_lua.md ├── examples ├── EXAMPLES_LICENSE ├── box.bjk ├── stylised_sword.bjk └── tp_cutter.bjk ├── godot_plugin └── addons │ └── blackjack_engine_godot │ ├── BgaFileResource.gd │ ├── BlackjackApi.gdns │ ├── BlackjackInspectorPlugin.gd │ ├── BlackjackJack.gd │ ├── BlackjackPropertiesTweaker.gd │ ├── BlackjackPropertiesTweaker.tscn │ ├── ErrorLabel.gd │ ├── ErrorLabel.tscn │ ├── JackImportPlugin.gd │ ├── ScalarProp.gd │ ├── ScalarProp.tscn │ ├── SelectionProp.gd │ ├── SelectionProp.tscn │ ├── StringProp.gd │ ├── StringProp.tscn │ ├── VectorProp.gd │ ├── VectorProp.tscn │ ├── blackjack_godot.gdnlib │ ├── icon.png │ ├── icon.png.import │ ├── plugin.cfg │ └── plugin.gd ├── ldoc.css ├── reinstall_godot_plugin.sh └── test └── test_mesh.obj /.gitattributes: -------------------------------------------------------------------------------- 1 | *.png filter=lfs diff=lfs merge=lfs -text 2 | *.jpg filter=lfs diff=lfs merge=lfs -text 3 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: setzer22 4 | ko_fi: setzer22 5 | 6 | # patreon: # Replace with a single Patreon username 7 | # open_collective: # Replace with a single Open Collective username 8 | # tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel 9 | # community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry 10 | # liberapay: # Replace with a single Liberapay username 11 | # issuehunt: # Replace with a single IssueHunt username 12 | # otechie: # Replace with a single Otechie username 13 | # lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry 14 | # custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2'] 15 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | on: 2 | push: 3 | branches: [ main ] 4 | pull_request: 5 | 6 | name: CI 7 | 8 | env: 9 | # This is required to enable the web_sys clipboard API which egui_web uses 10 | # https://rustwasm.github.io/wasm-bindgen/api/web_sys/struct.Clipboard.html 11 | # https://rustwasm.github.io/docs/wasm-bindgen/web-sys/unstable-apis.html 12 | RUSTFLAGS: --cfg=web_sys_unstable_apis 13 | 14 | jobs: 15 | test: 16 | name: Run tests 17 | runs-on: ubuntu-latest 18 | steps: 19 | - uses: actions/checkout@v2 20 | - uses: actions-rs/toolchain@v1 21 | with: 22 | profile: minimal 23 | toolchain: stable 24 | override: true 25 | - uses: Swatinem/rust-cache@v2 26 | - run: sudo apt-get update; sudo apt-get install libgtk-3-dev 27 | - run: cargo test --all-features 28 | 29 | fmt: 30 | name: Rustfmt 31 | runs-on: ubuntu-latest 32 | steps: 33 | - uses: actions/checkout@v2 34 | - uses: actions-rs/toolchain@v1 35 | with: 36 | profile: minimal 37 | toolchain: stable 38 | override: true 39 | - uses: Swatinem/rust-cache@v2 40 | - run: rustup component add rustfmt; sudo apt-get update; sudo apt-get install libgtk-3-dev 41 | - uses: actions-rs/cargo@v1 42 | with: 43 | command: fmt 44 | args: --all -- --check 45 | 46 | clippy: 47 | name: Clippy 48 | runs-on: ubuntu-latest 49 | steps: 50 | - uses: actions/checkout@v2 51 | - uses: Swatinem/rust-cache@v2 52 | - uses: actions-rs/toolchain@v1 53 | with: 54 | profile: minimal 55 | toolchain: stable 56 | override: true 57 | - uses: Swatinem/rust-cache@v2 58 | - run: rustup component add clippy; sudo apt-get update; sudo apt-get install libgtk-3-dev 59 | - uses: actions-rs/cargo@v1 60 | with: 61 | command: clippy 62 | args: -- -D warnings 63 | 64 | license: 65 | name: License 66 | runs-on: ubuntu-latest 67 | steps: 68 | - uses: actions/checkout@v2 69 | - uses: Swatinem/rust-cache@v2 70 | - uses: actions-rs/toolchain@v1 71 | with: 72 | profile: minimal 73 | toolchain: stable 74 | override: true 75 | # NOTE: Licensure uses an inconsistent newline style in version 0.3.* 76 | - run: cargo install licensure --version "0.2.1" 77 | - run: ~/.cargo/bin/licensure --project --check 78 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: Create new release 2 | on: 3 | push: 4 | tags: 5 | - v* 6 | jobs: 7 | linux-build: 8 | name: Build Blackjack for Linux 9 | runs-on: ubuntu-20.04 10 | steps: 11 | - uses: actions/checkout@v3 12 | with: 13 | branch: feature/release_ci 14 | repository: setzer22/blackjack 15 | lfs: true 16 | - uses: actions-rs/toolchain@v1 17 | with: 18 | profile: minimal 19 | toolchain: stable 20 | override: true 21 | - uses: Swatinem/rust-cache@v2 22 | - run: cargo build --release 23 | - run: ./build_godot_plugin.sh 24 | - run: | 25 | mkdir ./blackjack_linux 26 | cp ./target/release/blackjack_ui ./blackjack_linux 27 | chmod +x ./blackjack_linux/blackjack_ui 28 | cp -r ./blackjack_lua ./blackjack_linux 29 | - uses: actions/upload-artifact@v3 30 | with: 31 | name: blackjack-linux 32 | path: blackjack_linux 33 | - uses: actions/upload-artifact@v3 34 | with: 35 | name: blackjack-godot-base 36 | path: target/godot_plugin 37 | 38 | windows-build: 39 | name: Build Blackjack for Windows 40 | runs-on: windows-latest 41 | steps: 42 | - uses: actions/checkout@v3 43 | with: 44 | branch: feature/release_ci 45 | repository: setzer22/blackjack 46 | lfs: true 47 | - uses: actions-rs/toolchain@v1 48 | with: 49 | profile: minimal 50 | toolchain: stable 51 | override: true 52 | - uses: Swatinem/rust-cache@v2 53 | - run: cargo build --release 54 | - run: cargo build --release -p blackjack_godot 55 | - run: | 56 | mkdir ./blackjack_windows 57 | ls target/release 58 | cp ./target/release/blackjack_ui.exe ./blackjack_windows 59 | cp -r ./blackjack_lua ./blackjack_windows 60 | shell: bash 61 | - uses: actions/upload-artifact@v3 62 | with: 63 | name: blackjack-windows 64 | path: blackjack_windows/ 65 | - uses: actions/upload-artifact@v3 66 | with: 67 | name: blackjack-godot-windows-lib 68 | path: target/release/blackjack_godot.dll 69 | 70 | macos-build: 71 | name: Build Blackjack for MacOS 72 | runs-on: macos-latest 73 | steps: 74 | - uses: actions/checkout@v3 75 | with: 76 | branch: feature/release_ci 77 | repository: setzer22/blackjack 78 | lfs: true 79 | - uses: actions-rs/toolchain@v1 80 | with: 81 | profile: minimal 82 | toolchain: stable 83 | override: true 84 | - uses: Swatinem/rust-cache@v2 85 | - run: cargo build --release 86 | - run: cargo build --release -p blackjack_godot 87 | - run: | 88 | mkdir ./blackjack_macos 89 | ls target/release/ 90 | cp ./target/release/blackjack_ui ./blackjack_macos 91 | chmod +x ./blackjack_macos/blackjack_ui 92 | cp -r ./blackjack_lua ./blackjack_macos 93 | shell: bash 94 | - uses: actions/upload-artifact@v3 95 | with: 96 | name: blackjack-macos 97 | path: blackjack_macos/ 98 | - uses: actions/upload-artifact@v3 99 | with: 100 | name: blackjack-godot-macos-lib 101 | path: target/release/libblackjack_godot.dylib 102 | 103 | create-release: 104 | name: Assemble packages and create release 105 | needs: 106 | - windows-build 107 | - linux-build 108 | - macos-build 109 | runs-on: ubuntu-latest 110 | steps: 111 | - uses: actions/checkout@v3 112 | - name: Get pushed tag 113 | run: echo "RELEASE_VERSION=${GITHUB_REF#refs/*/}" >> $GITHUB_ENV 114 | - name: Print tag 115 | run: echo The tag is $RELEASE_VERSION 116 | 117 | - name: Download editor executable (Linux) 118 | uses: actions/download-artifact@v3 119 | with: 120 | name: blackjack-linux 121 | path: blackjack_linux 122 | - name: Download editor executable (Windows) 123 | uses: actions/download-artifact@v3 124 | with: 125 | name: blackjack-windows 126 | path: blackjack_windows 127 | - name: Download editor executable (MacOS) 128 | uses: actions/download-artifact@v3 129 | with: 130 | name: blackjack-macos 131 | path: blackjack_macos 132 | 133 | - name: Package the editor zips 134 | run: | 135 | ls -lh 136 | 137 | # Package for Windows 138 | mv blackjack_windows blackjack-$RELEASE_VERSION-windows 139 | zip -r blackjack-$RELEASE_VERSION-windows.zip blackjack-$RELEASE_VERSION-windows 140 | 141 | # Package for Linux 142 | mv blackjack_linux blackjack-$RELEASE_VERSION-linux 143 | zip -r blackjack-$RELEASE_VERSION-linux.zip blackjack-$RELEASE_VERSION-linux 144 | 145 | # Package for MacOS 146 | mv blackjack_macos blackjack-$RELEASE_VERSION-macos 147 | zip -r blackjack-$RELEASE_VERSION-macos.zip blackjack-$RELEASE_VERSION-macos 148 | 149 | - name: Download godot plugin Base (Linux) 150 | uses: actions/download-artifact@v3 151 | with: 152 | name: blackjack-godot-base 153 | path: godot_plugin 154 | - name: Download godot plugin Windows dll 155 | uses: actions/download-artifact@v3 156 | with: 157 | name: blackjack-godot-macos-lib 158 | path: godot_plugin/addons/blackjack_engine_godot/ 159 | - name: Download godot plugin MacOS dll 160 | uses: actions/download-artifact@v3 161 | with: 162 | name: blackjack-godot-windows-lib 163 | path: godot_plugin/addons/blackjack_engine_godot/ 164 | 165 | - name: Package the godot plugin 166 | run: | 167 | ls -lh 168 | pushd godot_plugin 169 | zip -r ../blackjack-godot-plugin-$RELEASE_VERSION.zip * 170 | popd 171 | ls -lh 172 | ls -lh godot_plugin 173 | 174 | - name: Create a release draft 175 | run: | 176 | gh release create \ 177 | --draft \ 178 | $RELEASE_VERSION \ 179 | blackjack-$RELEASE_VERSION-{windows,linux,macos}.zip \ 180 | blackjack-godot-plugin-$RELEASE_VERSION.zip 181 | env: 182 | GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} 183 | 184 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .idea/ 2 | gen/ 3 | target/ 4 | .cargo/ 5 | .vscode 6 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "rend3"] 2 | path = rend3 3 | url = https://github.com/setzer22/rend3.git 4 | -------------------------------------------------------------------------------- /.licensure.yml: -------------------------------------------------------------------------------- 1 | change_in_place: true 2 | excludes: 3 | - \.gitignore 4 | - .*lock 5 | - \.git/.* 6 | - \.licensure\.yml 7 | - README.* 8 | - LICENSE.* 9 | - .*\.(md|rst|txt) 10 | - priority_queue.lua 11 | licenses: 12 | - files: "(^.*\\.rs$|^.*\\.lua$|^.*\\.gd$)" 13 | ident: MPL-2.0 14 | authors: 15 | - name: setzer22 and contributors 16 | template: "Copyright (C) [year] [name of author]\n\n 17 | 18 | This Source Code Form is subject to the terms of the Mozilla Public 19 | License, v. 2.0. If a copy of the MPL was not distributed with this 20 | file, You can obtain one at https://mozilla.org/MPL/2.0/." 21 | comments: 22 | - columns: 70 23 | extensions: 24 | - rs 25 | commenter: 26 | type: line 27 | comment_char: "//" 28 | trailing_lines: 1 29 | - columns: 70 30 | extensions: 31 | - lua 32 | commenter: 33 | type: line 34 | comment_char: "--" 35 | trailing_lines: 1 36 | - columns: 70 37 | extensions: 38 | - gd 39 | commenter: 40 | type: line 41 | comment_char: "#" 42 | trailing_lines: 1 43 | -------------------------------------------------------------------------------- /.luaurc: -------------------------------------------------------------------------------- 1 | { 2 | "languageMode": "strict", 3 | "globals": [ 4 | "Blackjack", 5 | "vector", 6 | "Primitives", 7 | "Types", 8 | "Ops", 9 | "NodeLibrary" 10 | ] 11 | } -------------------------------------------------------------------------------- /.stylua.toml: -------------------------------------------------------------------------------- 1 | line_endings = "Unix" 2 | indent_type = "Spaces" 3 | indent_width = 4 4 | column_width = 100 5 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | members = [ 3 | "blackjack_commons", 4 | "blackjack_engine", 5 | "blackjack_ui", 6 | "blackjack_godot", 7 | "blackjack_macros", 8 | ] 9 | 10 | resolver = "2" 11 | 12 | # Image loading is too slow in debug mode. This compiles all dependencies with optimizations. 13 | [profile.dev.package."*"] 14 | opt-level = 3 15 | 16 | 17 | [patch.crates-io] 18 | # This is necessary until egui 0.20, because blackjack needs the changes from this PR: 19 | # https://github.com/emilk/egui/pull/2051 20 | egui = { git = "https://github.com/setzer22/egui", branch = "egui_0_19_plus_pr_2051" } 21 | egui-winit = { git = "https://github.com/setzer22/egui", branch = "egui_0_19_plus_pr_2051" } 22 | egui-wgpu = { git = "https://github.com/setzer22/egui", branch = "egui_0_19_plus_pr_2051" } 23 | 24 | # NOTE: These patches are only used for development, and should be left 25 | # commented out in any commits to the 'main' branch 26 | # egui_wgpu_backend = { path = "../egui_wgpu_backend" } 27 | # egui_winit_platform = { path = "../egui_winit_platform" } 28 | # egui_node_graph = { path = "../egui_node_graph/egui_node_graph", features = ["persistence"] } 29 | # egui = { path = "../egui/egui" } 30 | # egui-winit = { path = "../egui/egui-winit" } 31 | # egui-wgpu = { path = "../egui/egui-wgpu" } 32 | # rend3 = { path = "../rend3/rend3" } 33 | # rend3-routine = { path = "../rend3/rend3-routine" } 34 | # wavefront_rs = { path = "../wavefront_rs" } 35 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 | 3 |

4 | 5 | > Your Rusty 🦀 procedural 3d modeler 6 | 7 | ![GitHub CI](https://github.com/setzer22/blackjack/actions/workflows/ci.yml/badge.svg) 8 | ![MPL](https://img.shields.io/badge/license-MPL%202%2E0-blue.svg) 9 | 10 | **ARCHIVED REPOSITORY** This repository is now archived because of a variety of technical and political reasons that made me loose my motivation to continue contributing to the Rust community in my free time. 11 | 12 | **Blackjack** is a procedural modelling application, following the steps of great tools like [Houdini](https://www.sidefx.com/) or [Blender's geometry nodes project](https://docs.blender.org/manual/en/latest/modeling/geometry_nodes/index.html). At its core, Blackjack is a simple node-driven interface where you can compose operations to create a 3d mesh in a non-destructive way. 13 | 14 | 15 | ![Main interaface of Blackjack](https://user-images.githubusercontent.com/7241990/178053206-94f4e976-984c-4611-9d63-d591d231b014.png) 16 | 17 | ![Another gif showcasing procedural modelling in Blackjack](https://user-images.githubusercontent.com/7241990/178054513-75232c00-7bd0-4e26-9474-ab2b71960c6e.gif) 18 | ![A third gif showcasing procedural modelling in Blackjack](https://user-images.githubusercontent.com/7241990/178053667-9e72131c-bb2b-4ffa-aab9-afdf5c51033e.gif) 19 | ![Gif showcasing procedural modelling in Blackjack](./doc/resources/blackjack_gif3.gif) 20 | 21 | 22 | 23 | ## Features and goals 24 | Blackjack **does not aim to replace an industry powerhouse such as Houdini**. Instead, it aims to provide a less cluttered, more robust and user-friendly experience for a small subset of the features that make these tools a great fit in the world of game development and real-time simulations. 25 | 26 | Here are the main goals and philosophy behind blackjack, but note that this shows the direction where things are going, not their current state. 27 | 28 | - **Procedural polygonal modelling, first and foremost**: The main focus in Blackjack is the creation of low-poly procedural assets like the ones one would see in videogames. In particular, surface extraction of volumetric data is not among its goals. 29 | - **Flexible node-based interface:** Build complex node graphs, create reusable functions, and tweak every parameter in real-time! 30 | - **Integration with popular game engines:** Export your procedural assets as tiny programs and tweak their parameters at runtime by adding a simple library to your engine of choice. 31 | - **Error resilience, crash resistance:** When things go wrong, Blackjack will make an effort to *respect your time* as a user and not lose your work. Errors will be clearly communicated and fixing any bugs leading to a crash will take the highest priority. 32 | 33 | ## Install and usage 34 | > **Note**: A crates.io version cannot be published due to unreleased dependencies. Blackjack depends on the bleeding edge version of some crates and requires custom forks for some others. This situation may change once development stabilizes. 35 | 36 | Here are the steps in order to try out the early development version of Blackjack. Binaries and easier installation methods will be provided in the future. The steps below require a complete Rust toolchain using `cargo`, with a minimum supported Rust version (MSRV) of **1.62.0**. 37 | 38 | 1. Clone this repository, and make sure to download LFS files. In some systems, this may require separately installing a `git-lfs`[^1] package: 39 | ```bash 40 | git clone https://github.com/setzer22/blackjack 41 | git lfs install 42 | git lfs fetch --all 43 | git lfs pull 44 | ``` 45 | 46 | [^1]: Linux users can install `git-lfs` with their distro's package manager (`apt install git-lfs` / `yum install git-lfs` / `pacman -S git-lfs`). MacOS users using homebrew can use `brew install git-lfs`. Other users should follow the [git-lfs install instructions](https://git-lfs.github.com/). 47 | 48 | 2. Install build dependencies. This may not cover everything, please file an issue or a pull request if you find anything missing: 49 | * Ubuntu/Debian: `sudo apt install libfontconfig-dev` 50 | * Arch Linux: `sudo pacman -S fontconfig` 51 | * Fedora: `sudo dnf install fontconfig-devel` 52 | > **Note**: The `fontconfig` native dependency is temporary, and will no longer be necessary once this upstream issue is fixed: https://github.com/rust-windowing/winit/issues/2373 53 | 54 | 3. From the same folder, run `cargo run --release --bin blackjack_ui` to launch Blackjack. 55 | 56 | ### Usage 57 | Some minimal usage instructions. Please do note that these can and will change frequently during early development: 58 | 59 | - The bottom section of the screen is the node graph. 60 | - Use right click to open the node selector. Find a node and spawn it by clicking on it. You can also use the search bar. 61 | - Nodes can be dragged around, and its widgets interacted with using the mouse. 62 | - Dragging the mouse between two nodes' ports will create a connection. 63 | - Use the 'Set active' button under a node to make it render to the screen. 64 | 65 | ## Tech stack 66 | Blackjack is built using Rust 🦀 and stands on the shoulders of giants. Here's a shout out to some great rust crates being used in this project: 67 | 68 | - [rend3](https://github.com/BVE-Reborn/rend3) is used for all rendering purposes. 69 | - [egui](https://github.com/emilk/egui) is used as the UI toolkit powering all 2d interaction. 70 | - [wgpu](https://github.com/gfx-rs/wgpu), as the base of `rend3`, is used for all custom visual effects. 71 | - [mlua](https://github.com/khvzak/mlua) is used to integrate [Luau](https://github.com/Roblox/luau) as an extension language. 72 | 73 | ## Tool Maturity 74 | Blackjack is still under active development. Many features are missing and are bound to change. For now, **no promises are made with regards to stability**, but API breakage will be considered only when absolutely necessary. 75 | 76 | ## Contributing 77 | Contributions are welcome! Before writing a PR, please get in touch by filing an issue 😄 78 | 79 | 80 | -------------------------------------------------------------------------------- /blackjack_commons/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "blackjack_commons" 3 | description = "A procedural, node-based modelling tool, made in Rust" 4 | homepage = "https://github.com/setzer22/blackjack" 5 | repository = "https://github.com/setzer22/blackjack" 6 | version = "0.1.0" 7 | edition = "2021" 8 | rust-version = "1.62" 9 | license = "MPL-2.0" 10 | keywords = ["gamedev", "3d", "modelling", "procedural"] 11 | authors = ["setzer22"] 12 | 13 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 14 | 15 | [dependencies] 16 | glam = { version = "0.21.2", features = ["serde", "bytemuck"] } 17 | smallvec = { version = "1.7.0", features = ["serde"] } 18 | itertools = "0.10" 19 | float-ord = "0.3.2" -------------------------------------------------------------------------------- /blackjack_commons/src/lib.rs: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2023 setzer22 and contributors 2 | // 3 | // This Source Code Form is subject to the terms of the Mozilla Public 4 | // License, v. 2.0. If a copy of the MPL was not distributed with this 5 | // file, You can obtain one at https://mozilla.org/MPL/2.0/. 6 | 7 | /// Some utility math types and conversions 8 | pub mod math; 9 | 10 | /// General utility methods and helper traits 11 | pub mod utils; 12 | -------------------------------------------------------------------------------- /blackjack_commons/src/math.rs: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2023 setzer22 and contributors 2 | // 3 | // This Source Code Form is subject to the terms of the Mozilla Public 4 | // License, v. 2.0. If a copy of the MPL was not distributed with this 5 | // file, You can obtain one at https://mozilla.org/MPL/2.0/. 6 | 7 | use std::ops::{Add, Mul, Sub}; 8 | 9 | use float_ord::FloatOrd; 10 | 11 | #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] 12 | pub struct Vec3Ord([FloatOrd; 3]); 13 | 14 | pub trait ToOrd 15 | where 16 | T: Eq + PartialEq + Ord + PartialOrd + std::hash::Hash + Copy, 17 | { 18 | fn to_ord(&self) -> T; 19 | } 20 | 21 | impl ToOrd for glam::Vec3 { 22 | fn to_ord(&self) -> Vec3Ord { 23 | Vec3Ord([FloatOrd(self.x), FloatOrd(self.y), FloatOrd(self.z)]) 24 | } 25 | } 26 | 27 | pub trait ToVec { 28 | fn to_vec(&self) -> T; 29 | } 30 | 31 | impl ToVec for Vec3Ord { 32 | fn to_vec(&self) -> glam::Vec3 { 33 | glam::Vec3::new(self.0[0].0, self.0[1].0, self.0[2].0) 34 | } 35 | } 36 | 37 | pub fn lerp(start: T, end: T, t: f32) -> T 38 | where 39 | T: Copy + Add + Sub + Mul, 40 | { 41 | start + (end - start) * t 42 | } 43 | -------------------------------------------------------------------------------- /blackjack_commons/src/utils.rs: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2023 setzer22 and contributors 2 | // 3 | // This Source Code Form is subject to the terms of the Mozilla Public 4 | // License, v. 2.0. If a copy of the MPL was not distributed with this 5 | // file, You can obtain one at https://mozilla.org/MPL/2.0/. 6 | 7 | use itertools::Itertools; 8 | use smallvec::SmallVec; 9 | 10 | pub type SVec = SmallVec<[T; 4]>; 11 | pub type SVecN = SmallVec<[T; N]>; 12 | 13 | pub trait IteratorUtils: Iterator { 14 | fn collect_svec(self) -> SVec 15 | where 16 | Self: Sized, 17 | { 18 | self.collect() 19 | } 20 | 21 | fn branch( 22 | self, 23 | condition: bool, 24 | a_iter: impl FnOnce(Self) -> OutputIterA, 25 | b_iter: impl FnOnce(Self) -> OutputIterB, 26 | ) -> BranchIteratorOutput 27 | where 28 | Self: Sized, 29 | OutputIterA: Iterator, 30 | OutputIterB: Iterator, 31 | { 32 | let (a_iter, b_iter) = if condition { 33 | (Some(a_iter(self)), None) 34 | } else { 35 | (None, Some(b_iter(self))) 36 | }; 37 | 38 | BranchIterator { 39 | iterator: a_iter 40 | .into_iter() 41 | .flatten() 42 | .chain(b_iter.into_iter().flatten()), 43 | } 44 | } 45 | } 46 | 47 | /// Rotates the given iterator by shifting all elements `shift` positions 48 | /// forward. Any elements that would be out of bounds are instead put at the 49 | /// beginning. 50 | /// 51 | /// This method requires passing the `len` as a separate parameter. This is 52 | /// often known beforehand or can be found by calling .size_hint() for an 53 | /// ExactSizeIterator. 54 | pub fn rotate_iter( 55 | it: impl Iterator + Clone, 56 | shift: usize, 57 | len: usize, 58 | ) -> impl Iterator { 59 | it.cycle().dropping(shift).take(len) 60 | } 61 | 62 | impl IteratorUtils for T where T: Iterator {} 63 | 64 | pub trait SliceUtils { 65 | /// Same as .iter().copied(), but doesn't trigger rustfmt line breaks 66 | fn iter_cpy(&self) -> std::iter::Copied>; 67 | } 68 | 69 | impl SliceUtils for [T] { 70 | fn iter_cpy(&self) -> std::iter::Copied> { 71 | self.iter().copied() 72 | } 73 | } 74 | 75 | pub struct BranchIterator 76 | where 77 | I: Iterator, 78 | { 79 | iterator: I, 80 | } 81 | 82 | impl Iterator for BranchIterator 83 | where 84 | I: Iterator, 85 | { 86 | type Item = It; 87 | 88 | fn next(&mut self) -> Option { 89 | self.iterator.next() 90 | } 91 | } 92 | 93 | type BranchIteratorOutput = BranchIterator< 94 | std::iter::Chain< 95 | std::iter::Flatten>, 96 | std::iter::Flatten>, 97 | >, 98 | It, 99 | >; 100 | 101 | /// Extension trait for `Option`. 102 | /// 103 | /// NOTE: Functions use a final underscore to avoid colliding with stdlib 104 | /// functions that will be stabilized in the future. 105 | pub trait OptionExt { 106 | fn as_option(&self) -> &Option; 107 | /// Returns `true` if the option is a [`Some`] and the value inside of it 108 | /// matches a predicate. 109 | /// 110 | /// NOTE: This function is currently unstable but will be available in the 111 | /// stdlib once #![feature(is_some_with)] hits stable. 112 | fn is_some_and_(&self, f: impl FnOnce(&T) -> bool) -> bool { 113 | matches!(self.as_option(), Some(x) if f(x)) 114 | } 115 | 116 | /// Returns true if the function is a [`None`] or when the value inside 117 | /// matches a predicate. 118 | fn is_none_or_(&self, f: impl FnOnce(&T) -> bool) -> bool { 119 | let this = self.as_option(); 120 | this.is_none() || f(this.as_ref().unwrap()) 121 | } 122 | } 123 | impl OptionExt for Option { 124 | fn as_option(&self) -> &Option { 125 | self 126 | } 127 | } 128 | 129 | #[test] 130 | pub fn test() { 131 | fn tuple_windows(circular: bool) -> impl Iterator { 132 | vec![1, 2, 3, 4, 5].into_iter().branch( 133 | circular, 134 | |it| it.circular_tuple_windows(), 135 | |it| it.tuple_windows(), 136 | ) 137 | } 138 | 139 | assert_eq!( 140 | tuple_windows(false).collect_vec(), 141 | &[(1, 2), (2, 3), (3, 4), (4, 5)] 142 | ); 143 | assert_eq!( 144 | tuple_windows(true).collect_vec(), 145 | &[(1, 2), (2, 3), (3, 4), (4, 5), (5, 1)] 146 | ); 147 | } 148 | 149 | /// Transmutes a vector of `T`s into a vector of `U`s. 150 | /// 151 | /// # Safety 152 | /// This is only safe when `T` and `U` have the same size, plus all the 153 | /// additional safety considerations required when calling `transmute::` 154 | pub unsafe fn transmute_vec(v: Vec) -> Vec { 155 | let mut v = std::mem::ManuallyDrop::new(v); 156 | let ptr = v.as_mut_ptr(); 157 | let len = v.len(); 158 | let cap = v.capacity(); 159 | 160 | Vec::from_raw_parts(ptr as *mut U, len, cap) 161 | } 162 | -------------------------------------------------------------------------------- /blackjack_engine/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "blackjack_engine" 3 | description = "A procedural, node-based modelling tool, made in Rust" 4 | homepage = "https://github.com/setzer22/blackjack" 5 | repository = "https://github.com/setzer22/blackjack" 6 | version = "0.1.0" 7 | edition = "2021" 8 | rust-version = "1.62" 9 | license = "MPL-2.0" 10 | keywords = ["gamedev", "3d", "modelling", "procedural"] 11 | authors = ["setzer22"] 12 | 13 | [features] 14 | tracy = ["profiling/profile-with-tracy"] 15 | # The sync feature enables the HalfEdgeMesh and other associated types to be conditionally 16 | # compiled with Send + Sync counterparts to the normal indirection types that are used. 17 | sync = ["atomic_refcell"] 18 | 19 | [dependencies] 20 | # Workspace dependencies 21 | blackjack_macros = { path = "../blackjack_macros/" } 22 | blackjack_commons = { path = "../blackjack_commons" } 23 | 24 | # Crates.io crates 25 | glam = { version = "0.21.2", features = ["serde", "bytemuck"] } 26 | smallvec = { version = "1.7.0", features = ["serde"] } 27 | itertools = "0.10" 28 | anyhow = { version = "1.0", features = ["backtrace"] } 29 | serde = { version = "1.0", features = ["derive"] } 30 | float-ord = "0.3.2" 31 | rayon = "1.5.1" 32 | nonmax = "0.5" 33 | slotmap = { version = "1.0", features = ["serde"] } 34 | num-traits = "0.2.14" 35 | atomic_float = "0.1" 36 | profiling = { version = "1.0" } 37 | nom = "7.1" 38 | mlua = { version = "0.8.1", features = ["luau"] } 39 | notify = "4.0" 40 | walkdir = "2" 41 | bimap = "0.6.2" 42 | dyn-clone = "1.0" 43 | noise = "0.7" 44 | rstar = "0.9.3" 45 | wavefront_rs = "1.0.4" 46 | derive_more = "0.99" 47 | inventory = "0.3.0" 48 | ndarray = "0.15.6" 49 | ron = "0.7" 50 | atomic_refcell = { version = "0.1.9", optional = true } 51 | -------------------------------------------------------------------------------- /blackjack_engine/src/engine_tests.rs: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2023 setzer22 and contributors 2 | // 3 | // This Source Code Form is subject to the terms of the Mozilla Public 4 | // License, v. 2.0. If a copy of the MPL was not distributed with this 5 | // file, You can obtain one at https://mozilla.org/MPL/2.0/. 6 | 7 | use crate::graph::serialization::SerializedBjkGraph; 8 | use crate::graph::{BjkGraph, BjkNodeId}; 9 | use crate::graph_interpreter::run_graph; 10 | use crate::lua_engine::{LuaRuntime, ProgramResult, RenderableThing}; 11 | use crate::prelude::*; 12 | 13 | /// Looks for the first node with no outgoing parameters and assumes it to be 14 | /// the graph's final node. Comment nodes are ignored because examples typically 15 | /// contain those. 16 | pub fn infer_target_node(graph: &BjkGraph) -> BjkNodeId { 17 | // A set of all nodes which are dependencies to other nodes 18 | let mut dependencies = HashSet::new(); 19 | for (_, node) in &graph.nodes { 20 | for input in &node.inputs { 21 | if let crate::graph::DependencyKind::Connection { node: other, .. } = &input.kind { 22 | dependencies.insert(other); 23 | } 24 | } 25 | } 26 | for (node_id, node) in &graph.nodes { 27 | if !dependencies.contains(&node_id) && node.op_name != "MakeComment" { 28 | return node_id; 29 | } 30 | } 31 | panic!("Target node heuristic failed") 32 | } 33 | 34 | #[derive(Clone, Copy)] 35 | struct Example { 36 | path: &'static str, 37 | vertices: usize, 38 | halfedges: usize, 39 | faces: usize, 40 | } 41 | 42 | fn run_example(example: &Example, rt: &LuaRuntime) -> ProgramResult { 43 | let bjk_data = std::fs::read_to_string(example.path).unwrap(); 44 | let (rt_data, _, _) = SerializedBjkGraph::load_from_string(&bjk_data) 45 | .unwrap() 46 | .into_runtime() 47 | .unwrap(); 48 | run_graph( 49 | &rt.lua, 50 | &rt_data.graph, 51 | infer_target_node(&rt_data.graph), 52 | rt_data.external_parameters.unwrap(), 53 | &rt.node_definitions, 54 | None, 55 | ) 56 | .unwrap() 57 | } 58 | 59 | #[test] 60 | pub fn test_examples_folder() { 61 | let lua_runtime = LuaRuntime::initialize_with_std("../blackjack_lua".into()).unwrap(); 62 | 63 | let examples = &[ 64 | Example { 65 | path: "../examples/box.bjk", 66 | vertices: 8, 67 | halfedges: 24, 68 | faces: 6, 69 | }, 70 | Example { 71 | path: "../examples/tp_cutter.bjk", 72 | vertices: 184, 73 | halfedges: 680, 74 | faces: 170, 75 | }, 76 | Example { 77 | path: "../examples/stylised_sword.bjk", 78 | vertices: 284, 79 | halfedges: 988, 80 | faces: 228, 81 | }, 82 | ]; 83 | 84 | for example in examples { 85 | println!("Loading example at {}", example.path); 86 | let result = run_example(example, &lua_runtime); 87 | if let Some(RenderableThing::HalfEdgeMesh(h)) = result.renderable { 88 | assert_eq!(h.read_connectivity().num_vertices(), example.vertices); 89 | assert_eq!(h.read_connectivity().num_halfedges(), example.halfedges); 90 | assert_eq!(h.read_connectivity().num_faces(), example.faces); 91 | } else { 92 | panic!("Expected a mesh") 93 | } 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /blackjack_engine/src/lib.rs: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2023 setzer22 and contributors 2 | // 3 | // This Source Code Form is subject to the terms of the Mozilla Public 4 | // License, v. 2.0. If a copy of the MPL was not distributed with this 5 | // file, You can obtain one at https://mozilla.org/MPL/2.0/. 6 | 7 | /// Used by proc macros to refer to this crate unambiguously 8 | extern crate self as blackjack_engine; 9 | 10 | /// Some useful re-exports 11 | pub mod prelude; 12 | 13 | /// The halfedge graph data structure and main edit operations 14 | pub mod mesh; 15 | 16 | /// The blackjack engine core types 17 | pub mod lua_engine; 18 | 19 | /// The graph core datatypes 20 | pub mod graph; 21 | 22 | /// High level interpreter of blackjack graphs. 23 | pub mod graph_interpreter; 24 | 25 | /// Gizmos allow visual modifications of a node's parameters. 26 | pub mod gizmos; 27 | 28 | /// Conditional types to allow HalfEdgeMesh et al. be `Send` + `Sync` with the sync feature. 29 | pub mod sync; 30 | 31 | #[cfg(test)] 32 | pub mod engine_tests; 33 | -------------------------------------------------------------------------------- /blackjack_engine/src/lua_engine.rs: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2023 setzer22 and contributors 2 | // 3 | // This Source Code Form is subject to the terms of the Mozilla Public 4 | // License, v. 2.0. If a copy of the MPL was not distributed with this 5 | // file, You can obtain one at https://mozilla.org/MPL/2.0/. 6 | 7 | use std::{ 8 | sync::{ 9 | mpsc::{self, Receiver}, 10 | Arc, 11 | }, 12 | time::Duration, 13 | }; 14 | 15 | use crate::{ 16 | gizmos::BlackjackGizmo, 17 | graph::{BjkNodeId, NodeDefinitions}, 18 | graph_interpreter::ExternalParameterValues, 19 | mesh::heightmap::HeightMap, 20 | prelude::*, 21 | }; 22 | use mlua::Lua; 23 | use notify::{DebouncedEvent, Watcher}; 24 | use slotmap::SecondaryMap; 25 | 26 | use self::lua_stdlib::{load_node_definitions, LuaFileIo, StdLuaFileIo}; 27 | 28 | pub mod lua_stdlib; 29 | 30 | pub trait ToLuaError { 31 | fn map_lua_err(self) -> mlua::Result; 32 | } 33 | 34 | impl ToLuaError for anyhow::Result { 35 | fn map_lua_err(self) -> mlua::Result { 36 | self.map_err(|err| mlua::Error::RuntimeError(format!("{err:?}"))) 37 | } 38 | } 39 | 40 | impl ToLuaError for Result { 41 | fn map_lua_err(self) -> mlua::Result { 42 | self.map_err(|err| mlua::Error::RuntimeError(format!("{err:?}"))) 43 | } 44 | } 45 | 46 | #[allow(clippy::large_enum_variant)] 47 | pub enum RenderableThing { 48 | HalfEdgeMesh(HalfEdgeMesh), 49 | HeightMap(HeightMap), 50 | } 51 | 52 | impl RenderableThing { 53 | pub fn from_lua_value(renderable: mlua::Value<'_>) -> Result { 54 | match renderable { 55 | mlua::Value::UserData(renderable) if renderable.is::() => { 56 | Ok(RenderableThing::HalfEdgeMesh(renderable.take()?)) 57 | } 58 | mlua::Value::UserData(renderable) if renderable.is::() => { 59 | Ok(RenderableThing::HeightMap(renderable.take()?)) 60 | } 61 | _ => { 62 | bail!("Object {renderable:?} is not a thing we can render.") 63 | } 64 | } 65 | } 66 | } 67 | 68 | /// The result of an invocation to a lua program. 69 | pub struct ProgramResult { 70 | /// The renderable thing produced by this program to be shown on-screen. 71 | pub renderable: Option, 72 | /// The gizmos requested by graph nodes after an execution of this program. 73 | /// If you are implementing an integration, you can ignore this field. This 74 | /// field will be returned as None will be none when gizmos aren't run. 75 | pub updated_gizmos: Option>>, 76 | /// The updated external parameters. Any node may modify its own parameters 77 | /// when running its gizmo function. 78 | pub updated_values: ExternalParameterValues, 79 | } 80 | 81 | pub struct LuaFileWatcher { 82 | pub watcher: notify::RecommendedWatcher, 83 | pub watcher_channel: Receiver, 84 | } 85 | 86 | pub struct LuaRuntime { 87 | pub lua: Lua, 88 | pub node_definitions: NodeDefinitions, 89 | pub file_watcher: Option, 90 | pub lua_io: Arc, 91 | } 92 | 93 | impl LuaRuntime { 94 | /// Initializes and returns the Blackjack Lua runtime. This function will 95 | /// use the `std::fs` API to load Lua source files. Some integrations may 96 | /// prefer to use other file reading methods with `initialize_custom`. 97 | pub fn initialize_with_std(node_libraries_path: String) -> anyhow::Result { 98 | Self::initialize_custom(StdLuaFileIo { 99 | base_folder: node_libraries_path, 100 | }) 101 | } 102 | 103 | pub fn initialize_custom(lua_io: impl LuaFileIo + 'static) -> anyhow::Result { 104 | let lua = Lua::new(); 105 | let lua_io = Arc::new(lua_io); 106 | lua_stdlib::load_lua_bindings(&lua, lua_io.clone())?; 107 | let node_definitions = NodeDefinitions::new(load_node_definitions(&lua, lua_io.as_ref())?); 108 | 109 | Ok(LuaRuntime { 110 | lua, 111 | node_definitions, 112 | file_watcher: None, 113 | lua_io, 114 | }) 115 | } 116 | 117 | pub fn start_file_watcher(&mut self) -> Result<()> { 118 | let (tx, rx) = mpsc::channel(); 119 | let mut watcher = notify::watcher(tx, Duration::from_secs(1))?; 120 | watcher.watch(self.lua_io.base_folder(), notify::RecursiveMode::Recursive)?; 121 | self.file_watcher = Some(LuaFileWatcher { 122 | watcher, 123 | watcher_channel: rx, 124 | }); 125 | Ok(()) 126 | } 127 | 128 | /// Watches the lua source folders for changes. Returns true when a change 129 | /// was detected and the `NodeDefinitions` were successfully updated. 130 | pub fn watch_for_changes(&mut self) -> anyhow::Result { 131 | let file_watcher = self 132 | .file_watcher 133 | .as_ref() 134 | .ok_or_else(|| anyhow!("File watcher was not set up."))?; 135 | if let Ok(msg) = file_watcher.watcher_channel.try_recv() { 136 | match msg { 137 | DebouncedEvent::Create(_) 138 | | DebouncedEvent::Write(_) 139 | | DebouncedEvent::Remove(_) 140 | | DebouncedEvent::Rename(_, _) => { 141 | println!("Reloading Lua scripts..."); 142 | // Reset the _LOADED table to clear any required libraries 143 | // from the cache. This will trigger reloading of libraries 144 | // when the hot reloaded code first requires them, 145 | // effectively picking up changes in transitively required 146 | // libraries as well. 147 | self.lua 148 | .globals() 149 | .set("_LOADED", self.lua.create_table()?)?; 150 | 151 | // By calling this, all code under $BLACKJACK_LUA/run will 152 | // be executed and the node definitions will be reloaded. 153 | self.node_definitions 154 | .update(load_node_definitions(&self.lua, self.lua_io.as_ref())?); 155 | } 156 | _ => {} 157 | } 158 | Ok(true) 159 | } else { 160 | Ok(false) 161 | } 162 | } 163 | } 164 | -------------------------------------------------------------------------------- /blackjack_engine/src/lua_engine/blackjack_utils.lua: -------------------------------------------------------------------------------- 1 | -- Copyright (C) 2023 setzer22 and contributors 2 | -- 3 | -- This Source Code Form is subject to the terms of the Mozilla Public 4 | -- License, v. 2.0. If a copy of the MPL was not distributed with this 5 | -- file, You can obtain one at https://mozilla.org/MPL/2.0/. 6 | 7 | local BlackjackUtils = {} 8 | 9 | BlackjackUtils.load_function = function(code) 10 | local func, err = loadstring(code) 11 | if err ~= nil then 12 | error(err) 13 | end 14 | if typeof(func) ~= "function" then 15 | error("Code should be a single lua function") 16 | end 17 | return func 18 | end 19 | 20 | BlackjackUtils.parse_ch_key = function(s) 21 | if s == "Vertex" then 22 | return Types.VERTEX_ID 23 | elseif s == "Face" then 24 | return Types.FACE_ID 25 | elseif s == "Halfedge" then 26 | return Types.HALFEDGE_ID 27 | end 28 | end 29 | BlackjackUtils.parse_ch_val = function(s) 30 | if s == "f32" then 31 | return Types.F32 32 | elseif s == "Vec3" then 33 | return Types.VEC3 34 | end 35 | end 36 | 37 | return BlackjackUtils 38 | -------------------------------------------------------------------------------- /blackjack_engine/src/lua_engine/lua_stdlib.rs: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2023 setzer22 and contributors 2 | // 3 | // This Source Code Form is subject to the terms of the Mozilla Public 4 | // License, v. 2.0. If a copy of the MPL was not distributed with this 5 | // file, You can obtain one at https://mozilla.org/MPL/2.0/. 6 | 7 | use std::sync::Arc; 8 | 9 | use mlua::{FromLua, Lua, Table, ToLua, UserData}; 10 | 11 | use crate::{ 12 | lua_engine::ToLuaError, 13 | prelude::halfedge::{ 14 | id_types::{FaceId, HalfEdgeId, VertexId}, 15 | ChannelKeyType, ChannelValueType, 16 | }, 17 | }; 18 | 19 | mod runtime_types; 20 | pub use runtime_types::*; 21 | 22 | pub mod lua_require_io; 23 | pub use lua_require_io::*; 24 | 25 | mod lua_core_library; 26 | 27 | pub mod lua_documentation; 28 | 29 | /// A function pointer to register global lua functions. Stored globally using 30 | /// the `inventory` crate. 31 | pub struct LuaRegisterFn { 32 | pub f: fn(&mlua::Lua) -> mlua::Result<()>, 33 | } 34 | inventory::collect!(LuaRegisterFn); 35 | 36 | /// Lua docstrings for symbol names. Stored globally using `inventory`. 37 | pub struct LuaDocstringData { 38 | pub data: &'static [(&'static str, &'static str, &'static str)], 39 | } 40 | inventory::collect!(LuaDocstringData); 41 | 42 | /// Loads all blackjack Rust function wrappers to the Lua API 43 | pub fn load_lua_bindings(lua: &Lua, lua_io: Arc) -> anyhow::Result<()> { 44 | lua_core_library::load(lua, lua_io)?; 45 | 46 | // This collects functions from all over the codebase. Any module annotated 47 | // with `#[blackjack_macros::blackjack_lua_module]` is inspected and may 48 | // export any number of functions or constants marked with `#[lua]` 49 | // annotations. 50 | for register_fn in inventory::iter::() { 51 | (register_fn.f)(lua).expect("Failed to register Lua API"); 52 | } 53 | 54 | Ok(()) 55 | } 56 | -------------------------------------------------------------------------------- /blackjack_engine/src/lua_engine/lua_stdlib/lua_core_library.rs: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2023 setzer22 and contributors 2 | // 3 | // This Source Code Form is subject to the terms of the Mozilla Public 4 | // License, v. 2.0. If a copy of the MPL was not distributed with this 5 | // file, You can obtain one at https://mozilla.org/MPL/2.0/. 6 | 7 | use std::sync::Arc; 8 | 9 | use mlua::Value; 10 | 11 | use super::*; 12 | 13 | pub fn load(lua: &Lua, lua_io: Arc) -> anyhow::Result<()> { 14 | let globals = lua.globals(); 15 | 16 | // The _LOADED table stores things loaded by the `require` function 17 | globals.set("_LOADED", lua.create_table()?)?; 18 | 19 | globals.set( 20 | "require", 21 | lua.create_function(move |lua, file: String| -> Result { 22 | let loaded: Table = lua 23 | .globals() 24 | .get("_LOADED") 25 | .expect("The _LOADED table must always exist"); 26 | match loaded.get::<_, Value>(file.clone())? { 27 | Value::Nil => { 28 | macro_rules! def_lib { 29 | ($req_name:expr, $path:expr) => { 30 | if file == $req_name { 31 | let value = lua.load(include_str!($path)).eval::()?; 32 | loaded.set(file, value.clone())?; 33 | return Ok(value); 34 | } 35 | }; 36 | } 37 | 38 | // Here we list all the predefined libraries. These are 39 | // baked into the blackjack binary via `include_str!` 40 | def_lib!("params", "../node_params.lua"); 41 | def_lib!("node_library", "../node_library.lua"); 42 | def_lib!("utils", "../blackjack_utils.lua"); 43 | 44 | // The `def_lib!` calls above return. If none did, then we 45 | // know this is a regular lua file from the filesystem. 46 | { 47 | let file_chunk = lua_io.load_file_require(&file).map_lua_err()?; 48 | let value = lua.load(&file_chunk).eval::()?; 49 | loaded.set(file, value.clone())?; 50 | Ok(value) 51 | } 52 | } 53 | other => Ok(other), 54 | } 55 | })?, 56 | )?; 57 | 58 | globals.set( 59 | "loadstring", 60 | lua.create_function(|lua, s: String| -> Result { 61 | match lua.load(&s).eval::() { 62 | Ok(v) => Ok(mlua::MultiValue::from_vec(vec![v])), 63 | Err(err) => Ok(mlua::MultiValue::from_vec(vec![ 64 | mlua::Nil, 65 | format!("{err}").to_lua(lua)?, 66 | ])), 67 | } 68 | })?, 69 | )?; 70 | 71 | Ok(()) 72 | } 73 | 74 | #[blackjack_macros::blackjack_lua_module] 75 | mod lua_module { 76 | use anyhow::Result; 77 | 78 | /// Read the contents of the file at `path` and return as a string. Will 79 | /// fail if the path does not exist, or the user does not have correct 80 | /// access permissions. 81 | #[lua(under = "Io")] 82 | pub fn read_to_string(path: String) -> Result { 83 | Ok(std::fs::read_to_string(path)?) 84 | } 85 | 86 | /// Write the given string `contents` as a file to the given `path`. Will 87 | /// overwrite any previous existing file with that name. 88 | #[lua(under = "Io")] 89 | pub fn write(path: String, contents: String) -> Result<()> { 90 | std::fs::write(path, contents)?; 91 | Ok(()) 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /blackjack_engine/src/lua_engine/lua_stdlib/lua_documentation.rs: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2023 setzer22 and contributors 2 | // 3 | // This Source Code Form is subject to the terms of the Mozilla Public 4 | // License, v. 2.0. If a copy of the MPL was not distributed with this 5 | // file, You can obtain one at https://mozilla.org/MPL/2.0/. 6 | 7 | use anyhow::{bail, Result}; 8 | use std::io::Write; 9 | use std::{collections::BTreeMap, fs::File, path::PathBuf}; 10 | 11 | use crate::lua_engine::lua_stdlib::LuaDocstringData; 12 | 13 | pub fn generate_lua_documentation(out_path: &str) -> Result<()> { 14 | let mut docs_by_module = BTreeMap::<&str, Vec<&str>>::new(); 15 | let mut docs_by_class = BTreeMap::<&str, Vec<&str>>::new(); 16 | 17 | fn populate_from<'a>( 18 | it: &'a [(&'a str, &'a str, &'a str)], 19 | docs_by_module: &mut BTreeMap<&'a str, Vec<&'a str>>, 20 | docs_by_class: &mut BTreeMap<&'a str, Vec<&'a str>>, 21 | ) { 22 | for (t, m, f) in it { 23 | if *t == "module" { 24 | docs_by_module.entry(m).or_default().push(f); 25 | } 26 | if *t == "class" { 27 | docs_by_class.entry(m).or_default().push(f); 28 | } 29 | } 30 | } 31 | 32 | for docstring_data in inventory::iter::() { 33 | populate_from(docstring_data.data, &mut docs_by_module, &mut docs_by_class); 34 | } 35 | 36 | let out_file = File::open(out_path)?; 37 | if !out_file.metadata()?.is_dir() { 38 | bail!("Output path '{out_path}' must be a directory"); 39 | } 40 | 41 | let out_path = PathBuf::from(out_path); 42 | for (module, doc_fns) in docs_by_module.iter() { 43 | let file_path = out_path.join(format!("Mod_{module}.lua")); 44 | let mut w = std::io::BufWriter::new(File::create(file_path)?); 45 | writeln!(w, "--- Module {module}")?; 46 | writeln!(w, "--- @module {module}\n")?; 47 | 48 | for doc_fn in doc_fns { 49 | writeln!(w, "{doc_fn}")?; 50 | } 51 | } 52 | 53 | for (class, doc_fns) in docs_by_class.iter() { 54 | let file_path = out_path.join(format!("Class_{class}.lua")); 55 | let mut w = std::io::BufWriter::new(File::create(file_path)?); 56 | writeln!(w, "--- Class {class}")?; 57 | writeln!(w, "--- @classmod {class}\n")?; 58 | 59 | for doc_fn in doc_fns { 60 | writeln!(w, "{doc_fn}")?; 61 | } 62 | } 63 | 64 | Ok(()) 65 | } 66 | -------------------------------------------------------------------------------- /blackjack_engine/src/lua_engine/lua_stdlib/lua_require_io.rs: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2023 setzer22 and contributors 2 | // 3 | // This Source Code Form is subject to the terms of the Mozilla Public 4 | // License, v. 2.0. If a copy of the MPL was not distributed with this 5 | // file, You can obtain one at https://mozilla.org/MPL/2.0/. 6 | 7 | use std::{borrow::Cow, path::PathBuf}; 8 | 9 | use crate::graph::{NodeDefinition, NodeDefinitionsInner}; 10 | 11 | pub struct LuaSourceFile { 12 | pub contents: String, 13 | pub name: String, 14 | } 15 | 16 | impl<'lua> mlua::AsChunk<'lua> for LuaSourceFile { 17 | fn source(&self) -> std::result::Result, std::io::Error> { 18 | Ok(Cow::Borrowed(self.contents.as_bytes())) 19 | } 20 | 21 | fn name(&self) -> std::option::Option { 22 | Some(self.name.clone()) 23 | } 24 | } 25 | 26 | /// This trait is used to abstract Lua file IO to allow different 27 | /// implementations for different platforms. Some game engines, like Godot, use 28 | /// packed binary data and offer their own `File` APIs to handle that 29 | /// abstraction, using regular std::io in those cases would not work. 30 | /// 31 | /// If you are writing an integration and your engine can load assets using 32 | /// Rust's standard io functions, you can use the `StdLuaFileIo` default 33 | /// implementation which uses the Rust standard library. 34 | pub trait LuaFileIo { 35 | /// Returns the path of the base folder this FileIo is watching. This is 36 | /// used in blackjack_ui for hot reloading. Other integrations may use it in 37 | /// different ways. 38 | fn base_folder(&self) -> &str; 39 | 40 | /// Returns an iterator over the paths of all the blackjack initialization 41 | /// scripts on the lua folder. 42 | /// 43 | /// The calling code does not care about the format of the returned paths. 44 | /// The values should be valid to call the `load_file_absolute` function, 45 | /// which in practice means they should be absolute paths. 46 | fn find_run_files(&self) -> Box>; 47 | 48 | /// Returns a [`LuaSourceFile`] with the contents of the file at a given 49 | /// `path`. The path will be treated as absolute. 50 | fn load_file_absolute(&self, path: &str) -> anyhow::Result; 51 | 52 | /// Returns a [`LuaSourceFile`] with the contents of the file at a given 53 | /// `path`. The path is relative to $BLACKJACK_LUA/lib. This function will 54 | /// be used when Lua code calls the `require` function. 55 | fn load_file_require(&self, path: &str) -> anyhow::Result; 56 | } 57 | 58 | pub struct StdLuaFileIo { 59 | pub base_folder: String, 60 | } 61 | 62 | impl LuaFileIo for StdLuaFileIo { 63 | fn base_folder(&self) -> &str { 64 | &self.base_folder 65 | } 66 | 67 | fn find_run_files(&self) -> Box> { 68 | let run_path = PathBuf::from(&self.base_folder).join("run"); 69 | Box::new( 70 | walkdir::WalkDir::new(run_path) 71 | .follow_links(true) 72 | .into_iter() 73 | .filter_map(|e| e.ok()) 74 | .filter(|e| { 75 | e.file_type().is_file() 76 | && e.file_name() 77 | .to_str() 78 | .map(|s| s.ends_with(".lua")) 79 | .unwrap_or(false) 80 | }) 81 | .filter_map(|e| e.path().to_str().map(|x| x.to_owned())), 82 | ) 83 | } 84 | 85 | fn load_file_absolute(&self, path: &str) -> anyhow::Result { 86 | Ok(LuaSourceFile { 87 | contents: std::fs::read_to_string(path)?, 88 | name: path.into(), 89 | }) 90 | } 91 | 92 | fn load_file_require(&self, path: &str) -> anyhow::Result { 93 | let mut path = PathBuf::from(&self.base_folder).join("lib").join(path); 94 | path.set_extension("lua"); 95 | Ok(LuaSourceFile { 96 | contents: std::fs::read_to_string(&path).map_err(|err| { 97 | anyhow::anyhow!("Error loading file {}. Cause: {err}", path.display()) 98 | })?, 99 | name: path.display().to_string(), 100 | }) 101 | } 102 | } 103 | 104 | /// Scans and runs all files inside $BLACKJACK_LUA/run. Then, parses every 105 | /// registered node and returns a `NodeDefinitions` object with the nodes. 106 | pub fn load_node_definitions( 107 | lua: &mlua::Lua, 108 | lua_io: &dyn LuaFileIo, 109 | ) -> anyhow::Result { 110 | for path in lua_io.find_run_files() { 111 | let file = lua_io.load_file_absolute(&path)?; 112 | lua.load(&file).exec()?; 113 | } 114 | 115 | let table = lua 116 | .load("require('node_library')") 117 | .eval::()? 118 | .get::<_, mlua::Table>("nodes")?; 119 | NodeDefinition::load_nodes_from_table(table) 120 | } 121 | -------------------------------------------------------------------------------- /blackjack_engine/src/lua_engine/lua_stdlib/runtime_types.rs: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2023 setzer22 and contributors 2 | // 3 | // This Source Code Form is subject to the terms of the Mozilla Public 4 | // License, v. 2.0. If a copy of the MPL was not distributed with this 5 | // file, You can obtain one at https://mozilla.org/MPL/2.0/. 6 | 7 | use blackjack_commons::utils::transmute_vec; 8 | use noise::NoiseFn; 9 | 10 | use super::*; 11 | 12 | #[derive(Debug)] 13 | #[repr(transparent)] 14 | pub struct LVec3(pub glam::Vec3); 15 | impl<'lua> ToLua<'lua> for LVec3 { 16 | fn to_lua(self, _lua: &'lua Lua) -> mlua::Result> { 17 | Ok(mlua::Value::Vector(self.0.x, self.0.y, self.0.z)) 18 | } 19 | } 20 | impl<'lua> FromLua<'lua> for LVec3 { 21 | fn from_lua(lua_value: mlua::Value<'lua>, _lua: &'lua Lua) -> mlua::Result { 22 | match lua_value { 23 | mlua::Value::Vector(x, y, z) => Ok(LVec3(glam::Vec3::new(x, y, z))), 24 | _ => Err(mlua::Error::FromLuaConversionError { 25 | from: lua_value.type_name(), 26 | to: "Vec3", 27 | message: None, 28 | }), 29 | } 30 | } 31 | } 32 | impl From for LVec3 { 33 | fn from(v: glam::Vec3) -> Self { 34 | Self(v) 35 | } 36 | } 37 | impl LVec3 { 38 | /// Unwraps all the internal Vec values. 39 | pub fn cast_vector(this: Vec) -> Vec { 40 | // SAFETY: LVec3 is marked as #[repr(transparent)] and contains a 41 | // single glam::Vec3. Both types are copy and have no Drop logic. 42 | unsafe { transmute_vec(this) } 43 | } 44 | } 45 | 46 | /// Vertex ids cross the Rust<->Lua boundary a lot, so we can't pay the price of 47 | /// boxing that a `UserData` requires. Instead we use LightUserData by casting 48 | /// the slotmap key to u64, and then to a pointer. 49 | /// 50 | /// SAFETY: Note that the cast to pointer is completely safe, since we're never 51 | /// really dereferencing that pointer. It's just the mechanism Lua gives us to 52 | /// store an opaque u64 value which *could* be a pointer but in our case just 53 | /// isn't. 54 | macro_rules! ids_from_to_lua { 55 | ($id_ty:ty) => { 56 | impl<'lua> ToLua<'lua> for $id_ty { 57 | fn to_lua(self, _lua: &'lua Lua) -> mlua::Result> { 58 | use slotmap::Key; 59 | Ok(mlua::Value::LightUserData(keydata_to_lightdata( 60 | self.data(), 61 | ))) 62 | } 63 | } 64 | impl<'lua> FromLua<'lua> for $id_ty { 65 | fn from_lua(lua_value: mlua::Value<'lua>, _lua: &'lua Lua) -> mlua::Result { 66 | match lua_value { 67 | mlua::Value::LightUserData(lud) => { 68 | Ok(<$id_ty>::from(ligthdata_to_keydata(lud))) 69 | } 70 | _ => Err(mlua::Error::FromLuaConversionError { 71 | from: lua_value.type_name(), 72 | to: stringify!($id_ty), 73 | message: None, 74 | }), 75 | } 76 | } 77 | } 78 | }; 79 | } 80 | ids_from_to_lua!(VertexId); 81 | ids_from_to_lua!(FaceId); 82 | ids_from_to_lua!(HalfEdgeId); 83 | 84 | fn keydata_to_lightdata(k: slotmap::KeyData) -> mlua::LightUserData { 85 | mlua::LightUserData(k.as_ffi() as *mut std::ffi::c_void) 86 | } 87 | fn ligthdata_to_keydata(d: mlua::LightUserData) -> slotmap::KeyData { 88 | slotmap::KeyData::from_ffi(d.0 as u64) 89 | } 90 | 91 | impl UserData for ChannelKeyType {} 92 | impl UserData for ChannelValueType {} 93 | 94 | pub struct PerlinNoise(pub noise::Perlin); 95 | 96 | #[blackjack_macros::blackjack_lua_module] 97 | mod perlin_noise { 98 | use super::*; 99 | 100 | /// Constructs a new PerlinNoise 101 | #[lua(under = "PerlinNoise")] 102 | pub fn new() -> PerlinNoise { 103 | PerlinNoise(noise::Perlin::new()) 104 | } 105 | 106 | #[lua_impl] 107 | impl PerlinNoise { 108 | /// Sample a standard 3d perlin noise function at coordinates `(x, y, z)` 109 | #[lua] 110 | pub fn get_3d(&self, x: f64, y: f64, z: f64) -> f64 { 111 | // NOTE: Noise crate crashes when given weird numbers. We can't 112 | // afford to crash when weird numbers are sent from Lua, so we need 113 | // to add this guard here. 114 | if x.is_finite() && y.is_finite() && z.is_finite() { 115 | self.0.get([x, y, z]) 116 | } else { 117 | f64::NAN 118 | } 119 | } 120 | } 121 | } 122 | 123 | #[blackjack_macros::blackjack_lua_module] 124 | mod vector_math { 125 | use super::*; 126 | use glam::Quat; 127 | 128 | /// Return vector `v` rotated around given `axis` and `angle` (in radians). 129 | #[lua(under = "NativeMath")] 130 | pub fn rotate_around_axis(v: LVec3, axis: LVec3, angle: f32) -> LVec3 { 131 | let q = Quat::from_axis_angle(axis.0.normalize(), angle); 132 | LVec3(q * v.0) 133 | } 134 | 135 | /// Returns the `cross` product of vectors `v` and `v2`. 136 | #[lua(under = "NativeMath")] 137 | pub fn cross(v: LVec3, v2: LVec3) -> LVec3 { 138 | LVec3(v.0.cross(v2.0)) 139 | } 140 | } 141 | -------------------------------------------------------------------------------- /blackjack_engine/src/lua_engine/node_library.lua: -------------------------------------------------------------------------------- 1 | -- Copyright (C) 2023 setzer22 and contributors 2 | -- 3 | -- This Source Code Form is subject to the terms of the Mozilla Public 4 | -- License, v. 2.0. If a copy of the MPL was not distributed with this 5 | -- file, You can obtain one at https://mozilla.org/MPL/2.0/. 6 | 7 | local NodeLibrary = { 8 | nodes = {} 9 | } 10 | 11 | function NodeLibrary:addNodes(nodes) 12 | assert(type(nodes) == "table") 13 | 14 | for k, v in pairs(nodes) do 15 | if self.nodes[k] then 16 | print("[Engine] Redefinition for node "..k) 17 | else 18 | print("[Engine] Loading new node definition for "..k) 19 | end 20 | self.nodes[k] = v 21 | end 22 | end 23 | 24 | function NodeLibrary:listNodes() 25 | local nodes = {} 26 | for k, _ in pairs(self.nodes) do 27 | table.insert(nodes, k) 28 | end 29 | return nodes 30 | end 31 | 32 | function NodeLibrary:getNode(node_name) 33 | return self.nodes[node_name] 34 | end 35 | 36 | return NodeLibrary 37 | -------------------------------------------------------------------------------- /blackjack_engine/src/lua_engine/node_params.lua: -------------------------------------------------------------------------------- 1 | -- Copyright (C) 2023 setzer22 and contributors 2 | -- 3 | -- This Source Code Form is subject to the terms of the Mozilla Public 4 | -- License, v. 2.0. If a copy of the MPL was not distributed with this 5 | -- file, You can obtain one at https://mozilla.org/MPL/2.0/. 6 | 7 | local Params = {} 8 | 9 | --- A scalar parameter, with given `default`, `min` and `max` value 10 | Params.scalar = function(name, config) 11 | if config ~= nil then 12 | assert(type(config) == 'table', "config should be table") 13 | return { 14 | name = name, 15 | default = config.default or 0.0, 16 | min = config.min, 17 | max = config.max, 18 | soft_min = config.soft_min, 19 | soft_max = config.soft_max, 20 | type = "scalar", 21 | } 22 | else 23 | return { name = name, type = "scalar" } 24 | end 25 | end 26 | 27 | Params.scalar_int = function(name, config) 28 | local s = Params.scalar(name, config) 29 | s.num_decimals = 0 30 | return s 31 | end 32 | 33 | --- A vector parameter, with given `default` value 34 | Params.v3 = function(name, default) 35 | return { name = name, default = default, type = "vec3" } 36 | end 37 | 38 | --- A mesh parameter. Meshes can't be set by the user directly via widget, so 39 | --- this has no additional settings. 40 | Params.mesh = function(name) 41 | return { name = name, type = "mesh" } 42 | end 43 | 44 | --- A selection parameter. Lets user specify a group of vertices, halfedges or 45 | --- faces. The selected element is context-dependent. 46 | Params.selection = function(name) 47 | return { name = name, type = "selection" } 48 | end 49 | 50 | --- A string parameter, with a given `default` value. If `multiline` is set, the 51 | --- widget for this parameter will allow inserting newlines. 52 | Params.strparam = function(name, default, multiline) 53 | return { name = name, default = default, type = "string", multiline = multiline } 54 | end 55 | 56 | --- A special string parameter made to contain lua source code. The widget for 57 | --- this parameter supports syntax highlighting. 58 | Params.lua_str = function(name) 59 | return { name = name, type = "lua_string" } 60 | end 61 | 62 | --- Another special string parameter, which lets the user select among a given 63 | --- set of pre-defined `values`. The `selected` parameter may be used to 64 | --- optionally provide the index of the default selection. 65 | Params.enum = function(name, values, selected) 66 | return { 67 | name = name, 68 | type = "enum", 69 | values = values or {}, 70 | selected = selected, 71 | } 72 | end 73 | 74 | --- A file parameter. Internally handled as a string, but shows a file picker 75 | --- widget on the UI. 76 | --- 77 | --- The `mode` specifies whether the file picker is used to create a new file 78 | --- with `"save"` or open an existing one with `"open"` 79 | Params.file = function(name, mode) 80 | mode = mode or "save" -- keep backwards compatibility 81 | return { name = name, type = "file", mode = mode } 82 | end 83 | 84 | --- A heightmap mesh parameter. Like a regular mesh, it can't be set by the user 85 | --- so it has no widget. 86 | Params.heightmap = function(name) 87 | return { name = name, type = "heightmap" } 88 | end 89 | 90 | return Params 91 | -------------------------------------------------------------------------------- /blackjack_engine/src/mesh.rs: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2023 setzer22 and contributors 2 | // 3 | // This Source Code Form is subject to the terms of the Mozilla Public 4 | // License, v. 2.0. If a copy of the MPL was not distributed with this 5 | // file, You can obtain one at https://mozilla.org/MPL/2.0/. 6 | 7 | /// The HalfEdge data structure, with different kinds of topological and geometric operations. 8 | pub mod halfedge; 9 | 10 | /// A heightmap data structure. A different mesh representation based on 11 | /// heightmaps. Supports different operations. 12 | pub mod heightmap; 13 | -------------------------------------------------------------------------------- /blackjack_engine/src/mesh/halfedge/id_types.rs: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2023 setzer22 and contributors 2 | // 3 | // This Source Code Form is subject to the terms of the Mozilla Public 4 | // License, v. 2.0. If a copy of the MPL was not distributed with this 5 | // file, You can obtain one at https://mozilla.org/MPL/2.0/. 6 | 7 | slotmap::new_key_type! { pub struct HalfEdgeId; } 8 | slotmap::new_key_type! { pub struct VertexId; } 9 | slotmap::new_key_type! { pub struct FaceId; } 10 | 11 | #[blackjack_macros::blackjack_lua_module] 12 | mod ids_to_string { 13 | use super::*; 14 | 15 | /// Returns a string representation for this vertex id. This is only useful 16 | /// for debug purposes. 17 | #[lua(under = "Debug")] 18 | pub fn vertex_id_to_string(v: VertexId) -> String { 19 | format!("{v:?}") 20 | } 21 | 22 | /// Returns a string representation for this halfedge id. This is only useful 23 | /// for debug purposes. 24 | #[lua(under = "Debug")] 25 | pub fn halfedge_id_to_string(v: HalfEdgeId) -> String { 26 | format!("{v:?}") 27 | } 28 | 29 | /// Returns a string representation for this face id. This is only useful 30 | /// for debug purposes. 31 | #[lua(under = "Debug")] 32 | pub fn face_id_to_string(v: FaceId) -> String { 33 | format!("{v:?}") 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /blackjack_engine/src/mesh/halfedge/mappings.rs: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2023 setzer22 and contributors 2 | // 3 | // This Source Code Form is subject to the terms of the Mozilla Public 4 | // License, v. 2.0. If a copy of the MPL was not distributed with this 5 | // file, You can obtain one at https://mozilla.org/MPL/2.0/. 6 | 7 | use std::ops::Index; 8 | 9 | use slotmap::{SecondaryMap, SlotMap}; 10 | 11 | use crate::prelude::{FaceId, HalfEdgeId, VertexId}; 12 | 13 | pub struct MeshMapping { 14 | forward: SecondaryMap, 15 | inverse: Vec, 16 | } 17 | 18 | impl MeshMapping { 19 | pub fn new(s: &SlotMap) -> Self { 20 | let forward = s 21 | .iter() 22 | .enumerate() 23 | .map(|(i, (k, _))| (k, i as u32)) 24 | .collect(); 25 | let inverse = s.iter().map(|(k, _)| k).collect(); 26 | 27 | Self { forward, inverse } 28 | } 29 | } 30 | 31 | // NOTE: Macro impls are required because implementing for every slotmap::Key 32 | // would cause a conflict between the forward and inverse impls, since rustc 33 | // cannot know u32 isn't goint to implement slotmap::Key in the future. 34 | 35 | macro_rules! impl_forward { 36 | ($kty:ty) => { 37 | impl Index<$kty> for MeshMapping<$kty> { 38 | type Output = u32; 39 | fn index(&self, index: $kty) -> &Self::Output { 40 | &self.forward[index] 41 | } 42 | } 43 | }; 44 | } 45 | 46 | impl_forward!(VertexId); 47 | impl_forward!(FaceId); 48 | impl_forward!(HalfEdgeId); 49 | 50 | macro_rules! impl_inverse { 51 | ($kty:ty) => { 52 | impl Index for MeshMapping<$kty> { 53 | type Output = $kty; 54 | fn index(&self, index: u32) -> &Self::Output { 55 | &self.inverse[index as usize] 56 | } 57 | } 58 | }; 59 | } 60 | 61 | impl_inverse!(VertexId); 62 | impl_inverse!(FaceId); 63 | impl_inverse!(HalfEdgeId); 64 | 65 | macro_rules! impl_map_seq { 66 | ($kty:ty) => { 67 | impl MeshMapping<$kty> { 68 | pub fn map_seq(&self, seq: &[$kty]) -> Vec { 69 | seq.iter().map(|x| self[*x]).collect() 70 | } 71 | } 72 | }; 73 | } 74 | 75 | impl_map_seq!(VertexId); 76 | impl_map_seq!(FaceId); 77 | impl_map_seq!(HalfEdgeId); 78 | -------------------------------------------------------------------------------- /blackjack_engine/src/mesh/halfedge/mesh_index_impls.rs: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2023 setzer22 and contributors 2 | // 3 | // This Source Code Form is subject to the terms of the Mozilla Public 4 | // License, v. 2.0. If a copy of the MPL was not distributed with this 5 | // file, You can obtain one at https://mozilla.org/MPL/2.0/. 6 | 7 | use super::*; 8 | 9 | macro_rules! impl_index_traits { 10 | ($id_type:ty, $output_type:ty, $arena:ident) => { 11 | impl std::ops::Index<$id_type> for MeshConnectivity { 12 | type Output = $output_type; 13 | 14 | fn index(&self, index: $id_type) -> &Self::Output { 15 | self.$arena.get(index).unwrap_or_else(|| { 16 | panic!( 17 | "{} index error for {:?}. Has the value been deleted?", 18 | stringify!($id_type), 19 | index 20 | ) 21 | }) 22 | } 23 | } 24 | 25 | impl std::ops::IndexMut<$id_type> for MeshConnectivity { 26 | fn index_mut(&mut self, index: $id_type) -> &mut Self::Output { 27 | self.$arena.get_mut(index).unwrap_or_else(|| { 28 | panic!( 29 | "{} index error for {:?}. Has the value been deleted?", 30 | stringify!($id_type), 31 | index 32 | ) 33 | }) 34 | } 35 | } 36 | }; 37 | } 38 | 39 | impl_index_traits!(VertexId, Vertex, vertices); 40 | impl_index_traits!(FaceId, Face, faces); 41 | impl_index_traits!(HalfEdgeId, HalfEdge, halfedges); 42 | 43 | macro_rules! impl_index_ops { 44 | ($field_name:ident, $field_name_mut:ident, $id_type:ty, $output_type:ty, $arena:ident) => { 45 | /// Try to immutably borrow data 46 | pub fn $field_name(&self, id: $id_type) -> Option<&$output_type> { 47 | self.$arena.get(id) 48 | } 49 | 50 | /// Try to mutably borrow data 51 | pub fn $field_name_mut(&mut self, id: $id_type) -> Option<&mut $output_type> { 52 | self.$arena.get_mut(id) 53 | } 54 | }; 55 | } 56 | 57 | impl MeshConnectivity { 58 | impl_index_ops!(vertex, vertex_mut, VertexId, Vertex, vertices); 59 | impl_index_ops!(face, face_mut, FaceId, Face, faces); 60 | impl_index_ops!(halfedge, halfedge_mut, HalfEdgeId, HalfEdge, halfedges); 61 | } 62 | -------------------------------------------------------------------------------- /blackjack_engine/src/mesh/heightmap.rs: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2023 setzer22 and contributors 2 | // 3 | // This Source Code Form is subject to the terms of the Mozilla Public 4 | // License, v. 2.0. If a copy of the MPL was not distributed with this 5 | // file, You can obtain one at https://mozilla.org/MPL/2.0/. 6 | 7 | use anyhow::Result; 8 | use glam::{Vec2, Vec3}; 9 | use noise::NoiseFn; 10 | 11 | use crate::prelude::VertexIndexBuffers; 12 | 13 | #[derive(Clone)] 14 | pub struct HeightMap { 15 | inner: ndarray::Array2, 16 | } 17 | 18 | impl HeightMap { 19 | pub fn from_perlin( 20 | width: usize, 21 | height: usize, 22 | frequency: f32, 23 | offset: Vec2, 24 | amplitude: f32, 25 | ) -> HeightMap { 26 | let frequency = frequency.max(0.01); 27 | let perlin = noise::Perlin::new(); 28 | let inner = ndarray::Array2::from_shape_fn((width, height), |(x, y)| { 29 | let point = Vec2::new(x as f32 / frequency, y as f32 / frequency) + offset; 30 | perlin.get([point.x as f64, point.y as f64]) as f32 * amplitude 31 | }); 32 | 33 | Self { inner } 34 | } 35 | 36 | pub fn from_lua_fn(width: usize, height: usize, f: mlua::Function) -> Result { 37 | // We can't jump out of the closure, so if there's an error we store it 38 | // here and return at the end. 39 | let mut error = None; 40 | 41 | let inner = ndarray::Array2::from_shape_fn((width, height), |(i, j)| { 42 | match f.call::<_, f32>((i, j)).map_err(|err| anyhow::anyhow!(err)) { 43 | Ok(height) => height, 44 | Err(err) => { 45 | error = Some(err); 46 | 0.0 47 | } 48 | } 49 | }); 50 | 51 | if let Some(error) = error { 52 | Err(error) 53 | } else { 54 | Ok(Self { inner }) 55 | } 56 | } 57 | 58 | pub fn generate_triangle_buffers(&self) -> VertexIndexBuffers { 59 | // If the terrain is too small to compute normals, return an empty buffer 60 | if self.inner.ncols() < 4 || self.inner.nrows() < 4 { 61 | return VertexIndexBuffers { 62 | positions: vec![], 63 | normals: vec![], 64 | indices: vec![], 65 | }; 66 | } 67 | 68 | let scale = 0.05; 69 | 70 | let mut positions = vec![]; 71 | let mut indices = vec![]; 72 | let mut normals = vec![]; 73 | 74 | // Iterate 4x4 windows. 75 | // 76 | // NOTE: ndarray should have utilities for this, but in practice it 77 | // doesn't because it doesn't let you check the indices while iterating 78 | for i in 1..self.inner.nrows() - 1 { 79 | for j in 1..self.inner.ncols() - 1 { 80 | // SAFETY: Always in bounds 81 | let point = unsafe { 82 | let height = &self.inner; 83 | let y = *height.uget((i, j)); 84 | let x = j as f32 * scale; 85 | let z = i as f32 * scale; 86 | 87 | Vec3::new(x, y, z) 88 | }; 89 | 90 | positions.push(point); 91 | 92 | // SAFETY: Always in bounds due to loop bounds above 93 | let normal = unsafe { 94 | let height = &self.inner; 95 | Vec3::new( 96 | *height.uget((i, j - 1)) - *height.uget((i, j + 1)), 97 | 2.0 * scale, 98 | *height.uget((i - 1, j)) - *height.uget((i + 1, j)), 99 | ) 100 | .normalize() 101 | }; 102 | normals.push(normal); 103 | } 104 | } 105 | 106 | // We discarded the edge points so we could compute the normals, so now 107 | let nrows = self.inner.nrows() - 2; 108 | let ncols = self.inner.ncols() - 2; 109 | 110 | for i in 0..nrows - 1 { 111 | for j in 0..ncols - 1 { 112 | let a = j + i * ncols; 113 | let b = a + 1; 114 | let c = a + ncols; 115 | let d = a + ncols + 1; 116 | indices.extend([a, c, b, b, c, d].map(|x| x as u32)) 117 | } 118 | } 119 | 120 | VertexIndexBuffers { 121 | positions, 122 | normals, 123 | indices, 124 | } 125 | } 126 | } 127 | 128 | #[blackjack_macros::blackjack_lua_module] 129 | mod lua_api { 130 | use super::*; 131 | use crate::lua_engine::lua_stdlib::LVec3; 132 | 133 | use super::HeightMap; 134 | 135 | /// Builds a heightmap with a grid of `width` times `height` filled with 136 | /// perlin noise with given parameters. 137 | #[lua(under = "HeightMap")] 138 | pub fn from_perlin( 139 | width: usize, 140 | height: usize, 141 | frequency: f32, 142 | offset: LVec3, 143 | amplitude: f32, 144 | ) -> HeightMap { 145 | HeightMap::from_perlin(width, height, frequency, offset.0.truncate(), amplitude) 146 | } 147 | 148 | /// Builds a heightmap with a grid of `width` times `height` filled with 149 | /// perlin noise, where each cell is filled with the result of running the 150 | /// function `f`. The function is called with the (i, j) coordinates of the 151 | /// current cell. 152 | #[lua(under = "HeightMap")] 153 | pub fn from_fn(width: usize, height: usize, f: mlua::Function) -> Result { 154 | HeightMap::from_lua_fn(width, height, f) 155 | } 156 | 157 | #[lua_impl] 158 | impl HeightMap { 159 | /// Returns the width of this heightmap 160 | #[lua] 161 | fn width(&self) -> usize { 162 | self.inner.dim().0 163 | } 164 | 165 | /// Returns the height of this heightmap 166 | #[lua] 167 | fn height(&self) -> usize { 168 | self.inner.dim().1 169 | } 170 | } 171 | } 172 | -------------------------------------------------------------------------------- /blackjack_engine/src/prelude.rs: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2023 setzer22 and contributors 2 | // 3 | // This Source Code Form is subject to the terms of the Mozilla Public 4 | // License, v. 2.0. If a copy of the MPL was not distributed with this 5 | // file, You can obtain one at https://mozilla.org/MPL/2.0/. 6 | 7 | pub use anyhow::{anyhow, bail, Context, Result}; 8 | 9 | pub use glam::{Mat4, Quat, UVec2, UVec3, Vec2, Vec3, Vec4}; 10 | 11 | pub use itertools::Itertools; 12 | pub use std::collections::{HashMap, HashSet}; 13 | 14 | pub use crate::mesh::halfedge; 15 | pub use crate::mesh::halfedge::*; 16 | 17 | pub use blackjack_commons::math::*; 18 | pub use blackjack_commons::utils::*; 19 | -------------------------------------------------------------------------------- /blackjack_engine/src/sync.rs: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2023 setzer22 and contributors 2 | // 3 | // This Source Code Form is subject to the terms of the Mozilla Public 4 | // License, v. 2.0. If a copy of the MPL was not distributed with this 5 | // file, You can obtain one at https://mozilla.org/MPL/2.0/. 6 | 7 | #[cfg(not(feature = "sync"))] 8 | use std::{ 9 | cell::{Ref, RefCell, RefMut}, 10 | rc::Rc, 11 | }; 12 | 13 | #[cfg(feature = "sync")] 14 | use atomic_refcell::{AtomicRef, AtomicRefCell, AtomicRefMut}; 15 | #[cfg(feature = "sync")] 16 | use std::sync::Arc; 17 | 18 | #[cfg(feature = "sync")] 19 | pub type InteriorMutable = AtomicRefCell; 20 | 21 | #[cfg(not(feature = "sync"))] 22 | pub type InteriorMutable = RefCell; 23 | 24 | #[cfg(feature = "sync")] 25 | pub type RefCounted = Arc; 26 | 27 | #[cfg(not(feature = "sync"))] 28 | pub type RefCounted = Rc; 29 | 30 | #[cfg(feature = "sync")] 31 | pub type BorrowedRef<'a, T> = AtomicRef<'a, T>; 32 | 33 | #[cfg(not(feature = "sync"))] 34 | pub type BorrowedRef<'a, T> = Ref<'a, T>; 35 | 36 | #[cfg(feature = "sync")] 37 | pub type MutableRef<'a, T> = AtomicRefMut<'a, T>; 38 | 39 | #[cfg(not(feature = "sync"))] 40 | pub type MutableRef<'a, T> = RefMut<'a, T>; 41 | 42 | #[cfg(feature = "sync")] 43 | pub trait MaybeSync: Send + Sync + 'static {} 44 | 45 | #[cfg(not(feature = "sync"))] 46 | pub trait MaybeSync {} 47 | 48 | #[cfg(feature = "sync")] 49 | fn is_sync() { 50 | use crate::prelude::HalfEdgeMesh; 51 | 52 | fn assert_thread_safe(_: T) {} 53 | assert_thread_safe(HalfEdgeMesh::new()) 54 | } 55 | -------------------------------------------------------------------------------- /blackjack_godot/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "blackjack_godot" 3 | description = "A procedural, node-based modelling tool, made in Rust" 4 | homepage = "https://github.com/setzer22/blackjack" 5 | repository = "https://github.com/setzer22/blackjack" 6 | version = "0.1.0" 7 | edition = "2021" 8 | rust-version = "1.62" 9 | license = "MPL-2.0" 10 | keywords = ["gamedev", "3d", "modelling", "procedural"] 11 | authors = ["setzer22"] 12 | 13 | [lib] 14 | crate-type = ["lib", "cdylib"] 15 | 16 | [features] 17 | library = [] 18 | 19 | [dependencies] 20 | mlua = { version = "0.8.1", features = ["luau"] } 21 | glam = { version = "0.21.2", features = ["serde", "bytemuck"] } 22 | blackjack_engine = { path = "../blackjack_engine" } 23 | gdnative = "0.11.0" 24 | anyhow = { version = "1.0", features = ["backtrace"] } 25 | serde = { version = "1.0", features = ["derive"] } 26 | ron = "0.7" 27 | lazy_static = "1.4" 28 | once_cell = "1.12" 29 | parking_lot = "0.12" 30 | slotmap = { version = "1.0", features = ["serde"] } 31 | -------------------------------------------------------------------------------- /blackjack_godot/src/godot_lua_io.rs: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2023 setzer22 and contributors 2 | // 3 | // This Source Code Form is subject to the terms of the Mozilla Public 4 | // License, v. 2.0. If a copy of the MPL was not distributed with this 5 | // file, You can obtain one at https://mozilla.org/MPL/2.0/. 6 | 7 | use gdnative::prelude::*; 8 | 9 | use blackjack_engine::lua_engine::lua_stdlib::{LuaFileIo, LuaSourceFile}; 10 | 11 | use gdnative::api as gd; 12 | 13 | pub struct GodotLuaIo { 14 | pub base_folder: String, 15 | } 16 | 17 | impl LuaFileIo for GodotLuaIo { 18 | fn base_folder(&self) -> &str { 19 | &self.base_folder 20 | } 21 | 22 | fn find_run_files(&self) -> Box> { 23 | pub fn find_files_recursive( 24 | path: GodotString, 25 | files: &mut Vec, 26 | ) -> anyhow::Result<()> { 27 | let folder = gd::Directory::new(); 28 | folder.open(path)?; 29 | folder.list_dir_begin(true, false)?; 30 | let mut file_name = folder.get_next(); 31 | while file_name != "".into() { 32 | if folder.current_is_dir() { 33 | find_files_recursive(folder.get_current_dir(), files)?; 34 | } else if file_name.ends_with(&GodotString::from_str(".lua")) { 35 | let path = 36 | folder.get_current_dir() + GodotString::from_str("/") + file_name.clone(); 37 | files.push(path.to_string()); 38 | } 39 | 40 | file_name = folder.get_next(); 41 | } 42 | Ok(()) 43 | } 44 | let mut files = vec![]; 45 | match find_files_recursive( 46 | GodotString::from(self.base_folder.clone() + "/run"), 47 | &mut files, 48 | ) { 49 | Ok(_) => Box::new(files.into_iter()), 50 | Err(err) => { 51 | godot_error!("There was an error when loading blackjack files: {err}"); 52 | Box::new(std::iter::empty()) 53 | } 54 | } 55 | } 56 | 57 | fn load_file_absolute( 58 | &self, 59 | path: &str, 60 | ) -> anyhow::Result { 61 | let file = gd::File::new(); 62 | file.open(path, gd::File::READ)?; 63 | let contents = file.get_as_text(true); 64 | Ok(LuaSourceFile { 65 | contents: contents.to_string(), 66 | name: path.to_string(), 67 | }) 68 | } 69 | 70 | fn load_file_require( 71 | &self, 72 | path: &str, 73 | ) -> anyhow::Result { 74 | let mut path = self.base_folder.clone() + "/lib/" + path; 75 | if !path.ends_with(".lua") { 76 | path += ".lua"; 77 | } 78 | self.load_file_absolute(&path) 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /blackjack_lua/lib/gizmo_helpers.lua: -------------------------------------------------------------------------------- 1 | -- Copyright (C) 2023 setzer22 and contributors 2 | -- 3 | -- This Source Code Form is subject to the terms of the Mozilla Public 4 | -- License, v. 2.0. If a copy of the MPL was not distributed with this 5 | -- file, You can obtain one at https://mozilla.org/MPL/2.0/. 6 | 7 | local GizmoHelpers = {} 8 | 9 | --- A gizmo that allows tweaking a single input parameter `point_param` as a 10 | --- point in 3d space. 11 | GizmoHelpers.tweak_point = function(point_param) 12 | return { 13 | -- Called when a gizmo has changed in the UI. This function needs to 14 | -- set the parameters of the node according to the gizmo. 15 | update_params = function(inputs, gizmo) 16 | inputs[point_param] = gizmo:translation() 17 | return inputs 18 | end, 19 | -- Called after op. This function must return a gizmo for this node, to 20 | -- be applied for the next frame. Ghe `gizmo` variable will contain the 21 | -- existing gizmo for the current frame, if any. 22 | update_gizmos = function(inputs, gizmo, _outputs) 23 | if gizmo ~= nil then 24 | gizmo:set_translation(inputs[point_param]) 25 | return gizmo 26 | else 27 | local new_gizmo = 28 | TransformGizmo.new(inputs[point_param], vector(0, 0, 0), vector(1, 1, 1)) 29 | new_gizmo:set_enable_rotation(false) 30 | new_gizmo:set_enable_scale(false) 31 | return new_gizmo 32 | end 33 | end, 34 | --- Must return a list of parameter name lists, informing the engine of 35 | --- which params are affected by each gizmo. If all the parameters in 36 | --- this list have incoming connections, this gizmo will be skipped. 37 | affected_params = function() 38 | return { point_param } 39 | end, 40 | } 41 | end 42 | 43 | 44 | --- A gizmo that allows translating, rotating and scaling something in 3d space. 45 | --- The optional `opts` argument can pass extra keys 46 | --- `pre_{translation,rotation,scale}_param` to set a pre-transform. The 47 | --- pre-transform affects the position of the gizmo, but not the parameter. 48 | GizmoHelpers.tweak_transform = function(translation_param, rotation_param, scale_param, opts) 49 | local opts = opts or {} 50 | return { 51 | update_params = function(inputs, gizmo) 52 | inputs[translation_param] = gizmo:translation() 53 | inputs[rotation_param] = gizmo:rotation() 54 | inputs[scale_param] = gizmo:scale() 55 | return inputs 56 | end, 57 | update_gizmos = function(inputs, gizmo, _outputs) 58 | if gizmo == nil then 59 | gizmo = TransformGizmo.default() 60 | end 61 | gizmo:set_translation(inputs[translation_param]) 62 | gizmo:set_rotation(inputs[rotation_param]) 63 | gizmo:set_scale(inputs[scale_param]) 64 | 65 | if opts.pre_translation_param ~= nil then 66 | gizmo:set_pre_translation(inputs[opts.pre_translation_param]) 67 | end 68 | if opts.pre_rotation_param ~= nil then 69 | gizmo:set_pre_rotation(inputs[opts.pre_rotation_param]) 70 | end 71 | if opts.pre_scale_param ~= nil then 72 | gizmo:set_pre_rotation(inputs[opts.pre_scale_param]) 73 | end 74 | 75 | return gizmo 76 | end, 77 | affected_params = function() 78 | return { translation_param, rotation_param, scale_param } 79 | end, 80 | } 81 | end 82 | 83 | return GizmoHelpers 84 | -------------------------------------------------------------------------------- /blackjack_lua/lib/priority_queue.lua: -------------------------------------------------------------------------------- 1 | --[[ Priority Queue implemented in lua, based on a binary heap. 2 | Copyright (C) 2017 Lucas de Morais Siqueira 3 | License: zlib 4 | This software is provided 'as-is', without any express or implied 5 | warranty. In no event will the authors be held liable for any damages 6 | arising from the use of this software. 7 | Permission is granted to anyone to use this software for any purpose, 8 | including commercial applications, and to alter it and redistribute it 9 | freely, subject to the following restrictions: 10 | 1. The origin of this software must not be misrepresented; you must not 11 | claim that you wrote the original software. If you use this software 12 | in a product, an acknowledgement in the product documentation would be 13 | appreciated but is not required. 14 | 2. Altered source versions must be plainly marked as such, and must not be 15 | misrepresented as being the original software. 16 | 3. This notice may not be removed or altered from any source distribution. 17 | ]] 18 | -- 19 | -- modified by xxopxe@gmail.com 20 | 21 | local floor = math.floor 22 | 23 | local PriorityQueue = {} 24 | PriorityQueue.__index = PriorityQueue 25 | 26 | setmetatable(PriorityQueue, { 27 | __call = function() 28 | local new = {} 29 | setmetatable(new, PriorityQueue) 30 | new:initialize() 31 | return new 32 | end, 33 | }) 34 | 35 | function PriorityQueue:initialize() 36 | --[[ Initialization. 37 | Example: 38 | PriorityQueue = require("priority_queue") 39 | pq = PriorityQueue() 40 | ]] 41 | -- 42 | self.heap_val = {} 43 | self.heap_pri = {} 44 | self.current_size = 0 45 | end 46 | 47 | function PriorityQueue:empty() 48 | return self.current_size == 0 49 | end 50 | 51 | function PriorityQueue:size() 52 | return self.current_size 53 | end 54 | 55 | function PriorityQueue:swim() 56 | -- Swim up on the tree and fix the order heap property. 57 | local heap_val = self.heap_val 58 | local heap_pri = self.heap_pri 59 | local floor = floor 60 | local i = self.current_size 61 | 62 | while floor(i / 2) > 0 do 63 | local half = floor(i / 2) 64 | if heap_pri[i] < heap_pri[half] then 65 | heap_val[i], heap_val[half] = heap_val[half], heap_val[i] 66 | heap_pri[i], heap_pri[half] = heap_pri[half], heap_pri[i] 67 | end 68 | i = half 69 | end 70 | end 71 | 72 | function PriorityQueue:put(v, p) 73 | --[[ Put an item on the queue. 74 | Args: 75 | v: the item to be stored 76 | p(number): the priority of the item 77 | ]] 78 | -- 79 | -- 80 | self.current_size = self.current_size + 1 81 | self.heap_val[self.current_size] = v 82 | self.heap_pri[self.current_size] = p 83 | self:swim() 84 | end 85 | 86 | function PriorityQueue:sink() 87 | -- Sink down on the tree and fix the order heap property. 88 | local size = self.current_size 89 | local heap_val = self.heap_val 90 | local heap_pri = self.heap_pri 91 | local i = 1 92 | 93 | while (i * 2) <= size do 94 | local mc = self:min_child(i) 95 | if heap_pri[i] > heap_pri[mc] then 96 | heap_val[i], heap_val[mc] = heap_val[mc], heap_val[i] 97 | heap_pri[i], heap_pri[mc] = heap_pri[mc], heap_pri[i] 98 | end 99 | i = mc 100 | end 101 | end 102 | 103 | function PriorityQueue:min_child(i) 104 | if (i * 2) + 1 > self.current_size then 105 | return i * 2 106 | else 107 | if self.heap_pri[i * 2] < self.heap_pri[i * 2 + 1] then 108 | return i * 2 109 | else 110 | return i * 2 + 1 111 | end 112 | end 113 | end 114 | 115 | function PriorityQueue:pop() 116 | -- Remove and return the top priority item 117 | local heap_val = self.heap_val 118 | local heap_pri = self.heap_pri 119 | local retval, retprio = heap_val[1], heap_pri[1] 120 | heap_val[1], heap_pri[1] = heap_val[self.current_size], heap_pri[self.current_size] 121 | heap_val[self.current_size], heap_pri[self.current_size] = nil, nil 122 | self.current_size = self.current_size - 1 123 | self:sink() 124 | return retval, retprio 125 | end 126 | 127 | function PriorityQueue:peek() 128 | -- return the top priority item 129 | return self.heap_val[1], self.heap_pri[1] 130 | end 131 | 132 | return PriorityQueue 133 | -------------------------------------------------------------------------------- /blackjack_lua/lib/table_helpers.lua: -------------------------------------------------------------------------------- 1 | -- Copyright (C) 2023 setzer22 and contributors 2 | -- 3 | -- This Source Code Form is subject to the terms of the Mozilla Public 4 | -- License, v. 2.0. If a copy of the MPL was not distributed with this 5 | -- file, You can obtain one at https://mozilla.org/MPL/2.0/. 6 | 7 | local TableHelpers = {} 8 | 9 | --- Reverses the sequential part of this table. 10 | TableHelpers.reverse = function(t) 11 | local n = #t 12 | for i = 1, n do 13 | t[i], t[n] = t[n], t[i] 14 | n = n - 1 15 | end 16 | return t 17 | end 18 | 19 | --- Concatenates `t2` at the end of the sequential part of `t1`. 20 | TableHelpers.concat = function(t1, t2) 21 | for i=1,#t2 do 22 | t1[#t1+1] = t2[i] 23 | end 24 | return t1 25 | end 26 | 27 | return TableHelpers 28 | -------------------------------------------------------------------------------- /blackjack_lua/lib/vector_math.lua: -------------------------------------------------------------------------------- 1 | -- Copyright (C) 2023 setzer22 and contributors 2 | -- 3 | -- This Source Code Form is subject to the terms of the Mozilla Public 4 | -- License, v. 2.0. If a copy of the MPL was not distributed with this 5 | -- file, You can obtain one at https://mozilla.org/MPL/2.0/. 6 | 7 | local VectorMath = {} 8 | 9 | VectorMath.length = function(v) 10 | return math.sqrt(v.x * v.x + v.y * v.y + v.z * v.z) 11 | end 12 | 13 | VectorMath.normalize = function(v) 14 | return v * (1.0 / VectorMath.length(v)) 15 | end 16 | 17 | VectorMath.distance = function(v1, v2) 18 | local d = v2 - v1 19 | return math.sqrt(d.x * d.x + d.y * d.y + d.z * d.z) 20 | end 21 | 22 | VectorMath.distance_squared = function(v1, v2) 23 | local d = v2 - v1 24 | return d.x * d.x + d.y * d.y + d.z * d.z 25 | end 26 | 27 | VectorMath.floor = function(v) 28 | return vector(math.floor(v.x), math.floor(v.y), math.floor(v.z)) 29 | end 30 | 31 | VectorMath.display = function(v) 32 | return tostring(v.x) .. "," .. tostring(v.y) .. "," .. tostring(v.z) 33 | end 34 | 35 | VectorMath.from_string = function(s) 36 | local _, _, x, y, z = s:find("([^,]+),([^,]+),([^,]+)") 37 | if x and y and z then 38 | return vector(x, y, z) 39 | else 40 | error("Invalid vector format " .. s) 41 | end 42 | end 43 | 44 | VectorMath.dot = function(v1, v2) 45 | return v1.x * v2.x + v1.y * v2.y + v1.z * v2.z 46 | end 47 | 48 | VectorMath.cross = function(v, v2) 49 | return NativeMath.cross(v, v2) 50 | end 51 | 52 | VectorMath.rotate_around_axis = function(v, axis, angle) 53 | return NativeMath.rotate_around_axis(v, axis, angle) 54 | end 55 | 56 | return VectorMath 57 | -------------------------------------------------------------------------------- /blackjack_lua/run/.gitignore: -------------------------------------------------------------------------------- 1 | extras 2 | -------------------------------------------------------------------------------- /blackjack_macros/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "blackjack_macros" 3 | description = "A procedural, node-based modelling tool, made in Rust" 4 | homepage = "https://github.com/setzer22/blackjack" 5 | repository = "https://github.com/setzer22/blackjack" 6 | version = "0.1.0" 7 | edition = "2021" 8 | rust-version = "1.62" 9 | license = "MPL-2.0" 10 | keywords = ["gamedev", "3d", "modelling", "procedural"] 11 | authors = ["setzer22"] 12 | 13 | [lib] 14 | proc-macro = true 15 | 16 | [dependencies] 17 | syn = { version = "1.0", features = ["full", "extra-traits", "parsing"] } 18 | quote = "1.0" 19 | proc-macro2 = "1.0" -------------------------------------------------------------------------------- /blackjack_macros/src/blackjack_lua_module/fn_attr.rs: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2023 setzer22 and contributors 2 | // 3 | // This Source Code Form is subject to the terms of the Mozilla Public 4 | // License, v. 2.0. If a copy of the MPL was not distributed with this 5 | // file, You can obtain one at https://mozilla.org/MPL/2.0/. 6 | 7 | use proc_macro2::Ident; 8 | use syn::{ 9 | parse::{Parse, ParseStream}, 10 | Expr, Token, 11 | }; 12 | 13 | use crate::utils::{ExprUtils, SynParseBufferExt}; 14 | 15 | #[derive(Default, Debug)] 16 | pub struct LuaFnAttr { 17 | pub under: Option, 18 | pub coerce: bool, 19 | pub map_this: Option, 20 | pub map_result: Option, 21 | pub hidden_fn: bool, 22 | } 23 | 24 | #[derive(Default, Debug)] 25 | pub struct FunctionAttributes { 26 | pub lua_attr: LuaFnAttr, 27 | pub docstring_lines: Vec, 28 | } 29 | 30 | impl Parse for LuaFnAttr { 31 | fn parse(input: ParseStream) -> syn::Result { 32 | let properties = input.comma_separated_fn(|input| { 33 | let lhs: Ident = input.parse()?; 34 | let rhs: Option = if input.peek(Token![=]) { 35 | let _eq_sign = input.expect_token::(); 36 | Some(input.parse()?) 37 | } else { 38 | None 39 | }; 40 | Ok((lhs, rhs)) 41 | })?; 42 | 43 | let mut lua_attr = LuaFnAttr::default(); 44 | 45 | for (key, val) in properties.iter() { 46 | if key == "under" { 47 | lua_attr.under = Some( 48 | val.as_ref() 49 | .expect("'under' declaration should have an assigned value") 50 | .assume_string_literal("Value for 'under' must be a string")?, 51 | ); 52 | } else if key == "coerce" { 53 | lua_attr.coerce = true; 54 | } else if key == "this" { 55 | lua_attr.map_this = Some(syn::parse_str( 56 | &val.as_ref() 57 | .expect("'this' declaration should have an assigned value") 58 | .assume_string_literal("Value for 'self' must be a string")?, 59 | )?); 60 | } else if key == "map" { 61 | lua_attr.map_result = Some(syn::parse_str( 62 | &val.as_ref() 63 | .expect("'this' declaration should have an assigned value") 64 | .assume_string_literal("Value for 'self' must be a string")?, 65 | )?); 66 | } else if key == "hidden" { 67 | lua_attr.hidden_fn = true; 68 | } else { 69 | panic!("Unexpected annotation '{key}'"); 70 | } 71 | } 72 | 73 | Ok(lua_attr) 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /blackjack_macros/src/lib.rs: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2023 setzer22 and contributors 2 | // 3 | // This Source Code Form is subject to the terms of the Mozilla Public 4 | // License, v. 2.0. If a copy of the MPL was not distributed with this 5 | // file, You can obtain one at https://mozilla.org/MPL/2.0/. 6 | 7 | use syn::{parse_macro_input, ItemMod}; 8 | 9 | mod blackjack_lua_module; 10 | mod utils; 11 | 12 | #[proc_macro_attribute] 13 | pub fn blackjack_lua_module( 14 | _attr: proc_macro::TokenStream, 15 | tokens: proc_macro::TokenStream, 16 | ) -> proc_macro::TokenStream { 17 | let module = parse_macro_input!(tokens as ItemMod); 18 | match blackjack_lua_module::blackjack_lua_module2(module) { 19 | Ok(result) => result.into(), 20 | Err(err) => panic!("Error in Blackjack Lua module definition: {err:?}"), 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /blackjack_macros/src/utils.rs: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2023 setzer22 and contributors 2 | // 3 | // This Source Code Form is subject to the terms of the Mozilla Public 4 | // License, v. 2.0. If a copy of the MPL was not distributed with this 5 | // file, You can obtain one at https://mozilla.org/MPL/2.0/. 6 | 7 | use proc_macro2::Span; 8 | use syn::parse::ParseBuffer; 9 | use syn::token::Token; 10 | use syn::{Attribute, Ident, PathArguments, Token, Type}; 11 | 12 | #[cfg(test)] 13 | use std::{fs, io, path::Path, process::Command}; 14 | 15 | pub trait SynParseBufferExt { 16 | fn as_stream(&self) -> &ParseBuffer<'_>; 17 | 18 | fn comma_separated_fn( 19 | &self, 20 | item_parser: impl Fn(&ParseBuffer) -> syn::Result, 21 | ) -> syn::Result> { 22 | let input = self.as_stream(); 23 | let mut items = vec![item_parser(input)?]; 24 | while !input.is_empty() { 25 | input.parse::()?; 26 | items.push(item_parser(input)?); 27 | } 28 | Ok(items) 29 | } 30 | 31 | fn expect_ident(&self, name: &str) -> syn::Result<()> { 32 | let input = self.as_stream(); 33 | let id: Ident = input.parse()?; 34 | if id != name { 35 | panic!("Expected {name}, found {id}") 36 | } else { 37 | Ok(()) 38 | } 39 | } 40 | 41 | fn expect_token(&self) -> syn::Result<()> { 42 | let input = self.as_stream(); 43 | let _: T = input.parse()?; 44 | Ok(()) 45 | } 46 | } 47 | 48 | pub trait ExprUtils { 49 | fn as_expr(&self) -> &syn::Expr; 50 | fn assume_string_literal(&self, err_msg: &str) -> syn::Result { 51 | let expr = self.as_expr(); 52 | if let syn::Expr::Lit(lit) = expr { 53 | if let syn::Lit::Str(s) = &lit.lit { 54 | return Ok(s.value()); 55 | } 56 | } 57 | Err(syn::Error::new(Span::call_site(), err_msg.to_owned())) 58 | } 59 | } 60 | 61 | impl<'a> SynParseBufferExt for ParseBuffer<'a> { 62 | fn as_stream(&self) -> &ParseBuffer { 63 | self 64 | } 65 | } 66 | 67 | impl ExprUtils for syn::Expr { 68 | fn as_expr(&self) -> &syn::Expr { 69 | self 70 | } 71 | } 72 | 73 | #[cfg(test)] 74 | pub fn write_and_fmt, S: ToString>(path: P, code: S) -> io::Result<()> { 75 | fs::write(&path, code.to_string())?; 76 | Command::new("rustfmt").arg(path.as_ref()).spawn()?.wait()?; 77 | Ok(()) 78 | } 79 | 80 | /// When `typ` is of the form `Result`, returns the inner `Something`. 81 | pub fn unwrap_result(typ: &Type) -> Option<&Type> { 82 | if let Type::Path(typepath) = typ { 83 | if let Some(seg) = typepath.path.segments.first() { 84 | if seg.ident == "Result" { 85 | if let PathArguments::AngleBracketed(bracketed) = &seg.arguments { 86 | if let Some(syn::GenericArgument::Type(t)) = bracketed.args.iter().next() { 87 | return Some(t); 88 | } 89 | } 90 | } 91 | } 92 | } 93 | None 94 | } 95 | 96 | /// Assuming `attr` is of the form `#[doc = r"Some docstring line"]`, returns 97 | /// the inner string. Panics otherwise 98 | pub fn parse_doc_attr(attr: &Attribute) -> String { 99 | let meta = attr.parse_meta().unwrap(); 100 | 101 | match meta { 102 | syn::Meta::NameValue(nameval) => match nameval.lit { 103 | syn::Lit::Str(s) => s.value(), 104 | _ => panic!("Unexpected docstring attribute form"), 105 | }, 106 | _ => panic!("Unexpected docstring attribute form"), 107 | } 108 | } 109 | 110 | pub fn join_str(it: impl Iterator, sep: &str) -> String 111 | where 112 | S: AsRef, 113 | { 114 | let mut s = String::new(); 115 | let mut first = true; 116 | for i in it { 117 | if !first { 118 | s += sep; 119 | } 120 | first = false; 121 | s += i.as_ref(); 122 | } 123 | s 124 | } 125 | -------------------------------------------------------------------------------- /blackjack_ui/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "blackjack_ui" 3 | description = "A procedural, node-based modelling tool, made in Rust" 4 | homepage = "https://github.com/setzer22/blackjack" 5 | repository = "https://github.com/setzer22/blackjack" 6 | version = "0.1.0" 7 | edition = "2021" 8 | rust-version = "1.62" 9 | license = "MPL-2.0" 10 | keywords = ["gamedev", "3d", "modelling", "procedural"] 11 | authors = ["setzer22"] 12 | 13 | [features] 14 | tracy = ["profiling/profile-with-tracy"] 15 | 16 | [dependencies] 17 | # Workspace dependencies 18 | blackjack_commons = { path = "../blackjack_commons" } 19 | blackjack_engine = { path = "../blackjack_engine" } 20 | 21 | # Git dependencies 22 | egui_node_graph = { git = "https://github.com/setzer22/egui_node_graph", rev = "f4009fccc92a5f2132109a661e9bb57cc38b7e51" } 23 | rend3 = { git = "https://github.com/setzer22/rend3.git", rev = "c1df4dca5247eda11c04c529d6376690717ce4d5" } 24 | rend3-routine = { git = "https://github.com/setzer22/rend3.git", rev = "c1df4dca5247eda11c04c529d6376690717ce4d5" } 25 | glsl-include = { git = "https://github.com/setzer22/glsl-include", rev = "d9b1ade" } 26 | egui-gizmo = { git = "https://github.com/setzer22/egui-gizmo", rev = "a3415a075fc2b2ecd3b26cdef9cb7c1857d77478" } 27 | 28 | # Crates.io crates 29 | egui-winit = { version = "0.19" } 30 | egui-wgpu = { version = "0.19" } 31 | egui = { version = "0.19" } 32 | glam = { version = "0.21.2", features = ["bytemuck"] } 33 | env_logger = { version = "0.9", default-features = false, features = [ 34 | "termcolor", 35 | "atty", 36 | ] } 37 | winit = { version = "0.27.2" } 38 | wgpu = "0.13" 39 | pollster = "0.2" 40 | smallvec = { version = "1.7.0" } 41 | itertools = "0.10" 42 | anyhow = { version = "1.0", features = ["backtrace"] } 43 | bytemuck = { version = "1.7", features = ["derive"] } 44 | ron = "0.7" 45 | rfd = { version = "0.9.1", default-features = false, features = ["xdg-portal"] } 46 | float-ord = "0.3.2" 47 | spin_sleep = "1.0.0" 48 | rayon = "1.5.1" 49 | nonmax = "0.5" 50 | slotmap = { version = "1.0" } 51 | num-traits = "0.2.14" 52 | atomic_float = "0.1" 53 | profiling = { version = "1.0" } 54 | image = { version = "0.24", default-features = false, features = ["png"] } 55 | nom = "7.1" 56 | mlua = { version = "0.8.1", features = ["luau"] } 57 | notify = "4.0" 58 | walkdir = "2" 59 | syntect = "4.6" 60 | bimap = "0.6.2" 61 | dyn-clone = "1.0" 62 | noise = "0.7" 63 | rstar = "0.9.3" 64 | wavefront_rs = "1.0.4" 65 | derive_more = "0.99" 66 | clap = { version = "4.0", features = ["derive"] } 67 | once_cell = "1.15" 68 | -------------------------------------------------------------------------------- /blackjack_ui/assets/bevel_edge_test_case_1.obj: -------------------------------------------------------------------------------- 1 | # Blender v3.0.0 OBJ File: '' 2 | # www.blender.org 3 | o Cone 4 | v -0.797754 -0.014474 -0.364075 5 | v -0.486655 -0.748240 -0.563437 6 | v 0.436310 -0.947712 -0.222818 7 | v 0.830939 -0.234080 0.299393 8 | v 0.301032 0.600342 0.443160 9 | v -0.453901 0.592560 0.083980 10 | v 0.141544 -0.086777 -0.243270 11 | vt 0.250000 0.490000 12 | vt 0.250000 0.250000 13 | vt 0.457846 0.370000 14 | vt 0.457846 0.130000 15 | vt 0.250000 0.010000 16 | vt 0.042154 0.130000 17 | vt 0.042154 0.370000 18 | vn 0.1449 0.3162 -0.9376 19 | vn 0.3638 0.1026 -0.9258 20 | vn 0.6321 0.1986 -0.7490 21 | vn 0.6013 0.4905 -0.6307 22 | vn 0.3290 0.6281 -0.7052 23 | vn 0.1482 0.5316 -0.8339 24 | s off 25 | f 1/1/1 7/2/1 2/3/1 26 | f 2/3/2 7/2/2 3/4/2 27 | f 3/4/3 7/2/3 4/5/3 28 | f 4/5/4 7/2/4 5/6/4 29 | f 5/6/5 7/2/5 6/7/5 30 | f 6/7/6 7/2/6 1/1/6 31 | -------------------------------------------------------------------------------- /blackjack_ui/assets/bevel_edge_test_case_2.mtl: -------------------------------------------------------------------------------- 1 | # Blender MTL File: 'None' 2 | # Material Count: 1 3 | 4 | newmtl None 5 | Ns 500 6 | Ka 0.8 0.8 0.8 7 | Kd 0.8 0.8 0.8 8 | Ks 0.8 0.8 0.8 9 | d 1 10 | illum 2 11 | -------------------------------------------------------------------------------- /blackjack_ui/assets/debug/arrow.mtl: -------------------------------------------------------------------------------- 1 | # Blender MTL File: 'None' 2 | # Material Count: 1 3 | 4 | newmtl Default_OBJ 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 | -------------------------------------------------------------------------------- /blackjack_ui/assets/debug/arrow.obj: -------------------------------------------------------------------------------- 1 | # Blender v3.0.0 OBJ File: '' 2 | # www.blender.org 3 | mtllib arrow.mtl 4 | o Cylinder 5 | v 0.000000 -0.502453 -0.240399 6 | v 0.000000 0.497547 0.000000 7 | v 0.169987 -0.502453 -0.169987 8 | v 0.240399 -0.502453 -0.000000 9 | v 0.169987 -0.502453 0.169987 10 | v 0.000000 -0.502453 0.240399 11 | v -0.169987 -0.502453 0.169987 12 | v -0.240399 -0.502453 -0.000000 13 | v -0.169987 -0.502453 -0.169987 14 | v 0.000000 0.282636 -0.240399 15 | v 0.169987 0.282636 0.169987 16 | v 0.169987 0.282636 -0.169987 17 | v -0.240399 0.282636 0.000000 18 | v -0.169987 0.282636 0.169987 19 | v 0.000000 0.282636 0.240399 20 | v 0.240399 0.282636 0.000000 21 | v -0.169987 0.282636 -0.169987 22 | v 0.000000 0.282636 -0.676881 23 | v 0.478627 0.282636 0.478627 24 | v 0.478627 0.282636 -0.478627 25 | v -0.676881 0.282636 0.000000 26 | v -0.478627 0.282636 0.478627 27 | v 0.000000 0.282636 0.676881 28 | v 0.676881 0.282636 0.000000 29 | v -0.478627 0.282636 -0.478627 30 | vt 0.000000 0.884259 31 | vt 0.125000 0.884259 32 | vt 0.125000 0.884259 33 | vt 0.000000 0.884259 34 | vt 0.875000 0.884259 35 | vt 1.000000 0.884259 36 | vt 1.000000 0.884259 37 | vt 0.875000 0.884259 38 | vt 0.750000 0.884259 39 | vt 0.750000 0.884259 40 | vt 0.625000 0.884259 41 | vt 0.625000 0.884259 42 | vt 0.750000 0.490000 43 | vt 0.919706 0.419706 44 | vt 0.990000 0.250000 45 | vt 0.919706 0.080294 46 | vt 0.750000 0.010000 47 | vt 0.580294 0.080294 48 | vt 0.510000 0.250000 49 | vt 0.580294 0.419706 50 | vt 0.125000 0.500000 51 | vt 0.000000 0.500000 52 | vt 0.250000 0.500000 53 | vt 0.250000 0.884259 54 | vt 0.375000 0.500000 55 | vt 0.375000 0.884259 56 | vt 0.500000 0.500000 57 | vt 0.500000 0.884259 58 | vt 0.625000 0.500000 59 | vt 0.750000 0.500000 60 | vt 0.875000 0.500000 61 | vt 1.000000 0.500000 62 | vt 0.875000 1.000000 63 | vt 0.750000 1.000000 64 | vt 0.625000 1.000000 65 | vt 0.500000 1.000000 66 | vt 0.500000 0.884259 67 | vt 0.375000 1.000000 68 | vt 0.375000 0.884259 69 | vt 0.250000 1.000000 70 | vt 0.250000 0.884259 71 | vt 0.125000 1.000000 72 | vt 0.000000 1.000000 73 | vn 0.0000 -0.8042 -0.5944 74 | vn -0.4203 -0.8042 -0.4203 75 | vn -0.7010 -0.1312 -0.7010 76 | vn 0.0000 -0.1312 -0.9914 77 | vn 0.4203 -0.8042 -0.4203 78 | vn 0.7010 -0.1312 -0.7010 79 | vn 0.5944 -0.8042 -0.0000 80 | vn 0.9914 -0.1312 -0.0000 81 | vn 0.4203 -0.8042 0.4203 82 | vn 0.7010 -0.1312 0.7010 83 | vn 0.0000 -1.0000 0.0000 84 | vn -0.7071 0.0000 -0.7071 85 | vn 0.0000 0.0000 -1.0000 86 | vn -1.0000 -0.0000 -0.0000 87 | vn -0.5944 -0.8042 -0.0000 88 | vn -0.7071 -0.0000 0.7071 89 | vn -0.4203 -0.8042 0.4203 90 | vn 0.0000 -0.0000 1.0000 91 | vn 0.0000 -0.8042 0.5944 92 | vn 0.7071 -0.0000 0.7071 93 | vn 1.0000 0.0000 -0.0000 94 | vn 0.7071 0.0000 -0.7071 95 | vn -0.2447 0.9434 -0.2240 96 | vn 0.0000 -0.1312 0.9914 97 | vn -0.7010 -0.1312 0.7010 98 | vn -0.9914 -0.1312 0.0000 99 | usemtl Default_OBJ 100 | s 1 101 | f 10/1/1 17/2/2 25/3/3 18/4/4 102 | f 12/5/5 10/6/1 18/7/4 20/8/6 103 | f 16/9/7 12/5/5 20/8/6 24/10/8 104 | f 11/11/9 16/9/7 24/10/8 19/12/10 105 | f 1/13/11 3/14/11 4/15/11 5/16/11 6/17/11 7/18/11 8/19/11 9/20/11 106 | f 9/21/12 17/2/2 10/1/1 1/22/13 107 | f 8/23/14 13/24/15 17/2/2 9/21/12 108 | f 7/25/16 14/26/17 13/24/15 8/23/14 109 | f 6/27/18 15/28/19 14/26/17 7/25/16 110 | f 5/29/20 11/11/9 15/28/19 6/27/18 111 | f 4/30/21 16/9/7 11/11/9 5/29/20 112 | f 3/31/22 12/5/5 16/9/7 4/30/21 113 | f 1/32/13 10/6/1 12/5/5 3/31/22 114 | f 18/7/4 2/33/23 20/8/6 115 | f 20/8/6 2/34/23 24/10/8 116 | f 24/10/8 2/35/23 19/12/10 117 | f 19/12/10 2/36/23 23/37/24 118 | f 23/37/24 2/38/23 22/39/25 119 | f 22/39/25 2/40/23 21/41/26 120 | f 21/41/26 2/42/23 25/3/3 121 | f 25/3/3 2/43/23 18/4/4 122 | f 17/2/2 13/24/15 21/41/26 25/3/3 123 | f 13/24/15 14/26/17 22/39/25 21/41/26 124 | f 14/26/17 15/28/19 23/37/24 22/39/25 125 | f 15/28/19 11/11/9 19/12/10 23/37/24 126 | -------------------------------------------------------------------------------- /blackjack_ui/assets/debug/cylinder.obj: -------------------------------------------------------------------------------- 1 | # Blender v3.0.0 OBJ File: '' 2 | # www.blender.org 3 | o Cylinder 4 | v 0.000000 -0.500000 -0.500000 5 | v 0.000000 0.500000 -0.500000 6 | v 0.353553 -0.500000 -0.353553 7 | v 0.353553 0.500000 -0.353553 8 | v 0.500000 -0.500000 0.000000 9 | v 0.500000 0.500000 0.000000 10 | v 0.353553 -0.500000 0.353553 11 | v 0.353553 0.500000 0.353553 12 | v -0.000000 -0.500000 0.500000 13 | v -0.000000 0.500000 0.500000 14 | v -0.353553 -0.500000 0.353553 15 | v -0.353553 0.500000 0.353553 16 | v -0.500000 -0.500000 -0.000000 17 | v -0.500000 0.500000 -0.000000 18 | v -0.353553 -0.500000 -0.353553 19 | v -0.353553 0.500000 -0.353553 20 | vt 1.000000 0.500000 21 | vt 1.000000 1.000000 22 | vt 0.875000 1.000000 23 | vt 0.875000 0.500000 24 | vt 0.750000 1.000000 25 | vt 0.750000 0.500000 26 | vt 0.625000 1.000000 27 | vt 0.625000 0.500000 28 | vt 0.500000 1.000000 29 | vt 0.500000 0.500000 30 | vt 0.375000 1.000000 31 | vt 0.375000 0.500000 32 | vt 0.250000 1.000000 33 | vt 0.250000 0.500000 34 | vt 0.419706 0.419706 35 | vt 0.250000 0.490000 36 | vt 0.080294 0.419706 37 | vt 0.010000 0.250000 38 | vt 0.080294 0.080294 39 | vt 0.250000 0.010000 40 | vt 0.419706 0.080294 41 | vt 0.490000 0.250000 42 | vt 0.125000 1.000000 43 | vt 0.125000 0.500000 44 | vt 0.000000 1.000000 45 | vt 0.000000 0.500000 46 | vt 0.750000 0.490000 47 | vt 0.919706 0.419706 48 | vt 0.990000 0.250000 49 | vt 0.919706 0.080294 50 | vt 0.750000 0.010000 51 | vt 0.580294 0.080294 52 | vt 0.510000 0.250000 53 | vt 0.580294 0.419706 54 | vn -0.0000 0.0000 -1.0000 55 | vn 0.7071 0.0000 -0.7071 56 | vn 1.0000 0.0000 0.0000 57 | vn 0.7071 0.0000 0.7071 58 | vn 0.0000 0.0000 1.0000 59 | vn -0.7071 0.0000 0.7071 60 | vn -1.0000 0.0000 0.0000 61 | vn 0.0000 1.0000 -0.0000 62 | vn -0.7071 0.0000 -0.7071 63 | vn 0.0000 -1.0000 0.0000 64 | s 1 65 | f 1/1/1 2/2/1 4/3/2 3/4/2 66 | f 3/4/2 4/3/2 6/5/3 5/6/3 67 | f 5/6/3 6/5/3 8/7/4 7/8/4 68 | f 7/8/4 8/7/4 10/9/5 9/10/5 69 | f 9/10/5 10/9/5 12/11/6 11/12/6 70 | f 11/12/6 12/11/6 14/13/7 13/14/7 71 | f 4/15/8 2/16/8 16/17/8 14/18/8 12/19/8 10/20/8 8/21/8 6/22/8 72 | f 13/14/7 14/13/7 16/23/9 15/24/9 73 | f 15/24/9 16/23/9 2/25/1 1/26/1 74 | f 1/27/10 3/28/10 5/29/10 7/30/10 9/31/10 11/32/10 13/33/10 15/34/10 75 | -------------------------------------------------------------------------------- /blackjack_ui/assets/matcap/2E763A_78A0B7_B3D1CF_14F209.png: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:bff4e85aa542ee7762edf758a4dfd6fd6b688d2168ea113f2a1a908e32e50ae1 3 | size 484163 4 | -------------------------------------------------------------------------------- /blackjack_ui/assets/matcap/304FB1_69A1EF_5081DF_5C8CE6.png: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:e203e2147a36ff84867df57ca88d165ba589115bd9d2b0694ddcee607dda9a6c 3 | size 130878 4 | -------------------------------------------------------------------------------- /blackjack_ui/assets/matcap/313131_BBBBBB_878787_A3A4A4.png: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:8ce1302ce9f3c5e21c9d8ed2a9cc94388d5830e7130d374bbd9e4f5a55220d9b 3 | size 227778 4 | -------------------------------------------------------------------------------- /blackjack_ui/assets/matcap/326666_66CBC9_C0B8AE_52B3B4.png: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:4b952611980d13f59e08e9ed51dff9f1df0479d4c354743bd84d6a538dca0ed1 3 | size 583235 4 | -------------------------------------------------------------------------------- /blackjack_ui/assets/matcap/34352A_718184_50605E_6E6761.png: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:e9738c072ae289972f88555f8dc093b16ba1b8f603a72bfe278db3c7a2cbf657 3 | size 320410 4 | -------------------------------------------------------------------------------- /blackjack_ui/assets/matcap/A67362_36150C_5E2E1E_F6C3BF.jpg: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:7b6e95dba5236001537af4a787c1520ab0083d334b03a1badb73772cc8d3cbfc 3 | size 8104 4 | -------------------------------------------------------------------------------- /blackjack_ui/assets/matcap/CREDITS.txt: -------------------------------------------------------------------------------- 1 | Matcap images obtained from: https://github.com/nidorx/matcaps 2 | Credit goes to original authors 3 | -------------------------------------------------------------------------------- /blackjack_ui/assets/matcap/E8DEE1_B5A6AA_CCBCC1_C4BBBC.png: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:c3dbee683c80216037acd6b097249650360b13aef41a92318feead5aefd00f44 3 | size 110527 4 | -------------------------------------------------------------------------------- /blackjack_ui/assets/matcap/Perenyi-Midtgaard APLAS20.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/setzer22/blackjack/08ece4c0d1551ef60d2fc380952abf122d46f956/blackjack_ui/assets/matcap/Perenyi-Midtgaard APLAS20.pdf -------------------------------------------------------------------------------- /blackjack_ui/assets/test.obj: -------------------------------------------------------------------------------- 1 | # Blender v2.93.5 OBJ File: '' 2 | # www.blender.org 3 | mtllib test.mtl 4 | o Cube 5 | v 1.000000 1.000000 -1.000000 6 | v 1.000000 -1.000000 -1.000000 7 | v 1.000000 1.000000 1.000000 8 | v 1.000000 -1.000000 1.000000 9 | v -1.000000 1.000000 -1.000000 10 | v -1.000000 -1.000000 -1.000000 11 | v -1.000000 1.000000 1.000000 12 | v -1.000000 -1.000000 1.000000 13 | v 0.623645 0.000000 -0.623645 14 | v -0.623645 0.000000 0.623645 15 | v 0.623645 0.000000 0.623645 16 | v -0.623645 0.000000 -0.623645 17 | v -0.571046 -1.000000 0.000000 18 | v 0.571046 1.000000 0.000000 19 | v -0.571046 1.000000 0.000000 20 | v 0.571046 -1.000000 0.000000 21 | v 0.356130 0.000000 0.000000 22 | v -0.356130 0.000000 0.000000 23 | v 0.000000 -1.000000 -0.548317 24 | v 0.000000 1.000000 0.548317 25 | v 0.000000 -1.000000 0.548317 26 | v 0.000000 1.000000 -0.548317 27 | v 0.000000 0.000000 -0.341956 28 | v 0.000000 0.000000 0.341956 29 | v 0.000000 -1.000000 0.000000 30 | v 0.000000 1.000000 0.000000 31 | vt 0.750000 0.625000 32 | vt 0.875000 0.625000 33 | vt 0.875000 0.750000 34 | vt 0.750000 0.750000 35 | vt 0.500000 0.875000 36 | vt 0.625000 0.875000 37 | vt 0.625000 1.000000 38 | vt 0.500000 1.000000 39 | vt 0.500000 0.125000 40 | vt 0.625000 0.125000 41 | vt 0.625000 0.250000 42 | vt 0.500000 0.250000 43 | vt 0.250000 0.625000 44 | vt 0.375000 0.625000 45 | vt 0.375000 0.750000 46 | vt 0.250000 0.750000 47 | vt 0.500000 0.625000 48 | vt 0.625000 0.625000 49 | vt 0.625000 0.750000 50 | vt 0.500000 0.750000 51 | vt 0.500000 0.375000 52 | vt 0.625000 0.375000 53 | vt 0.625000 0.500000 54 | vt 0.500000 0.500000 55 | vt 0.375000 0.375000 56 | vt 0.375000 0.500000 57 | vt 0.375000 0.125000 58 | vt 0.375000 0.250000 59 | vt 0.375000 0.875000 60 | vt 0.375000 1.000000 61 | vt 0.375000 0.000000 62 | vt 0.500000 0.000000 63 | vt 0.250000 0.500000 64 | vt 0.625000 0.000000 65 | vt 0.750000 0.500000 66 | vt 0.875000 0.500000 67 | vt 0.125000 0.500000 68 | vt 0.125000 0.625000 69 | vt 0.125000 0.750000 70 | vn 0.0000 1.0000 0.0000 71 | vn 0.4045 -0.1848 0.8956 72 | vn -0.9016 -0.1938 0.3867 73 | vn 0.0000 -1.0000 0.0000 74 | vn 0.9016 -0.1938 -0.3867 75 | vn -0.4045 -0.1848 -0.8956 76 | vn -0.4045 0.1848 -0.8956 77 | vn 0.9016 0.1938 -0.3867 78 | vn -0.9016 0.1938 0.3867 79 | vn 0.4045 0.1848 0.8956 80 | vn -0.9016 0.1938 -0.3867 81 | vn 0.9016 0.1938 0.3867 82 | vn 0.9016 -0.1938 0.3867 83 | vn -0.9016 -0.1938 -0.3867 84 | vn -0.4045 0.1848 0.8956 85 | vn 0.4045 0.1848 -0.8956 86 | vn 0.4045 -0.1848 -0.8956 87 | vn -0.4045 -0.1848 0.8956 88 | usemtl Material 89 | s off 90 | f 26/1/1 15/2/1 7/3/1 20/4/1 91 | f 24/5/2 20/6/2 7/7/2 10/8/2 92 | f 18/9/3 15/10/3 5/11/3 12/12/3 93 | f 25/13/4 16/14/4 4/15/4 21/16/4 94 | f 17/17/5 14/18/5 3/19/5 11/20/5 95 | f 23/21/6 22/22/6 1/23/6 9/24/6 96 | f 19/25/7 23/21/7 9/24/7 2/26/7 97 | f 16/14/8 17/17/8 11/20/8 4/15/8 98 | f 13/27/9 18/9/9 12/12/9 6/28/9 99 | f 21/29/10 24/5/10 10/8/10 8/30/10 100 | f 8/31/11 10/32/11 18/9/11 13/27/11 101 | f 2/26/12 9/24/12 17/17/12 16/14/12 102 | f 9/24/13 1/23/13 14/18/13 17/17/13 103 | f 19/33/4 2/26/4 16/14/4 25/13/4 104 | f 10/32/14 7/34/14 15/10/14 18/9/14 105 | f 22/35/1 5/36/1 15/2/1 26/1/1 106 | f 1/23/1 22/35/1 26/1/1 14/18/1 107 | f 6/37/4 19/33/4 25/13/4 13/38/4 108 | f 4/15/15 11/20/15 24/5/15 21/29/15 109 | f 6/28/16 12/12/16 23/21/16 19/25/16 110 | f 12/12/17 5/11/17 22/22/17 23/21/17 111 | f 13/38/4 25/13/4 21/16/4 8/39/4 112 | f 11/20/18 3/19/18 20/6/18 24/5/18 113 | f 14/18/1 26/1/1 20/4/1 3/19/1 114 | -------------------------------------------------------------------------------- /blackjack_ui/src/app_window.rs: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2023 setzer22 and contributors 2 | // 3 | // This Source Code Form is subject to the terms of the Mozilla Public 4 | // License, v. 2.0. If a copy of the MPL was not distributed with this 5 | // file, You can obtain one at https://mozilla.org/MPL/2.0/. 6 | 7 | use crate::{application::RootViewport, prelude::*}; 8 | use std::time::{Duration, Instant}; 9 | 10 | use winit::{ 11 | dpi::PhysicalSize, 12 | event::{Event, WindowEvent}, 13 | event_loop::EventLoop, 14 | window::Window, 15 | }; 16 | 17 | pub mod gui_overlay; 18 | pub mod input; 19 | 20 | use crate::render_context::RenderContext; 21 | 22 | pub struct AppWindow { 23 | render_ctx: RenderContext, 24 | root_viewport: RootViewport, 25 | window: Window, 26 | } 27 | 28 | impl AppWindow { 29 | pub fn new() -> (Self, EventLoop<()>) { 30 | let event_loop = winit::event_loop::EventLoop::new(); 31 | let window = { 32 | let builder = winit::window::WindowBuilder::new() 33 | .with_title("Blackjack") 34 | .with_inner_size(PhysicalSize::new(800, 600)) 35 | .with_maximized(true); 36 | 37 | builder.build(&event_loop).expect("Could not build window") 38 | }; 39 | 40 | let window_size = window.inner_size(); 41 | let scale_factor = window.scale_factor(); 42 | let render_ctx = RenderContext::new(&window); 43 | let root_viewport = RootViewport::new( 44 | &render_ctx.renderer, 45 | UVec2::new(window_size.width, window_size.height), 46 | scale_factor, 47 | render_ctx.texture_format, 48 | ); 49 | 50 | ( 51 | AppWindow { 52 | window, 53 | render_ctx, 54 | root_viewport, 55 | }, 56 | // Event loop returned separately because we want to keep creating 57 | // &mut references to AppWindow after the event loop starts 58 | event_loop, 59 | ) 60 | } 61 | 62 | fn on_main_events_cleared(&mut self) { 63 | // Record the frame time at the start of the frame. 64 | let frame_start_time = Instant::now(); 65 | 66 | self.root_viewport 67 | .update(&mut self.render_ctx, &self.window); 68 | let platform_output = self.root_viewport.render(&mut self.render_ctx); 69 | self.root_viewport 70 | .handle_platform_output(&self.window, platform_output); 71 | 72 | // Sleep for the remaining time to cap at 60Hz 73 | let elapsed = Instant::now().duration_since(frame_start_time); 74 | //println!("elapsed {:?}", elapsed); 75 | let remaining = Duration::from_secs_f32(1.0 / 60.0).saturating_sub(elapsed); 76 | //println!("remaining {:?}", remaining); 77 | spin_sleep::sleep(remaining); 78 | } 79 | 80 | pub fn run_app(mut self, event_loop: EventLoop<()>) { 81 | self.root_viewport.setup(&mut self.render_ctx); 82 | 83 | event_loop.run(move |event, _, control| { 84 | match event { 85 | Event::WindowEvent { ref event, .. } => { 86 | match event { 87 | // Close requested 88 | WindowEvent::CloseRequested => { 89 | println!("Close requested"); 90 | *control = winit::event_loop::ControlFlow::Exit; 91 | } 92 | 93 | // Resize 94 | WindowEvent::Resized(ref new_size) => { 95 | self.render_ctx.on_resize(new_size.width, new_size.height); 96 | } 97 | 98 | _ => {} 99 | } 100 | } 101 | // Main events cleared 102 | Event::MainEventsCleared => self.on_main_events_cleared(), 103 | _ => {} 104 | } 105 | self.root_viewport.on_winit_event(event); 106 | }); 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /blackjack_ui/src/app_window/gui_overlay.rs: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2023 setzer22 and contributors 2 | // 3 | // This Source Code Form is subject to the terms of the Mozilla Public 4 | // License, v. 2.0. If a copy of the MPL was not distributed with this 5 | // file, You can obtain one at https://mozilla.org/MPL/2.0/. 6 | 7 | use crate::{application::viewport_3d::TextOverlayMode, prelude::*}; 8 | use blackjack_engine::{lua_engine::RenderableThing, prelude::HalfedgeTraversalHelpers}; 9 | use egui::*; 10 | 11 | // Need to divide by the pixels per point to accurately position on the 12 | // screen at given coordinates. 13 | pub fn project_point(view_proj: &Mat4, viewport_rect: Rect, point: Vec3) -> Pos2 { 14 | let size = glam::Vec2::new(viewport_rect.size().x, viewport_rect.size().y); 15 | let offset = glam::Vec2::new(viewport_rect.left_top().x, viewport_rect.left_top().y); 16 | let projected = RenderContext::project_point(view_proj, point, size, offset); 17 | egui::pos2(projected.x, projected.y) 18 | } 19 | 20 | pub fn draw_gui_overlays( 21 | view_proj: &Mat4, 22 | viewport_rect: egui::Rect, 23 | egui_ctx: &egui::Context, 24 | renderable_thing: &RenderableThing, 25 | overlay_type: TextOverlayMode, 26 | ) { 27 | match renderable_thing { 28 | RenderableThing::HalfEdgeMesh(mesh) => { 29 | let painter = egui_ctx.debug_painter(); 30 | 31 | let conn = mesh.read_connectivity(); 32 | let positions = mesh.read_positions(); 33 | 34 | let text = |point: Pos2, text: &str| { 35 | painter.text( 36 | point, 37 | egui::Align2::CENTER_BOTTOM, 38 | text, 39 | egui::FontId::default(), 40 | egui::Color32::WHITE, 41 | ); 42 | }; 43 | 44 | match overlay_type { 45 | TextOverlayMode::NoDraw => {} 46 | TextOverlayMode::MeshInfoVertices 47 | | TextOverlayMode::MeshInfoFaces 48 | | TextOverlayMode::MeshInfoHalfedges 49 | | TextOverlayMode::MeshInfoAll => { 50 | if matches!( 51 | overlay_type, 52 | TextOverlayMode::MeshInfoAll | TextOverlayMode::MeshInfoVertices 53 | ) { 54 | for (i, (v, _)) in conn.iter_vertices().enumerate() { 55 | text( 56 | project_point(view_proj, viewport_rect, positions[v]), 57 | &format!("v{i}"), 58 | ) 59 | } 60 | } 61 | if matches!( 62 | overlay_type, 63 | TextOverlayMode::MeshInfoAll | TextOverlayMode::MeshInfoHalfedges 64 | ) { 65 | for (i, (h, _)) in conn.iter_halfedges().enumerate() { 66 | let (src, dst) = conn.at_halfedge(h).src_dst_pair().unwrap(); 67 | let src_point = positions[src]; 68 | let dst_point = positions[dst]; 69 | let point = src_point * 0.333 + dst_point * 0.666; 70 | text( 71 | project_point(view_proj, viewport_rect, point), 72 | &format!("h{i}"), 73 | ) 74 | } 75 | } 76 | 77 | if matches!( 78 | overlay_type, 79 | TextOverlayMode::MeshInfoAll | TextOverlayMode::MeshInfoFaces 80 | ) { 81 | for (i, (f, _)) in conn.iter_faces().enumerate() { 82 | let point = conn.face_vertex_average(&positions, f); 83 | text( 84 | project_point(view_proj, viewport_rect, point), 85 | &format!("f{i}"), 86 | ) 87 | } 88 | } 89 | } 90 | TextOverlayMode::DevDebug => { 91 | for (&v, mark) in conn.iter_debug_vertices() { 92 | text( 93 | project_point(view_proj, viewport_rect, positions[v]), 94 | &mark.label, 95 | ); 96 | } 97 | 98 | for (&h, mark) in conn.iter_debug_halfedges() { 99 | let (src, dst) = conn.at_halfedge(h).src_dst_pair().unwrap(); 100 | let src_point = positions[src]; 101 | let dst_point = positions[dst]; 102 | let point = src_point * 0.333 + dst_point * 0.666; 103 | text(project_point(view_proj, viewport_rect, point), &mark.label); 104 | } 105 | } 106 | } 107 | } 108 | RenderableThing::HeightMap(_) => { 109 | // TODO @Heightmap 110 | } 111 | } 112 | } 113 | -------------------------------------------------------------------------------- /blackjack_ui/src/application/app_viewport.rs: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2023 setzer22 and contributors 2 | // 3 | // This Source Code Form is subject to the terms of the Mozilla Public 4 | // License, v. 2.0. If a copy of the MPL was not distributed with this 5 | // file, You can obtain one at https://mozilla.org/MPL/2.0/. 6 | 7 | use egui::*; 8 | 9 | /// This application renders several parts of the UI to offscreen textures which 10 | /// are then drawin inside an egui widget. This is the widget that handles this 11 | /// interaction. 12 | /// 13 | /// Offscreen rendering happens with a 1-frame lag. First, egui lays out the 14 | /// viewport widget and a size is computed and stored. Then, at the start of the 15 | /// next frame, the offscreen image is rendered and a `TextureId` is generated. 16 | /// This is then used to draw an `egui::Image` at the location. 17 | pub struct AppViewport { 18 | pub rect: Rect, 19 | pub texture_id: Option, 20 | } 21 | 22 | impl AppViewport { 23 | pub fn new() -> AppViewport { 24 | AppViewport { 25 | // Don't create an empty rect, because this size will be used to 26 | // create a render target and may fail if resolution is zero. 27 | rect: Rect::from_min_size(Pos2::ZERO, vec2(10.0, 10.0)), 28 | texture_id: None, 29 | } 30 | } 31 | 32 | pub fn show(&mut self, ui: &mut Ui, desired_size: Vec2) { 33 | let (_, rect) = ui.allocate_space(desired_size); 34 | self.rect = rect; 35 | if let Some(texture_id) = self.texture_id { 36 | let mut mesh = epaint::Mesh::with_texture(texture_id); 37 | let uv = Rect::from_min_max(pos2(0.0, 0.0), pos2(1.0, 1.0)); 38 | mesh.add_rect_with_uv(rect, uv, Color32::WHITE); 39 | ui.painter().add(Shape::mesh(mesh)); 40 | } else { 41 | ui.painter().rect_filled(rect, 0.0, egui::Color32::RED); 42 | } 43 | } 44 | } 45 | 46 | impl Default for AppViewport { 47 | fn default() -> Self { 48 | Self::new() 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /blackjack_ui/src/application/root_ui.rs: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2023 setzer22 and contributors 2 | // 3 | // This Source Code Form is subject to the terms of the Mozilla Public 4 | // License, v. 2.0. If a copy of the MPL was not distributed with this 5 | // file, You can obtain one at https://mozilla.org/MPL/2.0/. 6 | 7 | use super::*; 8 | use std::path::PathBuf; 9 | 10 | pub enum AppRootAction { 11 | Save(PathBuf), 12 | Load(PathBuf), 13 | } 14 | 15 | impl RootViewport { 16 | pub fn top_menubar(&mut self) -> Option { 17 | let mut action = None; 18 | egui::TopBottomPanel::top("top_menubar").show(&self.egui_context, |ui| { 19 | // When set, will load a new editor state at the end of this function 20 | egui::menu::bar(ui, |ui| { 21 | ui.menu_button("File", |ui| { 22 | ui.add_enabled_ui(false, |ui| ui.button("New")); 23 | if ui.button("Open…").clicked() { 24 | let file_location = rfd::FileDialog::new() 25 | .add_filter("Blackjack Model", &["bjk"]) 26 | .pick_file(); 27 | if let Some(path) = file_location { 28 | action = Some(AppRootAction::Load(path)) 29 | } 30 | } 31 | ui.separator(); 32 | if ui.button("Save As…").clicked() { 33 | let file_location = rfd::FileDialog::new() 34 | .set_file_name("Untitled.bjk") 35 | .add_filter("Blackjack Model", &["bjk"]) 36 | .save_file(); 37 | if let Some(path) = file_location { 38 | action = Some(AppRootAction::Save(path)) 39 | } 40 | } 41 | ui.separator(); 42 | ui.add_enabled_ui(false, |ui| ui.button("Quit")); 43 | }); 44 | ui.menu_button("Window", |ui| { 45 | ui.checkbox(&mut self.diagnostics_open, "Diagnostics"); 46 | }); 47 | }); 48 | }); 49 | 50 | action 51 | } 52 | 53 | pub fn diagnostics_ui(&mut self) { 54 | egui::Window::new("Diagnostics") 55 | .open(&mut self.diagnostics_open) 56 | .show(&self.egui_context, |ui| { 57 | ui.label(format!("HiDPI Scale: {}", ui.ctx().pixels_per_point())); 58 | }); 59 | } 60 | 61 | pub fn show_leaf(ui: &mut egui::Ui, payload: &mut Self, name: &str) { 62 | // TODO: These names here are hard-coded in the creation of the 63 | // SplitTree. We should be using some kind of identifier instead 64 | match name { 65 | "3d_view" => { 66 | if let Err(err) = payload.viewport_3d.show_ui( 67 | ui, 68 | payload 69 | .offscreen_viewports 70 | .get_mut(&OffscreenViewport::Viewport3d) 71 | .unwrap(), 72 | payload.app_context.renderable_thing.as_ref(), 73 | &payload.graph_editor, 74 | &mut payload.app_context.node_gizmo_states, 75 | ) { 76 | // TODO: Do something better for error reporting 77 | println!("Error in viewport: {err}") 78 | } 79 | } 80 | "graph_editor" => { 81 | payload 82 | .offscreen_viewports 83 | .get_mut(&OffscreenViewport::GraphEditor) 84 | .unwrap() 85 | .show(ui, ui.available_size()); 86 | } 87 | "inspector" => payload.inspector_tabs.ui( 88 | ui, 89 | payload.app_context.renderable_thing.as_ref(), 90 | &mut payload.graph_editor.editor_state, 91 | &mut payload.graph_editor.custom_state, 92 | ), 93 | _ => panic!("Invalid split name {name}"), 94 | } 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /blackjack_ui/src/application/viewport_3d/lerp.rs: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2023 setzer22 and contributors 2 | // 3 | // This Source Code Form is subject to the terms of the Mozilla Public 4 | // License, v. 2.0. If a copy of the MPL was not distributed with this 5 | // file, You can obtain one at https://mozilla.org/MPL/2.0/. 6 | 7 | use std::ops::{Add, AddAssign, Mul, Sub, SubAssign}; 8 | 9 | /// A generic lerper. Calling `get`, returns the current value, but calling 10 | /// `set` (or any update methods like AddAssign) update the target value. 11 | /// 12 | /// You need to call the lerp's update function each frame to bring the current 13 | /// value closer to the target. 14 | pub struct Lerp { 15 | current: T, 16 | target: T, 17 | } 18 | 19 | impl Lerp 20 | where 21 | T: Add + Sub + Mul + Copy, 22 | { 23 | pub fn new(v: T) -> Self { 24 | Self { 25 | current: v, 26 | target: v, 27 | } 28 | } 29 | 30 | pub fn set(&mut self, f: impl Fn(T) -> T) { 31 | self.target = f(self.target); 32 | } 33 | 34 | pub fn get(&self) -> T { 35 | self.current 36 | } 37 | 38 | pub fn update(&mut self, delta: f32) { 39 | self.current = self.current + (self.target - self.current) * delta 40 | } 41 | } 42 | 43 | impl AddAssign for Lerp 44 | where 45 | T: AddAssign, 46 | { 47 | fn add_assign(&mut self, rhs: T) { 48 | self.target += rhs; 49 | } 50 | } 51 | 52 | impl SubAssign for Lerp 53 | where 54 | T: SubAssign, 55 | { 56 | fn sub_assign(&mut self, rhs: T) { 57 | self.target -= rhs; 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /blackjack_ui/src/cli_args.rs: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2023 setzer22 and contributors 2 | // 3 | // This Source Code Form is subject to the terms of the Mozilla Public 4 | // License, v. 2.0. If a copy of the MPL was not distributed with this 5 | // file, You can obtain one at https://mozilla.org/MPL/2.0/. 6 | 7 | use clap::Parser; 8 | use once_cell::sync::Lazy; 9 | 10 | #[derive(Parser, Debug)] 11 | #[command(author, version, about, long_about = None)] 12 | pub struct Args { 13 | /// Loads the given `.bjk` file 14 | pub load: Option, 15 | 16 | /// Export mock Lua code annotated with ldoc comments for the blackjack API 17 | /// at the given folder. 18 | #[arg(long)] 19 | pub generate_ldoc: Option, 20 | 21 | /// If this argument is present, the Lua file watcher will not be started 22 | /// and the Lua code will be loaded once at startup. 23 | #[arg(long)] 24 | pub disable_lua_watcher: bool, 25 | } 26 | 27 | /// CLI args are stored in a lazy static variable so they're accessible from 28 | /// everywhere. Arguments are parsed on first access. 29 | pub static CLI_ARGS: Lazy = Lazy::new(Args::parse); 30 | -------------------------------------------------------------------------------- /blackjack_ui/src/color_hex_utils.rs: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2023 setzer22 and contributors 2 | // 3 | // This Source Code Form is subject to the terms of the Mozilla Public 4 | // License, v. 2.0. If a copy of the MPL was not distributed with this 5 | // file, You can obtain one at https://mozilla.org/MPL/2.0/. 6 | 7 | use egui::Color32; 8 | 9 | /// Converts a hex string with a leading '#' into a egui::Color32. 10 | /// - The first three channels are interpreted as R, G, B. 11 | /// - The fourth channel, if present, is used as the alpha value. 12 | /// - Both upper and lowercase characters can be used for the hex values. 13 | /// 14 | /// *Adapted from: https://docs.rs/raster/0.1.0/src/raster/lib.rs.html#425-725. 15 | /// Credit goes to original authors.* 16 | pub fn color_from_hex(hex: &str) -> Result { 17 | // Convert a hex string to decimal. Eg. "00" -> 0. "FF" -> 255. 18 | fn _hex_dec(hex_string: &str) -> Result { 19 | match u8::from_str_radix(hex_string, 16) { 20 | Ok(o) => Ok(o), 21 | Err(e) => Err(format!("Error parsing hex: {e}")), 22 | } 23 | } 24 | 25 | if hex.len() == 9 && hex.starts_with('#') { 26 | // #FFFFFFFF (Red Green Blue Alpha) 27 | return Ok(Color32::from_rgba_premultiplied( 28 | _hex_dec(&hex[1..3])?, 29 | _hex_dec(&hex[3..5])?, 30 | _hex_dec(&hex[5..7])?, 31 | _hex_dec(&hex[7..9])?, 32 | )); 33 | } else if hex.len() == 7 && hex.starts_with('#') { 34 | // #FFFFFF (Red Green Blue) 35 | return Ok(Color32::from_rgb( 36 | _hex_dec(&hex[1..3])?, 37 | _hex_dec(&hex[3..5])?, 38 | _hex_dec(&hex[5..7])?, 39 | )); 40 | } 41 | 42 | Err(format!( 43 | "Error parsing hex: {hex}. Example of valid formats: #FFFFFF or #ffffffff" 44 | )) 45 | } 46 | 47 | /// Converts a Color32 into its canonical hexadecimal representation. 48 | /// - The color string will be preceded by '#'. 49 | /// - If the alpha channel is completely opaque, it will be ommitted. 50 | /// - Characters from 'a' to 'f' will be written in lowercase. 51 | pub fn color_to_hex(color: Color32) -> String { 52 | if color.a() < 255 { 53 | format!( 54 | "#{:02x?}{:02x?}{:02x?}{:02x?}", 55 | color.r(), 56 | color.g(), 57 | color.b(), 58 | color.a() 59 | ) 60 | } else { 61 | format!("#{:02x?}{:02x?}{:02x?}", color.r(), color.g(), color.b()) 62 | } 63 | } 64 | 65 | #[cfg(test)] 66 | mod tests { 67 | use super::*; 68 | 69 | #[test] 70 | pub fn test_color_from_and_to_hex() { 71 | assert_eq!( 72 | color_from_hex("#00ff00").unwrap(), 73 | Color32::from_rgb(0, 255, 0) 74 | ); 75 | assert_eq!( 76 | color_from_hex("#5577AA").unwrap(), 77 | Color32::from_rgb(85, 119, 170) 78 | ); 79 | assert_eq!( 80 | color_from_hex("#E2e2e277").unwrap(), 81 | Color32::from_rgba_premultiplied(226, 226, 226, 119) 82 | ); 83 | assert!(color_from_hex("abcdefgh").is_err()); 84 | 85 | assert_eq!( 86 | color_to_hex(Color32::from_rgb(0, 255, 0)), 87 | "#00ff00".to_string() 88 | ); 89 | assert_eq!( 90 | color_to_hex(Color32::from_rgb(85, 119, 170)), 91 | "#5577aa".to_string() 92 | ); 93 | assert_eq!( 94 | color_to_hex(Color32::from_rgba_premultiplied(226, 226, 226, 119)), 95 | "#e2e2e277".to_string() 96 | ); 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /blackjack_ui/src/custom_widgets.rs: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2023 setzer22 and contributors 2 | // 3 | // This Source Code Form is subject to the terms of the Mozilla Public 4 | // License, v. 2.0. If a copy of the MPL was not distributed with this 5 | // file, You can obtain one at https://mozilla.org/MPL/2.0/. 6 | 7 | /// A better drag value, with support for a range selector 8 | pub mod smart_dragvalue; 9 | -------------------------------------------------------------------------------- /blackjack_ui/src/egui_ext.rs: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2023 setzer22 and contributors 2 | // 3 | // This Source Code Form is subject to the terms of the Mozilla Public 4 | // License, v. 2.0. If a copy of the MPL was not distributed with this 5 | // file, You can obtain one at https://mozilla.org/MPL/2.0/. 6 | 7 | pub trait ToEgui { 8 | fn to_egui(self) -> Out; 9 | } 10 | 11 | impl ToEgui for winit::dpi::PhysicalPosition { 12 | fn to_egui(self) -> egui::Pos2 { 13 | egui::pos2(self.x as f32, self.y as f32) 14 | } 15 | } 16 | 17 | pub trait ToWinit { 18 | fn to_winit(self) -> Out; 19 | } 20 | 21 | impl ToWinit> for egui::Pos2 { 22 | fn to_winit(self) -> winit::dpi::PhysicalPosition { 23 | winit::dpi::PhysicalPosition { 24 | x: self.x.into(), 25 | y: self.y.into(), 26 | } 27 | } 28 | } 29 | 30 | pub trait ColorUtils { 31 | /// Multiplies the color rgb values by `factor`, keeping alpha untouched. 32 | fn lighten(&self, factor: f32) -> Self; 33 | } 34 | 35 | impl ColorUtils for egui::Color32 { 36 | fn lighten(&self, factor: f32) -> Self { 37 | egui::Color32::from_rgba_premultiplied( 38 | (self.r() as f32 * factor) as u8, 39 | (self.g() as f32 * factor) as u8, 40 | (self.b() as f32 * factor) as u8, 41 | self.a(), 42 | ) 43 | } 44 | } 45 | 46 | pub trait RectUtils { 47 | /// Scales the rect by the given `scale` factor relative to the origin at (0,0) 48 | fn scale_from_origin(&self, scale: f32) -> Self; 49 | } 50 | 51 | impl RectUtils for egui::Rect { 52 | fn scale_from_origin(&self, scale: f32) -> Self { 53 | let mut result = *self; 54 | result.min = (self.min.to_vec2() * scale).to_pos2(); 55 | result.max = (self.max.to_vec2() * scale).to_pos2(); 56 | result 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /blackjack_ui/src/graph.rs: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2023 setzer22 and contributors 2 | // 3 | // This Source Code Form is subject to the terms of the Mozilla Public 4 | // License, v. 2.0. If a copy of the MPL was not distributed with this 5 | // file, You can obtain one at https://mozilla.org/MPL/2.0/. 6 | 7 | /// Provides implementations to the traits in `egui_node_graph` specific to blackjack 8 | pub mod node_graph; 9 | 10 | /// Functions to convert graphs from `egui_node_graph` into blacjkack graphs. 11 | pub mod graph_interop; 12 | -------------------------------------------------------------------------------- /blackjack_ui/src/main.rs: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2023 setzer22 and contributors 2 | // 3 | // This Source Code Form is subject to the terms of the Mozilla Public 4 | // License, v. 2.0. If a copy of the MPL was not distributed with this 5 | // file, You can obtain one at https://mozilla.org/MPL/2.0/. 6 | 7 | /// Some useful re-exports. 8 | pub mod prelude; 9 | 10 | /// Extension methods for egui types 11 | pub mod egui_ext; 12 | 13 | /// The application window. This controls the lifecycle of the application: 14 | /// Initialization and main loop. 15 | pub mod app_window; 16 | 17 | pub mod application; 18 | 19 | /// The rendering context. Provides a layer of abstraction over rend3. 20 | pub mod render_context; 21 | 22 | /// A customized rend3 rendergraph for viewport display. 23 | pub mod rendergraph; 24 | 25 | /// The graph editor and compiler 26 | pub mod graph; 27 | 28 | /// Conversion from hexadecimal string to egui colors and vice-versa. 29 | pub mod color_hex_utils; 30 | 31 | /// Custom egui widgets. 32 | pub mod custom_widgets; 33 | 34 | /// Command line argument parsing. 35 | pub mod cli_args; 36 | 37 | fn main() { 38 | #[cfg(feature = "tracy")] 39 | let _client = profiling::tracy_client::Client::start(); 40 | 41 | // Various setup calls 42 | env_logger::init(); 43 | 44 | // Handle luadoc flag 45 | if let Some(ldoc_path) = &cli_args::CLI_ARGS.generate_ldoc { 46 | use blackjack_engine::lua_engine::lua_stdlib::lua_documentation; 47 | lua_documentation::generate_lua_documentation(ldoc_path).unwrap(); 48 | println!("Wrote ldoc sources to {ldoc_path}"); 49 | return; // Do nothing else when generating luadoc 50 | } 51 | 52 | let (app_window, event_loop) = app_window::AppWindow::new(); 53 | app_window.run_app(event_loop); 54 | } 55 | -------------------------------------------------------------------------------- /blackjack_ui/src/prelude.rs: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2023 setzer22 and contributors 2 | // 3 | // This Source Code Form is subject to the terms of the Mozilla Public 4 | // License, v. 2.0. If a copy of the MPL was not distributed with this 5 | // file, You can obtain one at https://mozilla.org/MPL/2.0/. 6 | 7 | pub use anyhow::{anyhow, bail, Context, Result}; 8 | 9 | pub use crate::color_hex_utils::color_from_hex; 10 | pub use glam::{Mat4, Quat, UVec2, UVec3, Vec2, Vec3, Vec4}; 11 | 12 | pub mod r3 { 13 | pub use rend3::{ 14 | graph::{ 15 | DataHandle, DepthHandle, ReadyData, RenderGraph, RenderPassDepthTarget, 16 | RenderPassHandle, RenderPassTarget, RenderPassTargets, RenderTargetDescriptor, 17 | RenderTargetHandle, 18 | }, 19 | types::{ 20 | DirectionalLight, DirectionalLightHandle, Handedness, Material, MaterialHandle, Mesh, 21 | MeshBuilder, MeshHandle, Object, ObjectHandle, ObjectMeshKind, SampleCount, 22 | TextureFormat, TextureUsages, 23 | }, 24 | Renderer, RendererDataCore, 25 | }; 26 | 27 | pub use rend3_routine::base::{BaseRenderGraph, BaseRenderGraphIntermediateState}; 28 | pub use rend3_routine::common::PerMaterialArchetypeInterface; 29 | pub use rend3_routine::culling::PerMaterialArchetypeData; 30 | pub use rend3_routine::pbr::{AlbedoComponent, PbrMaterial, PbrRoutine, TransparencyType}; 31 | pub use rend3_routine::tonemapping::TonemappingRoutine; 32 | pub use rend3_routine::{depth::DepthRoutine, forward::ForwardRoutine}; 33 | } 34 | 35 | pub use itertools::Itertools; 36 | pub use std::collections::{HashMap, HashSet}; 37 | 38 | pub use crate::render_context::RenderContext; 39 | 40 | pub use crate::egui_ext::*; 41 | pub use blackjack_commons::math::*; 42 | pub use blackjack_commons::utils::*; 43 | 44 | pub mod graph { 45 | pub use crate::graph::node_graph::*; 46 | pub use egui_node_graph::{InputId, Node, NodeId, OutputId}; 47 | } 48 | -------------------------------------------------------------------------------- /blackjack_ui/src/render_context.rs: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2023 setzer22 and contributors 2 | // 3 | // This Source Code Form is subject to the terms of the Mozilla Public 4 | // License, v. 2.0. If a copy of the MPL was not distributed with this 5 | // file, You can obtain one at https://mozilla.org/MPL/2.0/. 6 | 7 | use std::sync::Arc; 8 | 9 | use crate::{ 10 | prelude::*, 11 | rendergraph::{ 12 | face_routine::FaceRoutine, grid_routine::GridRoutine, id_picking_routine::IdPickingRoutine, 13 | point_cloud_routine::PointCloudRoutine, shader_manager::ShaderManager, 14 | wireframe_routine::WireframeRoutine, 15 | }, 16 | }; 17 | 18 | use glam::Mat4; 19 | use rend3_routine::pbr::PbrRoutine; 20 | use wgpu::{Adapter, Surface, TextureFormat}; 21 | 22 | fn get_present_mode(surface: &Surface, adapter: &Adapter) -> rend3::types::PresentMode { 23 | let modes = surface.get_supported_modes(adapter); 24 | if modes.contains(&wgpu::PresentMode::Mailbox) { 25 | rend3::types::PresentMode::Mailbox 26 | } else { 27 | rend3::types::PresentMode::AutoVsync 28 | } 29 | } 30 | 31 | pub struct RenderContext { 32 | pub renderer: Arc, 33 | 34 | pub base_graph: r3::BaseRenderGraph, 35 | pub pbr_routine: r3::PbrRoutine, 36 | pub tonemapping_routine: r3::TonemappingRoutine, 37 | pub grid_routine: GridRoutine, 38 | pub wireframe_routine: WireframeRoutine, 39 | pub face_routine: FaceRoutine, 40 | pub point_cloud_routine: PointCloudRoutine, 41 | pub id_picking_routine: IdPickingRoutine, 42 | pub surface: Arc, 43 | pub adapter: Arc, 44 | pub texture_format: TextureFormat, 45 | pub shader_manager: ShaderManager, 46 | 47 | pub objects: Vec, 48 | lights: Vec, 49 | } 50 | 51 | impl RenderContext { 52 | pub fn new(window: &winit::window::Window) -> Self { 53 | let window_size = window.inner_size(); 54 | let iad = pollster::block_on(rend3::create_iad( 55 | None, 56 | None, 57 | Some(rend3::RendererProfile::CpuDriven), 58 | None, 59 | )) 60 | .unwrap(); 61 | 62 | let surface = Arc::new(unsafe { iad.instance.create_surface(&window) }); 63 | let adapter = iad.adapter.clone(); 64 | 65 | let format = surface.get_supported_formats(&iad.adapter)[0]; 66 | rend3::configure_surface( 67 | &surface, 68 | &iad.device, 69 | format, 70 | glam::UVec2::new(window_size.width, window_size.height), 71 | get_present_mode(&surface, &adapter), 72 | ); 73 | 74 | let renderer = r3::Renderer::new( 75 | iad, 76 | r3::Handedness::Left, 77 | Some(window_size.width as f32 / window_size.height as f32), 78 | ) 79 | .unwrap(); 80 | 81 | let mut spp = rend3::ShaderPreProcessor::new(); 82 | rend3_routine::builtin_shaders(&mut spp); 83 | 84 | let base_graph = r3::BaseRenderGraph::new(&renderer, &spp); 85 | let mut data_core = renderer.data_core.lock(); 86 | let pbr_routine = PbrRoutine::new(&renderer, &mut data_core, &spp, &base_graph.interfaces); 87 | let tonemapping_routine = 88 | r3::TonemappingRoutine::new(&renderer, &spp, &base_graph.interfaces, format); 89 | drop(data_core); // Release the lock 90 | 91 | let shader_manager = ShaderManager::new(&renderer.device); 92 | let grid_routine = GridRoutine::new(&renderer.device); 93 | let wireframe_routine = 94 | WireframeRoutine::new(&renderer.device, &base_graph, &shader_manager); 95 | let point_cloud_routine = 96 | PointCloudRoutine::new(&renderer.device, &base_graph, &shader_manager); 97 | let face_routine = FaceRoutine::new(&renderer, &base_graph, &shader_manager); 98 | let id_picking_routine = IdPickingRoutine::new(&renderer.device); 99 | 100 | RenderContext { 101 | renderer, 102 | pbr_routine, 103 | base_graph, 104 | tonemapping_routine, 105 | grid_routine, 106 | wireframe_routine, 107 | point_cloud_routine, 108 | face_routine, 109 | id_picking_routine, 110 | surface, 111 | adapter, 112 | texture_format: format, 113 | shader_manager, 114 | objects: vec![], 115 | lights: vec![], 116 | } 117 | } 118 | 119 | pub fn clear_objects(&mut self) { 120 | self.objects.clear(); 121 | self.point_cloud_routine.clear(); 122 | self.wireframe_routine.clear(); 123 | self.face_routine.clear(); 124 | } 125 | 126 | pub fn add_mesh_as_object(&mut self, mesh: r3::Mesh, material: Option) { 127 | let mesh_handle = self.renderer.add_mesh(mesh); 128 | let material_handle = if let Some(material) = material { 129 | self.renderer.add_material(material) 130 | } else { 131 | let material = r3::PbrMaterial { 132 | albedo: r3::AlbedoComponent::Value(glam::Vec4::new(0.8, 0.1, 0.1, 1.0)), 133 | ..Default::default() 134 | }; 135 | self.renderer.add_material(material) 136 | }; 137 | let object = r3::Object { 138 | mesh_kind: r3::ObjectMeshKind::Static(mesh_handle), 139 | material: material_handle, 140 | transform: glam::Mat4::IDENTITY, 141 | }; 142 | self.objects.push(self.renderer.add_object(object)); 143 | } 144 | 145 | pub fn add_object(&mut self, object: r3::Object) { 146 | self.objects.push(self.renderer.add_object(object)); 147 | } 148 | 149 | pub fn set_camera(&mut self, view_matrix: Mat4, vfov: f32) { 150 | self.renderer.set_camera_data(rend3::types::Camera { 151 | projection: rend3::types::CameraProjection::Perspective { vfov, near: 0.01 }, 152 | view: view_matrix, 153 | }); 154 | } 155 | 156 | pub fn project_point( 157 | view_proj: &Mat4, 158 | point: Vec3, 159 | viewport_size: Vec2, 160 | viewport_offset: Vec2, 161 | ) -> Vec2 { 162 | let clip = view_proj.project_point3(point); 163 | let clip = Vec2::new(clip.x, -clip.y); 164 | let zero_to_one = (Vec2::new(clip.x, clip.y) + Vec2::ONE) * 0.5; 165 | zero_to_one * viewport_size + viewport_offset 166 | } 167 | 168 | pub fn add_light(&mut self, light: r3::DirectionalLight) { 169 | let handle = self.renderer.add_directional_light(light); 170 | self.lights.push(handle); 171 | } 172 | 173 | pub fn on_resize(&mut self, width: u32, height: u32) { 174 | rend3::configure_surface( 175 | &self.surface, 176 | &self.renderer.device, 177 | self.texture_format, 178 | glam::uvec2(width, height), 179 | get_present_mode(&self.surface, &self.adapter), 180 | ); 181 | } 182 | } 183 | -------------------------------------------------------------------------------- /blackjack_ui/src/rendergraph.rs: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2023 setzer22 and contributors 2 | // 3 | // This Source Code Form is subject to the terms of the Mozilla Public 4 | // License, v. 2.0. If a copy of the MPL was not distributed with this 5 | // file, You can obtain one at https://mozilla.org/MPL/2.0/. 6 | 7 | use crate::{ 8 | application::{viewport_3d::Viewport3dSettings, ViewportRoutines}, 9 | prelude::*, 10 | }; 11 | 12 | pub mod grid_routine; 13 | 14 | /// Some common definitions to abstract wgpu boilerplate 15 | pub mod common; 16 | 17 | /// The common bits in all the 3d viewport routines 18 | pub mod viewport_3d_routine; 19 | 20 | /// A render routine to draw wireframe meshes 21 | pub mod wireframe_routine; 22 | 23 | /// A render routine to draw point clouds 24 | pub mod point_cloud_routine; 25 | 26 | /// A render routine to draw meshes 27 | pub mod face_routine; 28 | 29 | /// A routine to implement object picking, by reading the id_map buffer. 30 | pub mod id_picking_routine; 31 | 32 | /// Shader manager struct which sets up loading with a basic preprocessor 33 | pub mod shader_manager; 34 | 35 | /// Adds the necessary nodes to render the 3d viewport of the app. The viewport 36 | /// is rendered into a render target, and its handle is returned. 37 | #[allow(clippy::too_many_arguments)] 38 | pub fn blackjack_viewport_rendergraph<'node>( 39 | graph: &mut r3::RenderGraph<'node>, 40 | ready: &r3::ReadyData, 41 | routines: ViewportRoutines<'node>, 42 | resolution: UVec2, 43 | samples: r3::SampleCount, 44 | ambient: Vec4, 45 | settings: &'node Viewport3dSettings, 46 | ) -> r3::RenderTargetHandle { 47 | // Create intermediate storage 48 | let state = r3::BaseRenderGraphIntermediateState::new(graph, ready, resolution, samples); 49 | 50 | state.clear(graph, Vec4::new(0.027851, 0.027851, 0.027851, 1.0)); 51 | 52 | // Preparing and uploading data 53 | state.pbr_pre_culling(graph); 54 | state.create_frame_uniforms(graph, routines.base_graph, ambient, resolution); 55 | 56 | // Culling 57 | state.pbr_shadow_culling(graph, routines.base_graph, routines.pbr); 58 | state.pbr_culling(graph, routines.base_graph, routines.pbr); 59 | 60 | // Depth-only rendering 61 | state.pbr_prepass_rendering(graph, routines.pbr, samples); 62 | 63 | // Forward rendering 64 | state.pbr_forward_rendering(graph, routines.pbr, samples); 65 | 66 | let id_map = graph.add_render_target(r3::RenderTargetDescriptor { 67 | label: None, 68 | resolution, 69 | samples: r3::SampleCount::One, 70 | format: r3::TextureFormat::R32Uint, // Should match one in shader manager 71 | usage: r3::TextureUsages::RENDER_ATTACHMENT 72 | | r3::TextureUsages::TEXTURE_BINDING 73 | | r3::TextureUsages::COPY_SRC, 74 | }); 75 | 76 | use crate::application::viewport_3d::EdgeDrawMode::*; 77 | if matches!(settings.edge_mode, FullEdge | HalfEdge) { 78 | routines.wireframe.add_to_graph(graph, &state); 79 | } 80 | if settings.render_vertices { 81 | routines.point_cloud.add_to_graph(graph, &state); 82 | } 83 | use crate::application::viewport_3d::FaceDrawMode::*; 84 | if matches!(settings.face_mode, Flat | Smooth | Real) { 85 | routines.face.add_to_graph(graph, &state, id_map, settings); 86 | } 87 | 88 | routines.id_picking.add_to_graph(graph, resolution, id_map); 89 | 90 | routines.grid.add_to_graph(graph, &state); 91 | 92 | // Make the reference to the surface 93 | let output = graph.add_render_target(r3::RenderTargetDescriptor { 94 | label: Some("Blackjack Viewport Output".into()), 95 | resolution, 96 | samples, 97 | format: r3::TextureFormat::Bgra8UnormSrgb, 98 | usage: r3::TextureUsages::RENDER_ATTACHMENT | r3::TextureUsages::TEXTURE_BINDING, 99 | }); 100 | state.tonemapping(graph, routines.tonemapping, output); 101 | 102 | output 103 | } 104 | -------------------------------------------------------------------------------- /blackjack_ui/src/rendergraph/common.rs: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2023 setzer22 and contributors 2 | // 3 | // This Source Code Form is subject to the terms of the Mozilla Public 4 | // License, v. 2.0. If a copy of the MPL was not distributed with this 5 | // file, You can obtain one at https://mozilla.org/MPL/2.0/. 6 | 7 | pub fn primitive_state( 8 | topology: wgpu::PrimitiveTopology, 9 | front_face: wgpu::FrontFace, 10 | ) -> wgpu::PrimitiveState { 11 | wgpu::PrimitiveState { 12 | topology, 13 | strip_index_format: None, 14 | front_face, 15 | cull_mode: Some(wgpu::Face::Back), 16 | unclipped_depth: false, 17 | polygon_mode: wgpu::PolygonMode::Fill, 18 | conservative: false, 19 | } 20 | } 21 | 22 | pub fn depth_stencil(depth_write: bool) -> wgpu::DepthStencilState { 23 | wgpu::DepthStencilState { 24 | format: wgpu::TextureFormat::Depth32Float, 25 | depth_write_enabled: depth_write, 26 | depth_compare: wgpu::CompareFunction::GreaterEqual, 27 | stencil: wgpu::StencilState::default(), 28 | bias: wgpu::DepthBiasState::default(), 29 | } 30 | } 31 | 32 | pub const DEFAULT_COLOR_FORMAT: wgpu::TextureFormat = wgpu::TextureFormat::Rgba16Float; 33 | -------------------------------------------------------------------------------- /blackjack_ui/src/rendergraph/edge_wireframe_draw.wgsl: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | struct VertexOutput { 5 | @builtin(position) clip_position: vec4, 6 | @location(0) color: vec3, 7 | }; 8 | 9 | struct FragmentOutput { 10 | @builtin(frag_depth) depth: f32, 11 | @location(0) color: vec4, 12 | }; 13 | 14 | @group(1) @binding(0) 15 | var lines: Vec3Array; 16 | 17 | @group(1) @binding(1) 18 | var colors: Vec3Array; 19 | 20 | @vertex 21 | fn vs_main( 22 | @builtin(instance_index) instance_idx: u32, 23 | @builtin(vertex_index) vertex_idx: u32, 24 | ) -> VertexOutput { 25 | var current_point = unpack_v3(lines.inner[instance_idx * 2u + vertex_idx]); 26 | var color = unpack_v3(colors.inner[instance_idx]); 27 | 28 | var output : VertexOutput; 29 | output.clip_position = uniforms.view_proj * vec4(current_point, 1.0); 30 | output.color = color; 31 | return output; 32 | } 33 | 34 | @fragment 35 | fn fs_main(input: VertexOutput) -> FragmentOutput { 36 | var out : FragmentOutput; 37 | out.color = vec4(input.color, 1.0); 38 | // We want edges slightly over their actual positions towards the camera. 39 | // This prevents z-fighting when drawing the wireframe over the mesh. 40 | out.depth = input.clip_position.z * 1.01; 41 | return out; 42 | } 43 | -------------------------------------------------------------------------------- /blackjack_ui/src/rendergraph/face_draw.wgsl: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | struct VertexOutput { 5 | @builtin(position) clip_position: vec4, 6 | @location(1) normal: vec3, 7 | }; 8 | 9 | struct FragmentOutput { 10 | @location(0) color: vec4, 11 | }; 12 | 13 | @group(1) @binding(0) 14 | var positions: Vec3Array; 15 | @group(1) @binding(1) 16 | var normals: Vec3Array; 17 | @group(1) @binding(2) 18 | var matcap: texture_2d; 19 | 20 | @vertex 21 | fn vs_main( 22 | @builtin(vertex_index) vertex_idx: u32, 23 | ) -> VertexOutput { 24 | let position = unpack_v3(positions.inner[vertex_idx]); 25 | let normal = unpack_v3(normals.inner[vertex_idx]); 26 | 27 | var output : VertexOutput; 28 | output.clip_position = uniforms.view_proj * vec4(position, 1.0); 29 | output.normal = normalize(normal); 30 | return output; 31 | } 32 | 33 | @fragment 34 | fn fs_main(input: VertexOutput) -> FragmentOutput { 35 | var out : FragmentOutput; 36 | 37 | let muv = (uniforms.view * vec4(normalize(input.normal), 0.0)).xy; 38 | let muv = muv * 0.5 + vec2(0.5, 0.5); 39 | 40 | out.color = textureSample(matcap, primary_sampler, vec2(muv.x, 1.0 - muv.y)); 41 | 42 | return out; 43 | } 44 | -------------------------------------------------------------------------------- /blackjack_ui/src/rendergraph/face_overlay_draw.wgsl: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | struct VertexOutput { 5 | @builtin(position) clip_position: vec4, 6 | @location(0) color: vec4, 7 | @location(1) @interpolate(flat) id: u32, 8 | }; 9 | 10 | struct FragmentOutput { 11 | @location(0) color: vec4, 12 | @location(1) id: u32, 13 | }; 14 | 15 | @group(1) @binding(0) 16 | var positions: Vec3Array; 17 | @group(1) @binding(1) 18 | var colors: ColorArray; 19 | @group(1) @binding(2) 20 | var ids: U32Array; 21 | @group(1) @binding(3) 22 | var max_id: u32; 23 | 24 | @vertex 25 | fn vs_main( 26 | @builtin(instance_index) instance_idx: u32, 27 | @builtin(vertex_index) vertex_idx: u32, 28 | ) -> VertexOutput { 29 | let position = unpack_v3(positions.inner[instance_idx * 3u + vertex_idx]); 30 | let color = colors.inner[instance_idx]; 31 | let id = ids.inner[instance_idx]; 32 | 33 | var output : VertexOutput; 34 | output.clip_position = uniforms.view_proj * vec4(position, 1.0); 35 | output.color = color; 36 | output.id = id; 37 | return output; 38 | } 39 | 40 | @fragment 41 | fn fs_main(input: VertexOutput) -> FragmentOutput { 42 | var out : FragmentOutput; 43 | out.color = input.color; 44 | out.id = input.id; 45 | 46 | // Debug: Use random colors for each id 47 | /*let t = f32(input.id + 1u) / f32(max_id); 48 | let color = vec3( 49 | random(t + 2.1923810), 50 | random(t + 4.2123190), 51 | random(t + 3.5132098), 52 | ); 53 | out.color = vec4(color, 1.0); 54 | */ 55 | 56 | return out; 57 | } 58 | -------------------------------------------------------------------------------- /blackjack_ui/src/rendergraph/grid_shader.wgsl: -------------------------------------------------------------------------------- 1 | // Vertex shader 2 | 3 | struct VertexOutput { 4 | @builtin(position) clip_position: vec4, 5 | @location(0) near_point: vec3, 6 | @location(1) far_point: vec3, 7 | }; 8 | 9 | struct FragmentOutput { 10 | @builtin(frag_depth) depth: f32, 11 | @location(0) color: vec4, 12 | }; 13 | 14 | struct GridRoutineUniform { 15 | view: mat4x4, 16 | proj: mat4x4, 17 | inv_view: mat4x4, 18 | inv_proj: mat4x4, 19 | }; 20 | 21 | @group(0) @binding(0) 22 | var matrices: GridRoutineUniform; 23 | 24 | var vertices: array, 6> = array, 6>( 25 | vec2(-1.0, 1.0), 26 | vec2(-1.0, -1.0), 27 | vec2(1.0, 1.0), 28 | vec2(1.0, -1.0), 29 | vec2(1.0, 1.0), 30 | vec2(-1.0, -1.0), 31 | ); 32 | 33 | fn unproject_point(projected_point: vec3, inv_view: mat4x4, inv_proj: mat4x4) -> vec3 { 34 | let unprojected_point = inv_view * inv_proj * vec4(projected_point, 1.0); 35 | return unprojected_point.xyz / unprojected_point.w; 36 | } 37 | 38 | @vertex 39 | fn vs_main( 40 | @builtin(vertex_index) in_vertex_index: u32, 41 | ) -> VertexOutput { 42 | var out: VertexOutput; 43 | 44 | let pos_xy = vertices[in_vertex_index]; 45 | let pos = vec4(pos_xy.x, pos_xy.y, 0.0, 1.0); 46 | 47 | out.clip_position = pos; 48 | // TODO: Compute near_point / far_point 49 | out.near_point = unproject_point(vec3(pos.x, pos.y, 0.1), matrices.inv_view, matrices.inv_proj).xyz; 50 | out.far_point = unproject_point(vec3(pos.x, pos.y, 1.0), matrices.inv_view, matrices.inv_proj).xyz; 51 | 52 | return out; 53 | } 54 | 55 | // Fragment shader 56 | 57 | fn grid(frag_pos_3d: vec3, scale: f32) -> vec4 { 58 | let coord = frag_pos_3d.xz * scale; // use the scale variable to set the distance between the lines 59 | let derivative = fwidth(coord); 60 | let grid = abs(fract(coord - 0.5) - 0.5) / derivative; 61 | let grid_line = min(grid.x, grid.y); 62 | let minimumz = min(derivative.y, 1.0); 63 | let minimumx = min(derivative.x, 1.0); 64 | var color = vec4(0.2, 0.2, 0.2, 1.0 - min(grid_line, 1.0)); 65 | 66 | let threshold = 1.0 / scale; 67 | 68 | // z axis 69 | if (frag_pos_3d.x > -threshold * minimumx && frag_pos_3d.x < threshold * minimumx) { 70 | color.z = 1.0; 71 | } 72 | // x axis 73 | if (frag_pos_3d.z > -threshold * minimumz && frag_pos_3d.z < threshold * minimumz) { 74 | color.x = 1.0; 75 | } 76 | return color; 77 | } 78 | 79 | fn compute_depth(frag_pos_3d: vec3) -> f32 { 80 | let clip_space_pos = matrices.proj * matrices.view * vec4(frag_pos_3d, 1.0); 81 | return (clip_space_pos.z / clip_space_pos.w); 82 | } 83 | 84 | fn fading(frag_pos_3d: vec3, depth: f32) -> f32 { 85 | let znear = 0.001; 86 | // If you're using far plane at infinity as described here, then linearized depth is simply znear / depth. 87 | // From: https://www.reddit.com/r/GraphicsProgramming/comments/f9zwin/linearising_reverse_depth_buffer/ 88 | let linear_depth = znear / depth; 89 | return max(0.0, 2.5 - linear_depth); 90 | } 91 | 92 | @fragment 93 | fn fs_main(in: VertexOutput) -> FragmentOutput { 94 | let t = -in.near_point.y / (in.far_point.y - in.near_point.y); 95 | let frag_pos_3d = in.near_point + t * (in.far_point - in.near_point); 96 | 97 | let depth = compute_depth(frag_pos_3d); 98 | 99 | var out: FragmentOutput; 100 | out.color = grid(frag_pos_3d, 2.0) * f32(t < 0.0); 101 | out.depth = depth; 102 | out.color.a = out.color.a * fading(frag_pos_3d, depth); 103 | 104 | return out; 105 | } 106 | -------------------------------------------------------------------------------- /blackjack_ui/src/rendergraph/point_cloud_draw.wgsl: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | struct VertexOutput { 5 | @builtin(position) clip_position: vec4, 6 | }; 7 | 8 | struct FragmentOutput { 9 | @builtin(frag_depth) depth: f32, 10 | @location(0) color: vec4, 11 | }; 12 | 13 | @group(1) @binding(0) 14 | var point_cloud: Vec3Array; 15 | 16 | var screen_quad: array, 6> = array, 6>( 17 | vec2(0.0, 1.0), 18 | vec2(-1.0, 0.0), 19 | vec2(1.0, 0.0), 20 | vec2(-1.0, 0.0), 21 | vec2(0.0, -1.0), 22 | vec2(1.0, 0.0), 23 | ); 24 | 25 | @vertex 26 | fn vs_main( 27 | @builtin(instance_index) instance_idx: u32, 28 | @builtin(vertex_index) vertex_idx: u32, 29 | ) -> VertexOutput { 30 | // Get the current point 31 | let current_point = unpack_v3(point_cloud.inner[instance_idx]); 32 | 33 | 34 | // Compute its clip space position 35 | let point_clip = uniforms.view_proj * vec4(current_point, 1.0); 36 | 37 | // Get the offset for the current vertex in the quad 38 | let screen_quad_vertex = screen_quad[vertex_idx]; 39 | let pixel_size = vec2(1.0 / f32(uniforms.resolution.x), 1.0 / f32(uniforms.resolution.y)); 40 | let point_size = pixel_size * 5.0; 41 | let vertex_offset = screen_quad_vertex * point_size; 42 | 43 | // The final position is the clip space position for the point, plus the 44 | // screen-space quad offset. 45 | let clip_position = (point_clip / point_clip.w) + vec4(vertex_offset, 0.0, 0.0);; 46 | 47 | var output : VertexOutput; 48 | output.clip_position = clip_position; 49 | return output; 50 | } 51 | 52 | @fragment 53 | fn fs_main(input: VertexOutput) -> FragmentOutput { 54 | var out : FragmentOutput; 55 | out.color = vec4(0.2, 0.8, 0.2, 1.0); 56 | // We want vertices slightly over their actual positions towards the camera. 57 | // This prevents z-fighting when drawing the wireframe over the mesh. 58 | // Value is 1.02, which is slightly above the 1.01 used for edges 59 | out.depth = input.clip_position.z * 1.02; 60 | return out; 61 | } 62 | -------------------------------------------------------------------------------- /blackjack_ui/src/rendergraph/point_cloud_routine.rs: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2023 setzer22 and contributors 2 | // 3 | // This Source Code Form is subject to the terms of the Mozilla Public 4 | // License, v. 2.0. If a copy of the MPL was not distributed with this 5 | // file, You can obtain one at https://mozilla.org/MPL/2.0/. 6 | 7 | use crate::prelude::r3; 8 | use glam::Vec3; 9 | 10 | use rend3::managers::TextureManager; 11 | use rend3_routine::base::{BaseRenderGraph, BaseRenderGraphIntermediateState}; 12 | use wgpu::{ 13 | util::{BufferInitDescriptor, DeviceExt}, 14 | *, 15 | }; 16 | 17 | use super::{ 18 | shader_manager::ShaderManager, 19 | viewport_3d_routine::{DrawType, RoutineLayout, Viewport3dRoutine}, 20 | }; 21 | 22 | pub struct PointCloudLayout { 23 | buffer: Buffer, 24 | len: usize, 25 | } 26 | 27 | const NUM_BUFFERS: usize = 1; 28 | 29 | impl RoutineLayout for PointCloudLayout { 30 | type Settings = (); 31 | fn get_wgpu_buffers(&self, _settings: &()) -> [&Buffer; NUM_BUFFERS] { 32 | [&self.buffer] 33 | } 34 | 35 | fn get_wgpu_textures<'a>( 36 | &'a self, 37 | _texture_manager: &'a TextureManager, 38 | _settings: &(), 39 | ) -> [&'a TextureView; 0] { 40 | [] 41 | } 42 | 43 | fn get_wgpu_uniforms(&self, _settings: &Self::Settings) -> [&Buffer; 0] { 44 | [] 45 | } 46 | 47 | fn get_draw_type(&self, _settings: &Self::Settings) -> DrawType<'_> { 48 | DrawType::UseInstances { 49 | num_vertices: 6, 50 | num_instances: self.len, 51 | } 52 | } 53 | } 54 | 55 | pub struct PointCloudRoutine { 56 | inner: Viewport3dRoutine, 57 | } 58 | 59 | impl PointCloudRoutine { 60 | pub fn new(device: &Device, base: &BaseRenderGraph, shader_manager: &ShaderManager) -> Self { 61 | Self { 62 | inner: Viewport3dRoutine::new( 63 | "point cloud", 64 | device, 65 | base, 66 | shader_manager.get("point_cloud_draw"), 67 | PrimitiveTopology::TriangleList, 68 | FrontFace::Ccw, 69 | ), 70 | } 71 | } 72 | 73 | pub fn add_point_cloud(&mut self, device: &Device, points: &[Vec3]) { 74 | let buffer = device.create_buffer_init(&BufferInitDescriptor { 75 | label: None, 76 | contents: bytemuck::cast_slice(points), 77 | usage: BufferUsages::STORAGE, 78 | }); 79 | self.inner.layouts.push(PointCloudLayout { 80 | buffer, 81 | len: points.len(), 82 | }); 83 | } 84 | 85 | pub fn clear(&mut self) { 86 | self.inner.clear() 87 | } 88 | 89 | pub fn add_to_graph<'node>( 90 | &'node self, 91 | graph: &mut r3::RenderGraph<'node>, 92 | state: &BaseRenderGraphIntermediateState, 93 | ) { 94 | self.inner.add_to_graph(graph, state, &(), &[]); 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /blackjack_ui/src/rendergraph/rend3_common.wgsl: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | -------------------------------------------------------------------------------- /blackjack_ui/src/rendergraph/rend3_object.wgsl: -------------------------------------------------------------------------------- 1 | 2 | struct ObjectData { 3 | model_view: mat4x4, 4 | model_view_proj: mat4x4, 5 | // NOTE: This is unused in GPU mode 6 | material_idx: u32, 7 | inv_squared_scale: vec3, 8 | }; 9 | 10 | 11 | struct ObjectDataArray { 12 | objects: array, 13 | }; 14 | 15 | 16 | @group(1) @binding(0) 17 | var object_data: ObjectDataArray; 18 | -------------------------------------------------------------------------------- /blackjack_ui/src/rendergraph/rend3_uniforms.wgsl: -------------------------------------------------------------------------------- 1 | struct Plane { 2 | inner: vec4, 3 | }; 4 | 5 | struct Frustrum { 6 | left: Plane, 7 | right: Plane, 8 | top: Plane, 9 | bottom: Plane, 10 | // No far plane 11 | near: Plane, 12 | }; 13 | 14 | struct UniformData { 15 | view: mat4x4, 16 | view_proj: mat4x4, 17 | origin_view_proj: mat4x4, 18 | inv_view: mat4x4, 19 | inv_view_proj: mat4x4, 20 | inv_origin_view_proj: mat4x4, 21 | frustrum: Frustrum, 22 | ambient: vec4, 23 | resolution: vec2, 24 | }; 25 | 26 | @group(0) @binding(0) 27 | var primary_sampler: sampler; 28 | 29 | @group(0) @binding(3) 30 | var uniforms: UniformData; 31 | -------------------------------------------------------------------------------- /blackjack_ui/src/rendergraph/rend3_vertex.wgsl: -------------------------------------------------------------------------------- 1 | struct VertexInput { 2 | @location(0) position: vec3, 3 | @location(1) normal: vec3, 4 | @location(2) tangent: vec3, 5 | @location(3) uv_0: vec2, 6 | @location(4) uv_1: vec2, 7 | @location(5) color: vec4, 8 | }; 9 | -------------------------------------------------------------------------------- /blackjack_ui/src/rendergraph/shader_manager.rs: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2023 setzer22 and contributors 2 | // 3 | // This Source Code Form is subject to the terms of the Mozilla Public 4 | // License, v. 2.0. If a copy of the MPL was not distributed with this 5 | // file, You can obtain one at https://mozilla.org/MPL/2.0/. 6 | 7 | use std::collections::HashMap; 8 | 9 | use wgpu::{ 10 | BlendState, ColorTargetState, ColorWrites, FragmentState, TextureFormat, VertexBufferLayout, 11 | VertexState, 12 | }; 13 | 14 | #[derive(Clone, Debug)] 15 | pub enum ShaderColorTarget { 16 | // The shader will write to the main viewport texture 17 | Viewport { use_alpha: bool }, 18 | // The shader will write to an offscreen buffer with custom layout 19 | Offscreen(ColorTargetState), 20 | } 21 | 22 | pub struct Shader { 23 | pub fs_entry_point: String, 24 | pub vs_entry_point: String, 25 | pub module: wgpu::ShaderModule, 26 | pub color_target_descrs: Vec, 27 | pub color_targets: Vec>, 28 | } 29 | 30 | impl ShaderColorTarget { 31 | pub fn into_wgpu(&self) -> ColorTargetState { 32 | match self { 33 | ShaderColorTarget::Viewport { use_alpha } => ColorTargetState { 34 | format: TextureFormat::Rgba16Float, 35 | blend: use_alpha.then(|| BlendState::ALPHA_BLENDING), 36 | write_mask: ColorWrites::ALL, 37 | }, 38 | ShaderColorTarget::Offscreen(c) => c.clone(), 39 | } 40 | } 41 | } 42 | 43 | impl Shader { 44 | pub fn new( 45 | fs_entry_point: impl ToString, 46 | vs_entry_point: impl ToString, 47 | module: wgpu::ShaderModule, 48 | color_target_descrs: Vec, 49 | ) -> Self { 50 | let color_targets = color_target_descrs 51 | .iter() 52 | .map(|d| Some(d.into_wgpu())) 53 | .collect(); 54 | Self { 55 | fs_entry_point: fs_entry_point.to_string(), 56 | vs_entry_point: vs_entry_point.to_string(), 57 | module, 58 | color_target_descrs, 59 | color_targets, 60 | } 61 | } 62 | 63 | pub fn to_vertex_state<'a>(&'a self, buffers: &'a [VertexBufferLayout]) -> VertexState { 64 | VertexState { 65 | module: &self.module, 66 | entry_point: &self.vs_entry_point, 67 | buffers, 68 | } 69 | } 70 | 71 | pub fn get_fragment_state(&self) -> FragmentState { 72 | FragmentState { 73 | module: &self.module, 74 | entry_point: &self.fs_entry_point, 75 | targets: &self.color_targets, 76 | } 77 | } 78 | 79 | pub fn color_target_descriptors(&self) -> &[ShaderColorTarget] { 80 | &self.color_target_descrs 81 | } 82 | } 83 | 84 | pub struct ShaderManager { 85 | pub shaders: HashMap, 86 | } 87 | 88 | impl ShaderManager { 89 | pub fn new(device: &wgpu::Device) -> Self { 90 | let mut shaders = HashMap::new(); 91 | 92 | let mut context = glsl_include::Context::new(); 93 | let context = context 94 | .include("utils.wgsl", include_str!("utils.wgsl")) 95 | .include("rend3_common.wgsl", include_str!("rend3_common.wgsl")) 96 | .include("rend3_vertex.wgsl", include_str!("rend3_vertex.wgsl")) 97 | .include("rend3_object.wgsl", include_str!("rend3_object.wgsl")) 98 | .include("rend3_uniforms.wgsl", include_str!("rend3_uniforms.wgsl")); 99 | 100 | macro_rules! def_shader { 101 | ($name:expr, $src:expr, opaque) => { 102 | def_shader!($name, $src, with_alpha, false) 103 | }; 104 | ($name:expr, $src:expr, alpha_blend) => { 105 | def_shader!($name, $src, with_alpha, true) 106 | }; 107 | ($name:expr, $src:expr, with_alpha, $use_alpha:expr) => { 108 | def_shader!( 109 | $name, 110 | $src, 111 | custom, 112 | vec![ShaderColorTarget::Viewport { 113 | use_alpha: $use_alpha 114 | }] 115 | ) 116 | }; 117 | ($name:expr, $src:expr, custom, $targets:expr) => { 118 | shaders.insert( 119 | $name.to_string(), 120 | Shader::new( 121 | "fs_main", 122 | "vs_main", 123 | device.create_shader_module(wgpu::ShaderModuleDescriptor { 124 | label: Some($name), 125 | source: wgpu::ShaderSource::Wgsl( 126 | context 127 | .expand(include_str!($src)) 128 | .expect("Shader preprocessor") 129 | .into(), 130 | ), 131 | }), 132 | $targets, 133 | ), 134 | ); 135 | }; 136 | } 137 | 138 | // A bit unconventional, but shaders define their own color targets. 139 | // Most shaders will draw to a single Rgba16Float color buffer, either 140 | // in opaque mode or using alpha blending. 141 | def_shader!("edge_wireframe_draw", "edge_wireframe_draw.wgsl", opaque); 142 | def_shader!("point_cloud_draw", "point_cloud_draw.wgsl", opaque); 143 | def_shader!("face_draw", "face_draw.wgsl", opaque); 144 | 145 | // For some shaders, we use custom color targets when we have extra 146 | // offscreen buffers they draw to. 147 | def_shader!( 148 | "face_overlay_draw", 149 | "face_overlay_draw.wgsl", 150 | custom, 151 | vec![ 152 | // First, a regular color channel, to highlight faces. The channel 153 | // uses transparency because it draws on top of the actual mesh. 154 | ShaderColorTarget::Viewport { use_alpha: true }, 155 | // Then, the id channel, which draws to an offscreen u32 pixel 156 | // buffer to encode the triangle ids at each pixel. 157 | ShaderColorTarget::Offscreen(ColorTargetState { 158 | format: wgpu::TextureFormat::R32Uint, 159 | blend: None, 160 | write_mask: wgpu::ColorWrites::ALL, 161 | }), 162 | ] 163 | ); 164 | 165 | Self { shaders } 166 | } 167 | 168 | pub fn get(&self, shader_name: &str) -> &Shader { 169 | self.shaders.get(shader_name).unwrap() 170 | } 171 | } 172 | -------------------------------------------------------------------------------- /blackjack_ui/src/rendergraph/utils.wgsl: -------------------------------------------------------------------------------- 1 | // Sometimes we have a packed array of Vec3 and we want them to have an 2 | // alignment of 12, but vec3 aligns at 16. This struct gets around that 3 | // limitation. Use the `from_packed_v3` and `to_packed_v3` to convert. 4 | struct PackedVec3 { 5 | x: f32, 6 | y: f32, 7 | z: f32, 8 | }; 9 | 10 | /// Packs a vec3 into a PackedVec3 11 | fn pack_v3(v3: vec3) -> PackedVec3 { 12 | var out: PackedVec3; 13 | out.x = v3.x; 14 | out.y = v3.y; 15 | out.z = v3.z; 16 | return out; 17 | } 18 | 19 | /// Unpacks a PackedVec3 into a vec3 20 | fn unpack_v3(v3: PackedVec3) -> vec3 { 21 | return vec3(v3.x, v3.y, v3.z); 22 | } 23 | 24 | /// WGSL does not allow declaring storage arrays directly, so we need a wrapper 25 | /// struct to hold them. 26 | struct Vec3Array { 27 | inner: array, 28 | }; 29 | 30 | struct U32Array { 31 | inner: array, 32 | }; 33 | 34 | struct ColorArray { 35 | // Unlike vec3, vec4 has the same stride and alignment of 16, so we don't 36 | // need a Packed version 37 | inner: array> 38 | }; 39 | 40 | // Simple RNG. Borrowed from: https://stackoverflow.com/a/17479300/ 41 | // Translated to WGSL. Credit goes to original authors. 42 | 43 | fn hash(x: u32) -> u32 { 44 | var x = x; 45 | x += ( x << 10u ); 46 | x ^= ( x << 6u ); 47 | x += ( x << 3u ); 48 | x ^= ( x << 11u ); 49 | x += ( x << 15u ); 50 | return x; 51 | } 52 | 53 | fn hash_v2(v: vec2) -> u32 { 54 | return hash(v.x ^ hash(v.y)); 55 | } 56 | 57 | fn hash_v3(v: vec3) -> u32 { 58 | return hash(v.x ^ hash(v.y) ^ hash(v.z)); 59 | } 60 | 61 | fn hash_v4(v: vec4) -> u32 { 62 | return hash(v.x ^ hash(v.y) ^ hash(v.z) ^ hash(v.w)); 63 | } 64 | 65 | fn float_construct(m: u32) -> f32 { 66 | var m = m; 67 | let ieee_mantissa = 0x007FFFFFu; 68 | let ieee_one = 0x3F800000u; 69 | m &= ieee_mantissa; 70 | m |= ieee_one; 71 | 72 | let f = bitcast(m); 73 | return f - 1.0; 74 | } 75 | 76 | fn random(x: f32) -> f32 { 77 | return float_construct(hash(bitcast(x))); 78 | } 79 | 80 | fn random_v2(v: vec2) -> f32 { 81 | return float_construct(hash_v2(bitcast>(v))); 82 | } 83 | 84 | fn random_v3(v: vec3) -> f32 { 85 | return float_construct(hash_v3(bitcast>(v))); 86 | } 87 | 88 | fn random_v4(v: vec4) -> f32 { 89 | return float_construct(hash_v4(bitcast>(v))); 90 | } 91 | -------------------------------------------------------------------------------- /blackjack_ui/src/rendergraph/wireframe_routine.rs: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2023 setzer22 and contributors 2 | // 3 | // This Source Code Form is subject to the terms of the Mozilla Public 4 | // License, v. 2.0. If a copy of the MPL was not distributed with this 5 | // file, You can obtain one at https://mozilla.org/MPL/2.0/. 6 | 7 | use super::viewport_3d_routine::{DrawType, RoutineLayout, Viewport3dRoutine}; 8 | use crate::prelude::r3; 9 | use glam::Vec3; 10 | use rend3::managers::TextureManager; 11 | use rend3_routine::base::{BaseRenderGraph, BaseRenderGraphIntermediateState}; 12 | use wgpu::{ 13 | util::{BufferInitDescriptor, DeviceExt}, 14 | *, 15 | }; 16 | 17 | use super::shader_manager::ShaderManager; 18 | 19 | /// Stores a wgpu buffer containing the edges of a wireframe 20 | pub struct WireframeLayout { 21 | /// Contains 2*len Vec3 elements 22 | line_positions: Buffer, 23 | /// Contains len Vec3 elements (color) 24 | colors: Buffer, 25 | /// Number of elements 26 | len: usize, 27 | } 28 | 29 | const NUM_BUFFERS: usize = 2; 30 | 31 | impl RoutineLayout for WireframeLayout { 32 | type Settings = (); 33 | fn get_wgpu_buffers(&self, _settings: &()) -> [&Buffer; NUM_BUFFERS] { 34 | [&self.line_positions, &self.colors] 35 | } 36 | 37 | fn get_wgpu_textures<'a>( 38 | &'a self, 39 | _texture_manager: &'a TextureManager, 40 | _settings: &(), 41 | ) -> [&'a TextureView; 0] { 42 | [] 43 | } 44 | 45 | fn get_wgpu_uniforms(&self, _settings: &Self::Settings) -> [&Buffer; 0] { 46 | [] 47 | } 48 | 49 | fn get_draw_type(&self, _settings: &Self::Settings) -> DrawType<'_> { 50 | DrawType::UseInstances { 51 | num_vertices: 2, 52 | num_instances: self.len, 53 | } 54 | } 55 | } 56 | 57 | pub struct WireframeRoutine { 58 | inner: Viewport3dRoutine, 59 | } 60 | 61 | impl WireframeRoutine { 62 | pub fn new(device: &Device, base: &BaseRenderGraph, shader_manager: &ShaderManager) -> Self { 63 | Self { 64 | inner: Viewport3dRoutine::new( 65 | "edge wireframe", 66 | device, 67 | base, 68 | shader_manager.get("edge_wireframe_draw"), 69 | PrimitiveTopology::LineList, 70 | FrontFace::Ccw, 71 | ), 72 | } 73 | } 74 | 75 | pub fn add_wireframe(&mut self, device: &Device, lines: &[Vec3], colors: &[Vec3]) { 76 | let len = colors.len(); 77 | assert!( 78 | lines.len() == colors.len() * 2, 79 | "There must be exactly 2*N lines and N colors in a wireframe" 80 | ); 81 | 82 | let line_positions = device.create_buffer_init(&BufferInitDescriptor { 83 | label: None, 84 | contents: bytemuck::cast_slice(lines), 85 | usage: BufferUsages::STORAGE, 86 | }); 87 | let colors = device.create_buffer_init(&BufferInitDescriptor { 88 | label: None, 89 | contents: bytemuck::cast_slice(colors), 90 | usage: BufferUsages::STORAGE, 91 | }); 92 | 93 | self.inner.layouts.push(WireframeLayout { 94 | len, 95 | line_positions, 96 | colors, 97 | }); 98 | } 99 | 100 | pub fn clear(&mut self) { 101 | self.inner.clear() 102 | } 103 | 104 | pub fn add_to_graph<'node>( 105 | &'node self, 106 | graph: &mut r3::RenderGraph<'node>, 107 | state: &BaseRenderGraphIntermediateState, 108 | ) { 109 | self.inner.add_to_graph(graph, state, &(), &[]); 110 | } 111 | } 112 | -------------------------------------------------------------------------------- /build_godot_plugin.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | cargo build --release -p blackjack_godot 4 | mkdir -p target/ 5 | cp -r godot_plugin target/ 6 | cp -r blackjack_lua target/godot_plugin 7 | cp target/release/libblackjack_godot.so target/godot_plugin/addons/blackjack_engine_godot/libblackjack_godot_linux.so 8 | 9 | cd target/godot_plugin 10 | rm target/blackjack_engine_godot.zip 11 | zip -r ../blackjack_engine_godot.zip * 12 | -------------------------------------------------------------------------------- /build_ldoc.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | rm -r target/ldoc 4 | mkdir -p target/ldoc/sources 5 | cargo run -- --generate-ldoc target/ldoc/sources 6 | pushd target/ldoc 7 | ldoc sources \ 8 | --style ../../ \ 9 | --project "Blackjack" \ 10 | --title "Blackjack Lua API Reference" \ 11 | --format markdown \ 12 | popd 13 | -------------------------------------------------------------------------------- /doc/images/cube_primitive.png: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:2f023a0d3e936d10155e6c215e859dd079b82f65e2386fff765e488b151f3f70 3 | size 64283 4 | -------------------------------------------------------------------------------- /doc/images/dissolve_edge.png: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:2cd77d45674586cacd5f46d749cdd35e5d10c572015ded5f878121313817a79c 3 | size 45880 4 | -------------------------------------------------------------------------------- /doc/images/edge_divide.png: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:4eafbfede3010209e82d59f924bf86cb7f097876170270cdd951a0919717e1aa 3 | size 56411 4 | -------------------------------------------------------------------------------- /doc/images/edge_extrude.png: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:75a3653ce1d7517db1ce7dae829f53ffc2af66eb0ca373550ad56b21677d5472 3 | size 22784 4 | -------------------------------------------------------------------------------- /doc/images/navigate_edge_loop.png: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:b32162386e5f4931fe8775e7dba8d3e12686b8efd1cc6b4eb398f5db42560e9e 3 | size 51791 4 | -------------------------------------------------------------------------------- /doc/images/split_edge.png: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:ddacf318385b1e63a44d7dcd99bf027df8cfe6e354ce307f26e957bd2cb9a08e 3 | size 85964 4 | -------------------------------------------------------------------------------- /doc/images/split_vertex.png: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:7fb45ae01762ad4f30f9a08fbc9a820a29a80502418c98358e5b63d9c4553a8a 3 | size 122952 4 | -------------------------------------------------------------------------------- /doc/resources/blackjack.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/setzer22/blackjack/08ece4c0d1551ef60d2fc380952abf122d46f956/doc/resources/blackjack.gif -------------------------------------------------------------------------------- /doc/resources/blackjack_gif2.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/setzer22/blackjack/08ece4c0d1551ef60d2fc380952abf122d46f956/doc/resources/blackjack_gif2.gif -------------------------------------------------------------------------------- /doc/resources/blackjack_gif3.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/setzer22/blackjack/08ece4c0d1551ef60d2fc380952abf122d46f956/doc/resources/blackjack_gif3.gif -------------------------------------------------------------------------------- /doc/resources/blackjack_gif4.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/setzer22/blackjack/08ece4c0d1551ef60d2fc380952abf122d46f956/doc/resources/blackjack_gif4.gif -------------------------------------------------------------------------------- /doc/resources/blackjack_gif5.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/setzer22/blackjack/08ece4c0d1551ef60d2fc380952abf122d46f956/doc/resources/blackjack_gif5.gif -------------------------------------------------------------------------------- /doc/resources/showcase.png: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:9fce6067307bfc0720a038b217e4f9010b98549f0218c75a199ec03da761bc9d 3 | size 218864 4 | -------------------------------------------------------------------------------- /doc/resources/showcase2.png: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:9fce6067307bfc0720a038b217e4f9010b98549f0218c75a199ec03da761bc9d 3 | size 218864 4 | -------------------------------------------------------------------------------- /doc/resources/showcase3.png: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:0158da789ee62d3b5edc62f9c04148aa7da02ea3d7535303243dd524faa8ab63 3 | size 567317 4 | -------------------------------------------------------------------------------- /examples/EXAMPLES_LICENSE: -------------------------------------------------------------------------------- 1 | All the examples under this directory are licensed under Craetive Commons CC-BY-SA 4.0 2 | 3 | For more details, check: https://creativecommons.org/licenses/by-sa/4.0/ 4 | -------------------------------------------------------------------------------- /examples/box.bjk: -------------------------------------------------------------------------------- 1 | // BLACKJACK_VERSION_HEADER 0 1 0 2 | ( 3 | nodes: [ 4 | ( 5 | op_name: "MakeBox", 6 | return_value: Some("out_mesh"), 7 | inputs: [ 8 | ( 9 | name: "origin", 10 | data_type: "BJK_VECTOR", 11 | kind: External( 12 | promoted: None, 13 | ), 14 | ), 15 | ( 16 | name: "size", 17 | data_type: "BJK_VECTOR", 18 | kind: External( 19 | promoted: None, 20 | ), 21 | ), 22 | ], 23 | outputs: [ 24 | ( 25 | name: "out_mesh", 26 | data_type: "BJK_MESH", 27 | ), 28 | ], 29 | ), 30 | ( 31 | op_name: "MakeComment", 32 | return_value: None, 33 | inputs: [ 34 | ( 35 | name: "comment", 36 | data_type: "BJK_STRING", 37 | kind: External( 38 | promoted: None, 39 | ), 40 | ), 41 | ], 42 | outputs: [], 43 | ), 44 | ], 45 | default_node: Some(0), 46 | ui_data: Some(( 47 | node_positions: [ 48 | (403.9753, 165.62659), 49 | (126.67163, 64.81848), 50 | ], 51 | node_order: [ 52 | 0, 53 | 1, 54 | ], 55 | pan: (0.0, 0.0), 56 | zoom: 1.0, 57 | locked_gizmo_nodes: [], 58 | )), 59 | external_parameters: Some(( 60 | param_values: { 61 | ( 62 | node_idx: 0, 63 | param_name: "size", 64 | ): Vector((1.0, 1.0, 1.0)), 65 | ( 66 | node_idx: 1, 67 | param_name: "comment", 68 | ): String("Here\'s a box, one of the simplest, but most versatile nodes in blackjack!\n\nThe origin and size parameters let you specify the center and boundaries of the box.\n\nTry dragging one of he parameters left and right and see how the box updates!\n\nWhen the box is active, or its Gizmo flag is set, you can also modify these parameters in the editor."), 69 | ( 70 | node_idx: 0, 71 | param_name: "origin", 72 | ): Vector((0.0, 0.0, 0.0)), 73 | }, 74 | )), 75 | ) -------------------------------------------------------------------------------- /godot_plugin/addons/blackjack_engine_godot/BgaFileResource.gd: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2023 setzer22 and contributors 2 | # 3 | # This Source Code Form is subject to the terms of the Mozilla Public 4 | # License, v. 2.0. If a copy of the MPL was not distributed with this 5 | # file, You can obtain one at https://mozilla.org/MPL/2.0/. 6 | 7 | class_name BgaFileResource 8 | extends Resource 9 | 10 | export var contents: String 11 | -------------------------------------------------------------------------------- /godot_plugin/addons/blackjack_engine_godot/BlackjackApi.gdns: -------------------------------------------------------------------------------- 1 | [gd_resource type="NativeScript" load_steps=2 format=2] 2 | 3 | [ext_resource path="res://addons/blackjack_engine_godot/blackjack_godot.gdnlib" type="GDNativeLibrary" id=1] 4 | 5 | [resource] 6 | resource_name = "BlackjackInspectorPlugin" 7 | class_name = "BlackjackApi" 8 | library = ExtResource( 1 ) 9 | script_class_name = "BlackjackApi" 10 | -------------------------------------------------------------------------------- /godot_plugin/addons/blackjack_engine_godot/BlackjackInspectorPlugin.gd: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2023 setzer22 and contributors 2 | # 3 | # This Source Code Form is subject to the terms of the Mozilla Public 4 | # License, v. 2.0. If a copy of the MPL was not distributed with this 5 | # file, You can obtain one at https://mozilla.org/MPL/2.0/. 6 | 7 | extends EditorInspectorPlugin 8 | 9 | func can_handle(object): 10 | return object.get_class() == "BlackjackJack" 11 | 12 | func parse_begin(object): 13 | var gui = object.make_tweaker_gui() 14 | if gui != null: 15 | add_custom_control(gui) 16 | -------------------------------------------------------------------------------- /godot_plugin/addons/blackjack_engine_godot/BlackjackJack.gd: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2023 setzer22 and contributors 2 | # 3 | # This Source Code Form is subject to the terms of the Mozilla Public 4 | # License, v. 2.0. If a copy of the MPL was not distributed with this 5 | # file, You can obtain one at https://mozilla.org/MPL/2.0/. 6 | 7 | tool 8 | extends Spatial 9 | 10 | signal error_occurred(err_str) 11 | signal clear_error(err_str) 12 | 13 | var BlackjackApi = preload("res://addons/blackjack_engine_godot/BlackjackApi.gdns").new() 14 | var BlackjackPropertiesTweaker = preload("res://addons/blackjack_engine_godot/BlackjackPropertiesTweaker.tscn") 15 | 16 | # Exported vars, except we can't export because of 17 | # https://github.com/godotengine/godot/issues/5988 18 | # 19 | # These get saved with the scene 20 | var jack_resource : Resource setget set_jack, get_jack 21 | var show_gui : bool = false setget set_show_gui 22 | var overriden_params : Dictionary = {} 23 | var materials : Array = [] 24 | 25 | # Non exported vars 26 | var jack_id = null 27 | var needs_update = false 28 | var child_mesh 29 | var jack_params 30 | var runtime_child_gui = null 31 | 32 | onready var is_ready = false 33 | 34 | func set_jack(new_jack): 35 | if jack_resource != null and new_jack != null: 36 | # We're replacing this jack with another one. Clear any overriden params to avoid conflicts 37 | overriden_params = {} 38 | 39 | jack_resource = new_jack 40 | if is_ready and jack_resource != null: 41 | on_reload_jack_resource() 42 | 43 | func get_jack(): 44 | return jack_resource 45 | 46 | func set_show_gui(new_show_gui): 47 | show_gui = new_show_gui 48 | if not Engine.editor_hint: 49 | if new_show_gui != null and new_show_gui == true: 50 | if new_show_gui and jack_resource != null and jack_id != null and jack_params != null: 51 | if runtime_child_gui != null: 52 | remove_child(runtime_child_gui) 53 | runtime_child_gui = make_tweaker_gui() 54 | sync_gui_to_props(runtime_child_gui) 55 | add_child(runtime_child_gui) 56 | else: 57 | if runtime_child_gui != null: 58 | remove_child(runtime_child_gui) 59 | 60 | 61 | func on_reload_jack_resource(): 62 | if jack_resource != null and jack_resource.get("contents") != null: 63 | # Set the jack 64 | var err = BlackjackApi.set_jack(jack_id, jack_resource) 65 | jack_params = BlackjackApi.get_params(jack_id) 66 | needs_update = true 67 | for k in overriden_params.keys(): 68 | on_property_changed(k, overriden_params[k]) 69 | set_show_gui(show_gui) # Force reload of gui state. 70 | 71 | # Make sure the editor gets the changes and redraws the inspector 72 | property_list_changed_notify() 73 | 74 | func make_tweaker_gui(): 75 | if jack_params != null: 76 | var gui = BlackjackPropertiesTweaker.instance() 77 | gui.initialize(jack_params) 78 | gui.connect("property_changed", self, "on_property_changed") 79 | self.connect("error_occurred", gui, "set_error") 80 | self.connect("clear_error", gui, "clear_error") 81 | sync_gui_to_props(gui) 82 | return gui 83 | 84 | func sync_gui_to_props(gui): 85 | for prop_addr in overriden_params.keys(): 86 | # Need to call this deferred, because the GUI might not have setup itsp child Controls yet. 87 | gui.call_deferred("set_value_externally", prop_addr, overriden_params[prop_addr]) 88 | 89 | func on_property_changed(prop_addr, new_val): 90 | if jack_id != null: 91 | overriden_params[prop_addr] = new_val 92 | BlackjackApi.set_param(jack_id, prop_addr, new_val) 93 | needs_update = true 94 | 95 | func _ready(): 96 | if Engine.editor_hint: 97 | start() 98 | else: 99 | call_deferred("start") 100 | 101 | func start(): 102 | is_ready = true 103 | jack_id = BlackjackApi.make_jack() 104 | on_reload_jack_resource() 105 | child_mesh = MeshInstance.new() 106 | add_child(child_mesh) 107 | 108 | func _process(delta): 109 | if needs_update: 110 | needs_update = false 111 | var results = BlackjackApi.update_jack(jack_id, materials) 112 | if results != null and results.has("Ok"): 113 | child_mesh.mesh = results.Ok 114 | emit_signal("clear_error") 115 | elif results != null and results.has("Err"): 116 | emit_signal("error_occurred", str(results.Err)) 117 | else: 118 | push_error("Blackjack encountered an unexpected error") 119 | emit_signal("error_occurred", "Blackjack encountered an unexpected error") 120 | 121 | func is_class(other): return other == "BlackjackJack" or .is_class(other) 122 | func get_class(): return "BlackjackJack" 123 | 124 | func _get_property_list(): 125 | var properties = [ 126 | { 127 | name = "jack_resource", 128 | type = TYPE_OBJECT 129 | }, 130 | { 131 | name = "show_gui", 132 | type = TYPE_BOOL, 133 | }, 134 | { 135 | name = "overriden_params", 136 | type = TYPE_DICTIONARY, 137 | usage = PROPERTY_USAGE_STORAGE, # Do not show on inspector 138 | }, 139 | ] 140 | 141 | # Add one more property than we have materials. This allows setting the next material by drag & drop 142 | for i in range(0, len(materials) + 1): 143 | properties.push_back({ 144 | name = "material_slot_" + str(i), 145 | type = TYPE_OBJECT, 146 | hint = PROPERTY_HINT_RESOURCE_TYPE, 147 | hint_string = "Material", 148 | }) 149 | 150 | return properties 151 | 152 | func _set(property, value): 153 | if property.begins_with("material_slot_"): 154 | var idx = int(property.trim_prefix("material_slot_")) 155 | while len(materials) <= idx: 156 | materials.push_back(null) 157 | materials[idx] = value 158 | property_list_changed_notify() 159 | 160 | for i in range(0, len(materials)): 161 | if materials[len(materials) - 1] == null: 162 | materials.pop_back() 163 | else: 164 | break 165 | 166 | needs_update = true 167 | else: 168 | ._set(property, value) 169 | 170 | func _get(property): 171 | if property.begins_with("material_slot_"): 172 | var idx = int(property.trim_prefix("material_slot_")) 173 | if len(materials) > idx: 174 | return materials[idx] 175 | else: 176 | return null 177 | else: 178 | return ._get(property) 179 | -------------------------------------------------------------------------------- /godot_plugin/addons/blackjack_engine_godot/BlackjackPropertiesTweaker.gd: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2023 setzer22 and contributors 2 | # 3 | # This Source Code Form is subject to the terms of the Mozilla Public 4 | # License, v. 2.0. If a copy of the MPL was not distributed with this 5 | # file, You can obtain one at https://mozilla.org/MPL/2.0/. 6 | 7 | tool 8 | extends PanelContainer 9 | 10 | signal property_changed(prop_addr, new_val) 11 | 12 | onready var properties_vbox = find_node("PropertiesVBox") 13 | onready var error_label = find_node("ErrorLabel") 14 | 15 | var properties = [] 16 | var property_controls = [] 17 | 18 | func on_property_changed(new_val, prop_addr): 19 | emit_signal("property_changed", prop_addr, new_val) 20 | 21 | # Called from the outside, when a property changes (because it's being set from 22 | # the inspector, for instance). 23 | func set_value_externally(prop_addr, new_val): 24 | 25 | var idx = 0 26 | for prop in properties: 27 | if prop.addr == prop_addr: 28 | break 29 | idx = idx + 1 30 | 31 | 32 | if idx < len(property_controls): 33 | var control = property_controls[idx] 34 | control.set_value_externally(new_val) 35 | 36 | func set_error(err_str): 37 | error_label.text = err_str 38 | 39 | func clear_error(): 40 | error_label.text = "" 41 | 42 | func initialize(properties_: Array): 43 | self.properties = properties_ 44 | 45 | func _ready(): 46 | error_label.text = "" 47 | for prop in properties: 48 | var control 49 | match prop.typ: 50 | "Scalar": 51 | control = preload("ScalarProp.tscn").instance() 52 | control.init(prop.label, prop.val, prop.min, prop.max) 53 | "String": 54 | control = preload("StringProp.tscn").instance() 55 | control.init(prop.label, prop.val) 56 | "Vector": 57 | control = preload("VectorProp.tscn").instance() 58 | control.init(prop.label, prop.val) 59 | "Selection": 60 | control = preload("SelectionProp.tscn").instance() 61 | control.init(prop.label, prop.val) 62 | control.connect("on_changed", self, "on_property_changed", [prop.addr]) 63 | property_controls.push_back(control) 64 | 65 | properties_vbox.add_child(control) 66 | 67 | -------------------------------------------------------------------------------- /godot_plugin/addons/blackjack_engine_godot/BlackjackPropertiesTweaker.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=2 format=2] 2 | 3 | [ext_resource path="res://addons/blackjack_engine_godot/BlackjackPropertiesTweaker.gd" type="Script" id=1] 4 | 5 | [node name="BlackjackPropertiesTweaker" type="PanelContainer"] 6 | margin_right = 273.0 7 | margin_bottom = 403.0 8 | rect_min_size = Vector2( 0, 400 ) 9 | script = ExtResource( 1 ) 10 | 11 | [node name="ScrollContainer" type="ScrollContainer" parent="."] 12 | margin_left = 7.0 13 | margin_top = 7.0 14 | margin_right = 266.0 15 | margin_bottom = 396.0 16 | scroll_horizontal_enabled = false 17 | 18 | [node name="VBox" type="VBoxContainer" parent="ScrollContainer"] 19 | margin_right = 259.0 20 | margin_bottom = 262.0 21 | size_flags_horizontal = 3 22 | custom_constants/separation = 10 23 | 24 | [node name="Label" type="Label" parent="ScrollContainer/VBox"] 25 | margin_right = 259.0 26 | margin_bottom = 14.0 27 | text = "Blackjack Properties" 28 | 29 | [node name="MarginContainer" type="MarginContainer" parent="ScrollContainer/VBox"] 30 | margin_top = 24.0 31 | margin_right = 259.0 32 | margin_bottom = 238.0 33 | custom_constants/margin_right = 10 34 | custom_constants/margin_top = 10 35 | custom_constants/margin_left = 10 36 | custom_constants/margin_bottom = 10 37 | 38 | [node name="PropertiesVBox" type="VBoxContainer" parent="ScrollContainer/VBox/MarginContainer"] 39 | margin_left = 10.0 40 | margin_top = 10.0 41 | margin_right = 249.0 42 | margin_bottom = 204.0 43 | 44 | [node name="ScrollContainer" type="ScrollContainer" parent="ScrollContainer/VBox"] 45 | margin_top = 248.0 46 | margin_right = 259.0 47 | margin_bottom = 262.0 48 | size_flags_horizontal = 3 49 | scroll_vertical_enabled = false 50 | 51 | [node name="ErrorLabel" type="Label" parent="ScrollContainer/VBox/ScrollContainer"] 52 | margin_right = 79.0 53 | margin_bottom = 14.0 54 | custom_colors/font_color = Color( 0.988235, 0.188235, 0.0588235, 1 ) 55 | text = "ERROR AREA" 56 | -------------------------------------------------------------------------------- /godot_plugin/addons/blackjack_engine_godot/ErrorLabel.gd: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2023 setzer22 and contributors 2 | # 3 | # This Source Code Form is subject to the terms of the Mozilla Public 4 | # License, v. 2.0. If a copy of the MPL was not distributed with this 5 | # file, You can obtain one at https://mozilla.org/MPL/2.0/. 6 | 7 | tool 8 | extends Control 9 | 10 | func _on_error(err): 11 | $Label.text = err 12 | 13 | func _on_clear_error(): 14 | $Label.text = "" 15 | -------------------------------------------------------------------------------- /godot_plugin/addons/blackjack_engine_godot/ErrorLabel.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=2 format=2] 2 | 3 | [ext_resource path="res://addons/blackjack_engine_godot/ErrorLabel.gd" type="Script" id=1] 4 | 5 | [node name="ScrollContainer" type="ScrollContainer"] 6 | margin_right = 12.0 7 | margin_bottom = 12.0 8 | size_flags_horizontal = 3 9 | scroll_vertical_enabled = false 10 | script = ExtResource( 1 ) 11 | 12 | [node name="Label" type="Label" parent="."] 13 | margin_bottom = 14.0 14 | custom_colors/font_color = Color( 0.905882, 0.0588235, 0, 1 ) 15 | -------------------------------------------------------------------------------- /godot_plugin/addons/blackjack_engine_godot/JackImportPlugin.gd: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2023 setzer22 and contributors 2 | # 3 | # This Source Code Form is subject to the terms of the Mozilla Public 4 | # License, v. 2.0. If a copy of the MPL was not distributed with this 5 | # file, You can obtain one at https://mozilla.org/MPL/2.0/. 6 | 7 | extends EditorImportPlugin 8 | 9 | enum Presets { DEFAULT } 10 | 11 | func get_importer_name(): 12 | return "blackjack.bjk" 13 | 14 | func get_visible_name(): 15 | return "Blackjack Jack Asset" 16 | 17 | func get_recognized_extensions(): 18 | return ["bjk"] 19 | 20 | func get_save_extension(): 21 | return "res" 22 | 23 | func get_resource_type(): 24 | return "Resource" 25 | 26 | func get_preset_count(): 27 | return Presets.size() 28 | 29 | func get_preset_name(preset): 30 | match preset: 31 | Presets.DEFAULT: 32 | return "Default" 33 | _: 34 | return "Unknown" 35 | 36 | func get_import_options(preset): 37 | match preset: 38 | Presets.DEFAULT: 39 | return [] 40 | _: 41 | return [] 42 | 43 | func get_option_visibility(option, options): 44 | return true 45 | 46 | func import(source_file, save_path, options, platform_variants, gen_files): 47 | var file = File.new() 48 | var err = file.open(source_file, File.READ) 49 | if err != OK: 50 | return err 51 | 52 | var resource = BgaFileResource.new() 53 | resource.contents = file.get_as_text() 54 | 55 | file.close() 56 | 57 | return ResourceSaver.save("%s.%s" % [save_path, get_save_extension()], resource) 58 | 59 | 60 | -------------------------------------------------------------------------------- /godot_plugin/addons/blackjack_engine_godot/ScalarProp.gd: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2023 setzer22 and contributors 2 | # 3 | # This Source Code Form is subject to the terms of the Mozilla Public 4 | # License, v. 2.0. If a copy of the MPL was not distributed with this 5 | # file, You can obtain one at https://mozilla.org/MPL/2.0/. 6 | 7 | tool 8 | extends HBoxContainer 9 | 10 | signal on_changed(value) 11 | 12 | func init(label: String, value, min_val, max_val): 13 | var use_slider = min_val != null && max_val != null 14 | $HSlider.visible = use_slider 15 | $LineEdit.visible = !use_slider 16 | 17 | $Label.text = label 18 | $Label2.text = str(value) 19 | 20 | if use_slider: 21 | $HSlider.min_value = min_val 22 | $HSlider.max_value = max_val 23 | $HSlider.step = (max_val - min_val) / 100.0 24 | $HSlider.value = value 25 | else: 26 | $LineEdit.value = str(value) 27 | 28 | func _on_HSlider_value_changed(value): 29 | $Label2.text = str(value) 30 | emit_signal("on_changed", value) 31 | 32 | func set_value_externally(val): 33 | $HSlider.value = val 34 | $LineEdit.text = str(val) 35 | $Label2.text = str(val) 36 | 37 | func _on_LineEdit_text_changed(new_text): 38 | if new_text.is_valid_float(): 39 | var new_val = float(new_text) 40 | $Label2.text = new_text 41 | emit_signal("on_changed", new_val) 42 | -------------------------------------------------------------------------------- /godot_plugin/addons/blackjack_engine_godot/ScalarProp.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=2 format=2] 2 | 3 | [ext_resource path="res://addons/blackjack_engine_godot/ScalarProp.gd" type="Script" id=1] 4 | 5 | [node name="ScalarProp" type="HBoxContainer"] 6 | margin_right = 293.0 7 | margin_bottom = 16.0 8 | script = ExtResource( 1 ) 9 | 10 | [node name="Label" type="Label" parent="."] 11 | margin_top = 5.0 12 | margin_right = 62.0 13 | margin_bottom = 19.0 14 | text = "Iterations" 15 | 16 | [node name="HSlider" type="HSlider" parent="."] 17 | margin_left = 66.0 18 | margin_right = 150.0 19 | margin_bottom = 16.0 20 | size_flags_horizontal = 3 21 | 22 | [node name="LineEdit" type="LineEdit" parent="."] 23 | margin_left = 154.0 24 | margin_right = 239.0 25 | margin_bottom = 24.0 26 | size_flags_horizontal = 3 27 | 28 | [node name="Label2" type="Label" parent="."] 29 | margin_left = 243.0 30 | margin_top = 5.0 31 | margin_right = 293.0 32 | margin_bottom = 19.0 33 | rect_min_size = Vector2( 50, 0 ) 34 | text = "0.25" 35 | align = 2 36 | 37 | [connection signal="value_changed" from="HSlider" to="." method="_on_HSlider_value_changed"] 38 | [connection signal="text_changed" from="LineEdit" to="." method="_on_LineEdit_text_changed"] 39 | -------------------------------------------------------------------------------- /godot_plugin/addons/blackjack_engine_godot/SelectionProp.gd: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2023 setzer22 and contributors 2 | # 3 | # This Source Code Form is subject to the terms of the Mozilla Public 4 | # License, v. 2.0. If a copy of the MPL was not distributed with this 5 | # file, You can obtain one at https://mozilla.org/MPL/2.0/. 6 | 7 | tool 8 | extends HBoxContainer 9 | 10 | signal on_changed(value) 11 | 12 | func init(label: String, value: String): 13 | $Label.text = label 14 | $LineEdit3.text = value 15 | 16 | func _on_LineEdit3_text_changed(new_text): 17 | emit_signal("on_changed", new_text) 18 | 19 | func set_value_extenrally(val): 20 | $LineEdit3.text = val 21 | -------------------------------------------------------------------------------- /godot_plugin/addons/blackjack_engine_godot/SelectionProp.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=2 format=2] 2 | 3 | [ext_resource path="res://addons/blackjack_engine_godot/SelectionProp.gd" type="Script" id=1] 4 | 5 | [node name="SelectionProp" type="HBoxContainer"] 6 | margin_right = 293.0 7 | margin_bottom = 24.0 8 | script = ExtResource( 1 ) 9 | 10 | [node name="Label" type="Label" parent="."] 11 | margin_top = 5.0 12 | margin_right = 59.0 13 | margin_bottom = 19.0 14 | text = "Selection" 15 | 16 | [node name="LineEdit3" type="LineEdit" parent="."] 17 | margin_left = 63.0 18 | margin_right = 293.0 19 | margin_bottom = 24.0 20 | size_flags_horizontal = 3 21 | 22 | [connection signal="text_changed" from="LineEdit3" to="." method="_on_LineEdit3_text_changed"] 23 | -------------------------------------------------------------------------------- /godot_plugin/addons/blackjack_engine_godot/StringProp.gd: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2023 setzer22 and contributors 2 | # 3 | # This Source Code Form is subject to the terms of the Mozilla Public 4 | # License, v. 2.0. If a copy of the MPL was not distributed with this 5 | # file, You can obtain one at https://mozilla.org/MPL/2.0/. 6 | 7 | tool 8 | extends VBoxContainer 9 | 10 | signal on_changed(value) 11 | 12 | func init(label: String, value: String): 13 | $Label.text = label 14 | $TextEdit.text = value 15 | 16 | func _on_TextEdit_text_changed(): 17 | emit_signal("on_changed", $TextEdit.text) 18 | 19 | func set_value_externally(val): 20 | $TextEdit.text = val 21 | -------------------------------------------------------------------------------- /godot_plugin/addons/blackjack_engine_godot/StringProp.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=2 format=2] 2 | 3 | [ext_resource path="res://addons/blackjack_engine_godot/StringProp.gd" type="Script" id=1] 4 | 5 | [node name="StringProp" type="VBoxContainer"] 6 | margin_right = 293.0 7 | margin_bottom = 118.0 8 | script = ExtResource( 1 ) 9 | 10 | [node name="Label" type="Label" parent="."] 11 | margin_right = 293.0 12 | margin_bottom = 14.0 13 | text = "L-System:" 14 | 15 | [node name="TextEdit" type="TextEdit" parent="."] 16 | margin_top = 18.0 17 | margin_right = 293.0 18 | margin_bottom = 118.0 19 | rect_min_size = Vector2( 0, 100 ) 20 | size_flags_horizontal = 3 21 | 22 | [connection signal="text_changed" from="TextEdit" to="." method="_on_TextEdit_text_changed"] 23 | -------------------------------------------------------------------------------- /godot_plugin/addons/blackjack_engine_godot/VectorProp.gd: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2023 setzer22 and contributors 2 | # 3 | # This Source Code Form is subject to the terms of the Mozilla Public 4 | # License, v. 2.0. If a copy of the MPL was not distributed with this 5 | # file, You can obtain one at https://mozilla.org/MPL/2.0/. 6 | 7 | tool 8 | extends HBoxContainer 9 | 10 | signal on_changed(value) 11 | 12 | var stored_value: Vector3 13 | 14 | func init(label: String, value: Vector3): 15 | $Label.text = label 16 | stored_value = value 17 | $X.text = str(value.x) 18 | $Y.text = str(value.y) 19 | $Z.text = str(value.z) 20 | 21 | func emit_stored_value(): 22 | emit_signal("on_changed", stored_value) 23 | 24 | func _on_X_text_changed(new_text): 25 | stored_value.x = float(new_text) 26 | emit_stored_value() 27 | 28 | func _on_Y_text_changed(new_text): 29 | stored_value.y = float(new_text) 30 | emit_stored_value() 31 | 32 | func _on_Z_text_changed(new_text): 33 | stored_value.z = float(new_text) 34 | emit_stored_value() 35 | 36 | func set_value_externally(val): 37 | stored_value = val 38 | $X.text = val.x 39 | $Y.text = val.y 40 | $Z.text = val.z 41 | -------------------------------------------------------------------------------- /godot_plugin/addons/blackjack_engine_godot/VectorProp.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=2 format=2] 2 | 3 | [ext_resource path="res://addons/blackjack_engine_godot/VectorProp.gd" type="Script" id=1] 4 | 5 | [node name="VectorProp" type="HBoxContainer"] 6 | margin_top = 20.0 7 | margin_right = 293.0 8 | margin_bottom = 44.0 9 | script = ExtResource( 1 ) 10 | 11 | [node name="Label" type="Label" parent="."] 12 | margin_top = 5.0 13 | margin_right = 52.0 14 | margin_bottom = 19.0 15 | text = "Position" 16 | 17 | [node name="X" type="LineEdit" parent="."] 18 | margin_left = 56.0 19 | margin_right = 114.0 20 | margin_bottom = 24.0 21 | 22 | [node name="Y" type="LineEdit" parent="."] 23 | margin_left = 118.0 24 | margin_right = 176.0 25 | margin_bottom = 24.0 26 | 27 | [node name="Z" type="LineEdit" parent="."] 28 | margin_left = 180.0 29 | margin_right = 238.0 30 | margin_bottom = 24.0 31 | 32 | [connection signal="text_changed" from="X" to="." method="_on_X_text_changed"] 33 | [connection signal="text_changed" from="Y" to="." method="_on_Y_text_changed"] 34 | [connection signal="text_changed" from="Z" to="." method="_on_Z_text_changed"] 35 | -------------------------------------------------------------------------------- /godot_plugin/addons/blackjack_engine_godot/blackjack_godot.gdnlib: -------------------------------------------------------------------------------- 1 | [general] 2 | 3 | singleton=false 4 | load_once=true 5 | symbol_prefix="godot_" 6 | reloadable=true 7 | 8 | [entry] 9 | 10 | X11.64="res://addons/blackjack_engine_godot/libblackjack_godot_linux.so" 11 | OSX.64="res://addons/blackjack_engine_godot/libblackjack_godot_macos.dylib" 12 | Windows.64="res://addons/blackjack_engine_godot/blackjack_godot_windows.dll" 13 | 14 | [dependencies] 15 | 16 | X11.64=[ ] 17 | -------------------------------------------------------------------------------- /godot_plugin/addons/blackjack_engine_godot/icon.png: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:d458cb03a64fbf1bb2d2478f54a545df361456f0f49932ac24153b66a89d0004 3 | size 585 4 | -------------------------------------------------------------------------------- /godot_plugin/addons/blackjack_engine_godot/icon.png.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="texture" 4 | type="StreamTexture" 5 | path="res://.import/icon.png-7707978c51283c602ffb5677fbc4d2b3.stex" 6 | metadata={ 7 | "vram_texture": false 8 | } 9 | 10 | [deps] 11 | 12 | source_file="res://addons/blackjack_engine_godot/icon.png" 13 | dest_files=[ "res://.import/icon.png-7707978c51283c602ffb5677fbc4d2b3.stex" ] 14 | 15 | [params] 16 | 17 | compress/mode=0 18 | compress/lossy_quality=0.7 19 | compress/hdr_mode=0 20 | compress/bptc_ldr=0 21 | compress/normal_map=0 22 | flags/repeat=0 23 | flags/filter=true 24 | flags/mipmaps=false 25 | flags/anisotropic=false 26 | flags/srgb=2 27 | process/fix_alpha_border=true 28 | process/premult_alpha=false 29 | process/HDR_as_SRGB=false 30 | process/invert_color=false 31 | process/normal_map_invert_y=false 32 | stream=false 33 | size_limit=0 34 | detect_3d=true 35 | svg/scale=1.0 36 | -------------------------------------------------------------------------------- /godot_plugin/addons/blackjack_engine_godot/plugin.cfg: -------------------------------------------------------------------------------- 1 | [plugin] 2 | 3 | name="BlackJackEngineGodot" 4 | description="Game engine integration for Blackjack: A procedural, node-based modelling tool, made in rust." 5 | author="setzer22" 6 | version="0.1" 7 | script="plugin.gd" 8 | -------------------------------------------------------------------------------- /godot_plugin/addons/blackjack_engine_godot/plugin.gd: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2023 setzer22 and contributors 2 | # 3 | # This Source Code Form is subject to the terms of the Mozilla Public 4 | # License, v. 2.0. If a copy of the MPL was not distributed with this 5 | # file, You can obtain one at https://mozilla.org/MPL/2.0/. 6 | 7 | tool 8 | extends EditorPlugin 9 | 10 | var inspector_plugin 11 | var import_plugin 12 | 13 | func _enter_tree(): 14 | if !ProjectSettings.has_setting("Blackjack/library_path"): 15 | ProjectSettings.set_setting("Blackjack/library_path", "res://blackjack_lua") 16 | 17 | add_custom_type( 18 | "BlackjackJack", 19 | "Spatial", 20 | preload("BlackjackJack.gd"), 21 | preload("icon.png") 22 | ) 23 | 24 | inspector_plugin = preload("BlackjackInspectorPlugin.gd").new() 25 | add_inspector_plugin(inspector_plugin) 26 | 27 | import_plugin = preload("JackImportPlugin.gd").new() 28 | add_import_plugin(import_plugin) 29 | 30 | func _exit_tree(): 31 | remove_inspector_plugin(inspector_plugin) 32 | remove_import_plugin(import_plugin) 33 | remove_custom_type("BlackjackJack") 34 | -------------------------------------------------------------------------------- /reinstall_godot_plugin.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | if [ -z "$1" ] 4 | then 5 | echo "Expected an argument with the path to the project" 6 | exit 1 7 | fi 8 | 9 | GODOT_PROJECT="$1" 10 | 11 | ./build_godot_plugin.sh && \ 12 | cp target/blackjack_engine_godot.zip $GODOT_PROJECT && \ 13 | pushd $GODOT_PROJECT && \ 14 | rm -r addons; \ 15 | rm -r blackjack_lua; \ 16 | unzip blackjack_engine_godot.zip; \ 17 | rm blackjack_engine_godot.zip && \ 18 | popd 19 | --------------------------------------------------------------------------------