├── .gitignore ├── Cargo.lock ├── Cargo.toml ├── LICENSE ├── README.md ├── ammolite-math ├── .gitignore ├── Cargo.lock ├── Cargo.toml └── src │ ├── lib.rs │ ├── macros.rs │ ├── matrix.rs │ ├── ops.rs │ └── vector.rs └── src ├── buffer.rs ├── camera.rs ├── iter └── mod.rs ├── lib.rs ├── model ├── error.rs ├── import.rs ├── mod.rs └── resource.rs ├── pipeline.rs ├── sampler.rs ├── shaders.rs ├── shaders ├── gltf.vert ├── gltf_blend_finalize.frag ├── gltf_blend_preprocess.frag ├── gltf_common.frag ├── gltf_common.h ├── gltf_common_inputs.frag ├── gltf_common_uniforms.h ├── gltf_mask.frag └── gltf_opaque.frag ├── swapchain.rs └── vertex.rs /.gitignore: -------------------------------------------------------------------------------- 1 | /resources 2 | /target 3 | **/*.rs.bk 4 | **/*.cap 5 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "ammolite" 3 | version = "0.1.0" 4 | authors = ["Jakub Hlusička "] 5 | edition = "2018" 6 | 7 | [dependencies] 8 | ammolite-math = { path = "ammolite-math", default-features = false } 9 | # vulkano = "0.11.1" 10 | # vulkano-shaders = "0.11.0" 11 | # vulkano-win = "0.11.0" 12 | arr_macro = "0.1.1" 13 | arrayvec = "0.4.10" 14 | boolinator = "2.4.0" 15 | byteorder = "1.2.4" 16 | det = "0.1.0" 17 | failure = "0.1.2" 18 | fnv = "1.0.6" 19 | generic-array = "0.12.0" 20 | gltf = "0.14.0" 21 | image = "0.19.0" 22 | mikktspace = "0.1.1" 23 | openxr = { version = "0.9.4", features = ["static"] } 24 | # openxr = { path = "../openxrs/openxr", features = ["static"] } 25 | rayon = "1.0.2" 26 | safe-transmute = "0.10.1" 27 | tyenum = "0.1.0" 28 | typenum = "1.11.0" 29 | vulkano = { git = "https://github.com/Limeth/vulkano", branch = "feature-swapchain-image-trait" } 30 | vulkano-shaders = { git = "https://github.com/Limeth/vulkano", branch = "feature-swapchain-image-trait" } 31 | vulkano-win = { git = "https://github.com/Limeth/vulkano", branch = "feature-swapchain-image-trait" } 32 | # vulkano = { path = "../vulkano/vulkano" } 33 | # vulkano-shaders = { path = "../vulkano/vulkano-shaders" } 34 | # vulkano-win = { path = "../vulkano/vulkano-win" } 35 | weak-table = "0.2.3" 36 | # winit = "0.22.0" 37 | winit = { git = "https://github.com/rust-windowing/winit.git" } 38 | paste = "0.1" 39 | smallvec = "0.6.10" 40 | itertools = "0.8.1" 41 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | GNU LESSER GENERAL PUBLIC LICENSE 2 | Version 3, 29 June 2007 3 | 4 | Copyright (C) 2007 Free Software Foundation, Inc. 5 | Everyone is permitted to copy and distribute verbatim copies 6 | of this license document, but changing it is not allowed. 7 | 8 | 9 | This version of the GNU Lesser General Public License incorporates 10 | the terms and conditions of version 3 of the GNU General Public 11 | License, supplemented by the additional permissions listed below. 12 | 13 | 0. Additional Definitions. 14 | 15 | As used herein, "this License" refers to version 3 of the GNU Lesser 16 | General Public License, and the "GNU GPL" refers to version 3 of the GNU 17 | General Public License. 18 | 19 | "The Library" refers to a covered work governed by this License, 20 | other than an Application or a Combined Work as defined below. 21 | 22 | An "Application" is any work that makes use of an interface provided 23 | by the Library, but which is not otherwise based on the Library. 24 | Defining a subclass of a class defined by the Library is deemed a mode 25 | of using an interface provided by the Library. 26 | 27 | A "Combined Work" is a work produced by combining or linking an 28 | Application with the Library. The particular version of the Library 29 | with which the Combined Work was made is also called the "Linked 30 | Version". 31 | 32 | The "Minimal Corresponding Source" for a Combined Work means the 33 | Corresponding Source for the Combined Work, excluding any source code 34 | for portions of the Combined Work that, considered in isolation, are 35 | based on the Application, and not on the Linked Version. 36 | 37 | The "Corresponding Application Code" for a Combined Work means the 38 | object code and/or source code for the Application, including any data 39 | and utility programs needed for reproducing the Combined Work from the 40 | Application, but excluding the System Libraries of the Combined Work. 41 | 42 | 1. Exception to Section 3 of the GNU GPL. 43 | 44 | You may convey a covered work under sections 3 and 4 of this License 45 | without being bound by section 3 of the GNU GPL. 46 | 47 | 2. Conveying Modified Versions. 48 | 49 | If you modify a copy of the Library, and, in your modifications, a 50 | facility refers to a function or data to be supplied by an Application 51 | that uses the facility (other than as an argument passed when the 52 | facility is invoked), then you may convey a copy of the modified 53 | version: 54 | 55 | a) under this License, provided that you make a good faith effort to 56 | ensure that, in the event an Application does not supply the 57 | function or data, the facility still operates, and performs 58 | whatever part of its purpose remains meaningful, or 59 | 60 | b) under the GNU GPL, with none of the additional permissions of 61 | this License applicable to that copy. 62 | 63 | 3. Object Code Incorporating Material from Library Header Files. 64 | 65 | The object code form of an Application may incorporate material from 66 | a header file that is part of the Library. You may convey such object 67 | code under terms of your choice, provided that, if the incorporated 68 | material is not limited to numerical parameters, data structure 69 | layouts and accessors, or small macros, inline functions and templates 70 | (ten or fewer lines in length), you do both of the following: 71 | 72 | a) Give prominent notice with each copy of the object code that the 73 | Library is used in it and that the Library and its use are 74 | covered by this License. 75 | 76 | b) Accompany the object code with a copy of the GNU GPL and this license 77 | document. 78 | 79 | 4. Combined Works. 80 | 81 | You may convey a Combined Work under terms of your choice that, 82 | taken together, effectively do not restrict modification of the 83 | portions of the Library contained in the Combined Work and reverse 84 | engineering for debugging such modifications, if you also do each of 85 | the following: 86 | 87 | a) Give prominent notice with each copy of the Combined Work that 88 | the Library is used in it and that the Library and its use are 89 | covered by this License. 90 | 91 | b) Accompany the Combined Work with a copy of the GNU GPL and this license 92 | document. 93 | 94 | c) For a Combined Work that displays copyright notices during 95 | execution, include the copyright notice for the Library among 96 | these notices, as well as a reference directing the user to the 97 | copies of the GNU GPL and this license document. 98 | 99 | d) Do one of the following: 100 | 101 | 0) Convey the Minimal Corresponding Source under the terms of this 102 | License, and the Corresponding Application Code in a form 103 | suitable for, and under terms that permit, the user to 104 | recombine or relink the Application with a modified version of 105 | the Linked Version to produce a modified Combined Work, in the 106 | manner specified by section 6 of the GNU GPL for conveying 107 | Corresponding Source. 108 | 109 | 1) Use a suitable shared library mechanism for linking with the 110 | Library. A suitable mechanism is one that (a) uses at run time 111 | a copy of the Library already present on the user's computer 112 | system, and (b) will operate properly with a modified version 113 | of the Library that is interface-compatible with the Linked 114 | Version. 115 | 116 | e) Provide Installation Information, but only if you would otherwise 117 | be required to provide such information under section 6 of the 118 | GNU GPL, and only to the extent that such information is 119 | necessary to install and execute a modified version of the 120 | Combined Work produced by recombining or relinking the 121 | Application with a modified version of the Linked Version. (If 122 | you use option 4d0, the Installation Information must accompany 123 | the Minimal Corresponding Source and Corresponding Application 124 | Code. If you use option 4d1, you must provide the Installation 125 | Information in the manner specified by section 6 of the GNU GPL 126 | for conveying Corresponding Source.) 127 | 128 | 5. Combined Libraries. 129 | 130 | You may place library facilities that are a work based on the 131 | Library side by side in a single library together with other library 132 | facilities that are not Applications and are not covered by this 133 | License, and convey such a combined library under terms of your 134 | choice, if you do both of the following: 135 | 136 | a) Accompany the combined library with a copy of the same work based 137 | on the Library, uncombined with any other library facilities, 138 | conveyed under the terms of this License. 139 | 140 | b) Give prominent notice with the combined library that part of it 141 | is a work based on the Library, and explaining where to find the 142 | accompanying uncombined form of the same work. 143 | 144 | 6. Revised Versions of the GNU Lesser General Public License. 145 | 146 | The Free Software Foundation may publish revised and/or new versions 147 | of the GNU Lesser General Public License from time to time. Such new 148 | versions will be similar in spirit to the present version, but may 149 | differ in detail to address new problems or concerns. 150 | 151 | Each version is given a distinguishing version number. If the 152 | Library as you received it specifies that a certain numbered version 153 | of the GNU Lesser General Public License "or any later version" 154 | applies to it, you have the option of following the terms and 155 | conditions either of that published version or of any later version 156 | published by the Free Software Foundation. If the Library as you 157 | received it does not specify a version number of the GNU Lesser 158 | General Public License, you may choose any version of the GNU Lesser 159 | General Public License ever published by the Free Software Foundation. 160 | 161 | If the Library as you received it specifies that a proxy can decide 162 | whether future versions of the GNU Lesser General Public License shall 163 | apply, that proxy's public statement of acceptance of any version is 164 | permanent authorization for you to choose that version for the 165 | Library. 166 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ammolite 2 | Physically based glTF rendering with Vulkan in Rust 3 | 4 | This repository contains highly experimental code, proceed with caution. 5 | 6 | ## Design goals 7 | 8 | * An elegant implementation 9 | * Up-to-date real-time physically based rendering techniques 10 | * Support for glTF models (not necessarilly fully conformant to the glTF spec) 11 | * Thin layer on top of [vulkano](https://github.com/vulkano-rs/vulkano) 12 | * Expose the [vulkano](https://github.com/vulkano-rs/vulkano) API to allow for high customizability 13 | * Easily switch between rendering techniques (multiple BRDF implementations) 14 | -------------------------------------------------------------------------------- /ammolite-math/.gitignore: -------------------------------------------------------------------------------- 1 | /resources 2 | /target 3 | **/*.rs.bk 4 | **/*.cap 5 | -------------------------------------------------------------------------------- /ammolite-math/Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | [[package]] 4 | name = "alga" 5 | version = "0.9.3" 6 | source = "registry+https://github.com/rust-lang/crates.io-index" 7 | dependencies = [ 8 | "approx 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", 9 | "num-complex 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)", 10 | "num-traits 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", 11 | ] 12 | 13 | [[package]] 14 | name = "ammolite-math" 15 | version = "0.1.0" 16 | dependencies = [ 17 | "det 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", 18 | "generic-array 0.13.2 (registry+https://github.com/rust-lang/crates.io-index)", 19 | "nalgebra 0.20.0 (registry+https://github.com/rust-lang/crates.io-index)", 20 | "paste 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", 21 | "serde 1.0.103 (registry+https://github.com/rust-lang/crates.io-index)", 22 | "typenum 1.11.2 (registry+https://github.com/rust-lang/crates.io-index)", 23 | ] 24 | 25 | [[package]] 26 | name = "approx" 27 | version = "0.3.2" 28 | source = "registry+https://github.com/rust-lang/crates.io-index" 29 | dependencies = [ 30 | "num-traits 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", 31 | ] 32 | 33 | [[package]] 34 | name = "autocfg" 35 | version = "1.0.0" 36 | source = "registry+https://github.com/rust-lang/crates.io-index" 37 | 38 | [[package]] 39 | name = "c2-chacha" 40 | version = "0.2.3" 41 | source = "registry+https://github.com/rust-lang/crates.io-index" 42 | dependencies = [ 43 | "ppv-lite86 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", 44 | ] 45 | 46 | [[package]] 47 | name = "cfg-if" 48 | version = "0.1.10" 49 | source = "registry+https://github.com/rust-lang/crates.io-index" 50 | 51 | [[package]] 52 | name = "det" 53 | version = "0.1.0" 54 | source = "registry+https://github.com/rust-lang/crates.io-index" 55 | 56 | [[package]] 57 | name = "generic-array" 58 | version = "0.13.2" 59 | source = "registry+https://github.com/rust-lang/crates.io-index" 60 | dependencies = [ 61 | "typenum 1.11.2 (registry+https://github.com/rust-lang/crates.io-index)", 62 | ] 63 | 64 | [[package]] 65 | name = "getrandom" 66 | version = "0.1.14" 67 | source = "registry+https://github.com/rust-lang/crates.io-index" 68 | dependencies = [ 69 | "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", 70 | "libc 0.2.67 (registry+https://github.com/rust-lang/crates.io-index)", 71 | "wasi 0.9.0+wasi-snapshot-preview1 (registry+https://github.com/rust-lang/crates.io-index)", 72 | ] 73 | 74 | [[package]] 75 | name = "libc" 76 | version = "0.2.67" 77 | source = "registry+https://github.com/rust-lang/crates.io-index" 78 | 79 | [[package]] 80 | name = "libm" 81 | version = "0.2.1" 82 | source = "registry+https://github.com/rust-lang/crates.io-index" 83 | 84 | [[package]] 85 | name = "matrixmultiply" 86 | version = "0.2.3" 87 | source = "registry+https://github.com/rust-lang/crates.io-index" 88 | dependencies = [ 89 | "rawpointer 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", 90 | ] 91 | 92 | [[package]] 93 | name = "nalgebra" 94 | version = "0.20.0" 95 | source = "registry+https://github.com/rust-lang/crates.io-index" 96 | dependencies = [ 97 | "alga 0.9.3 (registry+https://github.com/rust-lang/crates.io-index)", 98 | "approx 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", 99 | "generic-array 0.13.2 (registry+https://github.com/rust-lang/crates.io-index)", 100 | "matrixmultiply 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", 101 | "num-complex 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)", 102 | "num-rational 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", 103 | "num-traits 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", 104 | "rand 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)", 105 | "rand_distr 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", 106 | "typenum 1.11.2 (registry+https://github.com/rust-lang/crates.io-index)", 107 | ] 108 | 109 | [[package]] 110 | name = "num-complex" 111 | version = "0.2.4" 112 | source = "registry+https://github.com/rust-lang/crates.io-index" 113 | dependencies = [ 114 | "autocfg 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", 115 | "num-traits 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", 116 | ] 117 | 118 | [[package]] 119 | name = "num-integer" 120 | version = "0.1.42" 121 | source = "registry+https://github.com/rust-lang/crates.io-index" 122 | dependencies = [ 123 | "autocfg 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", 124 | "num-traits 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", 125 | ] 126 | 127 | [[package]] 128 | name = "num-rational" 129 | version = "0.2.3" 130 | source = "registry+https://github.com/rust-lang/crates.io-index" 131 | dependencies = [ 132 | "autocfg 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", 133 | "num-integer 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)", 134 | "num-traits 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", 135 | ] 136 | 137 | [[package]] 138 | name = "num-traits" 139 | version = "0.2.11" 140 | source = "registry+https://github.com/rust-lang/crates.io-index" 141 | dependencies = [ 142 | "autocfg 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", 143 | "libm 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", 144 | ] 145 | 146 | [[package]] 147 | name = "paste" 148 | version = "0.1.7" 149 | source = "registry+https://github.com/rust-lang/crates.io-index" 150 | dependencies = [ 151 | "paste-impl 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", 152 | "proc-macro-hack 0.5.11 (registry+https://github.com/rust-lang/crates.io-index)", 153 | ] 154 | 155 | [[package]] 156 | name = "paste-impl" 157 | version = "0.1.7" 158 | source = "registry+https://github.com/rust-lang/crates.io-index" 159 | dependencies = [ 160 | "proc-macro-hack 0.5.11 (registry+https://github.com/rust-lang/crates.io-index)", 161 | "proc-macro2 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", 162 | "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", 163 | "syn 1.0.11 (registry+https://github.com/rust-lang/crates.io-index)", 164 | ] 165 | 166 | [[package]] 167 | name = "ppv-lite86" 168 | version = "0.2.6" 169 | source = "registry+https://github.com/rust-lang/crates.io-index" 170 | 171 | [[package]] 172 | name = "proc-macro-hack" 173 | version = "0.5.11" 174 | source = "registry+https://github.com/rust-lang/crates.io-index" 175 | dependencies = [ 176 | "proc-macro2 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", 177 | "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", 178 | "syn 1.0.11 (registry+https://github.com/rust-lang/crates.io-index)", 179 | ] 180 | 181 | [[package]] 182 | name = "proc-macro2" 183 | version = "1.0.6" 184 | source = "registry+https://github.com/rust-lang/crates.io-index" 185 | dependencies = [ 186 | "unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", 187 | ] 188 | 189 | [[package]] 190 | name = "quote" 191 | version = "1.0.2" 192 | source = "registry+https://github.com/rust-lang/crates.io-index" 193 | dependencies = [ 194 | "proc-macro2 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", 195 | ] 196 | 197 | [[package]] 198 | name = "rand" 199 | version = "0.7.3" 200 | source = "registry+https://github.com/rust-lang/crates.io-index" 201 | dependencies = [ 202 | "getrandom 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)", 203 | "libc 0.2.67 (registry+https://github.com/rust-lang/crates.io-index)", 204 | "rand_chacha 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", 205 | "rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", 206 | "rand_hc 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", 207 | ] 208 | 209 | [[package]] 210 | name = "rand_chacha" 211 | version = "0.2.1" 212 | source = "registry+https://github.com/rust-lang/crates.io-index" 213 | dependencies = [ 214 | "c2-chacha 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", 215 | "rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", 216 | ] 217 | 218 | [[package]] 219 | name = "rand_core" 220 | version = "0.5.1" 221 | source = "registry+https://github.com/rust-lang/crates.io-index" 222 | dependencies = [ 223 | "getrandom 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)", 224 | ] 225 | 226 | [[package]] 227 | name = "rand_distr" 228 | version = "0.2.2" 229 | source = "registry+https://github.com/rust-lang/crates.io-index" 230 | dependencies = [ 231 | "rand 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)", 232 | ] 233 | 234 | [[package]] 235 | name = "rand_hc" 236 | version = "0.2.0" 237 | source = "registry+https://github.com/rust-lang/crates.io-index" 238 | dependencies = [ 239 | "rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", 240 | ] 241 | 242 | [[package]] 243 | name = "rawpointer" 244 | version = "0.2.1" 245 | source = "registry+https://github.com/rust-lang/crates.io-index" 246 | 247 | [[package]] 248 | name = "serde" 249 | version = "1.0.103" 250 | source = "registry+https://github.com/rust-lang/crates.io-index" 251 | dependencies = [ 252 | "serde_derive 1.0.103 (registry+https://github.com/rust-lang/crates.io-index)", 253 | ] 254 | 255 | [[package]] 256 | name = "serde_derive" 257 | version = "1.0.103" 258 | source = "registry+https://github.com/rust-lang/crates.io-index" 259 | dependencies = [ 260 | "proc-macro2 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", 261 | "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", 262 | "syn 1.0.11 (registry+https://github.com/rust-lang/crates.io-index)", 263 | ] 264 | 265 | [[package]] 266 | name = "syn" 267 | version = "1.0.11" 268 | source = "registry+https://github.com/rust-lang/crates.io-index" 269 | dependencies = [ 270 | "proc-macro2 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", 271 | "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", 272 | "unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", 273 | ] 274 | 275 | [[package]] 276 | name = "typenum" 277 | version = "1.11.2" 278 | source = "registry+https://github.com/rust-lang/crates.io-index" 279 | 280 | [[package]] 281 | name = "unicode-xid" 282 | version = "0.2.0" 283 | source = "registry+https://github.com/rust-lang/crates.io-index" 284 | 285 | [[package]] 286 | name = "wasi" 287 | version = "0.9.0+wasi-snapshot-preview1" 288 | source = "registry+https://github.com/rust-lang/crates.io-index" 289 | 290 | [metadata] 291 | "checksum alga 0.9.3 (registry+https://github.com/rust-lang/crates.io-index)" = "4f823d037a7ec6ea2197046bafd4ae150e6bc36f9ca347404f46a46823fa84f2" 292 | "checksum approx 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f0e60b75072ecd4168020818c0107f2857bb6c4e64252d8d3983f6263b40a5c3" 293 | "checksum autocfg 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f8aac770f1885fd7e387acedd76065302551364496e46b3dd00860b2f8359b9d" 294 | "checksum c2-chacha 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "214238caa1bf3a496ec3392968969cab8549f96ff30652c9e56885329315f6bb" 295 | "checksum cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)" = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" 296 | "checksum det 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "811daaf89cb7b96652e6d3ef8044ee6418d1c3804d8cde198197303e27163022" 297 | "checksum generic-array 0.13.2 (registry+https://github.com/rust-lang/crates.io-index)" = "0ed1e761351b56f54eb9dcd0cfaca9fd0daecf93918e1cfc01c8a3d26ee7adcd" 298 | "checksum getrandom 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)" = "7abc8dd8451921606d809ba32e95b6111925cd2906060d2dcc29c070220503eb" 299 | "checksum libc 0.2.67 (registry+https://github.com/rust-lang/crates.io-index)" = "eb147597cdf94ed43ab7a9038716637d2d1bf2bc571da995d0028dec06bd3018" 300 | "checksum libm 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "c7d73b3f436185384286bd8098d17ec07c9a7d2388a6599f824d8502b529702a" 301 | "checksum matrixmultiply 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "d4f7ec66360130972f34830bfad9ef05c6610a43938a467bcc9ab9369ab3478f" 302 | "checksum nalgebra 0.20.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c6511777ed3da44b6a11e732a66a7d6274dfbbcd68ad968e64b778dcb829d94a" 303 | "checksum num-complex 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)" = "b6b19411a9719e753aff12e5187b74d60d3dc449ec3f4dc21e3989c3f554bc95" 304 | "checksum num-integer 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)" = "3f6ea62e9d81a77cd3ee9a2a5b9b609447857f3d358704331e4ef39eb247fcba" 305 | "checksum num-rational 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "da4dc79f9e6c81bef96148c8f6b8e72ad4541caa4a24373e900a36da07de03a3" 306 | "checksum num-traits 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "c62be47e61d1842b9170f0fdeec8eba98e60e90e5446449a0545e5152acd7096" 307 | "checksum paste 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "63e1afe738d71b1ebab5f1207c055054015427dbfc7bbe9ee1266894156ec046" 308 | "checksum paste-impl 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "6d4dc4a7f6f743211c5aab239640a65091535d97d43d92a52bca435a640892bb" 309 | "checksum ppv-lite86 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "74490b50b9fbe561ac330df47c08f3f33073d2d00c150f719147d7c54522fa1b" 310 | "checksum proc-macro-hack 0.5.11 (registry+https://github.com/rust-lang/crates.io-index)" = "ecd45702f76d6d3c75a80564378ae228a85f0b59d2f3ed43c91b4a69eb2ebfc5" 311 | "checksum proc-macro2 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)" = "9c9e470a8dc4aeae2dee2f335e8f533e2d4b347e1434e5671afc49b054592f27" 312 | "checksum quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "053a8c8bcc71fcce321828dc897a98ab9760bef03a4fc36693c231e5b3216cfe" 313 | "checksum rand 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)" = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03" 314 | "checksum rand_chacha 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "03a2a90da8c7523f554344f921aa97283eadf6ac484a6d2a7d0212fa7f8d6853" 315 | "checksum rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" 316 | "checksum rand_distr 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "96977acbdd3a6576fb1d27391900035bf3863d4a16422973a409b488cf29ffb2" 317 | "checksum rand_hc 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c" 318 | "checksum rawpointer 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "60a357793950651c4ed0f3f52338f53b2f809f32d83a07f72909fa13e4c6c1e3" 319 | "checksum serde 1.0.103 (registry+https://github.com/rust-lang/crates.io-index)" = "1217f97ab8e8904b57dd22eb61cde455fa7446a9c1cf43966066da047c1f3702" 320 | "checksum serde_derive 1.0.103 (registry+https://github.com/rust-lang/crates.io-index)" = "a8c6faef9a2e64b0064f48570289b4bf8823b7581f1d6157c1b52152306651d0" 321 | "checksum syn 1.0.11 (registry+https://github.com/rust-lang/crates.io-index)" = "dff0acdb207ae2fe6d5976617f887eb1e35a2ba52c13c7234c790960cdad9238" 322 | "checksum typenum 1.11.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6d2783fe2d6b8c1101136184eb41be8b1ad379e4657050b8aaff0c79ee7575f9" 323 | "checksum unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "826e7639553986605ec5979c7dd957c7895e93eabed50ab2ffa7f6128a75097c" 324 | "checksum wasi 0.9.0+wasi-snapshot-preview1 (registry+https://github.com/rust-lang/crates.io-index)" = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" 325 | -------------------------------------------------------------------------------- /ammolite-math/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "ammolite-math" 3 | version = "0.1.0" 4 | authors = ["Jakub Hlusička "] 5 | edition = "2018" 6 | 7 | [features] 8 | default = [ "nalgebra-interop" ] 9 | nalgebra-interop = [ "na" ] 10 | 11 | [dependencies] 12 | typenum = "1.10.0" 13 | det = "0.1.0" 14 | generic-array = "0.13.2" 15 | serde = { version = "1.0", features = ["derive"] } 16 | na = { version = "0.20", package = "nalgebra", optional = true } 17 | paste = "0.1" 18 | -------------------------------------------------------------------------------- /ammolite-math/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![feature(const_if_match)] 2 | #![feature(const_loop)] 3 | 4 | #[macro_use] 5 | pub mod ops; 6 | #[macro_use] 7 | pub mod macros; 8 | #[macro_use] 9 | pub mod vector; 10 | #[macro_use] 11 | pub mod matrix; 12 | 13 | pub use vector::*; 14 | pub use matrix::*; 15 | pub use ops::{DivEuclid, RemEuclid}; 16 | -------------------------------------------------------------------------------- /ammolite-math/src/macros.rs: -------------------------------------------------------------------------------- 1 | macro_rules! impl_mat_macro { 2 | ($ty_name:ident, $dims:expr, $macro_name:ident, $vector_ty_name:ident) => { 3 | #[allow(unused)] 4 | #[macro_export] 5 | macro_rules! $macro_name { 6 | { 7 | $component_array:expr 8 | } => {{ 9 | use $crate::{Matrix, $ty_name}; 10 | let components: [f32; $dims * $dims] = $component_array; 11 | let mut result = <$ty_name as Matrix>::ZERO; 12 | 13 | for (index, component) in components.into_iter().enumerate() { 14 | let column = index % $dims; 15 | let row = index / $dims; 16 | 17 | result.as_mut()[column][row] = *component; 18 | } 19 | 20 | result 21 | }}; 22 | } 23 | } 24 | } 25 | 26 | impl_mat_macro!(Mat1, 1, mat1, Vec1); 27 | impl_mat_macro!(Mat2, 2, mat2, Vec2); 28 | impl_mat_macro!(Mat3, 3, mat3, Vec3); 29 | impl_mat_macro!(Mat4, 4, mat4, Vec4); 30 | -------------------------------------------------------------------------------- /ammolite-math/src/matrix.rs: -------------------------------------------------------------------------------- 1 | use std::mem; 2 | use std::slice; 3 | use std::ops::{Div, Add, Deref, DerefMut, Mul, Neg}; 4 | use std::fmt::{Debug, Formatter, Error}; 5 | use serde::{Deserialize, Serialize}; 6 | use typenum::Unsigned; 7 | use crate::vector::*; 8 | 9 | pub trait Matrix: Neg + Mul + Mul + PartialEq + Sized + Clone + Debug { 10 | type Vector: Vector; 11 | type LowerDim: Matrix; 12 | 13 | const DIM: usize; 14 | const ZERO: Self; 15 | const IDENTITY: Self; 16 | 17 | /** 18 | * A matrix with 1 fewer dimensions, with given row and column removed. 19 | */ 20 | fn submatrix(&self, row: usize, col: usize) -> Self::LowerDim; 21 | 22 | /** 23 | * The determinant of the matrix with given row and column removed. 24 | * (The determinant of the given submatrix.) 25 | */ 26 | fn minor(&self, row: usize, col: usize) -> f32 { 27 | let submatrix = self.submatrix(row, col); 28 | submatrix.determinant() 29 | } 30 | 31 | /** 32 | * The minor, multiplied by `(-1)^(row + col)`. 33 | */ 34 | fn cofactor(&self, row: usize, col: usize) -> f32 { 35 | let minor = self.minor(row, col); 36 | if (row + col) % 2 == 0 { minor } else { -minor } 37 | } 38 | 39 | /** 40 | * A matrix made up of all cofactors of the current matrix. 41 | */ 42 | fn cofactor_matrix(&self) -> Self; 43 | fn determinant(&self) -> f32; 44 | fn transpose_mut(&mut self); 45 | 46 | fn transpose(&self) -> Self { 47 | let mut result = self.clone(); 48 | result.transpose_mut(); 49 | result 50 | } 51 | 52 | fn adjugate(&self) -> Self { 53 | let mut result = self.cofactor_matrix(); 54 | result.transpose_mut(); 55 | result 56 | } 57 | 58 | fn inverse(&self) -> Self { 59 | let determinant = self.determinant(); 60 | 61 | if determinant == 0.0 { 62 | panic!("Attempt to invert a non-invertible matrix."); 63 | } 64 | 65 | self.adjugate() * (1.0 / determinant) 66 | } 67 | } 68 | 69 | pub trait AffineTransformation: Matrix where V: Homogeneous { 70 | fn scale(coefficient: f32) -> Self; 71 | fn translation(translation: &>::ProjectedVector) -> Self; 72 | } 73 | 74 | pub trait Rotation3 where V: Homogeneous { 75 | fn rotation_yaw(yaw: f32) -> Self; 76 | fn rotation_pitch(pitch: f32) -> Self; 77 | fn rotation_roll(roll: f32) -> Self; 78 | } 79 | 80 | macro_rules! impl_mat { 81 | ($ty_name:ident, $lower_dim_ty_name:ty, $dims:expr, $macro_name:ident, $vector_ty_name:ident) => { 82 | #[derive(Clone, PartialEq, Deserialize, Serialize)] 83 | pub struct $ty_name([[f32; $dims]; $dims]); 84 | 85 | impl $ty_name { 86 | pub fn new(inner: [[f32; $dims]; $dims]) -> Self { 87 | Self(inner) 88 | } 89 | 90 | pub fn from_flat(matrix: [f32; $dims * $dims]) -> Self { 91 | let mut nested = [[0.0; $dims]; $dims]; 92 | 93 | for (index, item) in matrix.into_iter().enumerate() { 94 | nested[index % $dims][index / $dims] = *item; 95 | } 96 | 97 | nested.into() 98 | } 99 | 100 | pub fn as_ref(&self) -> &[[f32; $dims]; $dims] { 101 | &self.0 102 | } 103 | 104 | pub fn as_mut(&mut self) -> &mut [[f32; $dims]; $dims] { 105 | &mut self.0 106 | } 107 | 108 | pub fn as_flat_ref(&self) -> &[f32; $dims * $dims] { 109 | unsafe { mem::transmute(&self.0) } 110 | } 111 | 112 | pub fn as_flat_mut(&mut self) -> &mut [f32; $dims * $dims] { 113 | unsafe { mem::transmute(&mut self.0) } 114 | } 115 | 116 | pub fn as_slice_ref(&self) -> &[f32] { 117 | &self.as_flat_ref()[..] 118 | } 119 | 120 | pub fn as_slice_mut(&mut self) -> &mut [f32] { 121 | &mut self.as_flat_mut()[..] 122 | } 123 | 124 | pub fn inner(&self) -> &[[f32; $dims]; $dims] { 125 | &self.0 126 | } 127 | 128 | pub fn inner_mut(&mut self) -> &mut [[f32; $dims]; $dims] { 129 | &mut self.0 130 | } 131 | 132 | pub fn into_inner(self) -> [[f32; $dims]; $dims] { 133 | self.0 134 | } 135 | } 136 | 137 | impl Matrix for $ty_name { 138 | type Vector = $vector_ty_name; 139 | type LowerDim = $lower_dim_ty_name; 140 | 141 | const DIM: usize = $dims; 142 | const ZERO: Self = Self([[0.0; $dims]; $dims]); 143 | const IDENTITY: Self = { 144 | let mut result = Self::ZERO; 145 | let mut i = 0; 146 | 147 | while i < $dims { 148 | result.0[i][i] = 1.0; 149 | i += 1; 150 | } 151 | 152 | result 153 | }; 154 | 155 | fn cofactor_matrix(&self) -> Self { 156 | let mut result = Self::ZERO; 157 | 158 | for column in 0..$dims { 159 | for row in 0..$dims { 160 | result.0[column][row] = self.cofactor(row, column) 161 | } 162 | } 163 | 164 | result 165 | } 166 | 167 | fn determinant(&self) -> f32 { 168 | if $dims == 1 { 169 | self.0[0][0] 170 | } else { 171 | let mut result = 0.0; 172 | 173 | for col in 0..$dims { 174 | result += self.0[col][0] * self.cofactor(0, col); 175 | } 176 | 177 | result 178 | } 179 | } 180 | 181 | fn submatrix(&self, row: usize, col: usize) -> Self::LowerDim { 182 | if $dims <= 1 { 183 | panic!("Cannot get a submatrix of a matrix with dimension 1 or lower."); 184 | } 185 | 186 | let mut result = Self::LowerDim::ZERO; 187 | 188 | for result_col in 0..($dims - 1) { 189 | for result_row in 0..($dims - 1) { 190 | let source_col = result_col + if result_col >= col { 1 } else { 0 }; 191 | let source_row = result_row + if result_row >= row { 1 } else { 0 }; 192 | 193 | result.0[result_col][result_row] = self.0[source_col][source_row]; 194 | } 195 | } 196 | 197 | result 198 | } 199 | 200 | #[inline] 201 | fn transpose_mut(&mut self) { 202 | for column in 1..$dims { 203 | for row in 0..column { 204 | unsafe { 205 | let dst = slice::from_raw_parts_mut(self.as_mut_ptr(), self.len()); 206 | mem::swap(&mut self[column][row], &mut dst[row][column]); 207 | } 208 | } 209 | } 210 | } 211 | } 212 | 213 | impl Default for $ty_name { 214 | fn default() -> Self { 215 | Self::IDENTITY 216 | } 217 | } 218 | 219 | impl Neg for $ty_name { 220 | type Output = Self; 221 | 222 | #[inline] 223 | fn neg(mut self) -> Self::Output { 224 | for column in self.iter_mut() { 225 | for component in column { 226 | *component = -*component; 227 | } 228 | } 229 | 230 | self 231 | } 232 | } 233 | 234 | impl_binary_operator! { 235 | operator_type: [Mul]; 236 | inline: [false]; 237 | operator_fn: mul; 238 | generics: []; 239 | header: ($ty_name, $ty_name) -> $ty_name; 240 | |&lhs, &rhs| { 241 | let mut result = $ty_name::IDENTITY; 242 | 243 | for result_row in 0..$dims { 244 | for result_column in 0..$dims { 245 | let mut result_cell = 0.0; 246 | 247 | for cell_index in 0..$dims { 248 | result_cell += lhs[cell_index][result_row] * rhs[result_column][cell_index]; 249 | } 250 | 251 | result[result_column][result_row] = result_cell; 252 | } 253 | } 254 | 255 | result 256 | } 257 | } 258 | 259 | impl_binary_operator! { 260 | operator_type: [Mul]; 261 | inline: [false]; 262 | operator_fn: mul; 263 | generics: []; 264 | header: ($ty_name, <$ty_name as Matrix>::Vector) -> <$ty_name as Matrix>::Vector; 265 | |&lhs, &rhs| { 266 | let mut result = <<$ty_name as Matrix>::Vector as Vector>::ZERO; 267 | 268 | for (result_row, result_component) in result.iter_mut().enumerate() { 269 | for result_column in 0..$dims { 270 | *result_component += lhs[result_column][result_row] * rhs[result_column]; 271 | } 272 | } 273 | 274 | result 275 | } 276 | } 277 | 278 | impl_binary_operator! { 279 | operator_type: [Mul]; 280 | inline: [false]; 281 | operator_fn: mul; 282 | generics: []; 283 | header: ($ty_name, f32) -> $ty_name; 284 | |&lhs, &rhs| { 285 | let mut result = lhs.clone(); 286 | 287 | for column in result.iter_mut() { 288 | for component in column.iter_mut() { 289 | *component *= *rhs; 290 | } 291 | } 292 | 293 | result 294 | } 295 | } 296 | 297 | impl_binary_operator! { 298 | operator_type: [Div]; 299 | inline: [false]; 300 | operator_fn: div; 301 | generics: []; 302 | header: ($ty_name, f32) -> $ty_name; 303 | |&lhs, &rhs| { 304 | lhs * (1.0 / rhs) 305 | } 306 | } 307 | 308 | impl_binary_operator! { 309 | operator_type: [Add]; 310 | inline: [false]; 311 | operator_fn: add; 312 | generics: []; 313 | header: ($ty_name, $ty_name) -> $ty_name; 314 | |&lhs, &rhs| { 315 | let mut result = lhs.clone(); 316 | 317 | for (result_column, rhs_column) in result.iter_mut().zip(rhs.iter()) { 318 | for (result_component, rhs_component) in result_column.iter_mut().zip(rhs_column.iter()) { 319 | *result_component += *rhs_component; 320 | } 321 | } 322 | 323 | result 324 | } 325 | } 326 | 327 | impl Deref for $ty_name { 328 | type Target = [[f32; $dims]; $dims]; 329 | 330 | #[inline] 331 | fn deref(&self) -> &Self::Target { 332 | &self.0 333 | } 334 | } 335 | 336 | impl DerefMut for $ty_name { 337 | #[inline] 338 | fn deref_mut(&mut self) -> &mut ::Target { 339 | &mut self.0 340 | } 341 | } 342 | 343 | impl From<$ty_name> for [[f32; $dims]; $dims] { 344 | fn from(matrix: $ty_name) -> Self { 345 | *matrix 346 | } 347 | } 348 | 349 | impl From<[[f32; $dims]; $dims]> for $ty_name { 350 | fn from(matrix: [[f32; $dims]; $dims]) -> Self { 351 | Self::new(matrix) 352 | } 353 | } 354 | 355 | impl From<[f32; $dims * $dims]> for $ty_name { 356 | fn from(matrix: [f32; $dims * $dims]) -> Self { 357 | Self::from_flat(matrix) 358 | } 359 | } 360 | 361 | impl Debug for $ty_name { 362 | fn fmt(&self, f: &mut Formatter) -> Result<(), Error> { 363 | let prefix = format!("{ty}!([", ty=stringify!($macro_name)); 364 | 365 | write!(f, "{}", prefix)?; 366 | 367 | for row in 0..$dims { 368 | if row > 0 { 369 | write!(f, "\n")?; 370 | 371 | // offset the values 372 | for _ in 0..prefix.len() { 373 | write!(f, " ")?; 374 | } 375 | } 376 | 377 | for column in 0..$dims { 378 | if column > 0 { 379 | write!(f, "\t")?; 380 | } 381 | 382 | write!(f, "{:.4}", self[column][row])?; 383 | } 384 | } 385 | 386 | write!(f, "])")?; 387 | 388 | Ok(()) 389 | } 390 | } 391 | } 392 | } 393 | 394 | macro_rules! impl_affine_transformation { 395 | ($ty_name:ident, $vector_ty_name:ident) => { 396 | impl AffineTransformation<$vector_ty_name> for $ty_name { 397 | #[inline] 398 | fn scale(coefficient: f32) -> Self { 399 | let mut result = Self::IDENTITY; 400 | let dims = <<<$vector_ty_name as Homogeneous>::ProjectedVector as Vector>::Dimensions as Unsigned>::to_usize(); 401 | 402 | for i in 0..dims { 403 | result[i][i] = coefficient; 404 | } 405 | 406 | result 407 | } 408 | 409 | #[inline] 410 | fn translation(translation: &<$vector_ty_name as Homogeneous>::ProjectedVector) -> Self { 411 | let mut result = Self::IDENTITY; 412 | let dims = <<$vector_ty_name as Vector>::Dimensions as Unsigned>::to_usize(); 413 | 414 | for (index, component) in translation.iter().enumerate() { 415 | result[dims - 1][index] = *component; 416 | } 417 | 418 | result 419 | } 420 | } 421 | 422 | // Ambiguous transformation from ND to (N+1)D 423 | // impl_binary_operator! { 424 | // operator_type: [Mul]; 425 | // inline: [false]; 426 | // operator_fn: mul; 427 | // generics: []; 428 | // header: ($ty_name, <$vector_ty_name as Homogeneous>::ProjectedVector) -> <$vector_ty_name as Homogeneous>::ProjectedVector; 429 | // |&lhs, &rhs| { 430 | // (lhs * rhs.into_homogeneous{_position,_direction}()).into_projected() 431 | // } 432 | // } 433 | } 434 | } 435 | 436 | macro_rules! impl_conversion_to_homogeneous_space { 437 | ($ty_name_from:ident[$dim_from:expr] -> $ty_name_to:ident) => { 438 | impl $ty_name_from { 439 | pub fn to_homogeneous(self) -> $ty_name_to { 440 | let mut result = $ty_name_to::IDENTITY; 441 | 442 | for row in 0..$dim_from { 443 | for column in 0..$dim_from { 444 | result[column][row] = self[column][row]; 445 | } 446 | } 447 | 448 | result 449 | } 450 | } 451 | } 452 | } 453 | 454 | impl_mat!(Mat1, Mat1, 1, mat1, Vec1); 455 | impl_mat!(Mat2, Mat1, 2, mat2, Vec2); 456 | impl_mat!(Mat3, Mat2, 3, mat3, Vec3); 457 | impl_mat!(Mat4, Mat3, 4, mat4, Vec4); 458 | 459 | impl_conversion_to_homogeneous_space!(Mat1[1] -> Mat2); 460 | impl_conversion_to_homogeneous_space!(Mat2[2] -> Mat3); 461 | impl_conversion_to_homogeneous_space!(Mat3[3] -> Mat4); 462 | 463 | impl_affine_transformation!(Mat2, Vec2); 464 | impl_affine_transformation!(Mat3, Vec3); 465 | impl_affine_transformation!(Mat4, Vec4); 466 | 467 | impl Mat3 { 468 | pub fn from_quaternion(quaternion: [f32; 4]) -> Self { 469 | let [qx, qy, qz, qw] = quaternion; 470 | 471 | // source: https://www.euclideanspace.com/maths/geometry/rotations/conversions/quaternionToMatrix/index.htm 472 | mat3!([1.0 - 2.0 * (qy*qy + qz*qz), 2.0 * (qx*qy - qz*qw), 2.0 * (qx*qz + qy*qw), 473 | 2.0 * (qx*qy + qz*qw), 1.0 - 2.0 * (qx*qx + qz*qz), 2.0 * (qy*qz - qx*qw), 474 | 2.0 * (qx*qz - qy*qw), 2.0 * (qy*qz + qx*qw), 1.0 - 2.0 * (qx*qx + qy*qy)]) 475 | } 476 | } 477 | 478 | #[cfg(feature = "nalgebra-interop")] 479 | impl<'a> From<&'a na::base::MatrixN> for Mat3 { 480 | fn from(other: &'a na::base::MatrixN) -> Self { 481 | let mut result = Mat3::ZERO; 482 | 483 | for (column_index, column) in other.column_iter().enumerate() { 484 | for (row_index, component) in column.iter().enumerate() { 485 | result[column_index][row_index] = *component; 486 | } 487 | } 488 | 489 | result 490 | } 491 | } 492 | 493 | #[cfg(feature = "nalgebra-interop")] 494 | impl<'a> From<&'a na::base::MatrixN> for Mat4 { 495 | fn from(other: &'a na::base::MatrixN) -> Self { 496 | let mut result = Mat4::ZERO; 497 | 498 | for (column_index, column) in other.column_iter().enumerate() { 499 | for (row_index, component) in column.iter().enumerate() { 500 | result[column_index][row_index] = *component; 501 | } 502 | } 503 | 504 | result 505 | } 506 | } 507 | 508 | #[cfg(feature = "nalgebra-interop")] 509 | impl<'a> From<&'a na::base::MatrixN> for Mat4 { 510 | fn from(other: &'a na::base::MatrixN) -> Self { 511 | (&other.to_homogeneous()).into() 512 | } 513 | } 514 | 515 | #[cfg(feature = "nalgebra-interop")] 516 | impl<'a> From<&'a na::geometry::Rotation> for Mat3 { 517 | fn from(other: &'a na::geometry::Rotation) -> Self { 518 | other.matrix().into() 519 | } 520 | } 521 | 522 | #[cfg(feature = "nalgebra-interop")] 523 | impl<'a> From<&'a na::geometry::Rotation> for Mat4 { 524 | fn from(other: &'a na::geometry::Rotation) -> Self { 525 | other.matrix().into() 526 | } 527 | } 528 | 529 | #[cfg(feature = "nalgebra-interop")] 530 | impl<'a, C: na::geometry::TCategory> From<&'a na::geometry::Transform> for Mat4 { 531 | fn from(other: &'a na::geometry::Transform) -> Self { 532 | other.matrix().into() 533 | } 534 | } 535 | 536 | #[cfg(feature = "nalgebra-interop")] 537 | impl<'a> From<&'a na::geometry::UnitQuaternion> for Mat3 { 538 | fn from(other: &'a na::geometry::UnitQuaternion) -> Self { 539 | (&other.clone().to_rotation_matrix()).into() 540 | } 541 | } 542 | 543 | #[cfg(feature = "nalgebra-interop")] 544 | impl<'a> From<&'a na::geometry::UnitQuaternion> for Mat4 { 545 | fn from(other: &'a na::geometry::UnitQuaternion) -> Self { 546 | (&other.clone().to_rotation_matrix()).into() 547 | } 548 | } 549 | 550 | #[cfg(feature = "nalgebra-interop")] 551 | impl<'a> From<&'a na::geometry::Translation> for Mat4 { 552 | fn from(other: &'a na::geometry::Translation) -> Self { 553 | Mat4::translation(&other.vector.into()) 554 | } 555 | } 556 | 557 | #[cfg(feature = "nalgebra-interop")] 558 | impl<'a> From<&'a na::geometry::Isometry>> for Mat4 { 559 | fn from(other: &'a na::geometry::Isometry>) -> Self { 560 | (&other.clone().to_homogeneous()).into() 561 | } 562 | } 563 | 564 | #[cfg(feature = "nalgebra-interop")] 565 | impl<'a> From<&'a na::geometry::Isometry>> for Mat4 { 566 | fn from(other: &'a na::geometry::Isometry>) -> Self { 567 | (&other.clone().to_homogeneous()).into() 568 | } 569 | } 570 | 571 | impl Rotation3 for Mat3 { 572 | #[inline] 573 | fn rotation_pitch(pitch: f32) -> Self { 574 | mat3!([1.0, 0.0, 0.0, 575 | 0.0, pitch.cos(), -pitch.sin(), 576 | 0.0, pitch.sin(), pitch.cos()]) 577 | } 578 | 579 | #[inline] 580 | fn rotation_yaw(yaw: f32) -> Self { 581 | mat3!([ yaw.cos(), 0.0, yaw.sin(), 582 | 0.0, 1.0, 0.0, 583 | -yaw.sin(), 0.0, yaw.cos()]) 584 | } 585 | 586 | #[inline] 587 | fn rotation_roll(roll: f32) -> Self { 588 | mat3!([roll.cos(), -roll.sin(), 0.0, 589 | roll.sin(), roll.cos(), 0.0, 590 | 0.0, 0.0, 1.0]) 591 | } 592 | } 593 | 594 | impl Rotation3 for Mat4 { 595 | #[inline] 596 | fn rotation_pitch(pitch: f32) -> Self { 597 | mat4!([1.0, 0.0, 0.0, 0.0, 598 | 0.0, pitch.cos(), -pitch.sin(), 0.0, 599 | 0.0, pitch.sin(), pitch.cos(), 0.0, 600 | 0.0, 0.0, 0.0, 1.0]) 601 | } 602 | 603 | #[inline] 604 | fn rotation_yaw(yaw: f32) -> Self { 605 | mat4!([ yaw.cos(), 0.0, yaw.sin(), 0.0, 606 | 0.0, 1.0, 0.0, 0.0, 607 | -yaw.sin(), 0.0, yaw.cos(), 0.0, 608 | 0.0, 0.0, 0.0, 1.0]) 609 | } 610 | 611 | #[inline] 612 | fn rotation_roll(roll: f32) -> Self { 613 | mat4!([roll.cos(), -roll.sin(), 0.0, 0.0, 614 | roll.sin(), roll.cos(), 0.0, 0.0, 615 | 0.0, 0.0, 1.0, 0.0, 616 | 0.0, 0.0, 0.0, 1.0]) 617 | } 618 | } 619 | 620 | #[cfg(test)] 621 | mod tests { 622 | use std::f32::consts; 623 | use super::*; 624 | 625 | #[test] 626 | fn matrix_construction() { 627 | let using_constructor = Mat3([[00.0, 10.0, 20.0], 628 | [01.0, 11.0, 21.0], 629 | [02.0, 12.0, 22.0]]); 630 | let using_macro = mat3!([00.0, 01.0, 02.0, 631 | 10.0, 11.0, 12.0, 632 | 20.0, 21.0, 22.0]); 633 | 634 | assert_eq!(using_constructor, using_macro); 635 | } 636 | 637 | #[test] 638 | fn matrix_matrix_multiplication() { 639 | let a = mat2!([0.0, 1.0, 640 | 2.0, 3.0]); 641 | let b = mat2!([4.0, 5.0, 642 | 6.0, 7.0]); 643 | 644 | assert_eq!(a * b, mat2!([6.0, 7.0, 645 | 26.0, 31.0])); 646 | } 647 | 648 | #[test] 649 | fn matrix_vector_multiplication() { 650 | let mat = mat3!([1.0, 2.0, 3.0, 651 | 4.0, 5.0, 6.0, 652 | 7.0, 8.0, 9.0]); 653 | let vec: Vec3 = [10.0, 20.0, 40.0].into(); 654 | 655 | assert_eq!(mat * vec, [170.0, 380.0, 590.0].into()); 656 | } 657 | 658 | #[test] 659 | fn matrix_projected_vector_multiplication() { 660 | let mat = mat3!([1.0, 0.0, 8.0, 661 | 0.0, 1.0, 5.0, 662 | 0.0, 0.0, 1.0]); 663 | let vec: Vec2 = [10.0, 20.0].into(); 664 | 665 | assert_eq!(mat * vec, [18.0, 25.0].into()); 666 | } 667 | 668 | #[test] 669 | fn matrix_scalar_multiplication() { 670 | let mat = mat3!([1.0, 2.0, 3.0, 671 | 4.0, 5.0, 6.0, 672 | 7.0, 8.0, 9.0]); 673 | let scalar = 3.0; 674 | 675 | assert_eq!(mat * scalar, mat3!([3.0, 6.0, 9.0, 676 | 12.0, 15.0, 18.0, 677 | 21.0, 24.0, 27.0])); 678 | } 679 | 680 | #[test] 681 | fn matrix_vector_rotation() { 682 | let mat = Mat4::rotation_yaw(consts::FRAC_PI_2) 683 | * Mat4::rotation_pitch(consts::FRAC_PI_2); 684 | let vec: Vec3 = [1.0, 2.0, 3.0].into(); 685 | 686 | assert_eq!(mat * vec, [1.9999999, -3.0, -1.0000001].into()); 687 | } 688 | 689 | #[test] 690 | fn matrix_vector_translation() { 691 | let mat = Mat4::translation(&[10.0, 20.0, 30.0].into()); 692 | let vec: Vec3 = [1.0, 2.0, 3.0].into(); 693 | 694 | assert_eq!(mat * vec, [11.0, 22.0, 33.0].into()); 695 | } 696 | 697 | #[test] 698 | fn matrix_vector_scale() { 699 | let mat = Mat4::scale(10.0); 700 | let vec: Vec3 = [1.0, 2.0, 3.0].into(); 701 | 702 | assert_eq!(mat * vec, [10.0, 20.0, 30.0].into()); 703 | } 704 | 705 | #[test] 706 | fn matrix_determinant() { 707 | let matrix = mat3!([-2.0, 2.0, -3.0, 708 | -1.0, 1.0, 3.0, 709 | 2.0, 0.0, -1.0]); 710 | let determinant = matrix.determinant(); 711 | assert_eq!(determinant, 18.0); 712 | } 713 | 714 | #[test] 715 | fn matrix_adjugate() { 716 | let matrix = mat3!([-3.0, 2.0, -5.0, 717 | -1.0, 0.0, -2.0, 718 | 3.0, -4.0, 1.0]); 719 | let adjugate = matrix.adjugate(); 720 | assert_eq!(adjugate, mat3!([-8.0, 18.0, -4.0, 721 | -5.0, 12.0, -1.0, 722 | 4.0, -6.0, 2.0])); 723 | } 724 | 725 | #[test] 726 | fn matrix_inverse() { 727 | let matrix = mat3!([ 7.0, 2.0, 1.0, 728 | 0.0, 3.0, -1.0, 729 | -3.0, 4.0, -2.0]); 730 | let inverse = matrix.inverse(); 731 | assert_eq!(inverse, mat3!([-2.0, 8.0, -5.0, 732 | 3.0, -11.0, 7.0, 733 | 9.0, -34.0, 21.0])); 734 | } 735 | } 736 | 737 | // macro_rules! mat1 { 738 | // { 739 | // $m00:expr$(;)* 740 | // } => { 741 | // Mat1([[$m00]]) 742 | // }; 743 | // } 744 | 745 | // macro_rules! mat2 { 746 | // { 747 | // $m00:expr, $m10:expr; 748 | // $m01:expr, $m11:expr$(;)* 749 | // } => { 750 | // Mat2([[$m00, $m01], 751 | // [$m10, $m11]]) 752 | // }; 753 | // } 754 | 755 | // macro_rules! mat3 { 756 | // { 757 | // $m00:expr, $m10:expr, $m20:expr; 758 | // $m01:expr, $m11:expr, $m21:expr; 759 | // $m02:expr, $m12:expr, $m22:expr$(;)* 760 | // } => { 761 | // Mat3([[$m00, $m01, $m02], 762 | // [$m10, $m11, $m12], 763 | // [$m20, $m21, $m22]]) 764 | // }; 765 | // } 766 | 767 | // macro_rules! mat4 { 768 | // { 769 | // $m00:expr, $m10:expr, $m20:expr, $m30:expr; 770 | // $m01:expr, $m11:expr, $m21:expr, $m31:expr; 771 | // $m02:expr, $m12:expr, $m22:expr, $m32:expr; 772 | // $m03:expr, $m13:expr, $m23:expr, $m33:expr$(;)* 773 | // } => { 774 | // Mat4([[$m00, $m01, $m02, $m03], 775 | // [$m10, $m11, $m12, $m13], 776 | // [$m20, $m21, $m22, $m23], 777 | // [$m30, $m31, $m32, $m33]]) 778 | // }; 779 | // } 780 | -------------------------------------------------------------------------------- /ammolite-math/src/ops.rs: -------------------------------------------------------------------------------- 1 | /// Euclidean division. 2 | /// See `i32::div_euclid`. 3 | pub trait DivEuclid { 4 | type Output; 5 | 6 | fn div_euclid(self, rhs: RHS) -> Self::Output; 7 | } 8 | 9 | pub trait RemEuclid { 10 | type Output; 11 | 12 | fn rem_euclid(self, rhs: RHS) -> Self::Output; 13 | } 14 | 15 | /// Implements a binary operator on owned types 16 | macro_rules! impl_binary_operator_owned { 17 | (operator_type: [$($operator_type:tt)+]; 18 | inline: [false]; 19 | operator_fn: $operator_fn:ident; 20 | generics: [$($generics:tt)*]; 21 | header: ($lhs:ty, $rhs:ty) -> $output:ty; 22 | |$lhs_ident:ident, $rhs_ident:ident| $impl:expr) => { 23 | impl<$($generics)*> $($operator_type)+<$rhs> for $lhs { 24 | type Output = $output; 25 | 26 | fn $operator_fn(self, $rhs_ident: $rhs) -> Self::Output { 27 | let $lhs_ident = self; 28 | $impl 29 | } 30 | } 31 | }; 32 | 33 | (operator_type: [$($operator_type:tt)+]; 34 | inline: [true]; 35 | operator_fn: $operator_fn:ident; 36 | generics: [$($generics:tt)*]; 37 | header: ($lhs:ty, $rhs:ty) -> $output:ty; 38 | |$lhs_ident:ident, $rhs_ident:ident| $impl:expr) => { 39 | impl<$($generics)*> $($operator_type)+<$rhs> for $lhs { 40 | type Output = $output; 41 | 42 | #[inline] 43 | fn $operator_fn(self, $rhs_ident: $rhs) -> Self::Output { 44 | let $lhs_ident = self; 45 | $impl 46 | } 47 | } 48 | } 49 | } 50 | 51 | /// Implements an operator on all owned/borrowed type combinations 52 | macro_rules! impl_binary_operator { 53 | (operator_type: [$($operator_type:tt)+]; 54 | inline: [$($inline:tt)+]; 55 | operator_fn: $operator_fn:ident; 56 | generics: [$($generics:tt)*]; 57 | header: ($lhs:ty, $rhs:ty) -> $output:ty; 58 | |&$lhs_ident:ident, &$rhs_ident:ident| $impl:expr) => { 59 | impl_binary_operator_owned! { 60 | operator_type: [$($operator_type)+]; 61 | inline: [$($inline)+]; 62 | operator_fn: $operator_fn; 63 | generics: ['a, 'b, $($generics)*]; 64 | header: (&'a $lhs, &'b $rhs) -> $output; 65 | |$lhs_ident, $rhs_ident| $impl 66 | } 67 | 68 | impl_binary_operator_owned! { 69 | operator_type: [$($operator_type)+]; 70 | inline: [$($inline)+]; 71 | operator_fn: $operator_fn; 72 | generics: ['b, $($generics)*]; 73 | header: ($lhs, &'b $rhs) -> $output; 74 | |$lhs_ident, $rhs_ident| { 75 | $($operator_type)+::$operator_fn(&$lhs_ident, $rhs_ident) 76 | } 77 | } 78 | 79 | impl_binary_operator_owned! { 80 | operator_type: [$($operator_type)+]; 81 | inline: [$($inline)+]; 82 | operator_fn: $operator_fn; 83 | generics: ['a, $($generics)*]; 84 | header: (&'a $lhs, $rhs) -> $output; 85 | |$lhs_ident, $rhs_ident| { 86 | $($operator_type)+::$operator_fn($lhs_ident, &$rhs_ident) 87 | } 88 | } 89 | 90 | impl_binary_operator_owned! { 91 | operator_type: [$($operator_type)+]; 92 | inline: [$($inline)+]; 93 | operator_fn: $operator_fn; 94 | generics: [$($generics)*]; 95 | header: ($lhs, $rhs) -> $output; 96 | |$lhs_ident, $rhs_ident| { 97 | $($operator_type)+::$operator_fn(&$lhs_ident, &$rhs_ident) 98 | } 99 | } 100 | 101 | impl_binary_operator_owned! { 102 | operator_type: [$($operator_type)+]; 103 | inline: [$($inline)+]; 104 | operator_fn: $operator_fn; 105 | generics: ['a, 'b, $($generics)*]; 106 | header: (&'a mut $lhs, &'b mut $rhs) -> $output; 107 | |$lhs_ident, $rhs_ident| { 108 | $($operator_type)+::$operator_fn(&*$lhs_ident, &*$rhs_ident) 109 | } 110 | } 111 | 112 | impl_binary_operator_owned! { 113 | operator_type: [$($operator_type)+]; 114 | inline: [$($inline)+]; 115 | operator_fn: $operator_fn; 116 | generics: ['b, $($generics)*]; 117 | header: ($lhs, &'b mut $rhs) -> $output; 118 | |$lhs_ident, $rhs_ident| { 119 | $($operator_type)+::$operator_fn(&$lhs_ident, &*$rhs_ident) 120 | } 121 | } 122 | 123 | impl_binary_operator_owned! { 124 | operator_type: [$($operator_type)+]; 125 | inline: [$($inline)+]; 126 | operator_fn: $operator_fn; 127 | generics: ['a, $($generics)*]; 128 | header: (&'a mut $lhs, $rhs) -> $output; 129 | |$lhs_ident, $rhs_ident| { 130 | $($operator_type)+::$operator_fn(&*$lhs_ident, &$rhs_ident) 131 | } 132 | } 133 | 134 | impl_binary_operator_owned! { 135 | operator_type: [$($operator_type)+]; 136 | inline: [$($inline)+]; 137 | operator_fn: $operator_fn; 138 | generics: ['a, 'b, $($generics)*]; 139 | header: (&'a mut $lhs, &'b $rhs) -> $output; 140 | |$lhs_ident, $rhs_ident| { 141 | $($operator_type)+::$operator_fn(&*$lhs_ident, $rhs_ident) 142 | } 143 | } 144 | 145 | impl_binary_operator_owned! { 146 | operator_type: [$($operator_type)+]; 147 | inline: [$($inline)+]; 148 | operator_fn: $operator_fn; 149 | generics: ['a, 'b, $($generics)*]; 150 | header: (&'a $lhs, &'b mut $rhs) -> $output; 151 | |$lhs_ident, $rhs_ident| { 152 | $($operator_type)+::$operator_fn($lhs_ident, &*$rhs_ident) 153 | } 154 | } 155 | } 156 | } 157 | 158 | /// Implements an unary operator on owned types 159 | macro_rules! impl_unary_operator_owned { 160 | (operator_type: [$($operator_type:tt)+]; 161 | inline: [false]; 162 | operator_fn: $operator_fn:ident; 163 | generics: [$($generics:tt)*]; 164 | header: ($input:ty) -> $output:ty; 165 | |$self:ident| $impl:expr) => { 166 | impl<$($generics)*> $($operator_type)+ for $input { 167 | type Output = $output; 168 | 169 | fn $operator_fn(self) -> Self::Output { 170 | let $self = self; 171 | $impl 172 | } 173 | } 174 | }; 175 | 176 | (operator_type: [$($operator_type:tt)+]; 177 | inline: [true]; 178 | operator_fn: $operator_fn:ident; 179 | generics: [$($generics:tt)*]; 180 | header: ($input:ty) -> $output:ty; 181 | |$self:ident| $impl:expr) => { 182 | impl<$($generics)*> $($operator_type)+ for $input { 183 | type Output = $output; 184 | 185 | #[inline] 186 | fn $operator_fn(self) -> Self::Output { 187 | let $self = self; 188 | $impl 189 | } 190 | } 191 | } 192 | } 193 | 194 | macro_rules! impl_unary_operator { 195 | (operator_type: [$($operator_type:tt)+]; 196 | inline: [$($inline:tt)+]; 197 | operator_fn: $operator_fn:ident; 198 | generics: [$($generics:tt)*]; 199 | header: ($input:ty) -> $output:ty; 200 | |&$self:ident| $impl:expr) => { 201 | impl_unary_operator_owned! { 202 | operator_type: [$($operator_type)+]; 203 | inline: [$($inline)+]; 204 | operator_fn: $operator_fn; 205 | generics: ['a, $($generics)*]; 206 | header: (&'a $input) -> $output; 207 | |$self| $impl 208 | } 209 | 210 | impl_unary_operator_owned! { 211 | operator_type: [$($operator_type)+]; 212 | inline: [$($inline)+]; 213 | operator_fn: $operator_fn; 214 | generics: ['a, $($generics)*]; 215 | header: (&'a mut $input) -> $output; 216 | |$self| { 217 | $($operator_type)+::$operator_fn(&*$self) 218 | } 219 | } 220 | 221 | impl_unary_operator_owned! { 222 | operator_type: [$($operator_type)+]; 223 | inline: [$($inline)+]; 224 | operator_fn: $operator_fn; 225 | generics: [$($generics)*]; 226 | header: ($input) -> $output; 227 | |$self| { 228 | $($operator_type)+::$operator_fn(&$self) 229 | } 230 | } 231 | } 232 | } 233 | -------------------------------------------------------------------------------- /ammolite-math/src/vector.rs: -------------------------------------------------------------------------------- 1 | use std::ops::{Add, Deref, DerefMut, Sub, Neg, AddAssign, SubAssign, Div, Mul, DivAssign, MulAssign, Index, IndexMut}; 2 | use std::convert::TryFrom; 3 | use std::num::TryFromIntError; 4 | use std::fmt::{Debug, Formatter, Error}; 5 | use det::det_copy; 6 | use serde::{Deserialize, Serialize}; 7 | use typenum::{Unsigned, U1, U2, U3, U4}; 8 | use crate::matrix::Mat4; 9 | use crate::ops::{DivEuclid, RemEuclid}; 10 | use paste::{item, expr}; 11 | 12 | pub trait Component { 13 | const ZERO: Self; 14 | 15 | fn to_f32(self) -> f32; 16 | } 17 | 18 | macro_rules! impl_component { 19 | ($($comp_ty:ty),*$(,)?) => { 20 | $( 21 | impl Component for $comp_ty { 22 | const ZERO: Self = 0 as $comp_ty; 23 | 24 | fn to_f32(self) -> f32 { 25 | self as f32 26 | } 27 | } 28 | )* 29 | } 30 | } 31 | 32 | impl_component!(f32, u8, u16, u32, u64, u128, i8, i16, i32, i64, i128); 33 | 34 | pub trait Vector: Sized + Clone + Copy + Debug + PartialEq + Index { 35 | type Dimensions: Unsigned + Add + Sub; 36 | 37 | const ZERO: Self; 38 | const DIMENSIONS: usize; 39 | 40 | fn dot(&self, other: &Self) -> C; 41 | fn norm_squared(&self) -> C; 42 | 43 | fn norm(&self) -> f32 { 44 | self.norm_squared().to_f32().sqrt() 45 | } 46 | 47 | fn distance_to_squared(&self, other: &Self) -> C; 48 | fn distance_to(&self, other: &Self) -> f32 { 49 | self.distance_to_squared(other).to_f32().sqrt() 50 | } 51 | } 52 | 53 | pub trait FloatVector: Vector { 54 | type I32Vector: IntegerVector; 55 | type I64Vector: IntegerVector; 56 | 57 | fn normalize_mut(&mut self); 58 | fn normalize(&self) -> Self { 59 | let mut result = self.clone(); 60 | result.normalize_mut(); 61 | result 62 | } 63 | 64 | fn floor_mut(&mut self); 65 | fn floor(&self) -> Self { 66 | let mut result = self.clone(); 67 | result.floor_mut(); 68 | result 69 | } 70 | 71 | fn ceil_mut(&mut self); 72 | fn ceil(&self) -> Self { 73 | let mut result = self.clone(); 74 | result.ceil_mut(); 75 | result 76 | } 77 | 78 | fn floor_to_i32(&self) -> Self::I32Vector; 79 | fn ceil_to_i32(&self) -> Self::I32Vector; 80 | 81 | fn floor_to_i64(&self) -> Self::I64Vector; 82 | fn ceil_to_i64(&self) -> Self::I64Vector; 83 | } 84 | 85 | pub trait IntegerVector: Vector { 86 | type FVector: FloatVector; 87 | 88 | fn to_f32(self) -> Self::FVector; 89 | fn from_f32(other: Self::FVector) -> Self; 90 | } 91 | 92 | pub trait Projected: Vector { 93 | type HomogeneousVector: Vector + Homogeneous; 94 | 95 | fn into_homogeneous_direction(&self) -> Self::HomogeneousVector; 96 | fn into_homogeneous_position(&self) -> Self::HomogeneousVector; 97 | } 98 | 99 | pub trait Homogeneous: Vector { 100 | type ProjectedVector: Vector + Projected; 101 | 102 | fn into_projected(&self) -> Self::ProjectedVector; 103 | } 104 | 105 | pub trait UnitQuaternion { 106 | fn to_matrix(&self) -> Option; 107 | } 108 | 109 | macro_rules! impl_vec { 110 | ($ty_name:ident, $dims:expr, $dims_ty:ty, $comp_ty:ty) => { 111 | #[derive(Clone, Copy, PartialEq, Deserialize, Serialize)] 112 | pub struct $ty_name(pub [$comp_ty; $dims]); 113 | 114 | impl $ty_name { 115 | pub fn inner(&self) -> &[$comp_ty; $dims] { 116 | &self.0 117 | } 118 | 119 | pub fn inner_mut(&mut self) -> &mut [$comp_ty; $dims] { 120 | &mut self.0 121 | } 122 | 123 | pub fn into_inner(self) -> [$comp_ty; $dims] { 124 | self.0 125 | } 126 | 127 | pub fn min(&self, other: &Self) -> Self { 128 | let mut result = <$ty_name as Vector<_>>::ZERO; 129 | 130 | for (result_component, (a_component, b_component)) in result.iter_mut().zip(self.iter().zip(other.iter())) { 131 | *result_component = (*a_component).min(*b_component); 132 | } 133 | 134 | result 135 | } 136 | 137 | pub fn max(&self, other: &Self) -> Self { 138 | let mut result = <$ty_name as Vector<_>>::ZERO; 139 | 140 | for (result_component, (a_component, b_component)) in result.iter_mut().zip(self.iter().zip(other.iter())) { 141 | *result_component = (*a_component).max(*b_component); 142 | } 143 | 144 | result 145 | } 146 | } 147 | 148 | impl Vector<$comp_ty> for $ty_name { 149 | type Dimensions = $dims_ty; 150 | 151 | const ZERO: Self = Self([<$comp_ty as Component>::ZERO; $dims]); 152 | const DIMENSIONS: usize = $dims; 153 | 154 | fn dot(&self, other: &Self) -> $comp_ty { 155 | let mut result = <$comp_ty as Component>::ZERO; 156 | 157 | for (a, b) in self.iter().zip(other.iter()) { 158 | result += a * b; 159 | } 160 | 161 | result 162 | } 163 | 164 | fn distance_to_squared(&self, other: &Self) -> $comp_ty { 165 | (self - other).norm_squared() 166 | } 167 | 168 | fn norm_squared(&self) -> $comp_ty { 169 | self.dot(&self) 170 | } 171 | } 172 | 173 | impl Default for $ty_name { 174 | fn default() -> Self { 175 | Self::ZERO 176 | } 177 | } 178 | 179 | impl Index for $ty_name { 180 | type Output = $comp_ty; 181 | 182 | fn index(&self, idx: usize) -> &Self::Output { 183 | &self.0[idx] 184 | } 185 | } 186 | 187 | impl IndexMut for $ty_name { 188 | fn index_mut(&mut self, idx: usize) -> &mut Self::Output { 189 | &mut self.0[idx] 190 | } 191 | } 192 | 193 | impl_binary_operator! { 194 | operator_type: [Add]; 195 | inline: [true]; 196 | operator_fn: add; 197 | generics: []; 198 | header: ($ty_name, $ty_name) -> $ty_name; 199 | |&a, &b| { 200 | let mut result = $ty_name::ZERO; 201 | 202 | for (result_component, (a_component, b_component)) in result.iter_mut().zip(a.iter().zip(b.iter())) { 203 | *result_component = *a_component + *b_component; 204 | } 205 | 206 | result 207 | } 208 | } 209 | 210 | impl_binary_operator! { 211 | operator_type: [Add]; 212 | inline: [true]; 213 | operator_fn: add; 214 | generics: []; 215 | header: ($ty_name, $comp_ty) -> $ty_name; 216 | |&a, &b| { 217 | let mut result = $ty_name::ZERO; 218 | 219 | for (result_component, a_component) in result.iter_mut().zip(a.iter()) { 220 | *result_component = *a_component + *b; 221 | } 222 | 223 | result 224 | } 225 | } 226 | 227 | impl_binary_operator! { 228 | operator_type: [Sub]; 229 | inline: [true]; 230 | operator_fn: sub; 231 | generics: []; 232 | header: ($ty_name, $ty_name) -> $ty_name; 233 | |&a, &b| { 234 | let mut result = $ty_name::ZERO; 235 | 236 | for (result_component, (a_component, b_component)) in result.iter_mut().zip(a.iter().zip(b.iter())) { 237 | *result_component = *a_component - *b_component; 238 | } 239 | 240 | result 241 | } 242 | } 243 | 244 | impl_binary_operator! { 245 | operator_type: [Sub]; 246 | inline: [true]; 247 | operator_fn: sub; 248 | generics: []; 249 | header: ($ty_name, $comp_ty) -> $ty_name; 250 | |&a, &b| { 251 | let mut result = $ty_name::ZERO; 252 | 253 | for (result_component, a_component) in result.iter_mut().zip(a.iter()) { 254 | *result_component = *a_component - *b; 255 | } 256 | 257 | result 258 | } 259 | } 260 | 261 | impl_binary_operator! { 262 | operator_type: [Mul]; 263 | inline: [true]; 264 | operator_fn: mul; 265 | generics: []; 266 | header: ($ty_name, $ty_name) -> $ty_name; 267 | |&a, &b| { 268 | let mut result = $ty_name::ZERO; 269 | 270 | for (result_component, (a_component, b_component)) in result.iter_mut().zip(a.iter().zip(b.iter())) { 271 | *result_component = *a_component * *b_component; 272 | } 273 | 274 | result 275 | } 276 | } 277 | 278 | impl_binary_operator! { 279 | operator_type: [Mul]; 280 | inline: [true]; 281 | operator_fn: mul; 282 | generics: []; 283 | header: ($ty_name, $comp_ty) -> $ty_name; 284 | |&a, &b| { 285 | let mut result = $ty_name::ZERO; 286 | 287 | for (result_component, a_component) in result.iter_mut().zip(a.iter()) { 288 | *result_component = *a_component * *b; 289 | } 290 | 291 | result 292 | } 293 | } 294 | 295 | impl_binary_operator! { 296 | operator_type: [Mul]; 297 | inline: [true]; 298 | operator_fn: mul; 299 | generics: []; 300 | header: ($comp_ty, $ty_name) -> $ty_name; 301 | |&a, &b| { 302 | let mut result = $ty_name::ZERO; 303 | 304 | for (result_component, b_component) in result.iter_mut().zip(b.iter()) { 305 | *result_component = *a * *b_component; 306 | } 307 | 308 | result 309 | } 310 | } 311 | 312 | impl_binary_operator! { 313 | operator_type: [Div]; 314 | inline: [true]; 315 | operator_fn: div; 316 | generics: []; 317 | header: ($ty_name, $ty_name) -> $ty_name; 318 | |&a, &b| { 319 | let mut result = $ty_name::ZERO; 320 | 321 | for (result_component, (a_component, b_component)) in result.iter_mut().zip(a.iter().zip(b.iter())) { 322 | *result_component = *a_component / *b_component; 323 | } 324 | 325 | result 326 | } 327 | } 328 | 329 | impl_binary_operator! { 330 | operator_type: [Div]; 331 | inline: [true]; 332 | operator_fn: div; 333 | generics: []; 334 | header: ($ty_name, $comp_ty) -> $ty_name; 335 | |&a, &b| { 336 | let mut result = $ty_name::ZERO; 337 | 338 | for (result_component, a_component) in result.iter_mut().zip(a.iter()) { 339 | *result_component = *a_component / *b; 340 | } 341 | 342 | result 343 | } 344 | } 345 | 346 | impl_binary_operator! { 347 | operator_type: [Div]; 348 | inline: [true]; 349 | operator_fn: div; 350 | generics: []; 351 | header: ($comp_ty, $ty_name) -> $ty_name; 352 | |&a, &b| { 353 | let mut result = $ty_name::ZERO; 354 | 355 | for (result_component, b_component) in result.iter_mut().zip(b.iter()) { 356 | *result_component = *a / *b_component; 357 | } 358 | 359 | result 360 | } 361 | } 362 | 363 | impl_binary_operator! { 364 | operator_type: [DivEuclid]; 365 | inline: [true]; 366 | operator_fn: div_euclid; 367 | generics: []; 368 | header: ($ty_name, $ty_name) -> $ty_name; 369 | |&a, &b| { 370 | let mut result = <$ty_name as Vector<_>>::ZERO; 371 | 372 | for (result_component, (a_component, b_component)) in result.iter_mut().zip(a.iter().zip(b.iter())) { 373 | *result_component = (*a_component).div_euclid(*b_component); 374 | } 375 | 376 | result 377 | } 378 | } 379 | 380 | impl_binary_operator! { 381 | operator_type: [DivEuclid]; 382 | inline: [true]; 383 | operator_fn: div_euclid; 384 | generics: []; 385 | header: ($ty_name, $comp_ty) -> $ty_name; 386 | |&a, &b| { 387 | let mut result = <$ty_name as Vector<_>>::ZERO; 388 | 389 | for (result_component, a_component) in result.iter_mut().zip(a.iter()) { 390 | *result_component = (*a_component).div_euclid(*b); 391 | } 392 | 393 | result 394 | } 395 | } 396 | 397 | impl_binary_operator! { 398 | operator_type: [DivEuclid]; 399 | inline: [true]; 400 | operator_fn: div_euclid; 401 | generics: []; 402 | header: ($comp_ty, $ty_name) -> $ty_name; 403 | |&a, &b| { 404 | let mut result = <$ty_name as Vector<_>>::ZERO; 405 | 406 | for (result_component, b_component) in result.iter_mut().zip(b.iter()) { 407 | *result_component = (*a).div_euclid(*b_component); 408 | } 409 | 410 | result 411 | } 412 | } 413 | 414 | impl_binary_operator! { 415 | operator_type: [RemEuclid]; 416 | inline: [true]; 417 | operator_fn: rem_euclid; 418 | generics: []; 419 | header: ($ty_name, $ty_name) -> $ty_name; 420 | |&a, &b| { 421 | let mut result = <$ty_name as Vector<_>>::ZERO; 422 | 423 | for (result_component, (a_component, b_component)) in result.iter_mut().zip(a.iter().zip(b.iter())) { 424 | *result_component = (*a_component).rem_euclid(*b_component); 425 | } 426 | 427 | result 428 | } 429 | } 430 | 431 | impl_binary_operator! { 432 | operator_type: [RemEuclid]; 433 | inline: [true]; 434 | operator_fn: rem_euclid; 435 | generics: []; 436 | header: ($ty_name, $comp_ty) -> $ty_name; 437 | |&a, &b| { 438 | let mut result = <$ty_name as Vector<_>>::ZERO; 439 | 440 | for (result_component, a_component) in result.iter_mut().zip(a.iter()) { 441 | *result_component = (*a_component).rem_euclid(*b); 442 | } 443 | 444 | result 445 | } 446 | } 447 | 448 | impl_binary_operator! { 449 | operator_type: [RemEuclid]; 450 | inline: [true]; 451 | operator_fn: rem_euclid; 452 | generics: []; 453 | header: ($comp_ty, $ty_name) -> $ty_name; 454 | |&a, &b| { 455 | let mut result = <$ty_name as Vector<_>>::ZERO; 456 | 457 | for (result_component, b_component) in result.iter_mut().zip(b.iter()) { 458 | *result_component = (*a).rem_euclid(*b_component); 459 | } 460 | 461 | result 462 | } 463 | } 464 | 465 | impl AddAssign for $ty_name where Self: Add { 466 | fn add_assign(&mut self, other: T) { 467 | *self = self.clone() + other; 468 | } 469 | } 470 | 471 | impl SubAssign for $ty_name where Self: Sub { 472 | fn sub_assign(&mut self, other: T) { 473 | *self = self.clone() - other; 474 | } 475 | } 476 | 477 | impl MulAssign for $ty_name where Self: Mul { 478 | fn mul_assign(&mut self, other: T) { 479 | *self = self.clone() * other; 480 | } 481 | } 482 | 483 | impl DivAssign for $ty_name where Self: Div { 484 | fn div_assign(&mut self, other: T) { 485 | *self = self.clone() / other; 486 | } 487 | } 488 | 489 | impl From<$ty_name> for [$comp_ty; $dims] { 490 | fn from(vector: $ty_name) -> Self { 491 | *vector 492 | } 493 | } 494 | 495 | impl From<[$comp_ty; $dims]> for $ty_name { 496 | fn from(array: [$comp_ty; $dims]) -> Self { 497 | $ty_name(array) 498 | } 499 | } 500 | 501 | impl Debug for $ty_name { 502 | fn fmt(&self, f: &mut Formatter) -> Result<(), Error> { 503 | write!(f, "{ty}({comp:?})", ty=stringify!($ty_name), comp=self.0) 504 | } 505 | } 506 | 507 | impl Deref for $ty_name { 508 | type Target = [$comp_ty; $dims]; 509 | 510 | #[inline] 511 | fn deref(&self) -> &Self::Target { 512 | &self.0 513 | } 514 | } 515 | 516 | impl DerefMut for $ty_name { 517 | #[inline] 518 | fn deref_mut(&mut self) -> &mut ::Target { 519 | &mut self.0 520 | } 521 | } 522 | } 523 | } 524 | 525 | macro_rules! impl_vec_signed { 526 | ($ty_name:ident, $dims:expr, $dims_ty:ty) => { 527 | impl_unary_operator! { 528 | operator_type: [Neg]; 529 | inline: [false]; 530 | operator_fn: neg; 531 | generics: []; 532 | header: ($ty_name) -> $ty_name; 533 | |&myself| { 534 | let mut result = myself.clone(); 535 | 536 | for (result_component, myself_component) in result.iter_mut().zip(myself.iter()) { 537 | *result_component = -*myself_component; 538 | } 539 | 540 | result 541 | } 542 | } 543 | 544 | impl $ty_name { 545 | pub fn abs(&self) -> Self { 546 | let mut result = self.clone(); 547 | 548 | for coord in result.iter_mut() { 549 | *coord = coord.abs(); 550 | } 551 | 552 | result 553 | } 554 | } 555 | } 556 | } 557 | 558 | macro_rules! impl_vec_f32 { 559 | ($ty_name:ident, $dims:expr, $dims_ty:ty) => { 560 | impl_vec!($ty_name, $dims, $dims_ty, f32); 561 | impl_vec_signed!($ty_name, $dims, $dims_ty); 562 | 563 | impl FloatVector for $ty_name { 564 | paste::item! { 565 | type I32Vector = [< I32 $ty_name >]; 566 | type I64Vector = [< I64 $ty_name >]; 567 | } 568 | 569 | fn normalize_mut(&mut self) { 570 | let norm = self.norm(); 571 | 572 | for coord in &mut self.0 { 573 | *coord /= norm; 574 | } 575 | } 576 | 577 | fn floor_mut(&mut self) { 578 | for coord in self.inner_mut() { 579 | *coord = coord.floor(); 580 | } 581 | } 582 | 583 | fn ceil_mut(&mut self) { 584 | for coord in self.inner_mut() { 585 | *coord = coord.ceil(); 586 | } 587 | } 588 | 589 | fn floor_to_i32(&self) -> Self::I32Vector { 590 | let floored = self.floor(); 591 | let mut result = Self::I32Vector::ZERO; 592 | 593 | for (result_component, floored_component) in result.iter_mut().zip(floored.iter()) { 594 | *result_component = *floored_component as i32; 595 | } 596 | 597 | result 598 | } 599 | 600 | fn ceil_to_i32(&self) -> Self::I32Vector { 601 | let ceiled = self.ceil(); 602 | let mut result = Self::I32Vector::ZERO; 603 | 604 | for (result_component, ceiled_component) in result.iter_mut().zip(ceiled.iter()) { 605 | *result_component = *ceiled_component as i32; 606 | } 607 | 608 | result 609 | } 610 | 611 | fn floor_to_i64(&self) -> Self::I64Vector { 612 | let floored = self.floor(); 613 | let mut result = Self::I64Vector::ZERO; 614 | 615 | for (result_component, floored_component) in result.iter_mut().zip(floored.iter()) { 616 | *result_component = *floored_component as i64; 617 | } 618 | 619 | result 620 | } 621 | 622 | fn ceil_to_i64(&self) -> Self::I64Vector { 623 | let ceiled = self.ceil(); 624 | let mut result = Self::I64Vector::ZERO; 625 | 626 | for (result_component, ceiled_component) in result.iter_mut().zip(ceiled.iter()) { 627 | *result_component = *ceiled_component as i64; 628 | } 629 | 630 | result 631 | } 632 | } 633 | 634 | item! { 635 | #[cfg(feature = "nalgebra-interop")] 636 | use na::base::dimension::{ 637 | $dims_ty as [< Na $dims_ty >], 638 | }; 639 | 640 | #[cfg(feature = "nalgebra-interop")] 641 | impl From]>> for $ty_name { 642 | fn from(other: na::base::VectorN]>) -> $ty_name { 643 | (&other).into() 644 | } 645 | } 646 | 647 | #[cfg(feature = "nalgebra-interop")] 648 | impl<'a> From<&'a na::base::VectorN]>> for $ty_name { 649 | fn from(other: &'a na::base::VectorN]>) -> $ty_name { 650 | let mut result = <$ty_name as Vector>::ZERO; 651 | 652 | for (result_component, other_component) in result.iter_mut().zip(other.iter()) { 653 | *result_component = *other_component; 654 | } 655 | 656 | result 657 | } 658 | } 659 | 660 | #[cfg(feature = "nalgebra-interop")] 661 | impl From]>> for $ty_name { 662 | fn from(other: na::geometry::Point]>) -> $ty_name { 663 | (&other.coords).into() 664 | } 665 | } 666 | 667 | #[cfg(feature = "nalgebra-interop")] 668 | impl<'a> From<&'a na::geometry::Point]>> for $ty_name { 669 | fn from(other: &'a na::geometry::Point]>) -> $ty_name { 670 | (&other.coords).into() 671 | } 672 | } 673 | } 674 | } 675 | } 676 | 677 | macro_rules! impl_projected_homogeneous { 678 | ($lower_dim_ty_name:ident, $higher_dim_ty_name:ident) => { 679 | impl Projected for $lower_dim_ty_name { 680 | type HomogeneousVector = $higher_dim_ty_name; 681 | 682 | fn into_homogeneous_direction(&self) -> Self::HomogeneousVector { 683 | let mut result = >::ZERO; 684 | 685 | for (result_component, self_component) in result.iter_mut().zip(self.iter()) { 686 | *result_component = *self_component; 687 | } 688 | 689 | result 690 | } 691 | 692 | fn into_homogeneous_position(&self) -> Self::HomogeneousVector { 693 | let mut result = self.into_homogeneous_direction(); 694 | 695 | if let Some(last_component) = result.last_mut() { 696 | *last_component = 1.0; 697 | } else { 698 | panic!("No last element in vector {}.", stringify!($higher_dim_ty_name)) 699 | } 700 | 701 | result 702 | } 703 | } 704 | 705 | impl Homogeneous for $higher_dim_ty_name { 706 | type ProjectedVector = $lower_dim_ty_name; 707 | 708 | fn into_projected(&self) -> Self::ProjectedVector { 709 | let mut result = >::ZERO; 710 | 711 | let mut last_component = *self.last().unwrap_or_else(|| 712 | panic!("No last element in vector {}.", stringify!($higher_dim_ty_name))); 713 | 714 | if last_component == 0.0 { 715 | last_component = 1.0; 716 | } 717 | 718 | for (result_component, self_component) in result.iter_mut().zip(self.iter()) { 719 | *result_component = *self_component / last_component; 720 | } 721 | 722 | result 723 | } 724 | } 725 | } 726 | } 727 | 728 | macro_rules! impl_vec_integer { 729 | ( 730 | $ty_name:ident, $float_ty_name:ident, $dims:expr, $dims_ty:ty, $comp_ty:ty 731 | $(; $($lesser_comp_ty:ident),* $(,)? < $($greater_comp_ty:ident),* $(,)?)? 732 | ) => { 733 | impl_vec!($ty_name, $dims, $dims_ty, $comp_ty); 734 | 735 | impl Eq for $ty_name {} 736 | 737 | impl std::hash::Hash for $ty_name { 738 | fn hash(&self, state: &mut H) where H: std::hash::Hasher { 739 | self.inner().hash(state); 740 | } 741 | } 742 | 743 | impl IntegerVector<$comp_ty> for $ty_name { 744 | type FVector = $float_ty_name; 745 | 746 | fn to_f32(self) -> Self::FVector { 747 | let mut result = >::ZERO; 748 | 749 | for coord in 0..Self::DIMENSIONS { 750 | result[coord] = self[coord] as f32; 751 | } 752 | 753 | result 754 | } 755 | 756 | fn from_f32(other: Self::FVector) -> Self { 757 | let mut result = <$ty_name as Vector<_>>::ZERO; 758 | 759 | for coord in 0..Self::DIMENSIONS { 760 | result[coord] = other[coord] as $comp_ty; 761 | } 762 | 763 | result 764 | } 765 | } 766 | 767 | $( 768 | $( 769 | impl From<$ty_name> for [$greater_comp_ty; $dims] { 770 | fn from(vector: $ty_name) -> Self { 771 | let mut result = [<$greater_comp_ty as Component>::ZERO; $dims]; 772 | 773 | for (result_component, vector_component) in result.iter_mut().zip(vector.iter()) { 774 | *result_component = *vector_component as $greater_comp_ty; 775 | } 776 | 777 | result 778 | } 779 | } 780 | 781 | impl TryFrom<[$greater_comp_ty; $dims]> for $ty_name { 782 | type Error = TryFromIntError; 783 | 784 | fn try_from(array: [$greater_comp_ty; $dims]) -> Result { 785 | let mut result = <$ty_name as Vector<_>>::ZERO; 786 | 787 | for (result_component, array_component) in result.iter_mut().zip(array.iter()) { 788 | *result_component = <$comp_ty>::try_from(*array_component)?; 789 | } 790 | 791 | Ok(result) 792 | } 793 | } 794 | 795 | // impl From<$ty_name> for $greater_ty { 796 | // fn from(vector: $ty_name) -> Self { 797 | // $greater_ty(<[$greater_comp_ty; $dims]>::from(vector)) 798 | // } 799 | // } 800 | )* 801 | 802 | $( 803 | impl From<[$lesser_comp_ty; $dims]> for $ty_name { 804 | fn from(array: [$lesser_comp_ty; $dims]) -> Self { 805 | let mut result = <$ty_name as Vector<_>>::ZERO; 806 | 807 | for (result_component, array_component) in result.iter_mut().zip(array.iter()) { 808 | *result_component = *array_component as $comp_ty; 809 | } 810 | 811 | result 812 | } 813 | } 814 | 815 | impl TryFrom<$ty_name> for [$lesser_comp_ty; $dims] { 816 | type Error = TryFromIntError; 817 | 818 | fn try_from(vector: $ty_name) -> Result { 819 | let mut result = [<$lesser_comp_ty as Component>::ZERO; $dims]; 820 | 821 | for (result_component, vector_component) in result.iter_mut().zip(vector.iter()) { 822 | *result_component = <$lesser_comp_ty>::try_from(*vector_component)?; 823 | } 824 | 825 | Ok(result) 826 | } 827 | } 828 | 829 | // impl From<$lesser_ty> for $ty_name { 830 | // fn from(other: $lesser_ty) -> Self { 831 | // <$ty_name>::from(other.0) 832 | // } 833 | // } 834 | )* 835 | )? 836 | } 837 | } 838 | 839 | macro_rules! impl_vec_integer_signed { 840 | ( 841 | $ty_name:ident, $float_ty_name:ident, $dims:expr, $dims_ty:ty, $comp_ty:ty $(,)? 842 | $(; $($lesser_comp_ty:ident),* $(,)? < $($greater_comp_ty:ident),* $(,)?)? 843 | ) => { 844 | impl_vec_integer!($ty_name, $float_ty_name, $dims, $dims_ty, $comp_ty$(; $($lesser_comp_ty),* < $($greater_comp_ty),*)?); 845 | impl_vec_signed!($ty_name, $dims, $dims_ty); 846 | } 847 | } 848 | 849 | // impl_vec_f32!(F32Vec1, 1, U1); 850 | // impl_vec_f32!(F32Vec2, 2, U2); 851 | // impl_vec_f32!(F32Vec3, 3, U3); 852 | // impl_vec_f32!(F32Vec4, 4, U4); 853 | 854 | // impl_projected_homogeneous!(F32Vec1, F32Vec2); 855 | // impl_projected_homogeneous!(F32Vec2, F32Vec3); 856 | // impl_projected_homogeneous!(F32Vec3, F32Vec4); 857 | 858 | // pub type Vec1 = F32Vec1; 859 | // pub type Vec2 = F32Vec2; 860 | // pub type Vec3 = F32Vec3; 861 | // pub type Vec4 = F32Vec4; 862 | 863 | impl_vec_f32!(Vec1, 1, U1); 864 | impl_vec_f32!(Vec2, 2, U2); 865 | impl_vec_f32!(Vec3, 3, U3); 866 | impl_vec_f32!(Vec4, 4, U4); 867 | 868 | impl_projected_homogeneous!(Vec1, Vec2); 869 | impl_projected_homogeneous!(Vec2, Vec3); 870 | impl_projected_homogeneous!(Vec3, Vec4); 871 | 872 | impl_vec_integer!( U8Vec1, Vec1, 1, U1, u8; < u16, i16, u32, i32, u64, i64, u128, i128); 873 | impl_vec_integer!( U8Vec2, Vec2, 2, U2, u8; < u16, i16, u32, i32, u64, i64, u128, i128); 874 | impl_vec_integer!( U8Vec3, Vec3, 3, U3, u8; < u16, i16, u32, i32, u64, i64, u128, i128); 875 | impl_vec_integer!( U8Vec4, Vec4, 4, U4, u8; < u16, i16, u32, i32, u64, i64, u128, i128); 876 | 877 | impl_vec_integer!( U16Vec1, Vec1, 1, U1, u16; u8, i8, < u32, i32, u64, i64, u128, i128); 878 | impl_vec_integer!( U16Vec2, Vec2, 2, U2, u16; u8, i8, < u32, i32, u64, i64, u128, i128); 879 | impl_vec_integer!( U16Vec3, Vec3, 3, U3, u16; u8, i8, < u32, i32, u64, i64, u128, i128); 880 | impl_vec_integer!( U16Vec4, Vec4, 4, U4, u16; u8, i8, < u32, i32, u64, i64, u128, i128); 881 | 882 | impl_vec_integer!( U32Vec1, Vec1, 1, U1, u32; u8, i8, u16, i16, < u64, i64, u128, i128); 883 | impl_vec_integer!( U32Vec2, Vec2, 2, U2, u32; u8, i8, u16, i16, < u64, i64, u128, i128); 884 | impl_vec_integer!( U32Vec3, Vec3, 3, U3, u32; u8, i8, u16, i16, < u64, i64, u128, i128); 885 | impl_vec_integer!( U32Vec4, Vec4, 4, U4, u32; u8, i8, u16, i16, < u64, i64, u128, i128); 886 | 887 | impl_vec_integer!( U64Vec1, Vec1, 1, U1, u64; u8, i8, u16, i16, u32, i32, < u128, i128); 888 | impl_vec_integer!( U64Vec2, Vec2, 2, U2, u64; u8, i8, u16, i16, u32, i32, < u128, i128); 889 | impl_vec_integer!( U64Vec3, Vec3, 3, U3, u64; u8, i8, u16, i16, u32, i32, < u128, i128); 890 | impl_vec_integer!( U64Vec4, Vec4, 4, U4, u64; u8, i8, u16, i16, u32, i32, < u128, i128); 891 | 892 | impl_vec_integer!(U128Vec1, Vec1, 1, U1, u128; u8, i8, u16, i16, u32, i32, u64, i64 <); 893 | impl_vec_integer!(U128Vec2, Vec2, 2, U2, u128; u8, i8, u16, i16, u32, i32, u64, i64 <); 894 | impl_vec_integer!(U128Vec3, Vec3, 3, U3, u128; u8, i8, u16, i16, u32, i32, u64, i64 <); 895 | impl_vec_integer!(U128Vec4, Vec4, 4, U4, u128; u8, i8, u16, i16, u32, i32, u64, i64 <); 896 | 897 | pub type UVec1 = U32Vec1; 898 | pub type UVec2 = U32Vec2; 899 | pub type UVec3 = U32Vec3; 900 | pub type UVec4 = U32Vec4; 901 | 902 | impl_vec_integer_signed!( I8Vec1, Vec1, 1, U1, i8; < u16, i16, u32, i32, u64, i64, u128, i128); 903 | impl_vec_integer_signed!( I8Vec2, Vec2, 2, U2, i8; < u16, i16, u32, i32, u64, i64, u128, i128); 904 | impl_vec_integer_signed!( I8Vec3, Vec3, 3, U3, i8; < u16, i16, u32, i32, u64, i64, u128, i128); 905 | impl_vec_integer_signed!( I8Vec4, Vec4, 4, U4, i8; < u16, i16, u32, i32, u64, i64, u128, i128); 906 | 907 | impl_vec_integer_signed!( I16Vec1, Vec1, 1, U1, i16; u8, i8, < u32, i32, u64, i64, u128, i128); 908 | impl_vec_integer_signed!( I16Vec2, Vec2, 2, U2, i16; u8, i8, < u32, i32, u64, i64, u128, i128); 909 | impl_vec_integer_signed!( I16Vec3, Vec3, 3, U3, i16; u8, i8, < u32, i32, u64, i64, u128, i128); 910 | impl_vec_integer_signed!( I16Vec4, Vec4, 4, U4, i16; u8, i8, < u32, i32, u64, i64, u128, i128); 911 | 912 | impl_vec_integer_signed!( I32Vec1, Vec1, 1, U1, i32; u8, i8, u16, i16, < u64, i64, u128, i128); 913 | impl_vec_integer_signed!( I32Vec2, Vec2, 2, U2, i32; u8, i8, u16, i16, < u64, i64, u128, i128); 914 | impl_vec_integer_signed!( I32Vec3, Vec3, 3, U3, i32; u8, i8, u16, i16, < u64, i64, u128, i128); 915 | impl_vec_integer_signed!( I32Vec4, Vec4, 4, U4, i32; u8, i8, u16, i16, < u64, i64, u128, i128); 916 | 917 | impl_vec_integer_signed!( I64Vec1, Vec1, 1, U1, i64; u8, i8, u16, i16, u32, i32, < u128, i128); 918 | impl_vec_integer_signed!( I64Vec2, Vec2, 2, U2, i64; u8, i8, u16, i16, u32, i32, < u128, i128); 919 | impl_vec_integer_signed!( I64Vec3, Vec3, 3, U3, i64; u8, i8, u16, i16, u32, i32, < u128, i128); 920 | impl_vec_integer_signed!( I64Vec4, Vec4, 4, U4, i64; u8, i8, u16, i16, u32, i32, < u128, i128); 921 | 922 | impl_vec_integer_signed!(I128Vec1, Vec1, 1, U1, i128; u8, i8, u16, i16, u32, i32, u64, i64 <); 923 | impl_vec_integer_signed!(I128Vec2, Vec2, 2, U2, i128; u8, i8, u16, i16, u32, i32, u64, i64 <); 924 | impl_vec_integer_signed!(I128Vec3, Vec3, 3, U3, i128; u8, i8, u16, i16, u32, i32, u64, i64 <); 925 | impl_vec_integer_signed!(I128Vec4, Vec4, 4, U4, i128; u8, i8, u16, i16, u32, i32, u64, i64 <); 926 | 927 | pub type IVec1 = I32Vec1; 928 | pub type IVec2 = I32Vec2; 929 | pub type IVec3 = I32Vec3; 930 | pub type IVec4 = I32Vec4; 931 | 932 | impl UnitQuaternion for Vec4 { 933 | fn to_matrix(&self) -> Option { 934 | // Not actually a unit quaternion? Bail. 935 | if self.dot(&self) != 1.0 { 936 | return None; 937 | } 938 | 939 | let q = self; 940 | 941 | Some(mat4!([1.0-2.0*(q[2]*q[2]+q[3]*q[3]), 2.0*(q[1]*q[2]-q[0]*q[3]), 2.0*(q[0]*q[2]+q[1]*q[3]), 0.0, 942 | 2.0*(q[1]*q[2]+q[0]*q[3]), 1.0-2.0*(q[1]*q[1]+q[3]*q[3]), 2.0*(q[2]*q[3]-q[0]*q[1]), 0.0, 943 | 2.0*(q[1]*q[3]-q[0]*q[2]), 2.0*(q[0]*q[1]+q[2]*q[3]), 1.0-2.0*(q[1]*q[1]+q[2]*q[2]), 0.0, 944 | 0.0, 0.0, 0.0, 1.0])) 945 | } 946 | } 947 | 948 | pub trait Cross { 949 | fn cross(&self, other: &Self) -> Self; 950 | } 951 | 952 | impl Cross for Vec3 { 953 | fn cross(&self, other: &Self) -> Self { 954 | let a = self; 955 | let b = other; 956 | 957 | det_copy!(Vec3([1.0, 0.0, 0.0]), Vec3([0.0, 1.0, 0.0]), Vec3([0.0, 0.0, 1.0]), 958 | a[0], a[1], a[2], 959 | b[0], b[1], b[2]) 960 | } 961 | } 962 | -------------------------------------------------------------------------------- /src/buffer.rs: -------------------------------------------------------------------------------- 1 | use std::sync::Arc; 2 | use vulkano::buffer::{TypedBufferAccess, BufferUsage, CpuAccessibleBuffer, DeviceLocalBuffer}; 3 | use vulkano::device::Device; 4 | use vulkano::instance::QueueFamily; 5 | 6 | #[derive(Clone)] 7 | pub struct StagedBuffer { 8 | staging_buffer: Arc + Send + Sync>, 9 | device_buffer: Arc + Send + Sync>, 10 | } 11 | 12 | impl StagedBuffer { 13 | pub fn from_data(device: &Arc, queue_family: QueueFamily, usage: BufferUsage, data: T) 14 | -> StagedBuffer 15 | where CpuAccessibleBuffer: TypedBufferAccess, 16 | DeviceLocalBuffer: TypedBufferAccess, { 17 | let staging_buffer = CpuAccessibleBuffer::::from_data( 18 | device.clone(), 19 | BufferUsage { 20 | transfer_destination: true, 21 | transfer_source: true, 22 | .. usage.clone() 23 | }, 24 | data, 25 | ).unwrap(); 26 | let device_buffer = DeviceLocalBuffer::::new( 27 | device.clone(), 28 | BufferUsage { 29 | transfer_destination: true, 30 | .. usage.clone() 31 | }, 32 | [queue_family].into_iter().cloned(), // TODO: Figure out a way not to use a Vec 33 | ).unwrap(); 34 | 35 | StagedBuffer { 36 | staging_buffer, 37 | device_buffer, 38 | } 39 | } 40 | 41 | pub fn staging_buffer(&self) -> &Arc + Send + Sync> { 42 | &self.staging_buffer 43 | } 44 | 45 | pub fn device_buffer(&self) -> &Arc + Send + Sync> { 46 | &self.device_buffer 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/camera.rs: -------------------------------------------------------------------------------- 1 | use std; 2 | use std::collections::HashSet; 3 | use std::time::Duration; 4 | use winit::event::{MouseButton, VirtualKeyCode}; 5 | use boolinator::Boolinator; 6 | use ammolite_math::*; 7 | 8 | pub trait Camera: std::fmt::Debug { 9 | fn get_view_translation_matrix(&self) -> Mat4; 10 | fn get_view_rotation_matrix(&self) -> Mat4; 11 | 12 | /** 13 | * Returns the view matrix intended to be used in the MVP matrix chain. 14 | * Translation is applied first, then rotation. 15 | */ 16 | fn get_view_matrix(&self) -> Mat4 { 17 | self.get_view_rotation_matrix() * self.get_view_translation_matrix() 18 | } 19 | 20 | /** 21 | * Returns the position of the camera in the world. 22 | */ 23 | fn get_position(&self) -> Vec3; 24 | 25 | /** 26 | * Returns the forward unit vector of the camera. 27 | */ 28 | fn get_direction(&self) -> Vec3; 29 | 30 | fn get_rotation_axis_angles(&self) -> Vec3; 31 | 32 | /** 33 | * Handle controls. 34 | */ 35 | fn update(&mut self, 36 | delta_time: &Duration, 37 | cursor_delta: &[f64; 2], 38 | pressed_keys: &HashSet, 39 | pressed_mouse_buttons: &HashSet); 40 | } 41 | 42 | #[derive(Debug, Clone, PartialEq)] 43 | pub struct PitchYawCamera3 { 44 | position: Vec3, 45 | yaw: f64, 46 | pitch: f64, 47 | mouse_sensitivity: f64, 48 | speed: f64, 49 | fov: f64, 50 | } 51 | 52 | unsafe impl Sync for PitchYawCamera3 {} 53 | 54 | impl PitchYawCamera3 { 55 | pub fn new() -> PitchYawCamera3 { 56 | Self { 57 | position: [0.0, 0.0, 0.0].into(), 58 | yaw: std::f64::consts::PI, 59 | pitch: 0.0, 60 | mouse_sensitivity: 0.002, 61 | speed: 4.0, 62 | fov: std::f64::consts::FRAC_PI_2, 63 | } 64 | } 65 | 66 | pub fn new_with_position(position: Vec3) -> Self { 67 | Self { 68 | position, 69 | .. Self::default() 70 | } 71 | } 72 | 73 | fn update_rotation(&mut self, cursor_delta: &[f64; 2]) { 74 | let [delta_yaw, delta_pitch] = cursor_delta; 75 | 76 | self.yaw -= delta_yaw * self.mouse_sensitivity; 77 | self.pitch -= delta_pitch * self.mouse_sensitivity; 78 | 79 | if self.pitch > std::f64::consts::FRAC_PI_2 { 80 | self.pitch = std::f64::consts::FRAC_PI_2; 81 | } else if self.pitch < -std::f64::consts::FRAC_PI_2 { 82 | self.pitch = -std::f64::consts::FRAC_PI_2; 83 | } 84 | } 85 | } 86 | 87 | impl Default for PitchYawCamera3 { 88 | fn default() -> Self { 89 | Self::new() 90 | } 91 | } 92 | 93 | impl Camera for PitchYawCamera3 { 94 | fn get_view_rotation_matrix(&self) -> Mat4 { 95 | let rotation = -self.get_rotation_axis_angles(); 96 | 97 | Mat4::rotation_pitch(rotation[0]) 98 | * Mat4::rotation_yaw(rotation[1]) 99 | * Mat4::rotation_roll(rotation[2]) 100 | } 101 | 102 | fn get_view_translation_matrix(&self) -> Mat4 { 103 | Mat4::translation(&-self.get_position()) 104 | } 105 | 106 | fn get_position(&self) -> Vec3 { 107 | self.position.clone() 108 | } 109 | 110 | fn get_direction(&self) -> Vec3 { 111 | [ 112 | -(self.pitch.cos() * self.yaw.sin()) as f32, 113 | self.pitch.sin() as f32, 114 | -(self.pitch.cos() * self.yaw.cos()) as f32, 115 | ].into() 116 | } 117 | 118 | fn update(&mut self, 119 | delta_time: &Duration, 120 | cursor_delta: &[f64; 2], 121 | pressed_keys: &HashSet, 122 | _pressed_mouse_buttons: &HashSet) { 123 | self.update_rotation(cursor_delta); 124 | 125 | let delta_seconds = (delta_time.as_nanos() as f64) / 1.0e9; 126 | let distance = self.speed * delta_seconds; 127 | 128 | if distance == 0.0 { 129 | return; 130 | } 131 | 132 | let rotation = self.get_rotation_axis_angles(); 133 | let rotation_matrix = Mat4::rotation_roll(rotation[2]) 134 | * Mat4::rotation_yaw(rotation[1]) 135 | * Mat4::rotation_pitch(rotation[0]); 136 | 137 | let forward: Vec3 = (rotation_matrix * Vec3::from([0.0, 0.0, -1.0]).into_homogeneous_direction()).into_projected(); 138 | let right = forward.cross(&Vec3::from([0.0, 1.0, 0.0])); 139 | let mut direction: Vec3 = Vec3::ZERO; 140 | 141 | pressed_keys.contains(&VirtualKeyCode::W).as_option() 142 | .map(|()| direction += &forward); 143 | pressed_keys.contains(&VirtualKeyCode::S).as_option() 144 | .map(|()| direction -= &forward); 145 | pressed_keys.contains(&VirtualKeyCode::A).as_option() 146 | .map(|()| direction -= &right); 147 | pressed_keys.contains(&VirtualKeyCode::D).as_option() 148 | .map(|()| direction += &right); 149 | pressed_keys.contains(&VirtualKeyCode::LShift).as_option() 150 | .map(|()| direction += Vec3::from([0.0, 1.0, 0.0])); 151 | pressed_keys.contains(&VirtualKeyCode::LControl).as_option() 152 | .map(|()| direction -= Vec3::from([0.0, 1.0, 0.0])); 153 | 154 | let direction_norm = direction.norm(); 155 | 156 | if direction_norm != 0.0 { 157 | direction *= distance as f32 / direction_norm; 158 | 159 | pressed_keys.contains(&VirtualKeyCode::Space).as_option() 160 | .map(|()| direction *= 0.1); 161 | 162 | self.position += &direction; 163 | } 164 | } 165 | 166 | fn get_rotation_axis_angles(&self) -> Vec3 { 167 | [self.pitch as f32, self.yaw as f32, 0.0].into() 168 | } 169 | } 170 | 171 | pub fn construct_model_matrix(scale: f32, translation: &Vec3, rotation: &Vec3) -> Mat4 { 172 | Mat4::translation(translation) 173 | * Mat4::rotation_roll(rotation[2]) 174 | * Mat4::rotation_yaw(rotation[1]) 175 | * Mat4::rotation_pitch(rotation[0]) 176 | * Mat4::scale(scale) 177 | } 178 | 179 | pub fn construct_view_matrix(translation: &Vec3, rotation: &Vec3) -> Mat4 { 180 | // construct_model_matrix(1.0, &-translation, &Vec3::zero()) 181 | construct_model_matrix(1.0, &-translation, &-rotation) 182 | } 183 | 184 | pub fn construct_orthographic_projection_matrix(near_plane: f32, far_plane: f32, dimensions: Vec2) -> Mat4 { 185 | let z_n = near_plane; 186 | let z_f = far_plane; 187 | 188 | // Scale the X/Y-coordinates according to the dimensions. Translate and scale the Z-coordinate. 189 | mat4!([1.0 / dimensions[0], 0.0, 0.0, 0.0, 190 | 0.0, 1.0 / dimensions[1], 0.0, 0.0, 191 | 0.0, 0.0, 1.0 / (z_f - z_n), -z_n / (z_f - z_n), 192 | 0.0, 0.0, 0.0, 1.0]) 193 | } 194 | 195 | // pub fn construct_perspective_projection_matrix(near_plane: f32, far_plane: f32, aspect_ratio: f32, fov_rad: f32) -> Mat4 { 196 | // // This function generates matrix that transforms the view frustum to normalized device 197 | // // coordinates, which is, in case of Vulkan, the set [-1; 1]x[-1; 1]x[0; 1]. 198 | 199 | // // The resulting `(x, y, z, w)` vector gets normalized following the execution of the vertex 200 | // // shader to `(x/w, y/w, z/w)` (W-division). This makes it possible to create a perspective 201 | // // projection matrix. 202 | // // We copy the Z coordinate to the W coordinate so as to divide all coordinates by Z. 203 | // let z_n = near_plane; 204 | // let z_f = far_plane; 205 | // // With normalization, it is actually `1 / (z * tan(FOV / 2))`, which is the width of the 206 | // // screen at that point in space of the vector. 207 | // // The X coordinate needs to be divided by the aspect ratio to make it independent of the 208 | // // window size. 209 | // // Even though we could negate the Y coordinate so as to adjust the vectors to the Vulkan 210 | // // coordinate system, which has the Y axis pointing downwards, contrary to OpenGL, we need to 211 | // // apply the same transformation to other vertex attributes such as normal and tangent vectors, 212 | // // but those are not projected. 213 | // let f = 1.0 / (fov_rad / 2.0).tan(); 214 | 215 | // // Because we know, from the nature of the perspective projection, that the 216 | // // Z coordinate must be scaled and translated, we will be deriving A, B from 217 | // // the equation: 218 | // // `f(z) = A*z + B`, 219 | // // where A and B are the 3rd, respectively 4th column coefficients of the 3rd row. 220 | // // The equation changes to the following, after the W-division: 221 | // // `f(z) = A + B/z` 222 | // // And must satisfy the following conditions: 223 | // // `f(z_n) = 0` 224 | // // `f(z_f) = 1` 225 | // // Solving for A and B gives us the necessary coefficients to construct the matrix: 226 | // // A = (z_n * z_f) / (z_n - z_f) 227 | // // B = -z_f / (z_n - z_f) 228 | // // mat4!([f / aspect_ratio, 0.0, 0.0, 0.0, 229 | // // 0.0, -f, 0.0, 0.0, 230 | // // 0.0, 0.0, -z_f / (z_n - z_f), (z_n * z_f) / (z_n - z_f), 231 | // // 0.0, 0.0, 1.0, 0.0]) 232 | 233 | // // TODO: Investigate the mysterious requirement of flipping the X coordinate 234 | // // mat4!([-f / aspect_ratio, 0.0, 0.0, 0.0, 235 | // // 0.0, f, 0.0, 0.0, 236 | // // 0.0, 0.0, -z_f / (z_n - z_f), (z_n * z_f) / (z_n - z_f), 237 | // // 0.0, 0.0, 1.0, 0.0]) 238 | 239 | // // glTF spec formula: 240 | // mat4!([f / aspect_ratio, 0.0, 0.0, 0.0, 241 | // 0.0, 1.0 * f, 0.0, 0.0, 242 | // 0.0, 0.0, -1.0, -2.0 * z_n, 243 | // 0.0, 0.0, -1.0, 0.0]) 244 | // } 245 | 246 | pub fn construct_perspective_projection_matrix_asymmetric(near_plane: f32, far_plane: f32, angle_right: f32, angle_up: f32, angle_left: f32, angle_down: f32) -> Mat4 { 247 | // See http://www.songho.ca/opengl/gl_projectionmatrix.html 248 | // for a brilliant derivation of the projection matrix. 249 | 250 | // The resulting `(x, y, z, w)` vector gets normalized following the execution of the vertex 251 | // shader to `(x/w, y/w, z/w)` (W-division). This makes it possible to create a perspective 252 | // projection matrix. 253 | // We copy the Z coordinate to the W coordinate so as to divide all coordinates by Z. 254 | let z_n = near_plane; 255 | let z_f = far_plane; 256 | // With normalization, it is actually `1 / (z * tan(FOV / 2))`, which is the width of the 257 | // screen at that point in space of the vector. 258 | // The X coordinate needs to be divided by the aspect ratio to make it independent of the 259 | // window size. 260 | // Even though we could negate the Y coordinate so as to adjust the vectors to the Vulkan 261 | // coordinate system, which has the Y axis pointing downwards, contrary to OpenGL, we need to 262 | // apply the same transformation to other vertex attributes such as normal and tangent vectors, 263 | // but those are not projected. 264 | 265 | let l = angle_left.tan() * z_n; 266 | let r = angle_right.tan() * z_n; 267 | let b = angle_down.tan() * z_n; 268 | let t = angle_up.tan() * z_n; 269 | 270 | // mat4!([-2.0 * idx, 0.0, sx * idx, 0.0, 271 | // 0.0, 2.0 * idy, sy * idy, 0.0, 272 | // 0.0, 0.0, 0.0, z_n, 273 | // 0.0, 0.0, -1.0, 0.0]) 274 | mat4!([(2.0 * z_n) / (r - l), 0.0, (r + l) / (r - l), 0.0, 275 | 0.0, (2.0 * z_n) / (t - b), (t + b) / (t - b), 0.0, 276 | 0.0, 0.0, z_f / (z_n - z_f), (z_n * z_f) / (z_n - z_f), 277 | 0.0, 0.0, -1.0, 0.0]) 278 | } 279 | 280 | // pub fn construct_perspective_projection_matrix(near_plane: f32, far_plane: f32, aspect_ratio: f32, fov_rad: f32) -> Mat4 { 281 | // let angle_up = fov_rad / aspect_ratio; 282 | // let angle_right = fov_rad; 283 | // let angle_down = -angle_up; 284 | // let angle_left = -angle_right; 285 | 286 | // construct_perspective_projection_matrix_asymmetric( 287 | // near_plane, far_plane, 288 | // angle_right, angle_up, angle_left, angle_down, 289 | // ) 290 | // } 291 | -------------------------------------------------------------------------------- /src/iter/mod.rs: -------------------------------------------------------------------------------- 1 | use std::mem; 2 | use std::marker::PhantomData; 3 | use generic_array::{GenericArray, ArrayLength}; 4 | use safe_transmute::PodTransmutable; 5 | use gltf::accessor::Accessor; 6 | 7 | #[derive(Clone)] 8 | pub struct ArrayIterator> { 9 | next_index: usize, 10 | data: GenericArray, 11 | } 12 | 13 | impl> ArrayIterator { 14 | pub fn new>>(array: A) -> Self { 15 | Self { 16 | next_index: 0, 17 | data: array.into(), 18 | } 19 | } 20 | } 21 | 22 | impl> Iterator for ArrayIterator { 23 | type Item = T; 24 | 25 | fn next(&mut self) -> Option { 26 | if self.len() == 0 { 27 | None 28 | } else { 29 | let result = self.data[self.next_index].clone(); 30 | 31 | self.next_index += 1; 32 | 33 | Some(result) 34 | } 35 | } 36 | 37 | fn size_hint(&self) -> (usize, Option) { 38 | let len = self.len(); 39 | (len, Some(len)) 40 | } 41 | } 42 | 43 | impl> ExactSizeIterator for ArrayIterator { 44 | fn len(&self) -> usize { 45 | self.data.len() - self.next_index 46 | } 47 | } 48 | 49 | pub struct ForcedExactSizeIterator> { 50 | iterator: I, 51 | forced_remaining_len: usize, 52 | } 53 | 54 | impl> ForcedExactSizeIterator { 55 | pub fn new(iterator: I, forced_len: usize) -> Self { 56 | Self { 57 | iterator, 58 | forced_remaining_len: forced_len, 59 | } 60 | } 61 | } 62 | 63 | impl> Iterator for ForcedExactSizeIterator { 64 | type Item = T; 65 | 66 | fn next(&mut self) -> Option { 67 | if self.forced_remaining_len <= 0 { 68 | None 69 | } else { 70 | let result = self.iterator.next(); 71 | 72 | self.forced_remaining_len -= 1; 73 | 74 | result.or_else(|| Some(Self::Item::default())) 75 | } 76 | } 77 | 78 | fn size_hint(&self) -> (usize, Option) { 79 | (self.forced_remaining_len, Some(self.forced_remaining_len)) 80 | } 81 | } 82 | 83 | impl> ExactSizeIterator for ForcedExactSizeIterator { 84 | fn len(&self) -> usize { 85 | self.forced_remaining_len 86 | } 87 | } 88 | 89 | pub struct ByteBufferIterator<'a, T: PodTransmutable> { 90 | next_item_index: usize, 91 | /// Byte buffer with elements starting at index 0 (offset applied) 92 | bytes: &'a [u8], 93 | /// The number of bytes between the starting byte of each element 94 | stride: usize, 95 | /// Number of elements of type `T` to read from the `bytes` slice 96 | items: usize, 97 | _phantom_data: PhantomData, 98 | } 99 | 100 | impl<'a, T: PodTransmutable> ByteBufferIterator<'a, T> { 101 | pub fn new(bytes: &'a [u8], stride: usize, items: usize) -> Self { 102 | assert!(items == 0 || (items - 1) * stride + mem::size_of::() <= bytes.len()); 103 | 104 | Self { 105 | next_item_index: 0, 106 | bytes, 107 | stride, 108 | items, 109 | _phantom_data: PhantomData, 110 | } 111 | } 112 | 113 | pub fn from_accessor( 114 | buffer_data_array: &'a [gltf::buffer::Data], 115 | accessor: &Accessor, 116 | ) -> Self { 117 | let view = accessor.view(); 118 | let stride = view.stride().unwrap_or_else(|| accessor.size()); 119 | let slice_offset = view.offset() + accessor.offset(); 120 | let slice_len = stride * accessor.count(); 121 | let slice: &[u8] = &buffer_data_array[view.buffer().index()][slice_offset..(slice_offset + slice_len)]; 122 | 123 | Self::new(slice, stride, accessor.count()) 124 | } 125 | } 126 | 127 | impl<'a, T: PodTransmutable> Iterator for ByteBufferIterator<'a, T> { 128 | type Item = T; 129 | 130 | fn next(&mut self) -> Option { 131 | if self.next_item_index >= self.items { 132 | None 133 | } else { 134 | let item_slice_start_index = self.next_item_index * self.stride; 135 | let item_slice_range = item_slice_start_index..(item_slice_start_index + mem::size_of::()); 136 | let item_slice = &self.bytes[item_slice_range]; 137 | self.next_item_index += 1; 138 | 139 | Some(safe_transmute::guarded_transmute_pod::(item_slice).unwrap()) 140 | } 141 | } 142 | 143 | fn size_hint(&self) -> (usize, Option) { 144 | let len = self.len(); 145 | (len, Some(len)) 146 | } 147 | } 148 | 149 | impl<'a, T: PodTransmutable> ExactSizeIterator for ByteBufferIterator<'a, T> { 150 | fn len(&self) -> usize { 151 | self.items - self.next_item_index 152 | } 153 | } 154 | -------------------------------------------------------------------------------- /src/model/error.rs: -------------------------------------------------------------------------------- 1 | use failure::Fail; 2 | 3 | #[derive(Debug, Fail)] 4 | pub enum ModelInitializationError { 5 | #[fail(display = "The model has already been initialized and cannot be initialized again")] 6 | ModelAlreadyInitialized, 7 | } 8 | 9 | #[derive(Debug, Fail)] 10 | pub enum ModelDrawError { 11 | #[fail(display = "Trying to draw the default scene while no default scene is specified")] 12 | NoDefaultScene, 13 | #[fail(display = "Invalid toolchain index: {}", index)] 14 | InvalidSceneIndex { 15 | index: usize, 16 | }, 17 | } 18 | -------------------------------------------------------------------------------- /src/model/resource.rs: -------------------------------------------------------------------------------- 1 | use std::iter; 2 | use std::sync::Arc; 3 | use std::fmt; 4 | use std::marker::PhantomData; 5 | use core::num::NonZeroU32; 6 | use vulkano::command_buffer::AutoCommandBufferBuilder; 7 | use vulkano::device::Device; 8 | use vulkano::instance::QueueFamily; 9 | use vulkano::buffer::TypedBufferAccess; 10 | use vulkano::buffer::BufferUsage; 11 | use vulkano::buffer::CpuAccessibleBuffer; 12 | use vulkano::sampler::Filter; 13 | use vulkano::image::traits::ImageViewAccess; 14 | use vulkano::image::traits::ImageAccess; 15 | use vulkano::image::view::ImageView; 16 | use vulkano::image::sync::locker; 17 | use vulkano::image::SyncImage; 18 | use vulkano::image::ImageViewType; 19 | use vulkano::image::ImageDimensions; 20 | use vulkano::image::Swizzle; 21 | use vulkano::image::ImageSubresourceRange; 22 | use vulkano::image::layout::RequiredLayouts; 23 | use vulkano::image::layout::typesafety; 24 | use failure::Error; 25 | use crate::NodeUBO; 26 | use crate::MaterialUBO; 27 | use crate::iter::ForcedExactSizeIterator; 28 | 29 | pub enum InitializationTask { 30 | Buffer { 31 | data: Vec, 32 | initialization_buffer: Arc + Send + Sync>, 33 | }, 34 | ZeroBuffer { 35 | len: usize, 36 | initialization_buffer: Arc + Send + Sync>, 37 | }, 38 | // TODO: Investigate whether it should be merged with `ImageWithMipmaps` 39 | Image { 40 | data: Arc>, 41 | device_image: Arc, 42 | texel_conversion: Option Fn(&'a [u8]) -> Box + 'a>>>, 43 | }, 44 | ImageWithMipmaps { 45 | data: Arc>, 46 | device_image: Arc>, 47 | texel_conversion: Option Fn(&'a [u8]) -> Box + 'a>>>, 48 | }, 49 | NodeDescriptorSet { 50 | data: NodeUBO, 51 | initialization_buffer: Arc + Send + Sync>, 52 | }, 53 | MaterialDescriptorSet { 54 | data: MaterialUBO, 55 | initialization_buffer: Arc + Send + Sync>, 56 | }, 57 | } 58 | 59 | impl fmt::Debug for InitializationTask { 60 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 61 | match self { 62 | InitializationTask::Buffer { .. } => { 63 | write!(f, "buffer") 64 | }, 65 | InitializationTask::ZeroBuffer { .. } => { 66 | write!(f, "zero buffer") 67 | }, 68 | InitializationTask::Image { .. } => { 69 | write!(f, "image") 70 | }, 71 | InitializationTask::ImageWithMipmaps { .. } => { 72 | write!(f, "image with mipmaps") 73 | }, 74 | InitializationTask::NodeDescriptorSet { .. } => { 75 | write!(f, "node descriptor set") 76 | }, 77 | InitializationTask::MaterialDescriptorSet { .. } => { 78 | write!(f, "material descriptor set") 79 | }, 80 | } 81 | } 82 | } 83 | 84 | impl InitializationTask { 85 | fn initialize(self, device: &Arc, _queue_family: QueueFamily, command_buffer_builder: AutoCommandBufferBuilder) -> Result { 86 | match self { 87 | InitializationTask::Buffer { data, initialization_buffer, .. } => { 88 | let staging_buffer: Arc> = CpuAccessibleBuffer::from_iter( 89 | device.clone(), 90 | BufferUsage::transfer_source(), 91 | data.iter().cloned(), // FIXME: Single memcpy call, do not iterate 92 | )?; 93 | 94 | Ok(command_buffer_builder.copy_buffer(staging_buffer, initialization_buffer)?) 95 | }, 96 | InitializationTask::ZeroBuffer { len, initialization_buffer, .. } => { 97 | let staging_buffer: Arc> = CpuAccessibleBuffer::from_iter( 98 | device.clone(), 99 | BufferUsage::transfer_source(), 100 | ForcedExactSizeIterator::new(iter::repeat(0u8).take(len), len), 101 | )?; 102 | 103 | Ok(command_buffer_builder.copy_buffer(staging_buffer, initialization_buffer)?) 104 | }, 105 | InitializationTask::Image { data, device_image, texel_conversion, .. } => { 106 | let staging_buffer = CpuAccessibleBuffer::from_iter( 107 | device.clone(), 108 | BufferUsage::transfer_source(), 109 | if let Some(texel_conversion) = texel_conversion { 110 | (texel_conversion)(&data[..]) 111 | } else { 112 | Box::new(data.iter().cloned()) // FIXME: Single memcpy call, do not iterate 113 | }, 114 | )?; 115 | 116 | let command_buffer_builder = command_buffer_builder 117 | .copy_buffer_to_image(staging_buffer, device_image.clone())?; 118 | 119 | Ok(command_buffer_builder) 120 | }, 121 | // InitializationTask::ImageWithMipmaps { .. } => { 122 | // unimplemented!(); 123 | // }, 124 | InitializationTask::ImageWithMipmaps { data, device_image, texel_conversion, .. } => { 125 | // let mut required_layouts = RequiredLayouts::general(); 126 | let mut required_layouts = RequiredLayouts::none(); 127 | required_layouts.infer_mut(device_image.usage()); 128 | required_layouts.global = Some(typesafety::ImageLayoutEnd::ShaderReadOnlyOptimal); 129 | 130 | let mut source_layer = Arc::new(ImageView::new( 131 | device_image.clone(), 132 | Some(ImageViewType::Dim2D), 133 | Some(device_image.format()), 134 | Swizzle::identity(), 135 | Some(ImageSubresourceRange { 136 | array_layers: NonZeroU32::new(1).unwrap(), 137 | array_layers_offset: 0, 138 | mipmap_levels: NonZeroU32::new(1).unwrap(), 139 | mipmap_levels_offset: 0, 140 | }), 141 | required_layouts.clone(), 142 | )?); 143 | let staging_buffer = CpuAccessibleBuffer::from_iter( 144 | device.clone(), 145 | BufferUsage::transfer_source(), 146 | if let Some(texel_conversion) = texel_conversion { 147 | (texel_conversion)(&data[..]) 148 | } else { 149 | Box::new(data.iter().cloned()) // FIXME: Single memcpy call, do not iterate 150 | }, 151 | )?; 152 | 153 | let mut command_buffer_builder = command_buffer_builder 154 | .copy_buffer_to_image(staging_buffer, source_layer.clone())?; 155 | 156 | // TODO: Set mip count to Log2 157 | 158 | let (width, height) = if let ImageDimensions::Dim2D { width, height } = device_image.dimensions() { 159 | (width.get(), height.get()) 160 | } else { 161 | panic!("Texture mipmap generation is not implemented for non-2d textures."); 162 | }; 163 | 164 | for mip_level in 1..device_image.mipmap_levels().get() { 165 | let destination_layer = Arc::new(ImageView::new( 166 | device_image.clone(), 167 | Some(ImageViewType::Dim2D), 168 | Some(device_image.format()), 169 | Swizzle::identity(), 170 | Some(ImageSubresourceRange { 171 | array_layers: NonZeroU32::new(1).unwrap(), 172 | array_layers_offset: 0, 173 | mipmap_levels: NonZeroU32::new(1).unwrap(), 174 | mipmap_levels_offset: mip_level, 175 | }), 176 | required_layouts.clone(), 177 | )?); 178 | let source_mip_level = mip_level - 1; 179 | let mip_dimensions = (width >> mip_level, height >> mip_level); 180 | let source_mip_dimensions = (width >> source_mip_level, height >> source_mip_level); 181 | 182 | command_buffer_builder = command_buffer_builder.blit_image( 183 | source_layer.clone(), 184 | [0, 0, 0], 185 | [source_mip_dimensions.0 as i32, source_mip_dimensions.1 as i32, 1], 186 | 0, 187 | source_mip_level, 188 | destination_layer.clone(), 189 | [0, 0, 0], 190 | [mip_dimensions.0 as i32, mip_dimensions.1 as i32, 1], 191 | 0, 192 | mip_level, 193 | 1, 194 | Filter::Linear, 195 | )?; 196 | source_layer = destination_layer; 197 | } 198 | 199 | Ok(command_buffer_builder) 200 | }, 201 | InitializationTask::NodeDescriptorSet { data, initialization_buffer, .. } => { 202 | let staging_buffer: Arc> = CpuAccessibleBuffer::from_data( 203 | device.clone(), 204 | BufferUsage::transfer_source(), 205 | data, 206 | )?; 207 | 208 | Ok(command_buffer_builder.copy_buffer(staging_buffer, initialization_buffer)?) 209 | }, 210 | InitializationTask::MaterialDescriptorSet { data, initialization_buffer, .. } => { 211 | let staging_buffer: Arc> = CpuAccessibleBuffer::from_data( 212 | device.clone(), 213 | BufferUsage::transfer_source(), 214 | data, 215 | )?; 216 | 217 | Ok(command_buffer_builder.copy_buffer(staging_buffer, initialization_buffer)?) 218 | }, 219 | } 220 | } 221 | } 222 | 223 | pub trait UninitializedResource { 224 | fn initialize_resource( 225 | self, 226 | device: &Arc, 227 | queue_family: QueueFamily, 228 | command_buffer_builder: AutoCommandBufferBuilder 229 | ) -> Result<(AutoCommandBufferBuilder, T), Error>; 230 | 231 | fn join(self, other: URU) -> JoinUninitializedResource 232 | where Self: Sized, 233 | URU: UninitializedResource { 234 | JoinUninitializedResource { 235 | first: self, 236 | second: other, 237 | _marker: PhantomData, 238 | } 239 | } 240 | 241 | fn map(self, map: F) -> MapUninitializedResource 242 | where Self: Sized, 243 | F: FnOnce(T) -> O { 244 | MapUninitializedResource { 245 | input: self, 246 | map, 247 | _marker: PhantomData, 248 | } 249 | } 250 | } 251 | 252 | pub struct SimpleUninitializedResource { 253 | output: T, 254 | tasks: Vec, 255 | } 256 | 257 | impl SimpleUninitializedResource { 258 | pub fn new(output: T, tasks: Vec) -> Self { 259 | Self { 260 | output, 261 | tasks, 262 | } 263 | } 264 | } 265 | 266 | impl UninitializedResource for SimpleUninitializedResource { 267 | fn initialize_resource(self, device: &Arc, queue_family: QueueFamily, mut command_buffer_builder: AutoCommandBufferBuilder) -> Result<(AutoCommandBufferBuilder, T), Error> { 268 | for initialization_task in self.tasks.into_iter() { 269 | command_buffer_builder = initialization_task.initialize(device, queue_family, command_buffer_builder)?; 270 | } 271 | 272 | Ok((command_buffer_builder, self.output)) 273 | } 274 | } 275 | 276 | pub struct JoinUninitializedResource where URT: UninitializedResource, 277 | URU: UninitializedResource { 278 | first: URT, 279 | second: URU, 280 | _marker: PhantomData<(T, U)>, 281 | } 282 | 283 | impl UninitializedResource<(T, U)> for JoinUninitializedResource 284 | where URT: UninitializedResource, 285 | URU: UninitializedResource { 286 | fn initialize_resource(self, device: &Arc, queue_family: QueueFamily, command_buffer_builder: AutoCommandBufferBuilder) -> Result<(AutoCommandBufferBuilder, (T, U)), Error> { 287 | let (command_buffer_builder, first_result) = self.first.initialize_resource(device, queue_family, command_buffer_builder)?; 288 | let (command_buffer_builder, second_result) = self.second.initialize_resource(device, queue_family, command_buffer_builder)?; 289 | 290 | Ok((command_buffer_builder, (first_result, second_result))) 291 | } 292 | } 293 | 294 | pub struct MapUninitializedResource where URI: UninitializedResource, 295 | F: FnOnce(I) -> O { 296 | input: URI, 297 | map: F, 298 | _marker: PhantomData<(I, O)>, 299 | } 300 | 301 | impl UninitializedResource for MapUninitializedResource 302 | where URI: UninitializedResource, 303 | F: FnOnce(I) -> O { 304 | fn initialize_resource(self, device: &Arc, queue_family: QueueFamily, command_buffer_builder: AutoCommandBufferBuilder) -> Result<(AutoCommandBufferBuilder, O), Error> { 305 | let (command_buffer_builder, result) = self.input.initialize_resource(device, queue_family, command_buffer_builder)?; 306 | let mapped_result = (self.map)(result); 307 | 308 | Ok((command_buffer_builder, mapped_result)) 309 | } 310 | } 311 | -------------------------------------------------------------------------------- /src/pipeline.rs: -------------------------------------------------------------------------------- 1 | use std::cell::RefCell; 2 | use std::collections::hash_map::Entry; 3 | use std::sync::{Arc, RwLock, Mutex}; 4 | use std::ops::{BitOr, BitOrAssign, Not}; 5 | use std::collections::HashMap; 6 | use core::num::NonZeroU32; 7 | use vulkano; 8 | use vulkano::ordered_passes_renderpass; 9 | use vulkano::format::*; 10 | use vulkano::image::traits::ImageViewAccess; 11 | use vulkano::descriptor::descriptor_set::DescriptorSet; 12 | use vulkano::descriptor::descriptor_set::PersistentDescriptorSet; 13 | use vulkano::descriptor::descriptor_set::FixedSizeDescriptorSetsPool; 14 | use vulkano::descriptor::descriptor_set::FixedSizeDescriptorSetBuilder; 15 | use vulkano::descriptor::pipeline_layout::PipelineLayoutDesc; 16 | use vulkano::descriptor::pipeline_layout::PipelineLayoutDescAggregation; 17 | use vulkano::instance::QueueFamily; 18 | 19 | use vulkano::image::layout::RequiredLayouts; 20 | 21 | use vulkano::image::Swizzle; 22 | use vulkano::image::ImageDimensions; 23 | 24 | use vulkano::image::ImageUsage; 25 | use vulkano::image::MipmapsCount; 26 | use vulkano::image::SyncImage; 27 | use vulkano::image::view::ImageView; 28 | use vulkano::buffer::BufferUsage; 29 | use vulkano::buffer::immutable::ImmutableBuffer; 30 | use vulkano::framebuffer::Framebuffer; 31 | use vulkano::framebuffer::RenderPassAbstract; 32 | use vulkano::descriptor::pipeline_layout::PipelineLayout; 33 | use vulkano::pipeline::blend::AttachmentBlend; 34 | use vulkano::pipeline::blend::BlendFactor; 35 | use vulkano::pipeline::blend::BlendOp; 36 | use vulkano::pipeline::GraphicsPipelineAbstract; 37 | use vulkano::device::Device; 38 | use vulkano::format::Format; 39 | use vulkano::framebuffer::Subpass; 40 | use vulkano::pipeline::GraphicsPipeline; 41 | use vulkano::pipeline::depth_stencil::DepthStencil; 42 | use vulkano::pipeline::depth_stencil::Compare; 43 | use vulkano::pipeline::depth_stencil::DepthBounds; 44 | 45 | 46 | use gltf::material::Material; 47 | use gltf::mesh::Primitive; 48 | use failure::Error; 49 | use fnv::FnvBuildHasher; 50 | use crate::ViewSwapchain; 51 | use crate::vertex::{GltfVertexBufferDefinition, VertexAttributePropertiesSet}; 52 | use crate::model::{FramebufferWithClearValues, HelperResources}; 53 | use crate::model::resource::{InitializationTask, UninitializedResource, SimpleUninitializedResource}; 54 | use crate::buffer::StagedBuffer; 55 | use crate::iter::ArrayIterator; 56 | use crate::shaders::*; 57 | 58 | #[derive(PartialEq, Eq)] 59 | #[repr(C)] 60 | pub enum GraphicsPipelineFlag { 61 | DoubleSided, 62 | Len, 63 | } 64 | 65 | #[derive(Debug, Clone, Eq, PartialEq, Default, Hash)] 66 | pub struct GraphicsPipelineFlags(usize); 67 | 68 | impl<'a, 'b> From<&'b Material<'a>> for GraphicsPipelineFlags { 69 | fn from(material: &'b Material<'a>) -> Self { 70 | let mut result = GraphicsPipelineFlags::default(); 71 | 72 | if material.double_sided() { 73 | result |= GraphicsPipelineFlag::DoubleSided; 74 | } 75 | 76 | result 77 | } 78 | } 79 | 80 | impl BitOr for GraphicsPipelineFlags { 81 | type Output = Self; 82 | 83 | fn bitor(self, rhs: Self) -> Self { 84 | GraphicsPipelineFlags(self.0 | rhs.0) 85 | } 86 | } 87 | 88 | impl BitOr for GraphicsPipelineFlags { 89 | type Output = Self; 90 | 91 | fn bitor(self, rhs: GraphicsPipelineFlag) -> Self { 92 | if rhs == GraphicsPipelineFlag::Len { 93 | panic!("Invalid graphics pipeline flag."); 94 | } 95 | 96 | GraphicsPipelineFlags(self.0 | (1 << rhs as usize)) 97 | } 98 | } 99 | 100 | impl BitOrAssign for GraphicsPipelineFlags { 101 | fn bitor_assign(&mut self, rhs: Self) { 102 | self.0 |= rhs.0; 103 | } 104 | } 105 | 106 | impl BitOrAssign for GraphicsPipelineFlags { 107 | fn bitor_assign(&mut self, rhs: GraphicsPipelineFlag) { 108 | if rhs == GraphicsPipelineFlag::Len { 109 | panic!("Invalid graphics pipeline flag."); 110 | } 111 | 112 | self.0 |= 1 << rhs as usize; 113 | } 114 | } 115 | 116 | impl Not for GraphicsPipelineFlags { 117 | type Output = Self; 118 | 119 | fn not(self) -> Self { 120 | GraphicsPipelineFlags(!self.0) 121 | } 122 | } 123 | 124 | #[derive(Debug, Clone, Eq, PartialEq, Hash, Default)] 125 | pub struct GraphicsPipelineProperties { 126 | flags: GraphicsPipelineFlags, 127 | vertex_attribute_properties_set: VertexAttributePropertiesSet, 128 | } 129 | 130 | impl GraphicsPipelineProperties { 131 | pub fn from<'a>(primitive: &Primitive<'a>, material: &Material<'a>) -> Self { 132 | GraphicsPipelineProperties { 133 | flags: material.into(), 134 | vertex_attribute_properties_set: primitive.into(), 135 | } 136 | } 137 | } 138 | 139 | #[derive(Clone)] 140 | pub struct SwapchainDependentResources { 141 | pub depth_image: Arc, 142 | pub blend_accumulation_image: Arc, 143 | pub blend_revealage_image: Arc, 144 | } 145 | 146 | #[derive(Clone)] 147 | pub struct SharedGltfGraphicsPipelineResources { 148 | device: Arc, 149 | pub helper_resources: HelperResources, 150 | pub scene_ubo_buffer: StagedBuffer, 151 | pub default_material_ubo_buffer: Arc>, 152 | pub swapchain_dependent_resources: Vec>, 153 | } 154 | 155 | impl SharedGltfGraphicsPipelineResources { 156 | pub fn new(device: Arc, helper_resources: HelperResources, queue_family: QueueFamily, view_swapchains: &[&RefCell]) 157 | -> Result, Error> { 158 | let scene_ubo = SceneUBO::default(); 159 | let scene_ubo_buffer = StagedBuffer::from_data( 160 | &device, 161 | queue_family, 162 | BufferUsage::uniform_buffer(), 163 | scene_ubo.clone(), 164 | ); 165 | let (device_default_material_ubo_buffer, default_material_ubo_buffer_initialization) = unsafe { 166 | ImmutableBuffer::::uninitialized( 167 | device.clone(), 168 | BufferUsage::uniform_buffer_transfer_destination(), 169 | ) 170 | }?; 171 | let tasks = vec![ 172 | InitializationTask::MaterialDescriptorSet { 173 | data: MaterialUBO::default(), 174 | initialization_buffer: Arc::new(default_material_ubo_buffer_initialization), 175 | }, 176 | ]; 177 | 178 | Ok(SimpleUninitializedResource::new(Self { 179 | device, 180 | helper_resources, 181 | scene_ubo_buffer, 182 | default_material_ubo_buffer: device_default_material_ubo_buffer, 183 | swapchain_dependent_resources: vec![None; view_swapchains.len()], 184 | }, tasks)) 185 | } 186 | 187 | pub fn construct_swapchain_framebuffers(&mut self, 188 | render_pass: Arc, 189 | view_swapchain_index: usize, 190 | view_swapchain: &ViewSwapchain) 191 | -> Vec>>> { 192 | let render_pass = &render_pass; 193 | let resources = self.swapchain_dependent_resources[view_swapchain_index] 194 | .as_ref().expect("Framebuffer swapchain dependent images not initialized."); 195 | view_swapchain.swapchain.images().iter().map(|image| { 196 | Arc::new(Framebuffer::start(render_pass.clone()) 197 | .add(image.clone()).unwrap() 198 | .add(resources.depth_image.clone()).unwrap() 199 | .add(resources.blend_accumulation_image.clone()).unwrap() 200 | .add(resources.blend_revealage_image.clone()).unwrap() 201 | .build().unwrap()) as Arc> 202 | }).collect() 203 | } 204 | 205 | fn construct_attachment_image_view( 206 | &self, 207 | dimensions: [NonZeroU32; 2], 208 | format: F, 209 | usage: ImageUsage, 210 | ) -> Result>, Error> { 211 | let device_image = SyncImage::new( 212 | self.device.clone(), 213 | usage.clone(), 214 | format, 215 | ImageDimensions::Dim2D { 216 | width: dimensions[0], 217 | height: dimensions[1], 218 | }, 219 | NonZeroU32::new(1).unwrap(), 220 | MipmapsCount::One, 221 | )?; 222 | 223 | let mut required_layouts = RequiredLayouts::none(); 224 | // let mut required_layouts = RequiredLayouts::general(); 225 | required_layouts.infer_mut(usage); 226 | // required_layouts.global = Some(typesafety::ImageLayoutEnd::ColorAttachmentOptimal); 227 | 228 | Ok(Arc::new(ImageView::new::( 229 | device_image, 230 | None, 231 | None, 232 | Swizzle::identity(), 233 | None, 234 | required_layouts, 235 | )?)) 236 | } 237 | 238 | pub fn reconstruct_dimensions_dependent_images( 239 | &mut self, 240 | view_swapchain_index: usize, 241 | view_swapchain: &ViewSwapchain, 242 | ) -> Result<(), Error> { 243 | let dimensions = view_swapchain.swapchain.dimensions(); 244 | self.swapchain_dependent_resources[view_swapchain_index] = Some(SwapchainDependentResources { 245 | depth_image: self.construct_attachment_image_view( 246 | dimensions.clone(), 247 | D32Sfloat, 248 | ImageUsage { 249 | depth_stencil_attachment: true, 250 | .. ImageUsage::none() 251 | }, 252 | )?, 253 | blend_accumulation_image: self.construct_attachment_image_view( 254 | dimensions.clone(), 255 | R32G32B32A32Sfloat, 256 | ImageUsage { 257 | color_attachment: true, 258 | input_attachment: true, 259 | transient_attachment: true, 260 | .. ImageUsage::none() 261 | } 262 | )?, 263 | blend_revealage_image: self.construct_attachment_image_view( 264 | dimensions.clone(), 265 | R32G32B32A32Sfloat, //FIXME 266 | ImageUsage { 267 | color_attachment: true, 268 | input_attachment: true, 269 | transient_attachment: true, 270 | .. ImageUsage::none() 271 | }, 272 | )?, 273 | }); 274 | 275 | Ok(()) 276 | } 277 | } 278 | 279 | #[derive(Clone)] 280 | pub struct GltfGraphicsPipeline { 281 | pub layout: Arc, 282 | pub pipeline: Arc, 283 | pub layout_dependent_resources: GltfPipelineLayoutDependentResources, 284 | } 285 | 286 | impl GltfGraphicsPipeline { 287 | pub fn from(layout: Arc, 288 | pipeline: Arc, 289 | layout_dependent_resources: GltfPipelineLayoutDependentResources) -> Self { 290 | Self { 291 | layout, 292 | pipeline, 293 | layout_dependent_resources, 294 | } 295 | } 296 | } 297 | 298 | pub struct DescriptorSetMap { 299 | // Consider implementing a lightweight "map" that stores values linearly in a `Vec` until a 300 | // certain size is reached. 301 | pub map: HashMap< 302 | PipelineLayoutDescAggregation, 303 | Arc, 304 | FnvBuildHasher, 305 | >, 306 | } 307 | 308 | impl DescriptorSetMap { 309 | pub fn custom<'a>(pipelines: impl IntoIterator, 310 | set_provider: impl Fn(&'a GltfGraphicsPipeline) 311 | -> Arc) -> Self { 312 | let mut map = HashMap::< 313 | PipelineLayoutDescAggregation, 314 | Arc, 315 | FnvBuildHasher, 316 | >::default(); 317 | 318 | for pipeline in pipelines { 319 | match map.entry(pipeline.layout.desc().clone()) { 320 | Entry::Occupied(_) => (), 321 | Entry::Vacant(entry) => { 322 | let set = (set_provider)(pipeline); 323 | 324 | entry.insert(set); 325 | } 326 | } 327 | } 328 | 329 | Self { map } 330 | } 331 | 332 | pub fn new<'a>(pipelines: impl IntoIterator, 333 | pool_provider: impl Fn(GltfPipelineLayoutDependentResources) 334 | -> Arc>, 335 | set_builder_fn: impl Fn(FixedSizeDescriptorSetBuilder<()>) 336 | -> Arc) -> Self { 337 | Self::custom(pipelines, |pipeline| { 338 | let pool = pool_provider(pipeline.layout_dependent_resources.clone()); 339 | let mut pool = pool.lock().unwrap(); 340 | let set_builder = pool.next(); 341 | 342 | (set_builder_fn)(set_builder) 343 | }) 344 | } 345 | } 346 | 347 | #[derive(Clone)] 348 | pub struct GltfPipelineLayoutDependentResources { 349 | pub layout: Arc, 350 | pub descriptor_set_scene: Arc, 351 | pub descriptor_set_pool_instance: Arc>, 352 | pub descriptor_sets_blend: Option>>>, 353 | pub default_material_descriptor_set: Arc, 354 | } 355 | 356 | impl GltfPipelineLayoutDependentResources { 357 | pub fn from(layout: Arc, 358 | shared_resources: &SharedGltfGraphicsPipelineResources) -> Self { 359 | let descriptor_set_scene = Arc::new( 360 | PersistentDescriptorSet::start(layout.clone(), 0) 361 | .add_buffer(shared_resources.scene_ubo_buffer.device_buffer().clone()).unwrap() 362 | .build().unwrap() 363 | ); 364 | let descriptor_set_pool_instance = Arc::new(Mutex::new( 365 | FixedSizeDescriptorSetsPool::new(layout.clone(), 1) 366 | )); 367 | let default_material_descriptor_set: Arc = Arc::new( 368 | PersistentDescriptorSet::start(layout.clone(), 3) 369 | .add_buffer(shared_resources.default_material_ubo_buffer.clone()).unwrap() 370 | .add_image(shared_resources.helper_resources.empty_image.clone()).unwrap() 371 | .add_sampler(shared_resources.helper_resources.cheapest_sampler.clone()).unwrap() 372 | .add_image(shared_resources.helper_resources.empty_image.clone()).unwrap() 373 | .add_sampler(shared_resources.helper_resources.cheapest_sampler.clone()).unwrap() 374 | .add_image(shared_resources.helper_resources.empty_image.clone()).unwrap() 375 | .add_sampler(shared_resources.helper_resources.cheapest_sampler.clone()).unwrap() 376 | .add_image(shared_resources.helper_resources.empty_image.clone()).unwrap() 377 | .add_sampler(shared_resources.helper_resources.cheapest_sampler.clone()).unwrap() 378 | .add_image(shared_resources.helper_resources.empty_image.clone()).unwrap() 379 | .add_sampler(shared_resources.helper_resources.cheapest_sampler.clone()).unwrap() 380 | .build().unwrap() 381 | ); 382 | 383 | Self { 384 | layout, 385 | descriptor_set_scene, 386 | descriptor_set_pool_instance, 387 | descriptor_sets_blend: None, // late init with `reconstruct_descriptor_sets` 388 | default_material_descriptor_set, 389 | } 390 | } 391 | 392 | pub fn reconstruct_descriptor_sets(&mut self, shared_resources: &SharedGltfGraphicsPipelineResources, view_swapchains_len: usize, view_swapchain_index: usize, view_swapchain: &ViewSwapchain) { 393 | self.descriptor_set_scene = Arc::new( 394 | PersistentDescriptorSet::start(self.layout.clone(), 0) 395 | .add_buffer(shared_resources.scene_ubo_buffer.device_buffer().clone()).unwrap() 396 | .build().unwrap() 397 | ); 398 | 399 | if self.descriptor_sets_blend.is_none() { 400 | self.descriptor_sets_blend = Some(vec![None; view_swapchains_len]); 401 | } 402 | 403 | self.descriptor_sets_blend.as_mut().unwrap()[view_swapchain_index] = self.layout.descriptor_set_layout(4).map(|_| { 404 | let swapchain_resources = shared_resources 405 | .swapchain_dependent_resources[view_swapchain_index] 406 | .as_ref().expect("Swapchain dependent resources not initialized."); 407 | 408 | Arc::new(PersistentDescriptorSet::start(self.layout.clone(), 4) 409 | .add_image(swapchain_resources.blend_accumulation_image.clone()).unwrap() 410 | .add_image(swapchain_resources.blend_revealage_image.clone()).unwrap() 411 | .build().unwrap()) as Arc 412 | }); 413 | } 414 | } 415 | 416 | #[derive(Clone)] 417 | pub struct GraphicsPipelineSet { 418 | pub opaque: GltfGraphicsPipeline, 419 | pub mask: GltfGraphicsPipeline, 420 | pub blend_preprocess: GltfGraphicsPipeline, 421 | pub blend_finalize: GltfGraphicsPipeline, 422 | } 423 | 424 | impl GraphicsPipelineSet { 425 | pub fn iter(&self) -> impl Iterator { 426 | ArrayIterator::new([ 427 | &self.opaque, 428 | &self.mask, 429 | &self.blend_preprocess, 430 | &self.blend_finalize, 431 | ]) 432 | } 433 | } 434 | 435 | // Consider improving the synchronization data type 436 | pub struct GraphicsPipelineSetCache { 437 | pub pipeline_map: Arc>>, 438 | pub shared_resources: SharedGltfGraphicsPipelineResources, 439 | // TODO: Since the hashmap no longer has weak keys, the resources don't get freed automatically 440 | pub pipeline_layout_dependent_resources: Arc>>, 444 | // pub pipeline_layout_dependent_resources: Arc, 446 | // GltfPipelineLayoutDependentResources 447 | // >>>, 448 | pub device: Arc, 449 | pub render_pass: Arc, 450 | pub vertex_shader: gltf_vert::Shader, // Stored here to avoid unnecessary reloading 451 | } 452 | 453 | macro_rules! cache_layout { 454 | ($cache:expr, $builder:expr) => {{ 455 | let mut resources_map = $cache.pipeline_layout_dependent_resources 456 | .as_ref().write().expect("Layout dependent resources poisoned."); 457 | let layout_desc = $builder.construct_layout_desc(&[]).unwrap(); 458 | let (layout, resources) = if let Some(resources) = resources_map.get(&layout_desc) { 459 | (resources.layout.clone(), resources.clone()) 460 | } else { 461 | let layout: Arc = layout_desc.clone() 462 | .build($cache.device.clone()).unwrap(); 463 | let resources = GltfPipelineLayoutDependentResources::from( 464 | layout.clone(), 465 | &$cache.shared_resources, 466 | ); 467 | 468 | resources_map.insert(layout_desc, resources.clone()); 469 | 470 | (layout, resources) 471 | }; 472 | 473 | let pipeline = Arc::new( 474 | $builder.with_pipeline_layout($cache.device.clone(), layout.clone()) 475 | .unwrap() 476 | ); 477 | 478 | GltfGraphicsPipeline::from(layout, pipeline, resources) 479 | }} 480 | } 481 | 482 | macro_rules! construct_pipeline_opaque { 483 | ($cache:expr, $graphics_pipeline_builder:expr) => {{ 484 | let fs = gltf_opaque_frag::Shader::load($cache.device.clone()).expect("Failed to create shader module."); 485 | let builder = $graphics_pipeline_builder.clone() 486 | .depth_stencil(DepthStencil::simple_depth_test()) 487 | .fragment_shader(fs.main_entry_point(), ()) 488 | .render_pass(Subpass::from($cache.render_pass.clone(), 0).unwrap()); 489 | 490 | cache_layout!($cache, builder) 491 | // .build($cache.device.clone()) 492 | // .unwrap(); 493 | 494 | // Arc::new(GltfGraphicsPipeline::from(Arc::new(pipeline), $shared_resources)) 495 | }} 496 | } 497 | 498 | macro_rules! construct_pipeline_mask { 499 | ($cache:expr, $graphics_pipeline_builder:expr) => {{ 500 | let fs = gltf_mask_frag::Shader::load($cache.device.clone()).expect("Failed to create shader module."); 501 | let builder = $graphics_pipeline_builder.clone() 502 | .depth_stencil(DepthStencil::simple_depth_test()) 503 | .fragment_shader(fs.main_entry_point(), ()) 504 | .render_pass(Subpass::from($cache.render_pass.clone(), 1).unwrap()); 505 | 506 | cache_layout!($cache, builder) 507 | // .build($cache.device.clone()) 508 | // .unwrap(); 509 | 510 | // Arc::new(GltfGraphicsPipeline::from(Arc::new(pipeline), $shared_resources)) 511 | }} 512 | } 513 | 514 | macro_rules! construct_pipeline_blend_preprocess { 515 | ($cache:expr, $graphics_pipeline_builder:expr) => {{ 516 | let fs = gltf_blend_preprocess_frag::Shader::load($cache.device.clone()).expect("Failed to create shader module."); 517 | let builder = $graphics_pipeline_builder.clone() 518 | .depth_stencil(DepthStencil { 519 | depth_compare: Compare::Less, 520 | depth_write: false, 521 | depth_bounds_test: DepthBounds::Disabled, 522 | stencil_front: Default::default(), 523 | stencil_back: Default::default(), 524 | }) 525 | .fragment_shader(fs.main_entry_point(), ()) 526 | .blend_individual([ 527 | AttachmentBlend { 528 | enabled: true, 529 | color_op: BlendOp::Add, 530 | color_source: BlendFactor::One, 531 | color_destination: BlendFactor::One, 532 | alpha_op: BlendOp::Add, 533 | alpha_source: BlendFactor::One, 534 | alpha_destination: BlendFactor::One, 535 | mask_red: true, 536 | mask_green: true, 537 | mask_blue: true, 538 | mask_alpha: true, 539 | }, 540 | AttachmentBlend { 541 | enabled: true, 542 | color_op: BlendOp::Add, 543 | color_source: BlendFactor::Zero, 544 | color_destination: BlendFactor::OneMinusSrcAlpha, 545 | alpha_op: BlendOp::Add, 546 | alpha_source: BlendFactor::Zero, 547 | alpha_destination: BlendFactor::OneMinusSrcAlpha, 548 | mask_red: true, 549 | mask_green: true, 550 | mask_blue: true, 551 | mask_alpha: true, 552 | }, 553 | ].into_iter().cloned()) 554 | .render_pass(Subpass::from($cache.render_pass.clone(), 2).unwrap()); 555 | 556 | cache_layout!($cache, builder) 557 | // .build($cache.device.clone()) 558 | // .unwrap(); 559 | 560 | // Arc::new(GltfGraphicsPipeline::from(Arc::new(pipeline), $shared_resources)) 561 | }} 562 | } 563 | 564 | macro_rules! construct_pipeline_blend_finalize { 565 | ($cache:expr, $graphics_pipeline_builder:expr) => {{ 566 | let fs = gltf_blend_finalize_frag::Shader::load($cache.device.clone()).expect("Failed to create shader module."); 567 | let builder = $graphics_pipeline_builder.clone() 568 | .depth_stencil(DepthStencil { 569 | depth_compare: Compare::Less, 570 | depth_write: false, 571 | depth_bounds_test: DepthBounds::Disabled, 572 | stencil_front: Default::default(), 573 | stencil_back: Default::default(), 574 | }) 575 | .fragment_shader(fs.main_entry_point(), ()) 576 | .blend_individual([ 577 | AttachmentBlend { 578 | enabled: true, 579 | color_op: BlendOp::Add, 580 | color_source: BlendFactor::OneMinusSrcAlpha, 581 | color_destination: BlendFactor::SrcAlpha, 582 | alpha_op: BlendOp::Add, 583 | alpha_source: BlendFactor::OneMinusSrcAlpha, 584 | alpha_destination: BlendFactor::SrcAlpha, 585 | mask_red: true, 586 | mask_green: true, 587 | mask_blue: true, 588 | mask_alpha: true, 589 | }, 590 | ].into_iter().cloned()) 591 | .render_pass(Subpass::from($cache.render_pass.clone(), 3).unwrap()); 592 | 593 | cache_layout!($cache, builder) 594 | // .build($cache.device.clone()) 595 | // .unwrap(); 596 | 597 | // Arc::new(GltfGraphicsPipeline::from(Arc::new(pipeline), $shared_resources)) 598 | }} 599 | } 600 | 601 | impl GraphicsPipelineSetCache { 602 | pub fn create(device: Arc, view_swapchains: &[&RefCell], helper_resources: HelperResources, queue_family: QueueFamily) -> impl UninitializedResource { 603 | let swapchain_format = view_swapchains[0].borrow().swapchain.format(); 604 | 605 | for view_swapchain in view_swapchains.iter().skip(1) { 606 | assert_eq!(swapchain_format, view_swapchain.borrow().swapchain.format(), "All swapchains must use the same format."); 607 | } 608 | 609 | SharedGltfGraphicsPipelineResources::new(device.clone(), helper_resources, queue_family, view_swapchains) 610 | .unwrap() 611 | .map(move |shared_resources| { 612 | let result = GraphicsPipelineSetCache { 613 | pipeline_map: Arc::new(RwLock::new(HashMap::new())), 614 | shared_resources, 615 | pipeline_layout_dependent_resources: Arc::new(RwLock::new(HashMap::new())), 616 | // pipeline_layout_dependent_resources: Arc::new(RwLock::new(WeakKeyHashMap::new())), 617 | device: device.clone(), 618 | render_pass: Self::create_render_pass(&device, swapchain_format), 619 | vertex_shader: gltf_vert::Shader::load(device.clone()) 620 | .expect("Failed to create shader module."), 621 | }; 622 | 623 | // result.create_pipeline(&GraphicsPipelineProperties::default()); 624 | // FIXME: Add proper error handling (see `Self::get_default_pipeline`) 625 | // .expect("Couldn't create a pipeline set for default properties."); 626 | 627 | result 628 | }) 629 | } 630 | 631 | fn create_render_pass(device: &Arc, swapchain_format: Format) -> Arc { 632 | Arc::new(ordered_passes_renderpass! { 633 | device.clone(), 634 | attachments: { 635 | color: { 636 | load: Clear, 637 | store: Store, 638 | format: swapchain_format, 639 | samples: 1, 640 | initial_layout: ImageLayout::Undefined, 641 | final_layout: ImageLayout::ColorAttachmentOptimal, 642 | }, 643 | depth_stencil: { 644 | load: Clear, 645 | store: DontCare, 646 | format: Format::D32Sfloat, 647 | samples: 1, 648 | initial_layout: ImageLayout::Undefined, 649 | final_layout: ImageLayout::DepthStencilAttachmentOptimal, 650 | }, 651 | transparency_accumulation: { 652 | load: Clear, 653 | store: DontCare, 654 | format: Format::R32G32B32A32Sfloat, 655 | samples: 1, 656 | // initial_layout: ImageLayout::Undefined, 657 | // final_layout: ImageLayout::General, 658 | }, 659 | transparency_revealage: { 660 | load: Clear, 661 | store: DontCare, 662 | format: Format::R32G32B32A32Sfloat, //FIXME: Could be just a single channel 663 | samples: 1, 664 | // initial_layout: ImageLayout::Undefined, 665 | // final_layout: ImageLayout::General, 666 | } 667 | }, 668 | passes: [ 669 | { 670 | color: [color], 671 | depth_stencil: { depth_stencil }, 672 | input: [] 673 | // $(resolve: [$($resolve_atch:ident),*])*$(,)* 674 | }, 675 | { 676 | color: [color], 677 | depth_stencil: { depth_stencil }, 678 | input: [] 679 | }, 680 | { 681 | color: [transparency_accumulation, transparency_revealage], 682 | depth_stencil: { depth_stencil }, 683 | input: [] 684 | }, 685 | { 686 | color: [color], 687 | depth_stencil: { depth_stencil }, 688 | input: [transparency_accumulation, transparency_revealage] 689 | } 690 | ] 691 | }.expect("Could not create a render pass.")) 692 | } 693 | 694 | pub fn get_pipeline(&self, properties: &GraphicsPipelineProperties) -> Option { 695 | self.pipeline_map 696 | .as_ref() 697 | .read() 698 | .expect("The Graphics Pipeline Cache became poisoned.") 699 | .get(properties) 700 | .map(|pipeline| pipeline.clone()) 701 | } 702 | 703 | // FIXME: There shouldn't be a need for this function, use the pipeline layout instead. 704 | // pub fn get_default_pipeline(&self) -> Option> { 705 | // self.get_pipeline(&GraphicsPipelineProperties::default()) 706 | // } 707 | 708 | pub fn get_or_create_pipeline(&self, properties: &GraphicsPipelineProperties) -> GraphicsPipelineSet { 709 | if let Some(pipeline) = self.get_pipeline(properties) { 710 | pipeline 711 | } else { 712 | self.create_pipeline(properties) 713 | } 714 | } 715 | 716 | pub fn create_pipeline(&self, properties: &GraphicsPipelineProperties) -> GraphicsPipelineSet { 717 | let mut builder = GraphicsPipeline::start(); 718 | 719 | macro_rules! flag { 720 | ($flag:ident in $i:expr) => { 721 | ($i.0 & (1 << GraphicsPipelineFlag::$flag as usize)) != 0 722 | } 723 | } 724 | 725 | if flag!(DoubleSided in properties.flags) { 726 | builder = builder.cull_mode_disabled(); 727 | } else { 728 | builder = builder.cull_mode_back(); 729 | } 730 | 731 | let mut pipeline_map = self.pipeline_map 732 | .as_ref() 733 | .write() 734 | .expect("The Graphics Pipeline Cache became poisoned."); 735 | 736 | let vertex_input = GltfVertexBufferDefinition { 737 | properties_set: properties.vertex_attribute_properties_set.clone(), 738 | }; 739 | 740 | let builder = builder 741 | .vertex_input(vertex_input) 742 | .vertex_shader(self.vertex_shader.main_entry_point(), ()) 743 | // Configures the builder so that we use one viewport, and that the state of this viewport 744 | // is dynamic. This makes it possible to change the viewport for each draw command. If the 745 | // viewport state wasn't dynamic, then we would have to create a new pipeline object if we 746 | // wanted to draw to another image of a different size. 747 | // 748 | // Note: If you configure multiple viewports, you can use geometry shaders to choose which 749 | // viewport the shape is going to be drawn to. This topic isn't covered here. 750 | .viewports_dynamic_scissors_irrelevant(1); 751 | 752 | let pipeline_set = GraphicsPipelineSet { 753 | opaque: construct_pipeline_opaque!(self, builder), 754 | mask: construct_pipeline_mask!(self, builder), 755 | blend_preprocess: construct_pipeline_blend_preprocess!(self, builder), 756 | blend_finalize: construct_pipeline_blend_finalize!(self, builder), 757 | }; 758 | 759 | pipeline_map.insert(properties.clone(), pipeline_set.clone()); 760 | 761 | pipeline_set 762 | } 763 | } 764 | -------------------------------------------------------------------------------- /src/sampler.rs: -------------------------------------------------------------------------------- 1 | use vulkano::sampler::SamplerAddressMode; 2 | use vulkano::sampler::Filter; 3 | use vulkano::sampler::MipmapMode; 4 | use gltf::texture::WrappingMode; 5 | use gltf::texture::MagFilter; 6 | use gltf::texture::MinFilter; 7 | 8 | pub trait IntoVulkanEquivalent { 9 | type Output; 10 | 11 | fn into_vulkan_equivalent(self) -> Self::Output; 12 | } 13 | 14 | impl IntoVulkanEquivalent for WrappingMode { 15 | type Output = SamplerAddressMode; 16 | 17 | fn into_vulkan_equivalent(self) -> Self::Output { 18 | match self { 19 | WrappingMode::ClampToEdge => SamplerAddressMode::ClampToEdge, 20 | WrappingMode::MirroredRepeat => SamplerAddressMode::MirroredRepeat, 21 | WrappingMode::Repeat => SamplerAddressMode::Repeat, 22 | } 23 | } 24 | } 25 | 26 | impl IntoVulkanEquivalent for MagFilter { 27 | type Output = Filter; 28 | 29 | fn into_vulkan_equivalent(self) -> Self::Output { 30 | match self { 31 | MagFilter::Nearest => Filter::Nearest, 32 | MagFilter::Linear => Filter::Linear, 33 | } 34 | } 35 | } 36 | 37 | impl IntoVulkanEquivalent for MinFilter { 38 | type Output = (Filter, MipmapMode); 39 | 40 | fn into_vulkan_equivalent(self) -> Self::Output { 41 | match self { 42 | MinFilter::Nearest => (Filter::Nearest, MipmapMode::Linear), 43 | MinFilter::Linear => (Filter::Linear, MipmapMode::Linear), 44 | MinFilter::NearestMipmapNearest => (Filter::Nearest, MipmapMode::Nearest), 45 | MinFilter::LinearMipmapNearest => (Filter::Linear, MipmapMode::Nearest), 46 | MinFilter::NearestMipmapLinear => (Filter::Nearest, MipmapMode::Linear), 47 | MinFilter::LinearMipmapLinear => (Filter::Linear, MipmapMode::Linear), 48 | } 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/shaders.rs: -------------------------------------------------------------------------------- 1 | pub mod gltf_vert { 2 | vulkano_shaders::shader! { 3 | ty: "vertex", 4 | path: "src/shaders/gltf.vert", 5 | } 6 | } 7 | 8 | pub mod gltf_opaque_frag { 9 | vulkano_shaders::shader! { 10 | ty: "fragment", 11 | path: "src/shaders/gltf_opaque.frag", 12 | } 13 | } 14 | 15 | pub mod gltf_mask_frag { 16 | vulkano_shaders::shader! { 17 | ty: "fragment", 18 | path: "src/shaders/gltf_mask.frag", 19 | } 20 | } 21 | 22 | pub mod gltf_blend_preprocess_frag { 23 | vulkano_shaders::shader! { 24 | ty: "fragment", 25 | path: "src/shaders/gltf_blend_preprocess.frag", 26 | } 27 | } 28 | 29 | pub mod gltf_blend_finalize_frag { 30 | vulkano_shaders::shader! { 31 | ty: "fragment", 32 | path: "src/shaders/gltf_blend_finalize.frag", 33 | } 34 | } 35 | 36 | use ammolite_math::matrix::*; 37 | use ammolite_math::vector::*; 38 | 39 | pub use crate::shaders::gltf_opaque_frag::ty::*; 40 | 41 | impl SceneUBO { 42 | pub fn new(time_elapsed: f32, dimensions: Vec2, camera_position: Vec3, view: Mat4, projection: Mat4) -> SceneUBO { 43 | SceneUBO { 44 | time_elapsed, 45 | dimensions: dimensions.0, 46 | camera_position: camera_position.0, 47 | view: view.into_inner(), 48 | projection: projection.into_inner(), 49 | _dummy0: Default::default(), 50 | _dummy1: Default::default(), 51 | } 52 | } 53 | } 54 | 55 | impl Default for SceneUBO { 56 | fn default() -> Self { 57 | Self::new( 58 | 0.0, 59 | [0.0, 0.0].into(), 60 | [0.0, 0.0, 0.0].into(), 61 | Mat4::IDENTITY, 62 | Mat4::IDENTITY, 63 | ) 64 | } 65 | } 66 | 67 | impl InstanceUBO { 68 | pub fn new(model: Mat4) -> InstanceUBO { 69 | InstanceUBO { 70 | model: model.into_inner(), 71 | } 72 | } 73 | } 74 | 75 | impl Default for InstanceUBO { 76 | fn default() -> Self { 77 | Self::new( 78 | Mat4::IDENTITY, 79 | ) 80 | } 81 | } 82 | 83 | impl NodeUBO { 84 | pub fn new(matrix: Mat4) -> NodeUBO { 85 | NodeUBO { 86 | matrix: matrix.into_inner(), 87 | } 88 | } 89 | } 90 | 91 | impl Default for NodeUBO { 92 | fn default() -> Self { 93 | Self::new( 94 | Mat4::IDENTITY, 95 | ) 96 | } 97 | } 98 | 99 | impl MaterialUBO { 100 | pub fn new( 101 | alpha_cutoff: f32, 102 | base_color_texture_provided: bool, 103 | base_color_factor: Vec4, 104 | metallic_roughness_texture_provided: bool, 105 | metallic_roughness_factor: Vec2, 106 | normal_texture_provided: bool, 107 | normal_texture_scale: f32, 108 | occlusion_texture_provided: bool, 109 | occlusion_strength: f32, 110 | emissive_texture_provided: bool, 111 | emissive_factor: Vec3, 112 | ) -> Self { 113 | MaterialUBO { 114 | alpha_cutoff, 115 | base_color_texture_provided: base_color_texture_provided as u32, 116 | base_color_factor: base_color_factor.0, 117 | metallic_roughness_texture_provided: metallic_roughness_texture_provided as u32, 118 | metallic_roughness_factor: metallic_roughness_factor.0, 119 | normal_texture_provided: normal_texture_provided as u32, 120 | normal_texture_scale, 121 | occlusion_texture_provided: occlusion_texture_provided as u32, 122 | occlusion_strength, 123 | emissive_texture_provided: emissive_texture_provided as u32, 124 | emissive_factor: emissive_factor.0, 125 | _dummy0: Default::default(), 126 | _dummy1: Default::default(), 127 | _dummy2: Default::default(), 128 | } 129 | } 130 | } 131 | 132 | impl Default for MaterialUBO { 133 | fn default() -> Self { 134 | Self::new( 135 | 0.5, 136 | false, 137 | [1.0, 1.0, 1.0, 1.0].into(), 138 | false, 139 | [1.0, 1.0].into(), 140 | false, 141 | 1.0, 142 | false, 143 | 1.0, 144 | false, 145 | [0.0, 0.0, 0.0].into(), 146 | ) 147 | } 148 | } 149 | 150 | impl PushConstants { 151 | pub fn new(vertex_color_provided: bool) -> Self { 152 | Self { 153 | vertex_color_provided: vertex_color_provided as u32, 154 | } 155 | } 156 | } 157 | -------------------------------------------------------------------------------- /src/shaders/gltf.vert: -------------------------------------------------------------------------------- 1 | #version 450 2 | #include "gltf_common.h" 3 | #include "gltf_common_uniforms.h" 4 | 5 | layout(location = 0) in vec3 position; 6 | layout(location = 1) in vec3 normal; 7 | layout(location = 2) in vec4 tangent; 8 | layout(location = 3) in vec2 tex_coord; 9 | layout(location = 4) in vec4 vertex_color; 10 | 11 | layout(location = 0) out vec3 f_world_position; 12 | layout(location = 1) out vec3 f_world_normal; 13 | layout(location = 2) out vec4 f_world_tangent; 14 | layout(location = 3) out vec2 f_tex_coord; 15 | layout(location = 4) out vec4 f_vertex_color; 16 | 17 | const mat4 y_inversion = mat4( 18 | 1.0, 0.0, 0.0, 0.0, 19 | 0.0, -1.0, 0.0, 0.0, 20 | 0.0, 0.0, 1.0, 0.0, 21 | 0.0, 0.0, 0.0, 1.0 22 | ); 23 | 24 | void main() { 25 | // Ensure the normal and tangent are orthonormal 26 | vec3 normalized_normal = normalize(normal); 27 | vec3 normalized_tangent = normalize(tangent.xyz); 28 | vec3 corrected_tangent = GRAM_SCHMIDT(normalized_tangent, normalized_normal); 29 | 30 | // Apply the transformation of primitives to view space 31 | vec4 world_position = model * matrix * vec4(position, 1.0); 32 | // Note: trying to invert and transpose the 4x4 matrix results in artifacts 33 | vec3 world_normal = normalize(transpose(inverse(mat3(model * matrix))) * normalized_normal); 34 | vec3 world_tangent = mat3(model * matrix) * corrected_tangent.xyz; 35 | 36 | // Ensure the normal and tangent are orthonormal, again 37 | vec3 corrected_world_tangent = normalize(GRAM_SCHMIDT(world_tangent, world_normal)); 38 | 39 | f_world_position = PROJECT(world_position); 40 | f_world_normal = world_normal; 41 | f_world_tangent = vec4(corrected_world_tangent, tangent.w); 42 | f_tex_coord = tex_coord; 43 | f_vertex_color = vertex_color; 44 | gl_Position = y_inversion * projection * view * world_position; 45 | } 46 | -------------------------------------------------------------------------------- /src/shaders/gltf_blend_finalize.frag: -------------------------------------------------------------------------------- 1 | #version 450 2 | #include "gltf_common.frag" 3 | 4 | layout(set = 4, binding = 0, input_attachment_index = 0) uniform subpassInput attachment_accumulation; 5 | layout(set = 4, binding = 1, input_attachment_index = 1) uniform subpassInput attachment_revealage; 6 | 7 | layout(location = 0) out vec4 out_color; 8 | 9 | void main() { 10 | vec4 accumulation = subpassLoad(attachment_accumulation); 11 | float revealage = subpassLoad(attachment_revealage).r; 12 | out_color = vec4(accumulation.rgb / accumulation.a, revealage); 13 | } 14 | -------------------------------------------------------------------------------- /src/shaders/gltf_blend_preprocess.frag: -------------------------------------------------------------------------------- 1 | #version 450 2 | /* #include "gltf_common_inputs.frag" */ 3 | #include "gltf_common.frag" 4 | 5 | layout(location = 0) out vec4 out_accumulation_src; 6 | layout(location = 1) out vec4 out_revealage_src; //FIXME 7 | 8 | float weight_function_inverse(vec4 position, float alpha) { 9 | float z = position.z; 10 | return 1.0 / (z + 1.0); 11 | } 12 | 13 | float weight_function_paper_eq_7(vec4 position, float alpha) { 14 | float z = position.z; 15 | float base_mem1 = abs(z) / 5.0; 16 | float mem1 = base_mem1 * base_mem1; 17 | float base_mem2 = abs(z) / 200.0; 18 | float mem2 = base_mem2 * base_mem2 * base_mem2 * base_mem2 * base_mem2 * base_mem2; 19 | return alpha * max(1e-2, min(3e3, 10.0 / (1e-5 + mem1 + mem2))); 20 | } 21 | 22 | float weight_function_paper_eq_8(vec4 position, float alpha) { 23 | float z = position.z; 24 | float base_mem1 = abs(z) / 10.0; 25 | float mem1 = base_mem1 * base_mem1 * base_mem1; 26 | float base_mem2 = abs(z) / 200.0; 27 | float mem2 = base_mem2 * base_mem2 * base_mem2 * base_mem2 * base_mem2 * base_mem2; 28 | return alpha * max(1e-2, min(3e3, 10.0 / (1e-5 + mem1 + mem2))); 29 | } 30 | 31 | float weight_function_paper_eq_9(vec4 position, float alpha) { 32 | float z = position.z; 33 | float base_mem = abs(z) / 200.0; 34 | float mem = base_mem * base_mem * base_mem * base_mem; 35 | return alpha * max(1e-2, min(3e3, 0.03 / (1e-5 + mem))); 36 | } 37 | 38 | float weight_function_paper_eq_10(vec4 position, float alpha) { 39 | float one_minus_z = 1.0 - gl_FragCoord.z; 40 | float cubed = one_minus_z * one_minus_z * one_minus_z; 41 | return alpha * max(1e-2, 3e3 * cubed); 42 | } 43 | 44 | float weight_function(vec4 position, float alpha) { 45 | return weight_function_paper_eq_8(position, alpha); 46 | } 47 | 48 | void main() { 49 | vec4 base_color = get_final_color(); 50 | // Without premultiplication: 51 | vec4 premultiplied_alpha_color = vec4(base_color.rgb, 1.0); 52 | // With premultiplication: 53 | /* vec4 premultiplied_alpha_color = vec4(base_color.rgb * base_color.a, base_color.a); */ 54 | 55 | // Sums up both the numerator and the denominator of the WBOIT expression 56 | out_accumulation_src = premultiplied_alpha_color * weight_function(gl_FragCoord, base_color.a); 57 | out_revealage_src = base_color.aaaa; 58 | } 59 | -------------------------------------------------------------------------------- /src/shaders/gltf_common.frag: -------------------------------------------------------------------------------- 1 | #include "gltf_common.h" 2 | #include "gltf_common_inputs.frag" 3 | 4 | #define GET_FINAL_COLOR() get_final_color( \ 5 | time_elapsed, \ 6 | dimensions, \ 7 | view, \ 8 | camera_position, \ 9 | f_world_position, \ 10 | f_world_normal, \ 11 | f_world_tangent, \ 12 | f_tex_coord, \ 13 | f_vertex_color, \ 14 | \ 15 | base_color_texture_provided, \ 16 | base_color_factor, \ 17 | base_color_texture, \ 18 | base_color_sampler, \ 19 | \ 20 | metallic_roughness_texture_provided, \ 21 | metallic_roughness_factor, \ 22 | metallic_roughness_texture, \ 23 | metallic_roughness_sampler, \ 24 | \ 25 | normal_texture_provided, \ 26 | normal_texture_scale, \ 27 | normal_texture, \ 28 | normal_sampler, \ 29 | \ 30 | occlusion_texture_provided, \ 31 | occlusion_strength, \ 32 | occlusion_texture, \ 33 | occlusion_sampler, \ 34 | \ 35 | emissive_texture_provided, \ 36 | emissive_factor, \ 37 | emissive_texture, \ 38 | emissive_sampler \ 39 | ) 40 | 41 | struct BRDFParams { 42 | float NdotL; 43 | float NdotV; 44 | float NdotH; 45 | float LdotH; 46 | float VdotH; 47 | vec3 c_diff; 48 | vec3 F_0; 49 | float alpha; 50 | float roughness; 51 | }; 52 | 53 | vec2 get_normalized_frag_coord(in vec2 dimensions) { 54 | return gl_FragCoord.xy / dimensions; 55 | } 56 | 57 | vec4 sample_base_color(in bool base_color_texture_provided, 58 | in vec4 base_color_factor, 59 | in texture2D base_color_texture, 60 | in sampler base_color_sampler, 61 | in vec2 f_tex_coord, 62 | in vec4 f_vertex_color) { 63 | vec4 base_color; 64 | 65 | if (base_color_texture_provided) { 66 | base_color = texture( 67 | sampler2D(base_color_texture, base_color_sampler), 68 | vec2(f_tex_coord) 69 | ); 70 | } else { 71 | base_color = vec4(1.0); 72 | } 73 | 74 | if (vertex_color_provided) { 75 | base_color *= f_vertex_color; 76 | } 77 | 78 | return base_color_factor * base_color; 79 | } 80 | 81 | vec2 sample_metallic_roughness(in bool metallic_roughness_texture_provided, 82 | in vec2 metallic_roughness_factor, 83 | in texture2D metallic_roughness_texture, 84 | in sampler metallic_roughness_sampler, 85 | in vec2 f_tex_coord) { 86 | const float min_roughness = 0.04; 87 | vec2 metallic_roughness; 88 | 89 | if (metallic_roughness_texture_provided) { 90 | vec4 texture_value = texture( 91 | sampler2D(metallic_roughness_texture, metallic_roughness_sampler), 92 | vec2(f_tex_coord) 93 | ); 94 | 95 | // components stored in the opposite order 96 | metallic_roughness = metallic_roughness_factor * texture_value.bg; 97 | } else { 98 | metallic_roughness = metallic_roughness_factor; 99 | } 100 | 101 | return vec2(metallic_roughness.x, clamp(metallic_roughness.y, min_roughness, 1.0)); 102 | } 103 | 104 | float sample_occlusion(in bool occlusion_texture_provided, 105 | in float occlusion_strength, 106 | in texture2D occlusion_texture, 107 | in sampler occlusion_sampler, 108 | in vec2 f_tex_coord) { 109 | if (occlusion_texture_provided) { 110 | vec4 texture_value = texture( 111 | sampler2D(occlusion_texture, occlusion_sampler), 112 | vec2(f_tex_coord) 113 | ); 114 | 115 | return occlusion_strength * texture_value.r; 116 | } else { 117 | return occlusion_strength; 118 | } 119 | } 120 | 121 | vec3 sample_emissive(in bool emissive_texture_provided, 122 | in vec3 emissive_factor, 123 | in texture2D emissive_texture, 124 | in sampler emissive_sampler, 125 | in vec2 f_tex_coord) { 126 | if (emissive_texture_provided) { 127 | vec4 texture_value = texture( 128 | sampler2D(emissive_texture, emissive_sampler), 129 | vec2(f_tex_coord) 130 | ); 131 | 132 | return emissive_factor * texture_value.rgb; 133 | } else { 134 | return emissive_factor; 135 | } 136 | } 137 | 138 | vec3 sample_normal(in bool normal_texture_provided, 139 | in float normal_texture_scale, 140 | in texture2D normal_texture, 141 | in sampler normal_sampler, 142 | in vec2 f_tex_coord) { 143 | if (!normal_texture_provided) { 144 | return vec3(0.0, 0.0, 1.0); 145 | } 146 | 147 | vec3 normal_sample = texture( 148 | sampler2D(normal_texture, normal_sampler), 149 | vec2(f_tex_coord) 150 | ).xyz; 151 | 152 | vec3 scaled_normal = normalize((normal_sample * 2.0 - 1.0) 153 | * vec3(normal_texture_scale, normal_texture_scale, 1.0)); 154 | 155 | /* // convert left-handed to right-handed coordinates */ 156 | /* return vec3(scaled_normal.x, -scaled_normal.y, scaled_normal.z); */ 157 | return scaled_normal; 158 | } 159 | 160 | vec3 surface_reflection_ratio(in BRDFParams params) { 161 | float reflectance = max(max(params.F_0.r, params.F_0.g), params.F_0.b); 162 | float reflectance90 = clamp(reflectance * 25.0, 0.0, 1.0); 163 | vec3 r0 = params.F_0; 164 | vec3 r90 = vec3(1.0, 1.0, 1.0) * reflectance90; 165 | 166 | return r0 + (r90 - r0) * pow(clamp(1.0 - params.VdotH, 0.0, 1.0), 5.0); 167 | 168 | // Fresnel Schlick 169 | float term = 1.0 - params.VdotH; 170 | return params.F_0 + (1 - params.F_0) * (term * term * term * term * term); 171 | } 172 | 173 | float geometric_occlusion(in BRDFParams params) { 174 | /* return min( */ 175 | /* min( */ 176 | /* 2.0 * params.NdotV * params.NdotH / params.VdotH, */ 177 | /* 2.0 * params.NdotL * params.NdotH / params.VdotH), */ 178 | /* 1.0 */ 179 | /* ); */ 180 | const float coefficient = sqrt(2.0 / PI); 181 | float k = params.roughness * coefficient; 182 | float G_L = params.LdotH / (params.LdotH * (1.0 - k) + k); 183 | float G_N = params.NdotH / (params.NdotH * (1.0 - k) + k); 184 | return G_L * G_N; 185 | } 186 | 187 | float microfaced_distribution(in BRDFParams params) { 188 | float alpha_squared = params.alpha * params.alpha; 189 | float term = (params.NdotH * params.NdotH * (alpha_squared - 1.0) + 1.0); 190 | return alpha_squared / (PI * term * term); 191 | } 192 | 193 | // See https://github.com/KhronosGroup/glTF-WebGL-PBR/ for an exemplary 194 | // implementation 195 | vec3 brdf(in BRDFParams params, out vec3 F, out float G, out float D, out vec3 f_diffuse, out vec3 f_specular) { 196 | F = surface_reflection_ratio(params); 197 | G = geometric_occlusion(params); 198 | D = microfaced_distribution(params); 199 | vec3 diffuse = params.c_diff / PI; 200 | 201 | f_diffuse = (1.0 - F) * diffuse; 202 | f_specular = (F * G * D) / 4.0 * params.NdotL * params.NdotV; 203 | 204 | return f_diffuse + f_specular; 205 | } 206 | 207 | vec3 pbr(in vec4 base_color, 208 | in float metallic, 209 | in float roughness, 210 | in vec3 radiance, 211 | in vec3 eye_dir, // pointing towards the eye 212 | in vec3 light_dir, // pointing towards the light 213 | in vec3 normal, 214 | out vec3 F, 215 | out float G, 216 | out float D, 217 | out vec3 f_diffuse, 218 | out vec3 f_specular) { 219 | const vec3 dielectric_specular = vec3(0.04, 0.04, 0.04); 220 | const vec3 black = vec3(0, 0, 0); 221 | 222 | vec3 half_dir = normalize(eye_dir + light_dir); 223 | vec3 c_diff = mix(base_color.rgb * (1.0 - dielectric_specular.r), black, metallic); 224 | vec3 F_0 = mix(dielectric_specular, base_color.rgb, metallic); 225 | float alpha = roughness * roughness; 226 | 227 | vec3 V = eye_dir; 228 | vec3 L = light_dir; 229 | vec3 H = half_dir; 230 | vec3 N = normal; 231 | 232 | BRDFParams brdf_params = BRDFParams( 233 | max(dot(N, L), 0.001), 234 | max(dot(N, V), 0.001), 235 | max(dot(N, H), 0.0), 236 | max(dot(L, H), 0.0), 237 | max(dot(V, H), 0.0), 238 | c_diff, 239 | F_0, 240 | alpha, 241 | roughness 242 | ); 243 | 244 | vec3 f = brdf(brdf_params, F, G, D, f_diffuse, f_specular); 245 | 246 | return f * radiance * brdf_params.NdotL; 247 | } 248 | 249 | // Immediately returns if the current fragment is within the specified region. 250 | #define VISUALIZE_VECTOR_INVERT(vector, top_left, bottom_right, dimensions) do { \ 251 | vec2 coord = get_normalized_frag_coord(dimensions); \ 252 | \ 253 | if (coord.x > top_left.x && coord.y > top_left.y \ 254 | && coord.x < bottom_right.x && coord.y < bottom_right.y) { \ 255 | vec2 coord_within_region = (coord - top_left) \ 256 | / (bottom_right - top_left); \ 257 | \ 258 | if ((coord_within_region.x + coord_within_region.y > 1.0) \ 259 | != (coord_within_region.x - coord_within_region.y + 1.0 < 1.0)) { \ 260 | vector *= -1.0; \ 261 | } \ 262 | \ 263 | return vec4(vector, 1.0); \ 264 | } \ 265 | } while (false); 266 | 267 | #define VISUALIZE_VECTOR(vector, top_left, bottom_right, dimensions) do { \ 268 | vec2 coord = get_normalized_frag_coord(dimensions); \ 269 | \ 270 | if (coord.x > top_left.x && coord.y > top_left.y \ 271 | && coord.x < bottom_right.x && coord.y < bottom_right.y) { \ 272 | return vec4(vector, 1.0); \ 273 | } \ 274 | } while (false); 275 | 276 | vec4 get_final_color() { 277 | vec3 world_position = f_world_position; 278 | vec3 world_normal = normalize(f_world_normal); 279 | vec4 world_tangent = vec4(normalize(f_world_tangent.xyz), f_world_tangent.w); 280 | 281 | // Invert the normal for the back face of double-sided materials 282 | if (!gl_FrontFacing) { 283 | world_normal *= -1; 284 | } 285 | 286 | vec2 normalized_frag_coord = get_normalized_frag_coord(dimensions); 287 | vec3 world_bitangent = cross(world_normal, world_tangent.xyz) * world_tangent.w; 288 | vec4 base_color = sample_base_color( 289 | base_color_texture_provided, 290 | base_color_factor, 291 | base_color_texture, 292 | base_color_sampler, 293 | f_tex_coord, 294 | f_vertex_color 295 | ); 296 | vec2 metallic_roughness = sample_metallic_roughness( 297 | metallic_roughness_texture_provided, 298 | metallic_roughness_factor, 299 | metallic_roughness_texture, 300 | metallic_roughness_sampler, 301 | f_tex_coord 302 | ); 303 | vec3 sampled_normal = sample_normal( 304 | normal_texture_provided, 305 | normal_texture_scale, 306 | normal_texture, 307 | normal_sampler, 308 | f_tex_coord 309 | ); 310 | float occlusion = sample_occlusion( 311 | occlusion_texture_provided, 312 | occlusion_strength, 313 | occlusion_texture, 314 | occlusion_sampler, 315 | f_tex_coord 316 | ); 317 | vec3 emissive = sample_emissive( 318 | emissive_texture_provided, 319 | emissive_factor, 320 | emissive_texture, 321 | emissive_sampler, 322 | f_tex_coord 323 | ); 324 | 325 | // Construct an orthonormal TBN matrix 326 | mat3 tangent_to_canonical = mat3(world_tangent.xyz, world_bitangent, world_normal); 327 | // We can use `transpose` to invert the matrix as it's orthonormal 328 | mat3 canonical_to_tangent = transpose(tangent_to_canonical); 329 | 330 | vec3 light_world_position[3] = { 331 | vec3(1.0, 1.5, 2.0), 332 | vec3(-1.0, -1.5, 2.0), 333 | vec3(-1.0, 1.5, -2.0) 334 | }; 335 | 336 | /* if (normalized_frag_coord.x + normalized_frag_coord.y > 1.0) { */ 337 | /* world_normal = normalize(tangent_to_canonical * sampled_normal); */ 338 | /* } */ 339 | 340 | /* return vec4(world_normal, 1.0); */ 341 | 342 | world_normal = normalize(tangent_to_canonical * sampled_normal); 343 | 344 | /* return vec4(world_normal, 1.0); */ 345 | 346 | /* vec3 light_world_direction_x = vec3(0.0, 0.0, 1.0); */ 347 | /* float color_weight = clamp(dot(world_normal, light_world_direction_x), 0.0, 1.0); */ 348 | /* vec3 diffuse_color = base_color.rgb * mix(0.1, 1.0, color_weight); */ 349 | /* return vec4(diffuse_color, base_color.a); */ 350 | 351 | vec3 eye_direction = normalize(camera_position - world_position); 352 | vec3 accumulated_radiance = vec3(0.0); 353 | 354 | for (int i = 0; i < 3; i++) { 355 | vec3 light_world_direction = normalize(light_world_position[i] - world_position); 356 | vec3 incoming_light_radiance = vec3(1.0); 357 | 358 | vec3 F; 359 | float G; 360 | float D; 361 | vec3 f_diffuse; 362 | vec3 f_specular; 363 | vec3 outgoing_radiance = pbr( 364 | base_color, 365 | metallic_roughness.x, 366 | metallic_roughness.y, 367 | incoming_light_radiance, 368 | eye_direction, 369 | light_world_direction, 370 | world_normal, 371 | F, G, D, f_diffuse, f_specular 372 | ); 373 | 374 | accumulated_radiance += outgoing_radiance; 375 | } 376 | 377 | // Apply ambient radiance 378 | vec3 ambient_radiance = vec3(0.03) * base_color.rgb * occlusion; 379 | accumulated_radiance += ambient_radiance; 380 | 381 | // Apply emission 382 | accumulated_radiance += emissive; 383 | 384 | // Apply transparency 385 | vec4 color = vec4(accumulated_radiance, base_color.a); 386 | 387 | return color; 388 | } 389 | -------------------------------------------------------------------------------- /src/shaders/gltf_common.h: -------------------------------------------------------------------------------- 1 | #ifndef COMMON_H 2 | #define COMMON_H 3 | 4 | #define PI 3.1415926535897932384626433832795 5 | #define PI_2 1.57079632679489661923 6 | #define PI_4 0.785398163397448309616 7 | 8 | #define PROJECT(vector4) (vector4.w == 0 ? vector4.xyz : (vector4.xyz / vector4.w)) 9 | #define GRAM_SCHMIDT(a, b) ((a) - (b) * dot((a), (b))) 10 | 11 | #endif 12 | -------------------------------------------------------------------------------- /src/shaders/gltf_common_inputs.frag: -------------------------------------------------------------------------------- 1 | #include "gltf_common_uniforms.h" 2 | 3 | layout(location = 0) in vec3 f_world_position; 4 | layout(location = 1) in vec3 f_world_normal; 5 | layout(location = 2) in vec4 f_world_tangent; 6 | layout(location = 3) in vec2 f_tex_coord; 7 | layout(location = 4) in vec4 f_vertex_color; 8 | -------------------------------------------------------------------------------- /src/shaders/gltf_common_uniforms.h: -------------------------------------------------------------------------------- 1 | layout(set = 0, binding = 0) uniform SceneUBO { 2 | float time_elapsed; 3 | vec2 dimensions; 4 | vec3 camera_position; 5 | mat4 view; 6 | mat4 projection; 7 | }; 8 | 9 | layout(set = 1, binding = 0) uniform InstanceUBO { 10 | mat4 model; 11 | }; 12 | 13 | layout(set = 2, binding = 0) uniform NodeUBO { 14 | mat4 matrix; 15 | }; 16 | 17 | layout(set = 3, binding = 0) uniform MaterialUBO { 18 | float alpha_cutoff; 19 | 20 | bool base_color_texture_provided; 21 | vec4 base_color_factor; 22 | 23 | bool metallic_roughness_texture_provided; 24 | vec2 metallic_roughness_factor; 25 | 26 | bool normal_texture_provided; 27 | float normal_texture_scale; 28 | 29 | bool occlusion_texture_provided; 30 | float occlusion_strength; 31 | 32 | bool emissive_texture_provided; 33 | vec3 emissive_factor; 34 | }; 35 | layout(set = 3, binding = 1) uniform texture2D base_color_texture; 36 | layout(set = 3, binding = 2) uniform sampler base_color_sampler; 37 | layout(set = 3, binding = 3) uniform texture2D metallic_roughness_texture; 38 | layout(set = 3, binding = 4) uniform sampler metallic_roughness_sampler; 39 | layout(set = 3, binding = 5) uniform texture2D normal_texture; 40 | layout(set = 3, binding = 6) uniform sampler normal_sampler; 41 | layout(set = 3, binding = 7) uniform texture2D occlusion_texture; 42 | layout(set = 3, binding = 8) uniform sampler occlusion_sampler; 43 | layout(set = 3, binding = 9) uniform texture2D emissive_texture; 44 | layout(set = 3, binding = 10) uniform sampler emissive_sampler; 45 | 46 | layout(push_constant) uniform PushConstants { 47 | bool vertex_color_provided; 48 | }; 49 | -------------------------------------------------------------------------------- /src/shaders/gltf_mask.frag: -------------------------------------------------------------------------------- 1 | #version 450 2 | /* #include "gltf_common_inputs.frag" */ 3 | #include "gltf_common.frag" 4 | 5 | layout(location = 0) out vec4 out_color; 6 | 7 | void main() { 8 | vec4 base_color = get_final_color(); 9 | 10 | if (base_color.a < alpha_cutoff) { 11 | discard; 12 | } else { 13 | base_color = vec4(base_color.rgb, 1.0); 14 | } 15 | 16 | out_color = 0.0.xxxx 17 | + base_color 18 | + 0.0.xxxx; 19 | } 20 | -------------------------------------------------------------------------------- /src/shaders/gltf_opaque.frag: -------------------------------------------------------------------------------- 1 | #version 450 2 | /* #include "gltf_common_inputs.frag" */ 3 | #include "gltf_common.frag" 4 | 5 | layout(location = 0) out vec4 out_color; 6 | 7 | void main() { 8 | vec4 base_color = get_final_color(); 9 | base_color = vec4(base_color.rgb, 1.0); 10 | 11 | out_color = 0.0.xxxx 12 | + base_color 13 | + 0.0.xxxx; 14 | } 15 | -------------------------------------------------------------------------------- /src/swapchain.rs: -------------------------------------------------------------------------------- 1 | use std::fmt; 2 | use std::sync::Arc; 3 | use std::sync::atomic::{AtomicBool, Ordering}; 4 | use std::num::NonZeroU32; 5 | use std::time::Duration; 6 | use vulkano::VulkanObject; 7 | use vulkano::command_buffer::submit::SubmitSemaphoresWaitBuilder; 8 | use vulkano::image::ImageUsage; 9 | use vulkano::device::{Device, DeviceOwned, Queue}; 10 | use vulkano::sync::GpuFuture; 11 | use vulkano::sync::AccessCheckError; 12 | use vulkano::sync::Fence; 13 | use vulkano::sync::Semaphore; 14 | use vulkano::command_buffer::submit::SubmitAnyBuilder; 15 | use vulkano::sync::FlushError; 16 | use vulkano::sync::PipelineStages; 17 | use vulkano::sync::AccessFlagBits; 18 | use vulkano::format::{Format, FormatDesc}; 19 | 20 | use vulkano::swapchain::{self, AcquireError, SwapchainCreationError}; 21 | use vulkano::buffer::BufferAccess; 22 | use vulkano::image::{ 23 | sys::{UnsafeImage, UnsafeImageView, UnsafeImageViewCreationError}, 24 | layout::{ImageLayout, ImageLayoutEnd, RequiredLayouts}, 25 | ImageSubresourceLayoutError, 26 | SwapchainImage, 27 | ImageDimensions, 28 | ImageSubresourceRange, 29 | ImageViewType, 30 | ImageViewAccess, 31 | ImageAccess, 32 | }; 33 | use vulkano::sync::AccessError; 34 | use vulkano::OomError; 35 | use openxr; 36 | use crate::XrVkSession; 37 | 38 | // pub enum SwapchainAcquireNextImageFuture { 39 | // SwapchainAcquireFuture(SwapchainAcquireFuture), 40 | // Now(NowFuture), 41 | // } 42 | 43 | pub trait Swapchain: DeviceOwned { 44 | // Cannot be used, because PresentFuture has a generic type argument of the previous future 45 | //type PresentFuture: GpuFuture; 46 | 47 | // FIXME: error handling 48 | fn images(&self) -> &[Arc]; 49 | fn acquire_next_image_index(&mut self) -> Result<(usize, Box), AcquireError>; 50 | fn acquire_next_image(&mut self) -> Result<(Arc, Box), AcquireError> { 51 | let (index, future) = self.acquire_next_image_index()?; 52 | let image = self.images()[index].clone(); 53 | Ok((image, future)) 54 | } 55 | fn recreate_with_dimension(&mut self, dimensions: [NonZeroU32; 2]) -> Result<(), SwapchainCreationError>; 56 | fn dimensions(&self) -> [NonZeroU32; 2]; 57 | fn format(&self) -> Format; 58 | // TODO: remove Box 59 | fn present(&self, sync: Box, queue: Arc, index: usize) -> Box; 60 | fn downcast_xr(&self) -> Option<&XrSwapchain> { None } 61 | fn finish_rendering(&mut self) {} 62 | } 63 | 64 | /** 65 | * A swapchain bound to a regular desktop window `W`. 66 | */ 67 | pub struct VkSwapchain { 68 | vk_swapchain: Arc>, 69 | images: Vec>, 70 | } 71 | 72 | impl From<(Arc>, Vec>)> for VkSwapchain { 73 | fn from(other: (Arc>, Vec>)) -> Self { 74 | let (vk_swapchain, images) = other; 75 | return Self { vk_swapchain, images } 76 | } 77 | } 78 | 79 | impl Swapchain for VkSwapchain { 80 | fn acquire_next_image_index(&mut self) -> Result<(usize, Box), AcquireError> { 81 | let (index, future) = swapchain::acquire_next_image(self.vk_swapchain.clone(), None)?; 82 | // Ok((index, SwapchainAcquireNextImageFuture::SwapchainAcquireFuture(future))) 83 | Ok((index, Box::new(future))) 84 | } 85 | 86 | fn images(&self) -> &[Arc] { &self.images[..] } 87 | 88 | fn recreate_with_dimension(&mut self, dimensions: [NonZeroU32; 2]) -> Result<(), SwapchainCreationError> { 89 | let (vk_swapchain, images) = self.vk_swapchain.recreate_with_dimension(dimensions)?; 90 | 91 | self.vk_swapchain = vk_swapchain; 92 | self.images = images; 93 | 94 | Ok(()) 95 | } 96 | 97 | fn dimensions(&self) -> [NonZeroU32; 2] { 98 | self.vk_swapchain.dimensions() 99 | } 100 | 101 | fn format(&self) -> Format { 102 | self.vk_swapchain.format() 103 | } 104 | 105 | fn present(&self, sync: Box, queue: Arc, index: usize) -> Box { 106 | Box::new(sync.then_swapchain_present(queue, self.vk_swapchain.clone(), index)) 107 | } 108 | } 109 | 110 | unsafe impl DeviceOwned for VkSwapchain { 111 | fn device(&self) -> &Arc { 112 | &self.vk_swapchain.device() 113 | } 114 | } 115 | 116 | /** 117 | * A swapchain bound to an OpenXR device screen (e.g. the screen for the left eye). 118 | */ 119 | pub struct XrSwapchain { 120 | vk_device: Arc, 121 | inner: openxr::Swapchain, 122 | format: Format, 123 | dimensions: [NonZeroU32; 2], 124 | images: Arc>>, 125 | undefined_layouts: Arc>, 126 | image_acquired: AtomicBool, 127 | } 128 | 129 | impl XrSwapchain { 130 | pub fn new( 131 | vk_device: Arc, 132 | xr_session: XrVkSession, 133 | dimensions: [NonZeroU32; 2], 134 | layers: NonZeroU32, 135 | format: F, 136 | usage: ImageUsage, 137 | sample_count: NonZeroU32, 138 | ) -> Self { 139 | // println!("SWAPCHAIN FORMAT: {:?}", format.format()); 140 | let create_info = openxr::SwapchainCreateInfo { 141 | create_flags: openxr::SwapchainCreateFlags::EMPTY, 142 | usage_flags: xr_from_vk_usage(usage), 143 | format: format.format() as u32, 144 | sample_count: sample_count.get(), 145 | width: dimensions[0].get(), 146 | height: dimensions[1].get(), 147 | face_count: 1, // 6 for cubemaps, or 1 148 | array_size: layers.get(), 149 | mip_count: 1, 150 | }; 151 | let xr_swapchain = xr_session.create_swapchain(&create_info).unwrap(); 152 | let images: Vec> = xr_swapchain.enumerate_images().unwrap() 153 | .into_iter() 154 | .enumerate() 155 | .map(|(index, handle)| { 156 | let xr_swapchain_image = unsafe { 157 | XrSwapchainImage::from_raw( 158 | handle, 159 | &vk_device, 160 | index, 161 | layers, 162 | dimensions, 163 | usage, 164 | format.clone(), 165 | ) 166 | }.unwrap(); 167 | 168 | Arc::new(xr_swapchain_image) as Arc 169 | }) 170 | .collect(); 171 | let undefined_layouts = images.iter() 172 | .map(|_| AtomicBool::new(true)) 173 | .collect(); 174 | 175 | Self { 176 | vk_device, 177 | inner: xr_swapchain, 178 | format: format.format(), 179 | dimensions, 180 | images: Arc::new(images), 181 | undefined_layouts: Arc::new(undefined_layouts), 182 | image_acquired: AtomicBool::new(false), 183 | } 184 | } 185 | 186 | pub fn inner(&self) -> &openxr::Swapchain { 187 | &self.inner 188 | } 189 | } 190 | 191 | impl Swapchain for XrSwapchain { 192 | fn acquire_next_image_index(&mut self) -> Result<(usize, Box), AcquireError> { 193 | if self.image_acquired.swap(true, Ordering::SeqCst) { 194 | panic!("Swapchain image already acquired, call `finish_rendering` first."); 195 | } 196 | 197 | // FIXME: Error handling 198 | let index = self.inner.acquire_image().unwrap() as usize; 199 | // FIXME: Error handling & remove blocking 200 | self.inner.wait_image(openxr::Duration::INFINITE).unwrap(); 201 | // let future = vulkano::sync::now(self.vk_device.clone()); 202 | let future = XrSwapchainAcquireFuture { 203 | device: self.vk_device.clone(), 204 | images: self.images.clone(), 205 | undefined_layouts: self.undefined_layouts.clone(), 206 | image_id: index, 207 | // Semaphore that is signalled when the acquire is complete. Empty if the acquire has already 208 | // happened. 209 | semaphore: None, 210 | // Fence that is signalled when the acquire is complete. Empty if the acquire has already 211 | // happened. 212 | fence: None, 213 | finished: AtomicBool::new(false), 214 | }; 215 | 216 | // Ok((index as usize, SwapchainAcquireNextImageFuture::Now(future))) 217 | Ok((index, Box::new(future))) 218 | } 219 | 220 | fn finish_rendering(&mut self) { 221 | if !self.image_acquired.swap(false, Ordering::SeqCst) { 222 | panic!("Swapchain image already released."); 223 | } 224 | 225 | self.inner.release_image().unwrap(); 226 | } 227 | 228 | fn images(&self) -> &[Arc] { &self.images[..] } 229 | 230 | fn recreate_with_dimension(&mut self, _dimensions: [NonZeroU32; 2]) -> Result<(), SwapchainCreationError> { 231 | // no-op 232 | Ok(()) 233 | } 234 | 235 | fn dimensions(&self) -> [NonZeroU32; 2] { 236 | self.dimensions 237 | } 238 | 239 | fn format(&self) -> Format { 240 | self.format 241 | } 242 | 243 | fn present(&self, sync: Box, _queue: Arc, _index: usize) -> Box { 244 | // FIXME 245 | sync 246 | } 247 | 248 | fn downcast_xr(&self) -> Option<&XrSwapchain> { 249 | Some(&self) 250 | } 251 | } 252 | 253 | unsafe impl DeviceOwned for XrSwapchain { 254 | fn device(&self) -> &Arc { 255 | &self.vk_device 256 | } 257 | } 258 | 259 | 260 | 261 | fn xr_from_vk_usage(usage: ImageUsage) -> openxr::SwapchainUsageFlags { 262 | use openxr::SwapchainUsageFlags as SUF; 263 | 264 | let mut result = SUF::EMPTY; 265 | 266 | if usage.color_attachment { 267 | result |= SUF::COLOR_ATTACHMENT; 268 | } 269 | 270 | if usage.depth_stencil_attachment { 271 | result |= SUF::DEPTH_STENCIL_ATTACHMENT; 272 | } 273 | 274 | if usage.storage { 275 | result |= SUF::UNORDERED_ACCESS; // check validity 276 | } 277 | 278 | if usage.transfer_source { 279 | result |= SUF::TRANSFER_SRC; 280 | } 281 | 282 | if usage.transfer_destination { 283 | result |= SUF::TRANSFER_DST; 284 | } 285 | 286 | if usage.sampled { 287 | result |= SUF::SAMPLED; 288 | } 289 | 290 | //result |= SUF::MUTABLE_FORMAT; 291 | 292 | result 293 | } 294 | 295 | struct XrSwapchainImage { 296 | index: usize, 297 | image: UnsafeImage, 298 | view: UnsafeImageView, 299 | } 300 | 301 | impl XrSwapchainImage { 302 | const REQUIRED_LAYOUTS: RequiredLayouts = 303 | RequiredLayouts { global: Some(ImageLayoutEnd::PresentSrc), ..RequiredLayouts::none() }; 304 | 305 | unsafe fn from_raw(handle: u64, device: &Arc, index: usize, layers: NonZeroU32, dimensions: [NonZeroU32; 2], usage: ImageUsage, format: impl FormatDesc) -> Result { 306 | let dims = if layers.get() == 1 { 307 | ImageDimensions::Dim2D { width: dimensions[0], height: dimensions[1] } 308 | } else { 309 | ImageDimensions::Dim2DArray { 310 | width: dimensions[0], 311 | height: dimensions[1], 312 | array_layers: layers 313 | } 314 | }; 315 | 316 | let image = UnsafeImage::from_raw( 317 | device.clone(), 318 | handle, 319 | usage, 320 | format.format(), 321 | dims, 322 | crate::NONZERO_ONE, 323 | crate::NONZERO_ONE, 324 | ); 325 | 326 | let view = match UnsafeImageView::new( 327 | &image, 328 | Some(ImageViewType::Dim2D), 329 | None, 330 | Default::default(), 331 | ImageSubresourceRange { 332 | array_layers: crate::NONZERO_ONE, 333 | array_layers_offset: 0, 334 | 335 | mipmap_levels: crate::NONZERO_ONE, 336 | mipmap_levels_offset: 0 337 | } 338 | ) { 339 | Ok(v) => v, 340 | Err(UnsafeImageViewCreationError::OomError(e)) => return Err(e), 341 | e => panic!("Could not create swapchain view: {:?}", e) 342 | }; 343 | 344 | Ok(XrSwapchainImage { index, image, view }) 345 | } 346 | } 347 | 348 | impl SwapchainImage for XrSwapchainImage { 349 | fn inner_dimensions(&self) -> [NonZeroU32; 2] { 350 | self.inner_image().dimensions().width_height() 351 | } 352 | 353 | fn inner_image(&self) -> &UnsafeImage { 354 | &self.image 355 | } 356 | 357 | fn index(&self) -> usize { 358 | self.index 359 | } 360 | } 361 | 362 | unsafe impl DeviceOwned for XrSwapchainImage { 363 | fn device(&self) -> &Arc { self.image.device() } 364 | } 365 | 366 | impl fmt::Debug for XrSwapchainImage { 367 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 368 | write!(f, "XrSwapchainImage {{ index: {index}, view: {view:?} }}", index=self.index, view=self.view) 369 | } 370 | } 371 | 372 | // Basically copied from the source code of `vulkano::image::swapchain` 373 | unsafe impl ImageAccess for XrSwapchainImage { 374 | fn inner(&self) -> &UnsafeImage { self.inner_image() } 375 | 376 | fn conflicts_buffer(&self, _other: &dyn BufferAccess) -> bool { false } 377 | 378 | fn conflicts_image( 379 | &self, subresource_range: ImageSubresourceRange, other: &dyn ImageAccess, 380 | other_subresource_range: ImageSubresourceRange 381 | ) -> bool { 382 | if ImageAccess::inner(self).key() == other.conflict_key() { 383 | subresource_range.overlaps_with(&other_subresource_range) 384 | } else { 385 | false 386 | } 387 | } 388 | 389 | fn conflict_key(&self) -> u64 { ImageAccess::inner(self).key() } 390 | 391 | fn current_layout( 392 | &self, _: ImageSubresourceRange 393 | ) -> Result { 394 | // TODO: Is this okay? 395 | Ok(ImageLayout::PresentSrc) 396 | } 397 | 398 | fn initiate_gpu_lock( 399 | &self, _: ImageSubresourceRange, _: bool, _: ImageLayout 400 | ) -> Result<(), AccessError> { 401 | // Swapchain image are only accessible after being acquired. 402 | // This is handled by the swapchain itself. 403 | Err(AccessError::SwapchainImageAcquireOnly) 404 | } 405 | 406 | unsafe fn increase_gpu_lock(&self, _: ImageSubresourceRange) {} 407 | 408 | unsafe fn decrease_gpu_lock( 409 | &self, _: ImageSubresourceRange, _new_layout: Option 410 | ) { 411 | // TODO: store that the image was initialized? 412 | } 413 | } 414 | 415 | // Basically copied from the source code of `vulkano::image::swapchain` 416 | unsafe impl ImageViewAccess for XrSwapchainImage { 417 | fn parent(&self) -> &dyn ImageAccess { self } 418 | 419 | fn inner(&self) -> &UnsafeImageView { &self.view } 420 | 421 | fn dimensions(&self) -> ImageDimensions { self.view.dimensions() } 422 | 423 | fn conflicts_buffer(&self, _other: &dyn BufferAccess) -> bool { false } 424 | 425 | fn required_layouts(&self) -> &RequiredLayouts { &Self::REQUIRED_LAYOUTS } 426 | } 427 | 428 | 429 | /// An alternative to SwapchainAcquireFuture 430 | #[must_use] 431 | pub struct XrSwapchainAcquireFuture { 432 | device: Arc, 433 | images: Arc>>, 434 | undefined_layouts: Arc>, 435 | image_id: usize, 436 | // Semaphore that is signalled when the acquire is complete. Empty if the acquire has already 437 | // happened. 438 | semaphore: Option, 439 | // Fence that is signalled when the acquire is complete. Empty if the acquire has already 440 | // happened. 441 | fence: Option, 442 | finished: AtomicBool 443 | } 444 | unsafe impl GpuFuture for XrSwapchainAcquireFuture { 445 | fn cleanup_finished(&mut self) {} 446 | 447 | unsafe fn build_submission(&self) -> Result { 448 | if let Some(ref semaphore) = self.semaphore { 449 | let mut sem = SubmitSemaphoresWaitBuilder::new(); 450 | sem.add_wait_semaphore(&semaphore); 451 | Ok(SubmitAnyBuilder::SemaphoresWait(sem)) 452 | } else { 453 | Ok(SubmitAnyBuilder::Empty) 454 | } 455 | } 456 | 457 | fn flush(&self) -> Result<(), FlushError> { Ok(()) } 458 | 459 | unsafe fn signal_finished(&self) { self.finished.store(true, Ordering::SeqCst); } 460 | 461 | fn queue_change_allowed(&self) -> bool { true } 462 | 463 | fn queue(&self) -> Option> { None } 464 | 465 | fn check_buffer_access( 466 | &self, _: &dyn BufferAccess, _: bool, _: &Queue 467 | ) -> Result, AccessCheckError> { 468 | Err(AccessCheckError::Unknown) 469 | } 470 | 471 | fn check_image_access( 472 | &self, image: &dyn ImageViewAccess, layout: ImageLayout, _: bool, _: &Queue 473 | ) -> Result, AccessCheckError> { 474 | let swapchain_image = self.images[self.image_id].inner_image(); 475 | if swapchain_image.internal_object() != image.parent().inner().internal_object() { 476 | return Err(AccessCheckError::Unknown) 477 | } 478 | 479 | if self.undefined_layouts[self.image_id].load(Ordering::Relaxed) 480 | && layout != ImageLayout::Undefined 481 | { 482 | return Err(AccessCheckError::Denied(AccessError::ImageLayoutMismatch { 483 | actual: ImageLayout::Undefined, 484 | expected: layout 485 | })) 486 | } 487 | 488 | if layout != ImageLayout::Undefined && layout != ImageLayout::PresentSrc { 489 | return Err(AccessCheckError::Denied(AccessError::ImageLayoutMismatch { 490 | actual: ImageLayout::PresentSrc, 491 | expected: layout 492 | })) 493 | } 494 | 495 | Ok(None) 496 | } 497 | } 498 | unsafe impl DeviceOwned for XrSwapchainAcquireFuture { 499 | fn device(&self) -> &Arc { &self.device } 500 | } 501 | impl Drop for XrSwapchainAcquireFuture { 502 | fn drop(&mut self) { 503 | if !*self.finished.get_mut() { 504 | if let Some(ref fence) = self.fence { 505 | fence.wait(None).unwrap(); // TODO: handle error? 506 | self.semaphore = None; 507 | } 508 | } else { 509 | // We make sure that the fence is signalled. This also silences an error from the 510 | // validation layers about using a fence whose state hasn't been checked (even though 511 | // we know for sure that it must've been signalled). 512 | debug_assert!({ 513 | let dur = Some(Duration::new(0, 0)); 514 | self.fence.as_ref().map(|f| f.wait(dur).is_ok()).unwrap_or(true) 515 | }); 516 | } 517 | 518 | // TODO: if this future is destroyed without being presented, then eventually acquiring 519 | // a new image will block forever ; difficulty: hard 520 | } 521 | } 522 | impl fmt::Debug for XrSwapchainAcquireFuture { 523 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 524 | write!( 525 | f, 526 | "XrSwapchainAcquireFuture {{ image_id: {}, \ 527 | semaphore: {:?}, fence: {:?}, finished: {:?} }}", 528 | self.image_id, self.semaphore, self.fence, self.finished 529 | ) 530 | } 531 | } 532 | -------------------------------------------------------------------------------- /src/vertex.rs: -------------------------------------------------------------------------------- 1 | use std::mem; 2 | use std::sync::Arc; 3 | use vulkano; 4 | use vulkano::pipeline::vertex::VertexSource; 5 | use vulkano::buffer::BufferAccess; 6 | use vulkano::buffer::TypedBufferAccess; 7 | use vulkano::pipeline::vertex::IncompatibleVertexDefinitionError; 8 | use vulkano::pipeline::vertex::VertexDefinition; 9 | use vulkano::pipeline::vertex::InputRate; 10 | use vulkano::pipeline::vertex::AttributeInfo; 11 | use vulkano::pipeline::shader::ShaderInterfaceDef; 12 | use typenum::*; 13 | use safe_transmute::PodTransmutable; 14 | use gltf::mesh::Semantic; 15 | use gltf::mesh::Primitive; 16 | use crate::iter::ArrayIterator; 17 | use crate::shaders::gltf_vert::MainInput; 18 | 19 | #[repr(C)] 20 | #[derive(Clone, Copy, Default)] 21 | pub struct GltfVertexPosition(pub [f32; 3]); 22 | unsafe impl PodTransmutable for GltfVertexPosition {} 23 | 24 | #[repr(C)] 25 | #[derive(Clone, Copy, Default)] 26 | pub struct GltfVertexNormal(pub [f32; 3]); 27 | unsafe impl PodTransmutable for GltfVertexNormal {} 28 | 29 | #[repr(C)] 30 | #[derive(Clone, Copy, Default)] 31 | pub struct GltfVertexTangent(pub [f32; 4]); 32 | unsafe impl PodTransmutable for GltfVertexTangent {} 33 | 34 | #[repr(C)] 35 | #[derive(Clone, Copy, Default)] 36 | pub struct GltfVertexTexCoord(pub [f32; 2]); 37 | unsafe impl PodTransmutable for GltfVertexTexCoord {} 38 | 39 | #[repr(C)] 40 | #[derive(Clone, Copy, Default)] 41 | pub struct GltfVertexColor(pub [f32; 4]); 42 | unsafe impl PodTransmutable for GltfVertexColor {} 43 | 44 | #[derive(Debug, Clone, Eq, PartialEq, Hash)] 45 | pub struct VertexAttributeProperties { 46 | pub stride: usize, 47 | } 48 | 49 | macro_rules! impl_buffers { 50 | { 51 | $field_len:expr, $field_len_ty:ty; 52 | $([$field_name:ident: $($buffer_type_name:tt)+] of [$attribute_name:ident: $($attribute_type:tt)+] { 53 | default_stride: $default_stride:expr, 54 | missing_stride: $missing_stride:expr, 55 | semantic: $semantic:expr$(,)? 56 | }),+$(,)? 57 | } => { 58 | #[derive(Debug, Clone, Eq, PartialEq, Hash)] 59 | pub struct VertexAttributePropertiesSet { 60 | $(pub $attribute_name: VertexAttributeProperties),* 61 | } 62 | 63 | impl<'a, 'b> From<&'b Primitive<'a>> for VertexAttributePropertiesSet { 64 | #[allow(unreachable_code)] 65 | fn from(primitive: &'b Primitive<'a>) -> Self { 66 | let mut result = VertexAttributePropertiesSet::default(); 67 | 68 | $( 69 | if let Some(accessor) = primitive.get(&$semantic) { 70 | if let Some(stride) = accessor.view().stride() { 71 | result.$attribute_name.stride = stride; 72 | } 73 | } else { 74 | // For mandatory vertex attributes, set stride to default stride. 75 | // For other attributes, set stride to 0 because the zero buffer is used. 76 | result.$attribute_name.stride = $missing_stride; 77 | } 78 | )+ 79 | 80 | result 81 | } 82 | } 83 | 84 | impl Default for VertexAttributePropertiesSet { 85 | fn default() -> Self { 86 | VertexAttributePropertiesSet { 87 | $( 88 | $attribute_name: VertexAttributeProperties { 89 | stride: $default_stride, 90 | } 91 | ),+ 92 | } 93 | } 94 | } 95 | 96 | pub struct GltfVertexBuffers<$($($buffer_type_name),+),+> 97 | where $($($buffer_type_name)+: TypedBufferAccess + Send + Sync + 'static,)+ { 98 | $(pub $field_name: Option>,)+ 99 | } 100 | 101 | impl<$($($buffer_type_name),+),+> GltfVertexBuffers<$($($buffer_type_name),+),+> 102 | where $($($buffer_type_name)+: TypedBufferAccess + Send + Sync + 'static,)+ { 103 | pub fn get_individual_buffers(&self) -> Vec> { 104 | let mut result: Vec> = Vec::new(); 105 | 106 | $( 107 | if let Some(ref buffer) = self.$field_name { 108 | result.push(buffer.clone()); 109 | } 110 | )+ 111 | 112 | result 113 | } 114 | } 115 | 116 | #[derive(Clone)] 117 | pub struct GltfVertexBufferDefinition { 118 | pub properties_set: VertexAttributePropertiesSet, 119 | } 120 | 121 | impl GltfVertexBufferDefinition { 122 | pub fn new(properties_set: VertexAttributePropertiesSet) -> Self { 123 | Self { properties_set } 124 | } 125 | } 126 | 127 | unsafe impl<$($($buffer_type_name)+,)+> VertexSource> for GltfVertexBufferDefinition 128 | where $($($buffer_type_name)+: TypedBufferAccess + Send + Sync + 'static,)+ { 129 | fn decode(&self, buffers: GltfVertexBuffers<$($($buffer_type_name)+,)+>) 130 | -> (Vec>, usize, usize) { 131 | let GltfVertexBuffers {$( 132 | $field_name, 133 | )+} = buffers; 134 | let vertices = [$( 135 | ($field_name).as_ref().map(|buffer| buffer.size() / mem::size_of::<$($attribute_type)+>()), 136 | )+].into_iter() 137 | .cloned() 138 | .filter(Option::is_some) 139 | .map(Option::unwrap) 140 | .min() 141 | .unwrap(); 142 | let instances = 1; 143 | 144 | let individual_buffers: Vec> = { 145 | vec![$( 146 | ($field_name).map(|field| Box::new(field) as Box), 147 | )+].into_iter() 148 | .filter(Option::is_some) 149 | .map(Option::unwrap) 150 | .collect() 151 | }; 152 | 153 | (individual_buffers, vertices, instances) 154 | } 155 | } 156 | 157 | unsafe impl VertexSource>> for GltfVertexBufferDefinition { 158 | fn decode(&self, buffers: Vec>) 159 | -> (Vec>, usize, usize) { 160 | let attribute_sizes = [$( 161 | mem::size_of::<$($attribute_type)+>(), 162 | )+]; 163 | let vertices = buffers.iter().zip(attribute_sizes.into_iter()) 164 | .map(|(buffer, attribute_size)| buffer.size() / attribute_size) 165 | .min().unwrap(); 166 | let instances = 1; 167 | 168 | let individual_buffers: Vec> = { 169 | buffers.into_iter() 170 | .map(|x| Box::new(x) as Box) 171 | .collect() 172 | }; 173 | 174 | (individual_buffers, vertices, instances) 175 | } 176 | } 177 | 178 | unsafe impl VertexDefinition for GltfVertexBufferDefinition { 179 | type BuffersIter = ArrayIterator<(u32, usize, InputRate), $field_len_ty>; 180 | type AttribsIter = ArrayIterator<(u32, u32, AttributeInfo), $field_len_ty>; 181 | 182 | #[allow(unused_variables, unused_assignments)] 183 | fn definition(&self, interface: &MainInput) 184 | -> Result<(Self::BuffersIter, Self::AttribsIter), IncompatibleVertexDefinitionError> { 185 | let mut buffers: [(u32, usize, InputRate); $field_len] = [$({ 186 | let $field_name: (); 187 | (0, 0, InputRate::Vertex) 188 | },)+]; 189 | let mut attribs: [(u32, u32, AttributeInfo); $field_len] = [$({ 190 | let $field_name: (); 191 | (0, 0, AttributeInfo { 192 | offset: 0, 193 | format: vulkano::format::Format::R4G4UnormPack8, 194 | }) 195 | },)+]; 196 | 197 | // for (index, element) in interface.elements().enumerate() { 198 | // println!("element #{}: {:?}", index, element); 199 | // } 200 | 201 | let attribute_names = [$(stringify!($attribute_name)),+]; 202 | // let attribute_type_sizes = [$(mem::size_of::<$($attribute_type)+>()),+]; 203 | let attribute_properties = [$(&self.properties_set.$attribute_name),+]; 204 | 205 | debug_assert_eq!( 206 | interface.elements().len(), 207 | $field_len, 208 | "The number of fields in the shader and program code is inconsistent.", 209 | ); 210 | 211 | for element in interface.elements() { 212 | let field_index = element.location.start as usize; 213 | 214 | debug_assert_eq!( 215 | element.name.expect("Shader input attribute has no name.").as_ref(), 216 | attribute_names[field_index], 217 | "The field types in the shader and program code are inconsistent", 218 | ); 219 | 220 | buffers[field_index] = ( 221 | field_index as u32, 222 | // attribute_type_sizes[field_index], 223 | attribute_properties[field_index].stride, 224 | InputRate::Vertex 225 | ); 226 | attribs[field_index] = ( 227 | field_index as u32, 228 | field_index as u32, 229 | AttributeInfo { 230 | offset: 0, 231 | format: element.format, 232 | } 233 | ); 234 | } 235 | 236 | Ok((ArrayIterator::new(buffers), ArrayIterator::new(attribs))) 237 | } 238 | } 239 | } 240 | } 241 | 242 | impl_buffers! { 243 | 5, U5; 244 | 245 | [position_buffer: PositionBuffer] of [position: GltfVertexPosition] { 246 | default_stride: 4 * 3, 247 | missing_stride: unreachable!(), 248 | semantic: Semantic::Positions, 249 | }, 250 | [normal_buffer: NormalBuffer] of [normal: GltfVertexNormal] { 251 | default_stride: 4 * 3, 252 | missing_stride: 4 * 3, 253 | semantic: Semantic::Normals, 254 | }, 255 | [tangent_buffer: TangentBuffer] of [tangent: GltfVertexTangent] { 256 | default_stride: 4 * 4, 257 | missing_stride: 4 * 4, 258 | semantic: Semantic::Tangents, 259 | }, 260 | [tex_coord_buffer: TexCoordBuffer] of [tex_coord: GltfVertexTexCoord] { 261 | default_stride: 4 * 2, 262 | missing_stride: 0, 263 | semantic: Semantic::TexCoords(0), //TODO 264 | }, 265 | [vertex_color_buffer: VertexColorBuffer] of [vertex_color: GltfVertexColor] { 266 | default_stride: 4 * 4, 267 | missing_stride: 0, 268 | semantic: Semantic::Colors(0), //TODO 269 | }, 270 | } 271 | --------------------------------------------------------------------------------