Compare commits
2 Commits
eaa8a7383c
...
be2cca79b0
Author | SHA1 | Date |
---|---|---|
Mark | be2cca79b0 | |
Mark | 08e9958f2d |
|
@ -348,6 +348,27 @@ dependencies = [
|
||||||
"libc",
|
"libc",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cosmic-text"
|
||||||
|
version = "0.10.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "75acbfb314aeb4f5210d379af45ed1ec2c98c7f1790bf57b8a4c562ac0c51b71"
|
||||||
|
dependencies = [
|
||||||
|
"fontdb",
|
||||||
|
"libm",
|
||||||
|
"log",
|
||||||
|
"rangemap",
|
||||||
|
"rustc-hash",
|
||||||
|
"rustybuzz",
|
||||||
|
"self_cell",
|
||||||
|
"swash",
|
||||||
|
"sys-locale",
|
||||||
|
"unicode-bidi",
|
||||||
|
"unicode-linebreak",
|
||||||
|
"unicode-script",
|
||||||
|
"unicode-segmentation",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "crc32fast"
|
name = "crc32fast"
|
||||||
version = "1.3.2"
|
version = "1.3.2"
|
||||||
|
@ -472,6 +493,25 @@ version = "1.0.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5"
|
checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "etagere"
|
||||||
|
version = "0.2.10"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "306960881d6c46bd0dd6b7f07442a441418c08d0d3e63d8d080b0f64c6343e4e"
|
||||||
|
dependencies = [
|
||||||
|
"euclid",
|
||||||
|
"svg_fmt",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "euclid"
|
||||||
|
version = "0.22.9"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "87f253bc5c813ca05792837a0ff4b3a580336b224512d48f7eda1d7dd9210787"
|
||||||
|
dependencies = [
|
||||||
|
"num-traits",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "exr"
|
name = "exr"
|
||||||
version = "1.71.0"
|
version = "1.71.0"
|
||||||
|
@ -519,6 +559,29 @@ dependencies = [
|
||||||
"spin",
|
"spin",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "fontconfig-parser"
|
||||||
|
version = "0.5.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "674e258f4b5d2dcd63888c01c68413c51f565e8af99d2f7701c7b81d79ef41c4"
|
||||||
|
dependencies = [
|
||||||
|
"roxmltree",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "fontdb"
|
||||||
|
version = "0.15.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "020e203f177c0fb250fb19455a252e838d2bbbce1f80f25ecc42402aafa8cd38"
|
||||||
|
dependencies = [
|
||||||
|
"fontconfig-parser",
|
||||||
|
"log",
|
||||||
|
"memmap2 0.8.0",
|
||||||
|
"slotmap",
|
||||||
|
"tinyvec",
|
||||||
|
"ttf-parser 0.19.2",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "foreign-types"
|
name = "foreign-types"
|
||||||
version = "0.3.2"
|
version = "0.3.2"
|
||||||
|
@ -579,20 +642,16 @@ version = "0.0.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"cgmath",
|
"cgmath",
|
||||||
"galactica-constants",
|
|
||||||
"galactica-content",
|
"galactica-content",
|
||||||
"galactica-galaxy",
|
"galactica-galaxy",
|
||||||
"galactica-render",
|
"galactica-render",
|
||||||
"galactica-systemsim",
|
"galactica-systemsim",
|
||||||
|
"galactica-util",
|
||||||
"pollster",
|
"pollster",
|
||||||
"wgpu",
|
"wgpu",
|
||||||
"winit",
|
"winit",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "galactica-constants"
|
|
||||||
version = "0.0.0"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "galactica-content"
|
name = "galactica-content"
|
||||||
version = "0.0.0"
|
version = "0.0.0"
|
||||||
|
@ -621,7 +680,7 @@ name = "galactica-packer"
|
||||||
version = "0.0.0"
|
version = "0.0.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"galactica-constants",
|
"galactica-util",
|
||||||
"image",
|
"image",
|
||||||
"serde",
|
"serde",
|
||||||
"toml",
|
"toml",
|
||||||
|
@ -635,11 +694,12 @@ dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"bytemuck",
|
"bytemuck",
|
||||||
"cgmath",
|
"cgmath",
|
||||||
"galactica-constants",
|
|
||||||
"galactica-content",
|
"galactica-content",
|
||||||
"galactica-galaxy",
|
"galactica-galaxy",
|
||||||
"galactica-packer",
|
"galactica-packer",
|
||||||
"galactica-systemsim",
|
"galactica-systemsim",
|
||||||
|
"galactica-util",
|
||||||
|
"glyphon",
|
||||||
"image",
|
"image",
|
||||||
"rand",
|
"rand",
|
||||||
"wgpu",
|
"wgpu",
|
||||||
|
@ -659,6 +719,10 @@ dependencies = [
|
||||||
"rapier2d",
|
"rapier2d",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "galactica-util"
|
||||||
|
version = "0.0.0"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "getrandom"
|
name = "getrandom"
|
||||||
version = "0.2.11"
|
version = "0.2.11"
|
||||||
|
@ -720,6 +784,17 @@ dependencies = [
|
||||||
"gl_generator",
|
"gl_generator",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "glyphon"
|
||||||
|
version = "0.3.0"
|
||||||
|
source = "git+https://github.com/grovesNL/glyphon.git?branch=main#b15437b87f4082e7a96a2ba05ed5063a6047cf95"
|
||||||
|
dependencies = [
|
||||||
|
"cosmic-text",
|
||||||
|
"etagere",
|
||||||
|
"lru",
|
||||||
|
"wgpu",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "gpu-alloc"
|
name = "gpu-alloc"
|
||||||
version = "0.6.0"
|
version = "0.6.0"
|
||||||
|
@ -975,6 +1050,15 @@ version = "0.4.20"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f"
|
checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "lru"
|
||||||
|
version = "0.11.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a4a83fb7698b3643a0e34f9ae6f2e8f0178c0fd42f8b59d493aa271ff3a5bf21"
|
||||||
|
dependencies = [
|
||||||
|
"hashbrown",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "malloc_buf"
|
name = "malloc_buf"
|
||||||
version = "0.0.6"
|
version = "0.0.6"
|
||||||
|
@ -1009,6 +1093,15 @@ dependencies = [
|
||||||
"libc",
|
"libc",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "memmap2"
|
||||||
|
version = "0.8.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "43a5a03cefb0d953ec0be133036f14e109412fa594edc2f77227249db66cc3ed"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "memoffset"
|
name = "memoffset"
|
||||||
version = "0.6.5"
|
version = "0.6.5"
|
||||||
|
@ -1333,7 +1426,7 @@ version = "0.20.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d4586edfe4c648c71797a74c84bacb32b52b212eff5dfe2bb9f2c599844023e7"
|
checksum = "d4586edfe4c648c71797a74c84bacb32b52b212eff5dfe2bb9f2c599844023e7"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"ttf-parser",
|
"ttf-parser 0.20.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -1508,6 +1601,12 @@ version = "0.1.3"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "9c8a99fddc9f0ba0a85884b8d14e3592853e787d581ca1816c91349b10e4eeab"
|
checksum = "9c8a99fddc9f0ba0a85884b8d14e3592853e787d581ca1816c91349b10e4eeab"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rangemap"
|
||||||
|
version = "1.4.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "977b1e897f9d764566891689e642653e5ed90c6895106acd005eb4c1d0203991"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rapier2d"
|
name = "rapier2d"
|
||||||
version = "0.17.2"
|
version = "0.17.2"
|
||||||
|
@ -1590,6 +1689,15 @@ version = "1.1.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "cbf4a6aa5f6d6888f39e980649f3ad6b666acdce1d78e95b8a2cb076e687ae30"
|
checksum = "cbf4a6aa5f6d6888f39e980649f3ad6b666acdce1d78e95b8a2cb076e687ae30"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "roxmltree"
|
||||||
|
version = "0.18.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "862340e351ce1b271a378ec53f304a5558f7db87f3769dc655a8f6ecbb68b302"
|
||||||
|
dependencies = [
|
||||||
|
"xmlparser",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rustc-demangle"
|
name = "rustc-demangle"
|
||||||
version = "0.1.23"
|
version = "0.1.23"
|
||||||
|
@ -1602,6 +1710,23 @@ version = "1.1.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2"
|
checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rustybuzz"
|
||||||
|
version = "0.11.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "2ee8fe2a8461a0854a37101fe7a1b13998d0cfa987e43248e81d2a5f4570f6fa"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags 1.3.2",
|
||||||
|
"bytemuck",
|
||||||
|
"libm",
|
||||||
|
"smallvec",
|
||||||
|
"ttf-parser 0.20.0",
|
||||||
|
"unicode-bidi-mirroring",
|
||||||
|
"unicode-ccc",
|
||||||
|
"unicode-properties",
|
||||||
|
"unicode-script",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "safe_arch"
|
name = "safe_arch"
|
||||||
version = "0.7.1"
|
version = "0.7.1"
|
||||||
|
@ -1640,11 +1765,17 @@ checksum = "cda4e97be1fd174ccc2aae81c8b694e803fa99b34e8fd0f057a9d70698e3ed09"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"ab_glyph",
|
"ab_glyph",
|
||||||
"log",
|
"log",
|
||||||
"memmap2",
|
"memmap2 0.5.10",
|
||||||
"smithay-client-toolkit",
|
"smithay-client-toolkit",
|
||||||
"tiny-skia",
|
"tiny-skia",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "self_cell"
|
||||||
|
version = "1.0.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "58bf37232d3bb9a2c4e641ca2a11d83b5062066f88df7fed36c28772046d65ba"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde"
|
name = "serde"
|
||||||
version = "1.0.193"
|
version = "1.0.193"
|
||||||
|
@ -1728,7 +1859,7 @@ dependencies = [
|
||||||
"dlib",
|
"dlib",
|
||||||
"lazy_static",
|
"lazy_static",
|
||||||
"log",
|
"log",
|
||||||
"memmap2",
|
"memmap2 0.5.10",
|
||||||
"nix 0.24.3",
|
"nix 0.24.3",
|
||||||
"pkg-config",
|
"pkg-config",
|
||||||
"wayland-client",
|
"wayland-client",
|
||||||
|
@ -1779,6 +1910,22 @@ version = "0.1.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "6637bab7722d379c8b41ba849228d680cc12d0a45ba1fa2b48f2a30577a06731"
|
checksum = "6637bab7722d379c8b41ba849228d680cc12d0a45ba1fa2b48f2a30577a06731"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "svg_fmt"
|
||||||
|
version = "0.4.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8fb1df15f412ee2e9dfc1c504260fa695c1c3f10fe9f4a6ee2d2184d7d6450e2"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "swash"
|
||||||
|
version = "0.1.8"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3b7c73c813353c347272919aa1af2885068b05e625e5532b43049e4f641ae77f"
|
||||||
|
dependencies = [
|
||||||
|
"yazi",
|
||||||
|
"zeno",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "syn"
|
name = "syn"
|
||||||
version = "1.0.109"
|
version = "1.0.109"
|
||||||
|
@ -1801,6 +1948,15 @@ dependencies = [
|
||||||
"unicode-ident",
|
"unicode-ident",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "sys-locale"
|
||||||
|
version = "0.3.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e801cf239ecd6ccd71f03d270d67dd53d13e90aab208bf4b8fe4ad957ea949b0"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "termcolor"
|
name = "termcolor"
|
||||||
version = "1.4.0"
|
version = "1.4.0"
|
||||||
|
@ -1866,6 +2022,21 @@ dependencies = [
|
||||||
"strict-num",
|
"strict-num",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tinyvec"
|
||||||
|
version = "1.6.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50"
|
||||||
|
dependencies = [
|
||||||
|
"tinyvec_macros",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tinyvec_macros"
|
||||||
|
version = "0.1.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "toml"
|
name = "toml"
|
||||||
version = "0.8.8"
|
version = "0.8.8"
|
||||||
|
@ -1911,6 +2082,12 @@ dependencies = [
|
||||||
"winnow",
|
"winnow",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ttf-parser"
|
||||||
|
version = "0.19.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "49d64318d8311fc2668e48b63969f4343e0a85c4a109aa8460d6672e364b8bd1"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ttf-parser"
|
name = "ttf-parser"
|
||||||
version = "0.20.0"
|
version = "0.20.0"
|
||||||
|
@ -1923,12 +2100,54 @@ version = "1.17.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825"
|
checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "unicode-bidi"
|
||||||
|
version = "0.3.14"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "6f2528f27a9eb2b21e69c95319b30bd0efd85d09c379741b0f78ea1d86be2416"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "unicode-bidi-mirroring"
|
||||||
|
version = "0.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "56d12260fb92d52f9008be7e4bca09f584780eb2266dc8fecc6a192bec561694"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "unicode-ccc"
|
||||||
|
version = "0.1.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "cc2520efa644f8268dce4dcd3050eaa7fc044fca03961e9998ac7e2e92b77cf1"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "unicode-ident"
|
name = "unicode-ident"
|
||||||
version = "1.0.12"
|
version = "1.0.12"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
|
checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "unicode-linebreak"
|
||||||
|
version = "0.1.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3b09c83c3c29d37506a3e260c08c03743a6bb66a9cd432c6934ab501a190571f"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "unicode-properties"
|
||||||
|
version = "0.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c7f91c8b21fbbaa18853c3d0801c78f4fc94cdb976699bb03e832e75f7fd22f0"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "unicode-script"
|
||||||
|
version = "0.5.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7d817255e1bed6dfd4ca47258685d14d2bdcfbc64fdc9e3819bd5848057b8ecc"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "unicode-segmentation"
|
||||||
|
version = "1.10.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1dd624098567895118886609431a7c3b8f516e41d30e0643f03d94592a147e36"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "unicode-width"
|
name = "unicode-width"
|
||||||
version = "0.1.11"
|
version = "0.1.11"
|
||||||
|
@ -2491,6 +2710,24 @@ version = "0.8.19"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "0fcb9cbac069e033553e8bb871be2fbdffcab578eb25bd0f7c508cedc6dcd75a"
|
checksum = "0fcb9cbac069e033553e8bb871be2fbdffcab578eb25bd0f7c508cedc6dcd75a"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "xmlparser"
|
||||||
|
version = "0.13.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "66fee0b777b0f5ac1c69bb06d361268faafa61cd4682ae064a171c16c433e9e4"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "yazi"
|
||||||
|
version = "0.1.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c94451ac9513335b5e23d7a8a2b61a7102398b8cca5160829d313e84c9d98be1"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "zeno"
|
||||||
|
version = "0.2.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "dd15f8e0dbb966fd9245e7498c7e9e5055d9e5c8b676b95bd67091cd11a1e697"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "zerocopy"
|
name = "zerocopy"
|
||||||
version = "0.7.32"
|
version = "0.7.32"
|
||||||
|
|
|
@ -42,7 +42,7 @@ readme = ""
|
||||||
[workspace.lints]
|
[workspace.lints]
|
||||||
|
|
||||||
[workspace.dependencies]
|
[workspace.dependencies]
|
||||||
galactica-constants = { path = "crates/constants" }
|
galactica-util = { path = "crates/util" }
|
||||||
galactica-content = { path = "crates/content" }
|
galactica-content = { path = "crates/content" }
|
||||||
galactica-render = { path = "crates/render" }
|
galactica-render = { path = "crates/render" }
|
||||||
galactica-systemsim = { path = "crates/systemsim" }
|
galactica-systemsim = { path = "crates/systemsim" }
|
||||||
|
@ -66,3 +66,6 @@ cgmath = "0.18.0"
|
||||||
rand = "0.8.5"
|
rand = "0.8.5"
|
||||||
walkdir = "2.4.0"
|
walkdir = "2.4.0"
|
||||||
toml = "0.8.8"
|
toml = "0.8.8"
|
||||||
|
|
||||||
|
# Glyphon's crates.io release doesn't support wgpu 0.18 yet
|
||||||
|
glyphon = { git = "https://github.com/grovesNL/glyphon.git", branch = "main" }
|
||||||
|
|
|
@ -23,7 +23,7 @@ workspace = true
|
||||||
[dependencies]
|
[dependencies]
|
||||||
galactica-content = { workspace = true }
|
galactica-content = { workspace = true }
|
||||||
galactica-render = { workspace = true }
|
galactica-render = { workspace = true }
|
||||||
galactica-constants = { workspace = true }
|
galactica-util = { workspace = true }
|
||||||
galactica-systemsim = { workspace = true }
|
galactica-systemsim = { workspace = true }
|
||||||
galactica-galaxy = { workspace = true }
|
galactica-galaxy = { workspace = true }
|
||||||
|
|
||||||
|
|
|
@ -1,13 +1,15 @@
|
||||||
use galactica_content::{Content, FactionHandle, OutfitHandle, ShipHandle, SystemHandle};
|
use galactica_content::{Content, FactionHandle, OutfitHandle, ShipHandle, SystemHandle};
|
||||||
use galactica_galaxy::{ship::ShipPersonality, Galaxy, GxShipHandle};
|
use galactica_galaxy::{ship::ShipPersonality, Galaxy, GxShipHandle};
|
||||||
|
use galactica_util::{
|
||||||
|
constants::{ZOOM_MAX, ZOOM_MIN},
|
||||||
|
timing::Timing,
|
||||||
|
};
|
||||||
use std::time::Instant;
|
use std::time::Instant;
|
||||||
use winit::event::{ElementState, MouseButton, MouseScrollDelta, TouchPhase, VirtualKeyCode};
|
use winit::event::{ElementState, MouseButton, MouseScrollDelta, TouchPhase, VirtualKeyCode};
|
||||||
|
|
||||||
use crate::camera::Camera;
|
use crate::camera::Camera;
|
||||||
use crate::inputstatus::InputStatus;
|
use crate::inputstatus::InputStatus;
|
||||||
|
use galactica_render::RenderInput;
|
||||||
use galactica_constants;
|
|
||||||
use galactica_render::RenderState;
|
|
||||||
use galactica_systemsim::{objects::ShipControls, util, ParticleBuilder, StepResources, SystemSim};
|
use galactica_systemsim::{objects::ShipControls, util, ParticleBuilder, StepResources, SystemSim};
|
||||||
|
|
||||||
pub struct Game {
|
pub struct Game {
|
||||||
|
@ -24,6 +26,7 @@ pub struct Game {
|
||||||
systemsim: SystemSim,
|
systemsim: SystemSim,
|
||||||
|
|
||||||
new_particles: Vec<ParticleBuilder>,
|
new_particles: Vec<ParticleBuilder>,
|
||||||
|
timing: Timing,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Game {
|
impl Game {
|
||||||
|
@ -86,6 +89,7 @@ impl Game {
|
||||||
galaxy,
|
galaxy,
|
||||||
content: ct,
|
content: ct,
|
||||||
new_particles: Vec::new(),
|
new_particles: Vec::new(),
|
||||||
|
timing: Timing::new(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -117,8 +121,11 @@ impl Game {
|
||||||
pub fn update(&mut self) {
|
pub fn update(&mut self) {
|
||||||
let t: f32 = self.last_update.elapsed().as_secs_f32() * self.time_scale;
|
let t: f32 = self.last_update.elapsed().as_secs_f32() * self.time_scale;
|
||||||
|
|
||||||
|
self.timing.start();
|
||||||
self.galaxy.step(t);
|
self.galaxy.step(t);
|
||||||
|
self.timing.mark_galaxy();
|
||||||
|
|
||||||
|
self.timing.start();
|
||||||
self.systemsim.step(StepResources {
|
self.systemsim.step(StepResources {
|
||||||
player: self.player,
|
player: self.player,
|
||||||
player_controls: ShipControls {
|
player_controls: ShipControls {
|
||||||
|
@ -132,10 +139,10 @@ impl Game {
|
||||||
particles: &mut self.new_particles,
|
particles: &mut self.new_particles,
|
||||||
t,
|
t,
|
||||||
});
|
});
|
||||||
|
self.timing.mark_physics();
|
||||||
|
|
||||||
if self.input.v_scroll != 0.0 {
|
if self.input.v_scroll != 0.0 {
|
||||||
self.camera.zoom = (self.camera.zoom + self.input.v_scroll)
|
self.camera.zoom = (self.camera.zoom + self.input.v_scroll).clamp(ZOOM_MIN, ZOOM_MAX);
|
||||||
.clamp(galactica_constants::ZOOM_MIN, galactica_constants::ZOOM_MAX);
|
|
||||||
self.input.v_scroll = 0.0;
|
self.input.v_scroll = 0.0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -148,8 +155,8 @@ impl Game {
|
||||||
self.last_update = Instant::now();
|
self.last_update = Instant::now();
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_frame_state(&mut self) -> RenderState {
|
pub fn get_frame_state(&mut self) -> RenderInput {
|
||||||
RenderState {
|
RenderInput {
|
||||||
camera_pos: self.camera.pos,
|
camera_pos: self.camera.pos,
|
||||||
camera_zoom: self.camera.zoom,
|
camera_zoom: self.camera.zoom,
|
||||||
current_time: self.start_instant.elapsed().as_secs_f32(),
|
current_time: self.start_instant.elapsed().as_secs_f32(),
|
||||||
|
@ -159,6 +166,7 @@ impl Game {
|
||||||
player_data: self.player,
|
player_data: self.player,
|
||||||
data: &self.galaxy,
|
data: &self.galaxy,
|
||||||
current_system: SystemHandle { index: 0 },
|
current_system: SystemHandle { index: 0 },
|
||||||
|
timing: &self.timing,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,8 +3,8 @@ mod game;
|
||||||
mod inputstatus;
|
mod inputstatus;
|
||||||
|
|
||||||
use anyhow::{bail, Result};
|
use anyhow::{bail, Result};
|
||||||
use galactica_constants::{ASSET_CACHE, CONTENT_ROOT, IMAGE_ROOT, STARFIELD_SPRITE_NAME};
|
|
||||||
use galactica_content::Content;
|
use galactica_content::Content;
|
||||||
|
use galactica_util::constants::{ASSET_CACHE, CONTENT_ROOT, IMAGE_ROOT, STARFIELD_SPRITE_NAME};
|
||||||
use std::{
|
use std::{
|
||||||
fs,
|
fs,
|
||||||
path::{Path, PathBuf},
|
path::{Path, PathBuf},
|
||||||
|
@ -40,7 +40,9 @@ fn main() -> Result<()> {
|
||||||
|
|
||||||
let mut game = game::Game::new(content);
|
let mut game = game::Game::new(content);
|
||||||
gpu.update_starfield_buffer();
|
gpu.update_starfield_buffer();
|
||||||
game.set_camera_aspect(gpu.window_size.width as f32 / gpu.window_size.height as f32);
|
game.set_camera_aspect(
|
||||||
|
gpu.window().inner_size().width as f32 / gpu.window().inner_size().height as f32,
|
||||||
|
);
|
||||||
|
|
||||||
event_loop.run(move |event, _, control_flow| {
|
event_loop.run(move |event, _, control_flow| {
|
||||||
match event {
|
match event {
|
||||||
|
@ -62,7 +64,7 @@ fn main() -> Result<()> {
|
||||||
Event::WindowEvent {
|
Event::WindowEvent {
|
||||||
ref event,
|
ref event,
|
||||||
window_id,
|
window_id,
|
||||||
} if window_id == gpu.window.id() => match event {
|
} if window_id == gpu.window().id() => match event {
|
||||||
WindowEvent::Focused(state) => {
|
WindowEvent::Focused(state) => {
|
||||||
game.set_paused(!state);
|
game.set_paused(!state);
|
||||||
}
|
}
|
||||||
|
@ -85,13 +87,15 @@ fn main() -> Result<()> {
|
||||||
WindowEvent::Resized(_) => {
|
WindowEvent::Resized(_) => {
|
||||||
gpu.resize();
|
gpu.resize();
|
||||||
game.set_camera_aspect(
|
game.set_camera_aspect(
|
||||||
gpu.window_size.width as f32 / gpu.window_size.height as f32,
|
gpu.window().inner_size().width as f32
|
||||||
|
/ gpu.window().inner_size().height as f32,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
WindowEvent::ScaleFactorChanged { .. } => {
|
WindowEvent::ScaleFactorChanged { .. } => {
|
||||||
gpu.resize();
|
gpu.resize();
|
||||||
game.set_camera_aspect(
|
game.set_camera_aspect(
|
||||||
gpu.window_size.width as f32 / gpu.window_size.height as f32,
|
gpu.window().inner_size().width as f32
|
||||||
|
/ gpu.window().inner_size().height as f32,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
|
|
|
@ -21,7 +21,7 @@ readme = { workspace = true }
|
||||||
workspace = true
|
workspace = true
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
galactica-constants = { workspace = true }
|
galactica-util = { workspace = true }
|
||||||
|
|
||||||
image = { workspace = true }
|
image = { workspace = true }
|
||||||
toml = { workspace = true }
|
toml = { workspace = true }
|
||||||
|
|
|
@ -18,7 +18,7 @@ workspace = true
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
galactica-content = { workspace = true }
|
galactica-content = { workspace = true }
|
||||||
galactica-constants = { workspace = true }
|
galactica-util = { workspace = true }
|
||||||
galactica-packer = { workspace = true }
|
galactica-packer = { workspace = true }
|
||||||
galactica-systemsim = { workspace = true }
|
galactica-systemsim = { workspace = true }
|
||||||
galactica-galaxy = { workspace = true }
|
galactica-galaxy = { workspace = true }
|
||||||
|
@ -30,3 +30,4 @@ image = { workspace = true }
|
||||||
winit = { workspace = true }
|
winit = { workspace = true }
|
||||||
wgpu = { workspace = true }
|
wgpu = { workspace = true }
|
||||||
bytemuck = { workspace = true }
|
bytemuck = { workspace = true }
|
||||||
|
glyphon = { workspace = true }
|
||||||
|
|
|
@ -0,0 +1,81 @@
|
||||||
|
use cgmath::Point2;
|
||||||
|
use galactica_content::{Content, SystemHandle};
|
||||||
|
use galactica_galaxy::{Galaxy, GxShipHandle};
|
||||||
|
use galactica_systemsim::{ParticleBuilder, SystemSim};
|
||||||
|
use galactica_util::timing::Timing;
|
||||||
|
use glyphon::{FontSystem, SwashCache, TextAtlas, TextRenderer};
|
||||||
|
use std::rc::Rc;
|
||||||
|
use wgpu::BufferAddress;
|
||||||
|
use winit::window::Window;
|
||||||
|
|
||||||
|
use crate::{globaluniform::GlobalUniform, vertexbuffer::VertexBuffer};
|
||||||
|
|
||||||
|
/// Bundles parameters passed to a single call to GPUState::render
|
||||||
|
pub struct RenderInput<'a> {
|
||||||
|
/// Camera position, in world units
|
||||||
|
pub camera_pos: Point2<f32>,
|
||||||
|
|
||||||
|
/// Player ship data
|
||||||
|
pub player_data: GxShipHandle,
|
||||||
|
|
||||||
|
/// The system we're currently in
|
||||||
|
pub current_system: SystemHandle,
|
||||||
|
|
||||||
|
/// Height of screen, in world units
|
||||||
|
pub camera_zoom: f32,
|
||||||
|
|
||||||
|
/// The world state to render
|
||||||
|
pub systemsim: &'a SystemSim,
|
||||||
|
|
||||||
|
// TODO: handle overflow. is it a problem?
|
||||||
|
/// The current time, in seconds
|
||||||
|
pub current_time: f32,
|
||||||
|
|
||||||
|
/// Game content
|
||||||
|
pub content: &'a Content,
|
||||||
|
|
||||||
|
/// Game data
|
||||||
|
pub data: &'a Galaxy,
|
||||||
|
|
||||||
|
/// Particles to spawn during this frame
|
||||||
|
pub particles: &'a mut Vec<ParticleBuilder>,
|
||||||
|
|
||||||
|
/// Time we spent in each part of the game loop
|
||||||
|
pub timing: &'a Timing,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Renderer state. A reference to this struct is often passed to helper functions.
|
||||||
|
pub(crate) struct RenderState {
|
||||||
|
pub window: Window,
|
||||||
|
pub window_size: winit::dpi::PhysicalSize<u32>,
|
||||||
|
pub window_aspect: f32,
|
||||||
|
|
||||||
|
pub queue: wgpu::Queue,
|
||||||
|
pub global_uniform: GlobalUniform,
|
||||||
|
pub vertex_buffers: VertexBuffers,
|
||||||
|
|
||||||
|
pub text_font_system: FontSystem,
|
||||||
|
pub text_cache: SwashCache,
|
||||||
|
pub text_atlas: TextAtlas,
|
||||||
|
pub text_renderer: TextRenderer,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Vertex buffers
|
||||||
|
pub(crate) struct VertexBuffers {
|
||||||
|
// Keeps track of length of each buffer
|
||||||
|
// Most of these are reset on each frame.
|
||||||
|
//
|
||||||
|
// The exception is particle_counter, which
|
||||||
|
// is never reset, and loops back to zero once
|
||||||
|
// it exceeds buffer length
|
||||||
|
pub object_counter: BufferAddress,
|
||||||
|
pub ui_counter: BufferAddress,
|
||||||
|
pub particle_counter: BufferAddress,
|
||||||
|
pub radialbar_counter: BufferAddress,
|
||||||
|
|
||||||
|
pub object: Rc<VertexBuffer>,
|
||||||
|
pub starfield: Rc<VertexBuffer>,
|
||||||
|
pub ui: Rc<VertexBuffer>,
|
||||||
|
pub particle: Rc<VertexBuffer>,
|
||||||
|
pub radialbar: Rc<VertexBuffer>,
|
||||||
|
}
|
|
@ -1,5 +1,5 @@
|
||||||
use bytemuck::{Pod, Zeroable};
|
use bytemuck::{Pod, Zeroable};
|
||||||
use galactica_constants::IMAGE_LIMIT;
|
use galactica_util::constants::IMAGE_LIMIT;
|
||||||
use std::mem;
|
use std::mem;
|
||||||
use wgpu;
|
use wgpu;
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
use galactica_constants::{IMAGE_LIMIT, OBJECT_SPRITE_INSTANCE_LIMIT, SPRITE_LIMIT};
|
use galactica_util::constants::{IMAGE_LIMIT, OBJECT_SPRITE_INSTANCE_LIMIT, SPRITE_LIMIT};
|
||||||
use wgpu;
|
use wgpu;
|
||||||
|
|
||||||
use super::{object::ObjectLocationArray, AtlasArray, GlobalDataContent, SpriteDataArray};
|
use super::{object::ObjectLocationArray, AtlasArray, GlobalDataContent, SpriteDataArray};
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
use bytemuck::{Pod, Zeroable};
|
use bytemuck::{Pod, Zeroable};
|
||||||
use galactica_constants::OBJECT_SPRITE_INSTANCE_LIMIT;
|
use galactica_util::constants::OBJECT_SPRITE_INSTANCE_LIMIT;
|
||||||
use std::mem;
|
use std::mem;
|
||||||
use wgpu;
|
use wgpu;
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
use bytemuck::{Pod, Zeroable};
|
use bytemuck::{Pod, Zeroable};
|
||||||
use galactica_constants::SPRITE_LIMIT;
|
use galactica_util::constants::SPRITE_LIMIT;
|
||||||
use std::mem;
|
use std::mem;
|
||||||
use wgpu;
|
use wgpu;
|
||||||
|
|
||||||
|
|
|
@ -1,49 +1,23 @@
|
||||||
use anyhow::Result;
|
|
||||||
use bytemuck;
|
use bytemuck;
|
||||||
use cgmath::Point2;
|
use wgpu;
|
||||||
use galactica_constants;
|
use winit;
|
||||||
|
|
||||||
use galactica_content::Content;
|
|
||||||
use rand::seq::SliceRandom;
|
|
||||||
use std::{iter, rc::Rc};
|
|
||||||
use wgpu::{self, BufferAddress};
|
|
||||||
use winit::{self, window::Window};
|
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
globaluniform::{GlobalDataContent, GlobalUniform},
|
datastructs::RenderState, starfield::Starfield, texturearray::TextureArray, ui::UiManager,
|
||||||
pipeline::PipelineBuilder,
|
|
||||||
starfield::Starfield,
|
|
||||||
texturearray::TextureArray,
|
|
||||||
vertexbuffer::{
|
|
||||||
consts::{SPRITE_INDICES, SPRITE_VERTICES},
|
|
||||||
types::{
|
|
||||||
ObjectInstance, ParticleInstance, RadialBarInstance, StarfieldInstance, TexturedVertex,
|
|
||||||
UiInstance,
|
|
||||||
},
|
|
||||||
BufferObject, VertexBuffer,
|
|
||||||
},
|
|
||||||
RenderState,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// Additional implementaitons for GPUState
|
/// GPUState is very big, so its methods have been split
|
||||||
mod hud;
|
/// among the following files.
|
||||||
|
mod new;
|
||||||
|
mod render;
|
||||||
mod systemsim;
|
mod systemsim;
|
||||||
|
|
||||||
/// A high-level GPU wrapper. Consumes game state,
|
/// A high-level GPU wrapper. Reads game state (via RenderInput),
|
||||||
/// produces pretty pictures.
|
/// produces pretty pictures.
|
||||||
pub struct GPUState {
|
pub struct GPUState {
|
||||||
/// The window to we draw on
|
|
||||||
pub window: Window,
|
|
||||||
|
|
||||||
/// The size of the window we draw on
|
|
||||||
pub window_size: winit::dpi::PhysicalSize<u32>,
|
|
||||||
|
|
||||||
device: wgpu::Device,
|
device: wgpu::Device,
|
||||||
config: wgpu::SurfaceConfiguration,
|
config: wgpu::SurfaceConfiguration,
|
||||||
surface: wgpu::Surface,
|
surface: wgpu::Surface,
|
||||||
queue: wgpu::Queue,
|
|
||||||
|
|
||||||
window_aspect: f32,
|
|
||||||
|
|
||||||
object_pipeline: wgpu::RenderPipeline,
|
object_pipeline: wgpu::RenderPipeline,
|
||||||
starfield_pipeline: wgpu::RenderPipeline,
|
starfield_pipeline: wgpu::RenderPipeline,
|
||||||
|
@ -53,296 +27,24 @@ pub struct GPUState {
|
||||||
|
|
||||||
starfield: Starfield,
|
starfield: Starfield,
|
||||||
texture_array: TextureArray,
|
texture_array: TextureArray,
|
||||||
global_uniform: GlobalUniform,
|
|
||||||
vertex_buffers: VertexBuffers,
|
|
||||||
}
|
|
||||||
|
|
||||||
struct VertexBuffers {
|
state: RenderState,
|
||||||
// Keeps track of length of each buffer
|
ui: UiManager,
|
||||||
pub object_counter: BufferAddress,
|
|
||||||
pub ui_counter: BufferAddress,
|
|
||||||
pub particle_counter: BufferAddress,
|
|
||||||
pub radialbar_counter: BufferAddress,
|
|
||||||
|
|
||||||
object: Rc<VertexBuffer>,
|
|
||||||
starfield: Rc<VertexBuffer>,
|
|
||||||
ui: Rc<VertexBuffer>,
|
|
||||||
|
|
||||||
particle: Rc<VertexBuffer>,
|
|
||||||
radialbar: Rc<VertexBuffer>,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Basic wgsl preprocesser
|
|
||||||
fn preprocess_shader(
|
|
||||||
shader: &str,
|
|
||||||
global_uniform: &GlobalUniform,
|
|
||||||
global_uniform_group: u32,
|
|
||||||
) -> String {
|
|
||||||
// Insert dynamically-generated global definitions
|
|
||||||
let shader = shader.replace(
|
|
||||||
"// INCLUDE: global uniform header",
|
|
||||||
&global_uniform.shader_header(global_uniform_group),
|
|
||||||
);
|
|
||||||
|
|
||||||
// Insert common functions
|
|
||||||
let shader = shader.replace(
|
|
||||||
"// INCLUDE: animate.wgsl",
|
|
||||||
&include_str!(concat!(
|
|
||||||
env!("CARGO_MANIFEST_DIR"),
|
|
||||||
"/shaders/include/",
|
|
||||||
"animate.wgsl"
|
|
||||||
)),
|
|
||||||
);
|
|
||||||
|
|
||||||
let shader = shader.replace(
|
|
||||||
"// INCLUDE: anchor.wgsl",
|
|
||||||
&include_str!(concat!(
|
|
||||||
env!("CARGO_MANIFEST_DIR"),
|
|
||||||
"/shaders/include/",
|
|
||||||
"anchor.wgsl"
|
|
||||||
)),
|
|
||||||
);
|
|
||||||
|
|
||||||
return shader;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl GPUState {
|
impl GPUState {
|
||||||
/// Make a new GPUState that draws on `window`
|
/// Get the window we are attached to
|
||||||
pub async fn new(window: Window, ct: &Content) -> Result<Self> {
|
pub fn window(&self) -> &winit::window::Window {
|
||||||
let window_size = window.inner_size();
|
&self.state.window
|
||||||
let window_aspect = window_size.width as f32 / window_size.height as f32;
|
|
||||||
|
|
||||||
let instance = wgpu::Instance::new(wgpu::InstanceDescriptor {
|
|
||||||
backends: wgpu::Backends::all(),
|
|
||||||
..Default::default()
|
|
||||||
});
|
|
||||||
|
|
||||||
let surface = unsafe { instance.create_surface(&window) }.unwrap();
|
|
||||||
|
|
||||||
// Basic setup
|
|
||||||
let device;
|
|
||||||
let queue;
|
|
||||||
let config;
|
|
||||||
|
|
||||||
{
|
|
||||||
let adapter = instance
|
|
||||||
.request_adapter(&wgpu::RequestAdapterOptions {
|
|
||||||
power_preference: wgpu::PowerPreference::default(),
|
|
||||||
compatible_surface: Some(&surface),
|
|
||||||
force_fallback_adapter: false,
|
|
||||||
})
|
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
(device, queue) = adapter
|
|
||||||
.request_device(
|
|
||||||
&wgpu::DeviceDescriptor {
|
|
||||||
features: wgpu::Features::TEXTURE_BINDING_ARRAY | wgpu::Features::SAMPLED_TEXTURE_AND_STORAGE_BUFFER_ARRAY_NON_UNIFORM_INDEXING,
|
|
||||||
// We may need limits if we compile for wasm
|
|
||||||
limits: wgpu::Limits::default(),
|
|
||||||
label: Some("gpu device"),
|
|
||||||
},
|
|
||||||
None,
|
|
||||||
)
|
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
// Assume sRGB
|
|
||||||
let surface_caps = surface.get_capabilities(&adapter);
|
|
||||||
let surface_format = surface_caps
|
|
||||||
.formats
|
|
||||||
.iter()
|
|
||||||
.copied()
|
|
||||||
.filter(|f| f.is_srgb())
|
|
||||||
.filter(|f| f.has_stencil_aspect())
|
|
||||||
.next()
|
|
||||||
.unwrap_or(surface_caps.formats[0]);
|
|
||||||
|
|
||||||
config = wgpu::SurfaceConfiguration {
|
|
||||||
usage: wgpu::TextureUsages::RENDER_ATTACHMENT,
|
|
||||||
format: surface_format,
|
|
||||||
width: window_size.width,
|
|
||||||
height: window_size.height,
|
|
||||||
present_mode: surface_caps.present_modes[0],
|
|
||||||
alpha_mode: surface_caps.alpha_modes[0],
|
|
||||||
view_formats: vec![],
|
|
||||||
};
|
|
||||||
|
|
||||||
surface.configure(&device, &config);
|
|
||||||
}
|
|
||||||
|
|
||||||
let vertex_buffers = VertexBuffers {
|
|
||||||
object_counter: 0,
|
|
||||||
ui_counter: 0,
|
|
||||||
particle_counter: 0,
|
|
||||||
radialbar_counter: 0,
|
|
||||||
|
|
||||||
object: Rc::new(VertexBuffer::new::<TexturedVertex, ObjectInstance>(
|
|
||||||
"object",
|
|
||||||
&device,
|
|
||||||
Some(SPRITE_VERTICES),
|
|
||||||
Some(SPRITE_INDICES),
|
|
||||||
galactica_constants::OBJECT_SPRITE_INSTANCE_LIMIT,
|
|
||||||
)),
|
|
||||||
|
|
||||||
starfield: Rc::new(VertexBuffer::new::<TexturedVertex, StarfieldInstance>(
|
|
||||||
"starfield",
|
|
||||||
&device,
|
|
||||||
Some(SPRITE_VERTICES),
|
|
||||||
Some(SPRITE_INDICES),
|
|
||||||
galactica_constants::STARFIELD_SPRITE_INSTANCE_LIMIT,
|
|
||||||
)),
|
|
||||||
|
|
||||||
ui: Rc::new(VertexBuffer::new::<TexturedVertex, UiInstance>(
|
|
||||||
"ui",
|
|
||||||
&device,
|
|
||||||
Some(SPRITE_VERTICES),
|
|
||||||
Some(SPRITE_INDICES),
|
|
||||||
galactica_constants::UI_SPRITE_INSTANCE_LIMIT,
|
|
||||||
)),
|
|
||||||
|
|
||||||
particle: Rc::new(VertexBuffer::new::<TexturedVertex, ParticleInstance>(
|
|
||||||
"particle",
|
|
||||||
&device,
|
|
||||||
Some(SPRITE_VERTICES),
|
|
||||||
Some(SPRITE_INDICES),
|
|
||||||
galactica_constants::PARTICLE_SPRITE_INSTANCE_LIMIT,
|
|
||||||
)),
|
|
||||||
|
|
||||||
radialbar: Rc::new(VertexBuffer::new::<TexturedVertex, RadialBarInstance>(
|
|
||||||
"radial bar",
|
|
||||||
&device,
|
|
||||||
Some(SPRITE_VERTICES),
|
|
||||||
Some(SPRITE_INDICES),
|
|
||||||
10,
|
|
||||||
)),
|
|
||||||
};
|
|
||||||
|
|
||||||
// Load uniforms
|
|
||||||
let global_uniform = GlobalUniform::new(&device);
|
|
||||||
let texture_array = TextureArray::new(&device, &queue, ct)?;
|
|
||||||
|
|
||||||
// Make sure these match the indices in each shader
|
|
||||||
let bind_group_layouts = &[
|
|
||||||
&texture_array.bind_group_layout,
|
|
||||||
&global_uniform.bind_group_layout,
|
|
||||||
];
|
|
||||||
|
|
||||||
// Create render pipelines
|
|
||||||
let object_pipeline = PipelineBuilder::new("object", &device)
|
|
||||||
.set_shader(&preprocess_shader(
|
|
||||||
&include_str!(concat!(
|
|
||||||
env!("CARGO_MANIFEST_DIR"),
|
|
||||||
"/shaders/",
|
|
||||||
"object.wgsl"
|
|
||||||
)),
|
|
||||||
&global_uniform,
|
|
||||||
1,
|
|
||||||
))
|
|
||||||
.set_format(config.format)
|
|
||||||
.set_triangle(true)
|
|
||||||
.set_vertex_buffer(&vertex_buffers.object)
|
|
||||||
.set_bind_group_layouts(bind_group_layouts)
|
|
||||||
.build();
|
|
||||||
|
|
||||||
let starfield_pipeline = PipelineBuilder::new("starfield", &device)
|
|
||||||
.set_shader(&preprocess_shader(
|
|
||||||
&include_str!(concat!(
|
|
||||||
env!("CARGO_MANIFEST_DIR"),
|
|
||||||
"/shaders/",
|
|
||||||
"starfield.wgsl"
|
|
||||||
)),
|
|
||||||
&global_uniform,
|
|
||||||
1,
|
|
||||||
))
|
|
||||||
.set_format(config.format)
|
|
||||||
.set_triangle(true)
|
|
||||||
.set_vertex_buffer(&vertex_buffers.starfield)
|
|
||||||
.set_bind_group_layouts(bind_group_layouts)
|
|
||||||
.build();
|
|
||||||
|
|
||||||
let ui_pipeline = PipelineBuilder::new("ui", &device)
|
|
||||||
.set_shader(&preprocess_shader(
|
|
||||||
&include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/shaders/", "ui.wgsl")),
|
|
||||||
&global_uniform,
|
|
||||||
1,
|
|
||||||
))
|
|
||||||
.set_format(config.format)
|
|
||||||
.set_triangle(true)
|
|
||||||
.set_vertex_buffer(&vertex_buffers.ui)
|
|
||||||
.set_bind_group_layouts(bind_group_layouts)
|
|
||||||
.build();
|
|
||||||
|
|
||||||
let particle_pipeline = PipelineBuilder::new("particle", &device)
|
|
||||||
.set_shader(&preprocess_shader(
|
|
||||||
&include_str!(concat!(
|
|
||||||
env!("CARGO_MANIFEST_DIR"),
|
|
||||||
"/shaders/",
|
|
||||||
"particle.wgsl"
|
|
||||||
)),
|
|
||||||
&global_uniform,
|
|
||||||
1,
|
|
||||||
))
|
|
||||||
.set_format(config.format)
|
|
||||||
.set_triangle(true)
|
|
||||||
.set_vertex_buffer(&vertex_buffers.particle)
|
|
||||||
.set_bind_group_layouts(bind_group_layouts)
|
|
||||||
.build();
|
|
||||||
|
|
||||||
let radialbar_pipeline = PipelineBuilder::new("radialbar", &device)
|
|
||||||
.set_shader(&preprocess_shader(
|
|
||||||
&include_str!(concat!(
|
|
||||||
env!("CARGO_MANIFEST_DIR"),
|
|
||||||
"/shaders/",
|
|
||||||
"radialbar.wgsl"
|
|
||||||
)),
|
|
||||||
&global_uniform,
|
|
||||||
1,
|
|
||||||
))
|
|
||||||
.set_format(config.format)
|
|
||||||
.set_triangle(true)
|
|
||||||
.set_vertex_buffer(&vertex_buffers.radialbar)
|
|
||||||
.set_bind_group_layouts(bind_group_layouts)
|
|
||||||
.build();
|
|
||||||
|
|
||||||
let mut starfield = Starfield::new();
|
|
||||||
starfield.regenerate();
|
|
||||||
|
|
||||||
return Ok(Self {
|
|
||||||
device,
|
|
||||||
config,
|
|
||||||
surface,
|
|
||||||
queue,
|
|
||||||
|
|
||||||
window,
|
|
||||||
window_size,
|
|
||||||
window_aspect,
|
|
||||||
|
|
||||||
object_pipeline,
|
|
||||||
starfield_pipeline,
|
|
||||||
ui_pipeline,
|
|
||||||
particle_pipeline,
|
|
||||||
radialbar_pipeline,
|
|
||||||
|
|
||||||
starfield,
|
|
||||||
texture_array,
|
|
||||||
global_uniform,
|
|
||||||
vertex_buffers,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Get the window this GPUState is attached to
|
|
||||||
pub fn window(&self) -> &Window {
|
|
||||||
&self.window
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Update window size.
|
/// Update window size.
|
||||||
/// This should be called whenever our window is resized.
|
/// This should be called whenever our window is resized.
|
||||||
pub fn resize(&mut self) {
|
pub fn resize(&mut self) {
|
||||||
let new_size = self.window.inner_size();
|
let new_size = self.state.window.inner_size();
|
||||||
if new_size.width > 0 && new_size.height > 0 {
|
if new_size.width > 0 && new_size.height > 0 {
|
||||||
self.window_size = new_size;
|
self.state.window_size = new_size;
|
||||||
self.window_aspect = new_size.width as f32 / new_size.height as f32;
|
self.state.window_aspect = new_size.width as f32 / new_size.height as f32;
|
||||||
self.config.width = new_size.width;
|
self.config.width = new_size.width;
|
||||||
self.config.height = new_size.height;
|
self.config.height = new_size.height;
|
||||||
self.surface.configure(&self.device, &self.config);
|
self.surface.configure(&self.device, &self.config);
|
||||||
|
@ -350,208 +52,32 @@ impl GPUState {
|
||||||
self.update_starfield_buffer()
|
self.update_starfield_buffer()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Entrypoint for all vertex buffer builders
|
|
||||||
pub(super) fn update_all_buffers(&mut self, state: &RenderState) {
|
|
||||||
// Game coordinates (relative to camera) of ne and sw corners of screen.
|
|
||||||
// Used to skip off-screen sprites.
|
|
||||||
let clip_ne = Point2::from((-self.window_aspect, 1.0)) * state.camera_zoom;
|
|
||||||
let clip_sw = Point2::from((self.window_aspect, -1.0)) * state.camera_zoom;
|
|
||||||
|
|
||||||
// TODO: sorting. We don't need to sort ships, but we do need to sort system objects by z-level
|
|
||||||
// (which we don't yet draw)
|
|
||||||
// that should probably be done in iter_system().
|
|
||||||
|
|
||||||
// Order matters, it determines what is drawn on top.
|
|
||||||
// The order inside ships and projectiles doesn't matter,
|
|
||||||
// but ships should always be under projectiles.
|
|
||||||
self.sysim_push_system(state, (clip_ne, clip_sw));
|
|
||||||
self.sysim_push_ship(state, (clip_ne, clip_sw));
|
|
||||||
self.sysim_push_projectile(state, (clip_ne, clip_sw));
|
|
||||||
|
|
||||||
self.hud_add_radar(state);
|
|
||||||
self.hud_add_status(state);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Make a StarfieldInstance for each star that needs to be drawn.
|
/// Make a StarfieldInstance for each star that needs to be drawn.
|
||||||
/// Will panic if STARFIELD_INSTANCE_LIMIT is exceeded.
|
/// Will panic if STARFIELD_INSTANCE_LIMIT is exceeded.
|
||||||
///
|
///
|
||||||
/// Starfield data rarely changes, so this is called only when it's needed.
|
/// Starfield data rarely changes, so this is called only when it's needed.
|
||||||
pub fn update_starfield_buffer(&mut self) {
|
pub fn update_starfield_buffer(&mut self) {
|
||||||
self.queue.write_buffer(
|
self.state.queue.write_buffer(
|
||||||
&self.vertex_buffers.starfield.instances,
|
&self.state.vertex_buffers.starfield.instances,
|
||||||
0,
|
0,
|
||||||
bytemuck::cast_slice(&self.starfield.make_instances(self.window_aspect)),
|
bytemuck::cast_slice(&self.starfield.make_instances(self.state.window_aspect)),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Initialize the rendering engine
|
/// Initialize the rendering engine
|
||||||
pub fn init(&mut self) {
|
pub fn init(&mut self) {
|
||||||
// Update global values
|
// Update global values
|
||||||
self.queue.write_buffer(
|
self.state.queue.write_buffer(
|
||||||
&self.global_uniform.atlas_buffer,
|
&self.state.global_uniform.atlas_buffer,
|
||||||
0,
|
0,
|
||||||
bytemuck::cast_slice(&[self.texture_array.image_locations]),
|
bytemuck::cast_slice(&[self.texture_array.image_locations]),
|
||||||
);
|
);
|
||||||
self.queue.write_buffer(
|
self.state.queue.write_buffer(
|
||||||
&self.global_uniform.sprite_buffer,
|
&self.state.global_uniform.sprite_buffer,
|
||||||
0,
|
0,
|
||||||
bytemuck::cast_slice(&[self.texture_array.sprite_data]),
|
bytemuck::cast_slice(&[self.texture_array.sprite_data]),
|
||||||
);
|
);
|
||||||
|
|
||||||
self.update_starfield_buffer();
|
self.update_starfield_buffer();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Main render function. Draws sprites on a window.
|
|
||||||
pub fn render(&mut self, state: RenderState) -> Result<(), wgpu::SurfaceError> {
|
|
||||||
let output = self.surface.get_current_texture()?;
|
|
||||||
let view = output.texture.create_view(&Default::default());
|
|
||||||
|
|
||||||
let mut encoder = self
|
|
||||||
.device
|
|
||||||
.create_command_encoder(&wgpu::CommandEncoderDescriptor {
|
|
||||||
label: Some("render encoder"),
|
|
||||||
});
|
|
||||||
|
|
||||||
let mut render_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
|
|
||||||
label: Some("render pass"),
|
|
||||||
|
|
||||||
color_attachments: &[Some(wgpu::RenderPassColorAttachment {
|
|
||||||
view: &view,
|
|
||||||
resolve_target: None,
|
|
||||||
ops: wgpu::Operations {
|
|
||||||
load: wgpu::LoadOp::Clear(wgpu::Color {
|
|
||||||
r: 0.0,
|
|
||||||
g: 0.0,
|
|
||||||
b: 0.0,
|
|
||||||
a: 1.0,
|
|
||||||
}),
|
|
||||||
store: wgpu::StoreOp::Store,
|
|
||||||
},
|
|
||||||
})],
|
|
||||||
depth_stencil_attachment: None,
|
|
||||||
occlusion_query_set: None,
|
|
||||||
timestamp_writes: None,
|
|
||||||
});
|
|
||||||
|
|
||||||
self.vertex_buffers.object_counter = 0;
|
|
||||||
self.vertex_buffers.ui_counter = 0;
|
|
||||||
self.vertex_buffers.radialbar_counter = 0;
|
|
||||||
// Don't reset particle counter, it's special
|
|
||||||
|
|
||||||
let s = state.content.get_starfield_handle();
|
|
||||||
|
|
||||||
// Update global values
|
|
||||||
self.queue.write_buffer(
|
|
||||||
&self.global_uniform.data_buffer,
|
|
||||||
0,
|
|
||||||
bytemuck::cast_slice(&[GlobalDataContent {
|
|
||||||
camera_position: state.camera_pos.into(),
|
|
||||||
camera_zoom: [state.camera_zoom, 0.0],
|
|
||||||
camera_zoom_limits: [galactica_constants::ZOOM_MIN, galactica_constants::ZOOM_MAX],
|
|
||||||
window_size: [
|
|
||||||
self.window_size.width as f32,
|
|
||||||
self.window_size.height as f32,
|
|
||||||
],
|
|
||||||
window_scale: [self.window.scale_factor() as f32, 0.0],
|
|
||||||
window_aspect: [self.window_aspect, 0.0],
|
|
||||||
starfield_sprite: [s.get_index(), 0],
|
|
||||||
starfield_tile_size: [galactica_constants::STARFIELD_SIZE as f32, 0.0],
|
|
||||||
starfield_size_limits: [
|
|
||||||
galactica_constants::STARFIELD_SIZE_MIN,
|
|
||||||
galactica_constants::STARFIELD_SIZE_MAX,
|
|
||||||
],
|
|
||||||
current_time: [state.current_time, 0.0],
|
|
||||||
}]),
|
|
||||||
);
|
|
||||||
|
|
||||||
// Write all new particles to GPU buffer
|
|
||||||
state.particles.shuffle(&mut rand::thread_rng());
|
|
||||||
for i in state.particles.iter() {
|
|
||||||
self.queue.write_buffer(
|
|
||||||
&self.vertex_buffers.particle.instances,
|
|
||||||
ParticleInstance::SIZE * self.vertex_buffers.particle_counter,
|
|
||||||
bytemuck::cast_slice(&[ParticleInstance {
|
|
||||||
position: [i.pos.x, i.pos.y],
|
|
||||||
velocity: i.velocity.into(),
|
|
||||||
angle: i.angle.0,
|
|
||||||
angvel: i.angvel.0,
|
|
||||||
size: i.size,
|
|
||||||
sprite_index: i.sprite.get_index(),
|
|
||||||
created: state.current_time,
|
|
||||||
expires: state.current_time + i.lifetime,
|
|
||||||
fade: i.fade,
|
|
||||||
}]),
|
|
||||||
);
|
|
||||||
self.vertex_buffers.particle_counter += 1;
|
|
||||||
if self.vertex_buffers.particle_counter
|
|
||||||
== galactica_constants::PARTICLE_SPRITE_INSTANCE_LIMIT
|
|
||||||
{
|
|
||||||
self.vertex_buffers.particle_counter = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
state.particles.clear();
|
|
||||||
|
|
||||||
// Create sprite instances
|
|
||||||
self.update_all_buffers(&state);
|
|
||||||
|
|
||||||
// These should match the indices in each shader,
|
|
||||||
// and should each have a corresponding bind group layout.
|
|
||||||
render_pass.set_bind_group(0, &self.texture_array.bind_group, &[]);
|
|
||||||
render_pass.set_bind_group(1, &self.global_uniform.bind_group, &[]);
|
|
||||||
|
|
||||||
// Starfield pipeline
|
|
||||||
self.vertex_buffers.starfield.set_in_pass(&mut render_pass);
|
|
||||||
render_pass.set_pipeline(&self.starfield_pipeline);
|
|
||||||
render_pass.draw_indexed(
|
|
||||||
0..SPRITE_INDICES.len() as u32,
|
|
||||||
0,
|
|
||||||
0..self.starfield.instance_count,
|
|
||||||
);
|
|
||||||
|
|
||||||
// Sprite pipeline
|
|
||||||
self.vertex_buffers.object.set_in_pass(&mut render_pass);
|
|
||||||
render_pass.set_pipeline(&self.object_pipeline);
|
|
||||||
render_pass.draw_indexed(
|
|
||||||
0..SPRITE_INDICES.len() as u32,
|
|
||||||
0,
|
|
||||||
0..self.vertex_buffers.object_counter as _,
|
|
||||||
);
|
|
||||||
|
|
||||||
// Particle pipeline
|
|
||||||
self.vertex_buffers.particle.set_in_pass(&mut render_pass);
|
|
||||||
render_pass.set_pipeline(&self.particle_pipeline);
|
|
||||||
render_pass.draw_indexed(
|
|
||||||
0..SPRITE_INDICES.len() as u32,
|
|
||||||
0,
|
|
||||||
0..galactica_constants::PARTICLE_SPRITE_INSTANCE_LIMIT as _,
|
|
||||||
);
|
|
||||||
|
|
||||||
// Ui pipeline
|
|
||||||
self.vertex_buffers.ui.set_in_pass(&mut render_pass);
|
|
||||||
render_pass.set_pipeline(&self.ui_pipeline);
|
|
||||||
render_pass.draw_indexed(
|
|
||||||
0..SPRITE_INDICES.len() as u32,
|
|
||||||
0,
|
|
||||||
0..self.vertex_buffers.ui_counter as _,
|
|
||||||
);
|
|
||||||
|
|
||||||
// Radial progress bars
|
|
||||||
// TODO: do we need to do this every time?
|
|
||||||
self.vertex_buffers.radialbar.set_in_pass(&mut render_pass);
|
|
||||||
render_pass.set_pipeline(&self.radialbar_pipeline);
|
|
||||||
render_pass.draw_indexed(
|
|
||||||
0..SPRITE_INDICES.len() as u32,
|
|
||||||
0,
|
|
||||||
0..self.vertex_buffers.radialbar_counter as _,
|
|
||||||
);
|
|
||||||
|
|
||||||
// begin_render_pass borrows encoder mutably, so we can't call finish()
|
|
||||||
// without dropping this variable.
|
|
||||||
drop(render_pass);
|
|
||||||
|
|
||||||
self.queue.submit(iter::once(encoder.finish()));
|
|
||||||
output.present();
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,275 @@
|
||||||
|
use anyhow::Result;
|
||||||
|
use galactica_content::Content;
|
||||||
|
use galactica_util::constants::{
|
||||||
|
OBJECT_SPRITE_INSTANCE_LIMIT, PARTICLE_SPRITE_INSTANCE_LIMIT, STARFIELD_SPRITE_INSTANCE_LIMIT,
|
||||||
|
UI_SPRITE_INSTANCE_LIMIT,
|
||||||
|
};
|
||||||
|
use glyphon::{FontSystem, SwashCache, TextAtlas, TextRenderer};
|
||||||
|
use std::rc::Rc;
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
datastructs::{RenderState, VertexBuffers},
|
||||||
|
globaluniform::GlobalUniform,
|
||||||
|
pipeline::PipelineBuilder,
|
||||||
|
shaderprocessor::preprocess_shader,
|
||||||
|
starfield::Starfield,
|
||||||
|
texturearray::TextureArray,
|
||||||
|
ui::UiManager,
|
||||||
|
vertexbuffer::{
|
||||||
|
consts::{SPRITE_INDICES, SPRITE_VERTICES},
|
||||||
|
types::{
|
||||||
|
ObjectInstance, ParticleInstance, RadialBarInstance, StarfieldInstance, TexturedVertex,
|
||||||
|
UiInstance,
|
||||||
|
},
|
||||||
|
VertexBuffer,
|
||||||
|
},
|
||||||
|
GPUState,
|
||||||
|
};
|
||||||
|
|
||||||
|
impl GPUState {
|
||||||
|
/// Make a new GPUState that draws on `window`
|
||||||
|
pub async fn new(window: winit::window::Window, ct: &Content) -> Result<Self> {
|
||||||
|
let window_size = window.inner_size();
|
||||||
|
let window_aspect = window_size.width as f32 / window_size.height as f32;
|
||||||
|
|
||||||
|
let instance = wgpu::Instance::new(wgpu::InstanceDescriptor {
|
||||||
|
backends: wgpu::Backends::all(),
|
||||||
|
..Default::default()
|
||||||
|
});
|
||||||
|
|
||||||
|
let surface = unsafe { instance.create_surface(&window) }.unwrap();
|
||||||
|
|
||||||
|
// Basic setup
|
||||||
|
let device;
|
||||||
|
let queue;
|
||||||
|
let config;
|
||||||
|
|
||||||
|
{
|
||||||
|
let adapter = instance
|
||||||
|
.request_adapter(&wgpu::RequestAdapterOptions {
|
||||||
|
power_preference: wgpu::PowerPreference::default(),
|
||||||
|
compatible_surface: Some(&surface),
|
||||||
|
force_fallback_adapter: false,
|
||||||
|
})
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
(device, queue) = adapter
|
||||||
|
.request_device(
|
||||||
|
&wgpu::DeviceDescriptor {
|
||||||
|
features: wgpu::Features::TEXTURE_BINDING_ARRAY | wgpu::Features::SAMPLED_TEXTURE_AND_STORAGE_BUFFER_ARRAY_NON_UNIFORM_INDEXING,
|
||||||
|
// We may need limits if we compile for wasm
|
||||||
|
limits: wgpu::Limits::default(),
|
||||||
|
label: Some("gpu device"),
|
||||||
|
},
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
// Assume sRGB
|
||||||
|
let surface_caps = surface.get_capabilities(&adapter);
|
||||||
|
let surface_format = surface_caps
|
||||||
|
.formats
|
||||||
|
.iter()
|
||||||
|
.copied()
|
||||||
|
.filter(|f| f.is_srgb())
|
||||||
|
.filter(|f| f.has_stencil_aspect())
|
||||||
|
.next()
|
||||||
|
.unwrap_or(surface_caps.formats[0]);
|
||||||
|
|
||||||
|
config = wgpu::SurfaceConfiguration {
|
||||||
|
usage: wgpu::TextureUsages::RENDER_ATTACHMENT,
|
||||||
|
format: surface_format,
|
||||||
|
width: window_size.width,
|
||||||
|
height: window_size.height,
|
||||||
|
present_mode: surface_caps.present_modes[0],
|
||||||
|
alpha_mode: surface_caps.alpha_modes[0],
|
||||||
|
view_formats: vec![],
|
||||||
|
};
|
||||||
|
|
||||||
|
surface.configure(&device, &config);
|
||||||
|
}
|
||||||
|
|
||||||
|
let vertex_buffers = VertexBuffers {
|
||||||
|
object_counter: 0,
|
||||||
|
ui_counter: 0,
|
||||||
|
particle_counter: 0,
|
||||||
|
radialbar_counter: 0,
|
||||||
|
|
||||||
|
object: Rc::new(VertexBuffer::new::<TexturedVertex, ObjectInstance>(
|
||||||
|
"object",
|
||||||
|
&device,
|
||||||
|
Some(SPRITE_VERTICES),
|
||||||
|
Some(SPRITE_INDICES),
|
||||||
|
OBJECT_SPRITE_INSTANCE_LIMIT,
|
||||||
|
)),
|
||||||
|
|
||||||
|
starfield: Rc::new(VertexBuffer::new::<TexturedVertex, StarfieldInstance>(
|
||||||
|
"starfield",
|
||||||
|
&device,
|
||||||
|
Some(SPRITE_VERTICES),
|
||||||
|
Some(SPRITE_INDICES),
|
||||||
|
STARFIELD_SPRITE_INSTANCE_LIMIT,
|
||||||
|
)),
|
||||||
|
|
||||||
|
ui: Rc::new(VertexBuffer::new::<TexturedVertex, UiInstance>(
|
||||||
|
"ui",
|
||||||
|
&device,
|
||||||
|
Some(SPRITE_VERTICES),
|
||||||
|
Some(SPRITE_INDICES),
|
||||||
|
UI_SPRITE_INSTANCE_LIMIT,
|
||||||
|
)),
|
||||||
|
|
||||||
|
particle: Rc::new(VertexBuffer::new::<TexturedVertex, ParticleInstance>(
|
||||||
|
"particle",
|
||||||
|
&device,
|
||||||
|
Some(SPRITE_VERTICES),
|
||||||
|
Some(SPRITE_INDICES),
|
||||||
|
PARTICLE_SPRITE_INSTANCE_LIMIT,
|
||||||
|
)),
|
||||||
|
|
||||||
|
radialbar: Rc::new(VertexBuffer::new::<TexturedVertex, RadialBarInstance>(
|
||||||
|
"radial bar",
|
||||||
|
&device,
|
||||||
|
Some(SPRITE_VERTICES),
|
||||||
|
Some(SPRITE_INDICES),
|
||||||
|
10,
|
||||||
|
)),
|
||||||
|
};
|
||||||
|
|
||||||
|
// Load uniforms
|
||||||
|
let global_uniform = GlobalUniform::new(&device);
|
||||||
|
let texture_array = TextureArray::new(&device, &queue, ct)?;
|
||||||
|
|
||||||
|
// Make sure these match the indices in each shader
|
||||||
|
let bind_group_layouts = &[
|
||||||
|
&texture_array.bind_group_layout,
|
||||||
|
&global_uniform.bind_group_layout,
|
||||||
|
];
|
||||||
|
|
||||||
|
// Text renderer
|
||||||
|
let mut text_atlas = TextAtlas::new(&device, &queue, wgpu::TextureFormat::Bgra8UnormSrgb);
|
||||||
|
let text_font_system = FontSystem::new();
|
||||||
|
|
||||||
|
let text_cache = SwashCache::new();
|
||||||
|
let text_renderer = TextRenderer::new(
|
||||||
|
&mut text_atlas,
|
||||||
|
&device,
|
||||||
|
wgpu::MultisampleState::default(),
|
||||||
|
None,
|
||||||
|
);
|
||||||
|
|
||||||
|
// Create render pipelines
|
||||||
|
let object_pipeline = PipelineBuilder::new("object", &device)
|
||||||
|
.set_shader(&preprocess_shader(
|
||||||
|
&include_str!(concat!(
|
||||||
|
env!("CARGO_MANIFEST_DIR"),
|
||||||
|
"/shaders/",
|
||||||
|
"object.wgsl"
|
||||||
|
)),
|
||||||
|
&global_uniform,
|
||||||
|
1,
|
||||||
|
))
|
||||||
|
.set_format(config.format)
|
||||||
|
.set_triangle(true)
|
||||||
|
.set_vertex_buffer(&vertex_buffers.object)
|
||||||
|
.set_bind_group_layouts(bind_group_layouts)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
let starfield_pipeline = PipelineBuilder::new("starfield", &device)
|
||||||
|
.set_shader(&preprocess_shader(
|
||||||
|
&include_str!(concat!(
|
||||||
|
env!("CARGO_MANIFEST_DIR"),
|
||||||
|
"/shaders/",
|
||||||
|
"starfield.wgsl"
|
||||||
|
)),
|
||||||
|
&global_uniform,
|
||||||
|
1,
|
||||||
|
))
|
||||||
|
.set_format(config.format)
|
||||||
|
.set_triangle(true)
|
||||||
|
.set_vertex_buffer(&vertex_buffers.starfield)
|
||||||
|
.set_bind_group_layouts(bind_group_layouts)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
let ui_pipeline = PipelineBuilder::new("ui", &device)
|
||||||
|
.set_shader(&preprocess_shader(
|
||||||
|
&include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/shaders/", "ui.wgsl")),
|
||||||
|
&global_uniform,
|
||||||
|
1,
|
||||||
|
))
|
||||||
|
.set_format(config.format)
|
||||||
|
.set_triangle(true)
|
||||||
|
.set_vertex_buffer(&vertex_buffers.ui)
|
||||||
|
.set_bind_group_layouts(bind_group_layouts)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
let particle_pipeline = PipelineBuilder::new("particle", &device)
|
||||||
|
.set_shader(&preprocess_shader(
|
||||||
|
&include_str!(concat!(
|
||||||
|
env!("CARGO_MANIFEST_DIR"),
|
||||||
|
"/shaders/",
|
||||||
|
"particle.wgsl"
|
||||||
|
)),
|
||||||
|
&global_uniform,
|
||||||
|
1,
|
||||||
|
))
|
||||||
|
.set_format(config.format)
|
||||||
|
.set_triangle(true)
|
||||||
|
.set_vertex_buffer(&vertex_buffers.particle)
|
||||||
|
.set_bind_group_layouts(bind_group_layouts)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
let radialbar_pipeline = PipelineBuilder::new("radialbar", &device)
|
||||||
|
.set_shader(&preprocess_shader(
|
||||||
|
&include_str!(concat!(
|
||||||
|
env!("CARGO_MANIFEST_DIR"),
|
||||||
|
"/shaders/",
|
||||||
|
"radialbar.wgsl"
|
||||||
|
)),
|
||||||
|
&global_uniform,
|
||||||
|
1,
|
||||||
|
))
|
||||||
|
.set_format(config.format)
|
||||||
|
.set_triangle(true)
|
||||||
|
.set_vertex_buffer(&vertex_buffers.radialbar)
|
||||||
|
.set_bind_group_layouts(bind_group_layouts)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
let mut starfield = Starfield::new();
|
||||||
|
starfield.regenerate();
|
||||||
|
|
||||||
|
let mut state = RenderState {
|
||||||
|
queue,
|
||||||
|
|
||||||
|
window,
|
||||||
|
window_size,
|
||||||
|
window_aspect,
|
||||||
|
global_uniform,
|
||||||
|
|
||||||
|
vertex_buffers,
|
||||||
|
|
||||||
|
text_atlas,
|
||||||
|
text_cache,
|
||||||
|
text_font_system,
|
||||||
|
text_renderer,
|
||||||
|
};
|
||||||
|
|
||||||
|
return Ok(Self {
|
||||||
|
ui: UiManager::new(&mut state),
|
||||||
|
device,
|
||||||
|
config,
|
||||||
|
surface,
|
||||||
|
starfield,
|
||||||
|
texture_array,
|
||||||
|
object_pipeline,
|
||||||
|
starfield_pipeline,
|
||||||
|
ui_pipeline,
|
||||||
|
particle_pipeline,
|
||||||
|
radialbar_pipeline,
|
||||||
|
|
||||||
|
state,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,219 @@
|
||||||
|
use anyhow::Result;
|
||||||
|
use bytemuck;
|
||||||
|
use cgmath::Point2;
|
||||||
|
use galactica_util::constants::{
|
||||||
|
PARTICLE_SPRITE_INSTANCE_LIMIT, STARFIELD_SIZE, STARFIELD_SIZE_MAX, STARFIELD_SIZE_MIN,
|
||||||
|
ZOOM_MAX, ZOOM_MIN,
|
||||||
|
};
|
||||||
|
use glyphon::Resolution;
|
||||||
|
use rand::seq::SliceRandom;
|
||||||
|
use std::iter;
|
||||||
|
use wgpu;
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
globaluniform::GlobalDataContent,
|
||||||
|
vertexbuffer::{consts::SPRITE_INDICES, types::ParticleInstance, BufferObject},
|
||||||
|
RenderInput,
|
||||||
|
};
|
||||||
|
|
||||||
|
impl super::GPUState {
|
||||||
|
/// Main render function. Draws sprites on a window.
|
||||||
|
pub fn render(&mut self, input: RenderInput) -> Result<(), wgpu::SurfaceError> {
|
||||||
|
// Set up text renderer
|
||||||
|
|
||||||
|
let output = self.surface.get_current_texture()?;
|
||||||
|
let view = output.texture.create_view(&Default::default());
|
||||||
|
|
||||||
|
let mut encoder = self
|
||||||
|
.device
|
||||||
|
.create_command_encoder(&wgpu::CommandEncoderDescriptor {
|
||||||
|
label: Some("render encoder"),
|
||||||
|
});
|
||||||
|
|
||||||
|
let mut render_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
|
||||||
|
label: Some("render pass"),
|
||||||
|
|
||||||
|
color_attachments: &[Some(wgpu::RenderPassColorAttachment {
|
||||||
|
view: &view,
|
||||||
|
resolve_target: None,
|
||||||
|
ops: wgpu::Operations {
|
||||||
|
load: wgpu::LoadOp::Clear(wgpu::Color {
|
||||||
|
r: 0.0,
|
||||||
|
g: 0.0,
|
||||||
|
b: 0.0,
|
||||||
|
a: 1.0,
|
||||||
|
}),
|
||||||
|
store: wgpu::StoreOp::Store,
|
||||||
|
},
|
||||||
|
})],
|
||||||
|
depth_stencil_attachment: None,
|
||||||
|
occlusion_query_set: None,
|
||||||
|
timestamp_writes: None,
|
||||||
|
});
|
||||||
|
|
||||||
|
self.state.vertex_buffers.object_counter = 0;
|
||||||
|
self.state.vertex_buffers.ui_counter = 0;
|
||||||
|
self.state.vertex_buffers.radialbar_counter = 0;
|
||||||
|
// Don't reset particle counter, it's special
|
||||||
|
|
||||||
|
let s = input.content.get_starfield_handle();
|
||||||
|
|
||||||
|
// Update global values
|
||||||
|
self.state.queue.write_buffer(
|
||||||
|
&self.state.global_uniform.data_buffer,
|
||||||
|
0,
|
||||||
|
bytemuck::cast_slice(&[GlobalDataContent {
|
||||||
|
camera_position: input.camera_pos.into(),
|
||||||
|
camera_zoom: [input.camera_zoom, 0.0],
|
||||||
|
camera_zoom_limits: [ZOOM_MIN, ZOOM_MAX],
|
||||||
|
window_size: [
|
||||||
|
self.state.window_size.width as f32,
|
||||||
|
self.state.window_size.height as f32,
|
||||||
|
],
|
||||||
|
window_scale: [self.state.window.scale_factor() as f32, 0.0],
|
||||||
|
window_aspect: [self.state.window_aspect, 0.0],
|
||||||
|
starfield_sprite: [s.get_index(), 0],
|
||||||
|
starfield_tile_size: [STARFIELD_SIZE as f32, 0.0],
|
||||||
|
starfield_size_limits: [STARFIELD_SIZE_MIN, STARFIELD_SIZE_MAX],
|
||||||
|
current_time: [input.current_time, 0.0],
|
||||||
|
}]),
|
||||||
|
);
|
||||||
|
|
||||||
|
// Write all new particles to GPU buffer
|
||||||
|
input.particles.shuffle(&mut rand::thread_rng());
|
||||||
|
for i in input.particles.iter() {
|
||||||
|
self.state.queue.write_buffer(
|
||||||
|
&self.state.vertex_buffers.particle.instances,
|
||||||
|
ParticleInstance::SIZE * self.state.vertex_buffers.particle_counter,
|
||||||
|
bytemuck::cast_slice(&[ParticleInstance {
|
||||||
|
position: [i.pos.x, i.pos.y],
|
||||||
|
velocity: i.velocity.into(),
|
||||||
|
angle: i.angle.0,
|
||||||
|
angvel: i.angvel.0,
|
||||||
|
size: i.size,
|
||||||
|
sprite_index: i.sprite.get_index(),
|
||||||
|
created: input.current_time,
|
||||||
|
expires: input.current_time + i.lifetime,
|
||||||
|
fade: i.fade,
|
||||||
|
}]),
|
||||||
|
);
|
||||||
|
self.state.vertex_buffers.particle_counter += 1;
|
||||||
|
if self.state.vertex_buffers.particle_counter == PARTICLE_SPRITE_INSTANCE_LIMIT {
|
||||||
|
self.state.vertex_buffers.particle_counter = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
input.particles.clear();
|
||||||
|
|
||||||
|
// Create sprite instances
|
||||||
|
|
||||||
|
// Game coordinates (relative to camera) of ne and sw corners of screen.
|
||||||
|
// Used to skip off-screen sprites.
|
||||||
|
let clip_ne = Point2::from((-self.state.window_aspect, 1.0)) * input.camera_zoom;
|
||||||
|
let clip_sw = Point2::from((self.state.window_aspect, -1.0)) * input.camera_zoom;
|
||||||
|
|
||||||
|
// TODO: sorting. We don't need to sort ships, but we do need to sort system objects by z-level
|
||||||
|
// (which we don't yet draw)
|
||||||
|
// that should probably be done in iter_system().
|
||||||
|
|
||||||
|
// Order matters, it determines what is drawn on top.
|
||||||
|
// The order inside ships and projectiles doesn't matter,
|
||||||
|
// but ships should always be under projectiles.
|
||||||
|
self.sysim_push_system(&input, (clip_ne, clip_sw));
|
||||||
|
self.sysim_push_ship(&input, (clip_ne, clip_sw));
|
||||||
|
self.sysim_push_projectile(&input, (clip_ne, clip_sw));
|
||||||
|
self.ui.draw(&input, &mut self.state);
|
||||||
|
|
||||||
|
// These should match the indices in each shader,
|
||||||
|
// and should each have a corresponding bind group layout.
|
||||||
|
render_pass.set_bind_group(0, &self.texture_array.bind_group, &[]);
|
||||||
|
render_pass.set_bind_group(1, &self.state.global_uniform.bind_group, &[]);
|
||||||
|
|
||||||
|
// Starfield pipeline
|
||||||
|
self.state
|
||||||
|
.vertex_buffers
|
||||||
|
.starfield
|
||||||
|
.set_in_pass(&mut render_pass);
|
||||||
|
render_pass.set_pipeline(&self.starfield_pipeline);
|
||||||
|
render_pass.draw_indexed(
|
||||||
|
0..SPRITE_INDICES.len() as u32,
|
||||||
|
0,
|
||||||
|
0..self.starfield.instance_count,
|
||||||
|
);
|
||||||
|
|
||||||
|
// Sprite pipeline
|
||||||
|
self.state
|
||||||
|
.vertex_buffers
|
||||||
|
.object
|
||||||
|
.set_in_pass(&mut render_pass);
|
||||||
|
render_pass.set_pipeline(&self.object_pipeline);
|
||||||
|
render_pass.draw_indexed(
|
||||||
|
0..SPRITE_INDICES.len() as u32,
|
||||||
|
0,
|
||||||
|
0..self.state.vertex_buffers.object_counter as _,
|
||||||
|
);
|
||||||
|
|
||||||
|
// Particle pipeline
|
||||||
|
self.state
|
||||||
|
.vertex_buffers
|
||||||
|
.particle
|
||||||
|
.set_in_pass(&mut render_pass);
|
||||||
|
render_pass.set_pipeline(&self.particle_pipeline);
|
||||||
|
render_pass.draw_indexed(
|
||||||
|
0..SPRITE_INDICES.len() as u32,
|
||||||
|
0,
|
||||||
|
0..PARTICLE_SPRITE_INSTANCE_LIMIT as _,
|
||||||
|
);
|
||||||
|
|
||||||
|
// Ui pipeline
|
||||||
|
self.state.vertex_buffers.ui.set_in_pass(&mut render_pass);
|
||||||
|
render_pass.set_pipeline(&self.ui_pipeline);
|
||||||
|
render_pass.draw_indexed(
|
||||||
|
0..SPRITE_INDICES.len() as u32,
|
||||||
|
0,
|
||||||
|
0..self.state.vertex_buffers.ui_counter as _,
|
||||||
|
);
|
||||||
|
|
||||||
|
// Radial progress bars
|
||||||
|
// TODO: do we need to do this every time?
|
||||||
|
self.state
|
||||||
|
.vertex_buffers
|
||||||
|
.radialbar
|
||||||
|
.set_in_pass(&mut render_pass);
|
||||||
|
render_pass.set_pipeline(&self.radialbar_pipeline);
|
||||||
|
render_pass.draw_indexed(
|
||||||
|
0..SPRITE_INDICES.len() as u32,
|
||||||
|
0,
|
||||||
|
0..self.state.vertex_buffers.radialbar_counter as _,
|
||||||
|
);
|
||||||
|
|
||||||
|
self.state
|
||||||
|
.text_renderer
|
||||||
|
.prepare(
|
||||||
|
&self.device,
|
||||||
|
&self.state.queue,
|
||||||
|
&mut self.state.text_font_system,
|
||||||
|
&mut self.state.text_atlas,
|
||||||
|
Resolution {
|
||||||
|
width: self.state.window_size.width,
|
||||||
|
height: self.state.window_size.height,
|
||||||
|
},
|
||||||
|
self.ui.get_textareas(),
|
||||||
|
&mut self.state.text_cache,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
self.state
|
||||||
|
.text_renderer
|
||||||
|
.render(&self.state.text_atlas, &mut render_pass)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
// begin_render_pass borrows encoder mutably, so we can't call finish()
|
||||||
|
// without dropping this variable.
|
||||||
|
drop(render_pass);
|
||||||
|
|
||||||
|
self.state.queue.submit(iter::once(encoder.finish()));
|
||||||
|
output.present();
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
|
@ -3,17 +3,18 @@
|
||||||
use bytemuck;
|
use bytemuck;
|
||||||
use cgmath::{EuclideanSpace, InnerSpace, Point2, Vector2};
|
use cgmath::{EuclideanSpace, InnerSpace, Point2, Vector2};
|
||||||
use galactica_systemsim::util;
|
use galactica_systemsim::util;
|
||||||
|
use galactica_util::constants::OBJECT_SPRITE_INSTANCE_LIMIT;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
globaluniform::ObjectData,
|
globaluniform::ObjectData,
|
||||||
vertexbuffer::{types::ObjectInstance, BufferObject},
|
vertexbuffer::{types::ObjectInstance, BufferObject},
|
||||||
GPUState, RenderState,
|
GPUState, RenderInput,
|
||||||
};
|
};
|
||||||
|
|
||||||
impl GPUState {
|
impl GPUState {
|
||||||
pub(super) fn sysim_push_ship(
|
pub(super) fn sysim_push_ship(
|
||||||
&mut self,
|
&mut self,
|
||||||
state: &RenderState,
|
state: &RenderInput,
|
||||||
// NE and SW corners of screen
|
// NE and SW corners of screen
|
||||||
screen_clip: (Point2<f32>, Point2<f32>),
|
screen_clip: (Point2<f32>, Point2<f32>),
|
||||||
) {
|
) {
|
||||||
|
@ -45,10 +46,10 @@ impl GPUState {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
let idx = self.vertex_buffers.object_counter;
|
let idx = self.state.vertex_buffers.object_counter;
|
||||||
// Write this object's location data
|
// Write this object's location data
|
||||||
self.queue.write_buffer(
|
self.state.queue.write_buffer(
|
||||||
&self.global_uniform.object_buffer,
|
&self.state.global_uniform.object_buffer,
|
||||||
ObjectData::SIZE * idx as u64,
|
ObjectData::SIZE * idx as u64,
|
||||||
bytemuck::cast_slice(&[ObjectData {
|
bytemuck::cast_slice(&[ObjectData {
|
||||||
xpos: ship_pos.x,
|
xpos: ship_pos.x,
|
||||||
|
@ -63,23 +64,21 @@ impl GPUState {
|
||||||
);
|
);
|
||||||
|
|
||||||
// Enforce buffer limit
|
// Enforce buffer limit
|
||||||
if self.vertex_buffers.object_counter as u64
|
if self.state.vertex_buffers.object_counter as u64 > OBJECT_SPRITE_INSTANCE_LIMIT {
|
||||||
> galactica_constants::OBJECT_SPRITE_INSTANCE_LIMIT
|
|
||||||
{
|
|
||||||
// TODO: no panic, handle this better.
|
// TODO: no panic, handle this better.
|
||||||
panic!("Sprite limit exceeded!")
|
panic!("Sprite limit exceeded!")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Push this object's instance
|
// Push this object's instance
|
||||||
self.queue.write_buffer(
|
self.state.queue.write_buffer(
|
||||||
&self.vertex_buffers.object.instances,
|
&self.state.vertex_buffers.object.instances,
|
||||||
ObjectInstance::SIZE * self.vertex_buffers.object_counter,
|
ObjectInstance::SIZE * self.state.vertex_buffers.object_counter,
|
||||||
bytemuck::cast_slice(&[ObjectInstance {
|
bytemuck::cast_slice(&[ObjectInstance {
|
||||||
sprite_index: ship_cnt.sprite.get_index(),
|
sprite_index: ship_cnt.sprite.get_index(),
|
||||||
object_index: idx as u32,
|
object_index: idx as u32,
|
||||||
}]),
|
}]),
|
||||||
);
|
);
|
||||||
self.vertex_buffers.object_counter += 1;
|
self.state.vertex_buffers.object_counter += 1;
|
||||||
|
|
||||||
// This will be None if this ship is dead.
|
// This will be None if this ship is dead.
|
||||||
// (physics object stays around to complete the death animation)
|
// (physics object stays around to complete the death animation)
|
||||||
|
@ -92,9 +91,9 @@ impl GPUState {
|
||||||
let flare = ship.get_outfits().get_flare_sprite(state.content);
|
let flare = ship.get_outfits().get_flare_sprite(state.content);
|
||||||
if s.get_controls().thrust && flare.is_some() {
|
if s.get_controls().thrust && flare.is_some() {
|
||||||
for engine_point in &ship_cnt.engines {
|
for engine_point in &ship_cnt.engines {
|
||||||
self.queue.write_buffer(
|
self.state.queue.write_buffer(
|
||||||
&self.global_uniform.object_buffer,
|
&self.state.global_uniform.object_buffer,
|
||||||
ObjectData::SIZE * self.vertex_buffers.object_counter as u64,
|
ObjectData::SIZE * self.state.vertex_buffers.object_counter as u64,
|
||||||
bytemuck::cast_slice(&[ObjectData {
|
bytemuck::cast_slice(&[ObjectData {
|
||||||
xpos: engine_point.pos.x,
|
xpos: engine_point.pos.x,
|
||||||
ypos: engine_point.pos.y - engine_point.size / 2.0,
|
ypos: engine_point.pos.y - engine_point.size / 2.0,
|
||||||
|
@ -108,22 +107,22 @@ impl GPUState {
|
||||||
);
|
);
|
||||||
|
|
||||||
// Enforce buffer limit
|
// Enforce buffer limit
|
||||||
if self.vertex_buffers.object_counter as u64
|
if self.state.vertex_buffers.object_counter as u64
|
||||||
> galactica_constants::OBJECT_SPRITE_INSTANCE_LIMIT
|
> OBJECT_SPRITE_INSTANCE_LIMIT
|
||||||
{
|
{
|
||||||
// TODO: no panic, handle this better.
|
// TODO: no panic, handle this better.
|
||||||
panic!("Sprite limit exceeded!")
|
panic!("Sprite limit exceeded!")
|
||||||
}
|
}
|
||||||
|
|
||||||
self.queue.write_buffer(
|
self.state.queue.write_buffer(
|
||||||
&self.vertex_buffers.object.instances,
|
&self.state.vertex_buffers.object.instances,
|
||||||
ObjectInstance::SIZE * self.vertex_buffers.object_counter,
|
ObjectInstance::SIZE * self.state.vertex_buffers.object_counter,
|
||||||
bytemuck::cast_slice(&[ObjectInstance {
|
bytemuck::cast_slice(&[ObjectInstance {
|
||||||
sprite_index: flare.unwrap().get_index(),
|
sprite_index: flare.unwrap().get_index(),
|
||||||
object_index: self.vertex_buffers.object_counter as u32,
|
object_index: self.state.vertex_buffers.object_counter as u32,
|
||||||
}]),
|
}]),
|
||||||
);
|
);
|
||||||
self.vertex_buffers.object_counter += 1;
|
self.state.vertex_buffers.object_counter += 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -131,7 +130,7 @@ impl GPUState {
|
||||||
|
|
||||||
pub(super) fn sysim_push_projectile(
|
pub(super) fn sysim_push_projectile(
|
||||||
&mut self,
|
&mut self,
|
||||||
state: &RenderState,
|
state: &RenderInput,
|
||||||
// NE and SW corners of screen
|
// NE and SW corners of screen
|
||||||
screen_clip: (Point2<f32>, Point2<f32>),
|
screen_clip: (Point2<f32>, Point2<f32>),
|
||||||
) {
|
) {
|
||||||
|
@ -163,10 +162,10 @@ impl GPUState {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
let idx = self.vertex_buffers.object_counter;
|
let idx = self.state.vertex_buffers.object_counter;
|
||||||
// Write this object's location data
|
// Write this object's location data
|
||||||
self.queue.write_buffer(
|
self.state.queue.write_buffer(
|
||||||
&self.global_uniform.object_buffer,
|
&self.state.global_uniform.object_buffer,
|
||||||
ObjectData::SIZE * idx as u64,
|
ObjectData::SIZE * idx as u64,
|
||||||
bytemuck::cast_slice(&[ObjectData {
|
bytemuck::cast_slice(&[ObjectData {
|
||||||
xpos: proj_pos.x,
|
xpos: proj_pos.x,
|
||||||
|
@ -181,29 +180,27 @@ impl GPUState {
|
||||||
);
|
);
|
||||||
|
|
||||||
// Enforce buffer limit
|
// Enforce buffer limit
|
||||||
if self.vertex_buffers.object_counter as u64
|
if self.state.vertex_buffers.object_counter as u64 > OBJECT_SPRITE_INSTANCE_LIMIT {
|
||||||
> galactica_constants::OBJECT_SPRITE_INSTANCE_LIMIT
|
|
||||||
{
|
|
||||||
// TODO: no panic, handle this better.
|
// TODO: no panic, handle this better.
|
||||||
panic!("Sprite limit exceeded!")
|
panic!("Sprite limit exceeded!")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Push this object's instance
|
// Push this object's instance
|
||||||
self.queue.write_buffer(
|
self.state.queue.write_buffer(
|
||||||
&self.vertex_buffers.object.instances,
|
&self.state.vertex_buffers.object.instances,
|
||||||
ObjectInstance::SIZE * self.vertex_buffers.object_counter,
|
ObjectInstance::SIZE * self.state.vertex_buffers.object_counter,
|
||||||
bytemuck::cast_slice(&[ObjectInstance {
|
bytemuck::cast_slice(&[ObjectInstance {
|
||||||
sprite_index: proj_cnt.sprite.get_index(),
|
sprite_index: proj_cnt.sprite.get_index(),
|
||||||
object_index: idx as u32,
|
object_index: idx as u32,
|
||||||
}]),
|
}]),
|
||||||
);
|
);
|
||||||
self.vertex_buffers.object_counter += 1;
|
self.state.vertex_buffers.object_counter += 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) fn sysim_push_system(
|
pub(super) fn sysim_push_system(
|
||||||
&mut self,
|
&mut self,
|
||||||
state: &RenderState,
|
state: &RenderInput,
|
||||||
// NE and SW corners of screen
|
// NE and SW corners of screen
|
||||||
screen_clip: (Point2<f32>, Point2<f32>),
|
screen_clip: (Point2<f32>, Point2<f32>),
|
||||||
) {
|
) {
|
||||||
|
@ -233,10 +230,10 @@ impl GPUState {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
let idx = self.vertex_buffers.object_counter;
|
let idx = self.state.vertex_buffers.object_counter;
|
||||||
// Write this object's location data
|
// Write this object's location data
|
||||||
self.queue.write_buffer(
|
self.state.queue.write_buffer(
|
||||||
&self.global_uniform.object_buffer,
|
&self.state.global_uniform.object_buffer,
|
||||||
ObjectData::SIZE * idx as u64,
|
ObjectData::SIZE * idx as u64,
|
||||||
bytemuck::cast_slice(&[ObjectData {
|
bytemuck::cast_slice(&[ObjectData {
|
||||||
xpos: o.pos.x,
|
xpos: o.pos.x,
|
||||||
|
@ -251,23 +248,21 @@ impl GPUState {
|
||||||
);
|
);
|
||||||
|
|
||||||
// Enforce buffer limit
|
// Enforce buffer limit
|
||||||
if self.vertex_buffers.object_counter as u64
|
if self.state.vertex_buffers.object_counter as u64 > OBJECT_SPRITE_INSTANCE_LIMIT {
|
||||||
> galactica_constants::OBJECT_SPRITE_INSTANCE_LIMIT
|
|
||||||
{
|
|
||||||
// TODO: no panic, handle this better.
|
// TODO: no panic, handle this better.
|
||||||
panic!("Sprite limit exceeded!")
|
panic!("Sprite limit exceeded!")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Push this object's instance
|
// Push this object's instance
|
||||||
self.queue.write_buffer(
|
self.state.queue.write_buffer(
|
||||||
&self.vertex_buffers.object.instances,
|
&self.state.vertex_buffers.object.instances,
|
||||||
ObjectInstance::SIZE * self.vertex_buffers.object_counter,
|
ObjectInstance::SIZE * self.state.vertex_buffers.object_counter,
|
||||||
bytemuck::cast_slice(&[ObjectInstance {
|
bytemuck::cast_slice(&[ObjectInstance {
|
||||||
sprite_index: o.sprite.get_index(),
|
sprite_index: o.sprite.get_index(),
|
||||||
object_index: idx as u32,
|
object_index: idx as u32,
|
||||||
}]),
|
}]),
|
||||||
);
|
);
|
||||||
self.vertex_buffers.object_counter += 1;
|
self.state.vertex_buffers.object_counter += 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,17 +8,19 @@
|
||||||
//! (Excluding data structs, like [`ObjectSprite`])
|
//! (Excluding data structs, like [`ObjectSprite`])
|
||||||
|
|
||||||
mod anchoredposition;
|
mod anchoredposition;
|
||||||
|
mod datastructs;
|
||||||
mod globaluniform;
|
mod globaluniform;
|
||||||
mod gpustate;
|
mod gpustate;
|
||||||
mod pipeline;
|
mod pipeline;
|
||||||
mod renderstate;
|
mod shaderprocessor;
|
||||||
mod starfield;
|
mod starfield;
|
||||||
mod texturearray;
|
mod texturearray;
|
||||||
|
mod ui;
|
||||||
mod vertexbuffer;
|
mod vertexbuffer;
|
||||||
|
|
||||||
pub use anchoredposition::PositionAnchor;
|
pub use anchoredposition::PositionAnchor;
|
||||||
|
pub use datastructs::RenderInput;
|
||||||
pub use gpustate::GPUState;
|
pub use gpustate::GPUState;
|
||||||
pub use renderstate::RenderState;
|
|
||||||
|
|
||||||
use cgmath::Matrix4;
|
use cgmath::Matrix4;
|
||||||
|
|
||||||
|
|
|
@ -1,35 +0,0 @@
|
||||||
use cgmath::Point2;
|
|
||||||
use galactica_content::{Content, SystemHandle};
|
|
||||||
use galactica_galaxy::{Galaxy, GxShipHandle};
|
|
||||||
use galactica_systemsim::{ParticleBuilder, SystemSim};
|
|
||||||
|
|
||||||
/// Bundles parameters passed to a single call to GPUState::render
|
|
||||||
pub struct RenderState<'a> {
|
|
||||||
/// Camera position, in world units
|
|
||||||
pub camera_pos: Point2<f32>,
|
|
||||||
|
|
||||||
/// Player ship data
|
|
||||||
pub player_data: GxShipHandle,
|
|
||||||
|
|
||||||
/// The system we're currently in
|
|
||||||
pub current_system: SystemHandle,
|
|
||||||
|
|
||||||
/// Height of screen, in world units
|
|
||||||
pub camera_zoom: f32,
|
|
||||||
|
|
||||||
/// The world state to render
|
|
||||||
pub systemsim: &'a SystemSim,
|
|
||||||
|
|
||||||
// TODO: handle overflow. is it a problem?
|
|
||||||
/// The current time, in seconds
|
|
||||||
pub current_time: f32,
|
|
||||||
|
|
||||||
/// Game content
|
|
||||||
pub content: &'a Content,
|
|
||||||
|
|
||||||
/// Game data
|
|
||||||
pub data: &'a Galaxy,
|
|
||||||
|
|
||||||
/// Particles to spawn during this frame
|
|
||||||
pub particles: &'a mut Vec<ParticleBuilder>,
|
|
||||||
}
|
|
|
@ -0,0 +1,35 @@
|
||||||
|
use crate::globaluniform::GlobalUniform;
|
||||||
|
|
||||||
|
/// Basic wgsl preprocesser
|
||||||
|
pub(crate) fn preprocess_shader(
|
||||||
|
shader: &str,
|
||||||
|
global_uniform: &GlobalUniform,
|
||||||
|
global_uniform_group: u32,
|
||||||
|
) -> String {
|
||||||
|
// Insert dynamically-generated global definitions
|
||||||
|
let shader = shader.replace(
|
||||||
|
"// INCLUDE: global uniform header",
|
||||||
|
&global_uniform.shader_header(global_uniform_group),
|
||||||
|
);
|
||||||
|
|
||||||
|
// Insert common functions
|
||||||
|
let shader = shader.replace(
|
||||||
|
"// INCLUDE: animate.wgsl",
|
||||||
|
&include_str!(concat!(
|
||||||
|
env!("CARGO_MANIFEST_DIR"),
|
||||||
|
"/shaders/include/",
|
||||||
|
"animate.wgsl"
|
||||||
|
)),
|
||||||
|
);
|
||||||
|
|
||||||
|
let shader = shader.replace(
|
||||||
|
"// INCLUDE: anchor.wgsl",
|
||||||
|
&include_str!(concat!(
|
||||||
|
env!("CARGO_MANIFEST_DIR"),
|
||||||
|
"/shaders/include/",
|
||||||
|
"anchor.wgsl"
|
||||||
|
)),
|
||||||
|
);
|
||||||
|
|
||||||
|
return shader;
|
||||||
|
}
|
|
@ -1,5 +1,8 @@
|
||||||
use cgmath::{Point2, Point3, Vector2, Vector3};
|
use cgmath::{Point2, Point3, Vector2, Vector3};
|
||||||
use galactica_constants;
|
use galactica_util::constants::{
|
||||||
|
STARFIELD_COUNT, STARFIELD_SIZE, STARFIELD_SIZE_MAX, STARFIELD_SIZE_MIN,
|
||||||
|
STARFIELD_SPRITE_INSTANCE_LIMIT, STARFIELD_Z_MAX, STARFIELD_Z_MIN, ZOOM_MAX,
|
||||||
|
};
|
||||||
use rand::{self, Rng};
|
use rand::{self, Rng};
|
||||||
|
|
||||||
use crate::vertexbuffer::types::StarfieldInstance;
|
use crate::vertexbuffer::types::StarfieldInstance;
|
||||||
|
@ -34,20 +37,15 @@ impl Starfield {
|
||||||
pub fn regenerate(&mut self) {
|
pub fn regenerate(&mut self) {
|
||||||
// TODO: save seed in system, regenerate on jump
|
// TODO: save seed in system, regenerate on jump
|
||||||
let mut rng = rand::thread_rng();
|
let mut rng = rand::thread_rng();
|
||||||
let sz = galactica_constants::STARFIELD_SIZE as f32 / 2.0;
|
let sz = STARFIELD_SIZE as f32 / 2.0;
|
||||||
self.stars = (0..galactica_constants::STARFIELD_COUNT)
|
self.stars = (0..STARFIELD_COUNT)
|
||||||
.map(|_| StarfieldStar {
|
.map(|_| StarfieldStar {
|
||||||
pos: Point3 {
|
pos: Point3 {
|
||||||
x: rng.gen_range(-sz..=sz),
|
x: rng.gen_range(-sz..=sz),
|
||||||
y: rng.gen_range(-sz..=sz),
|
y: rng.gen_range(-sz..=sz),
|
||||||
z: rng.gen_range(
|
z: rng.gen_range(STARFIELD_Z_MIN..STARFIELD_Z_MAX),
|
||||||
galactica_constants::STARFIELD_Z_MIN..galactica_constants::STARFIELD_Z_MAX,
|
|
||||||
),
|
|
||||||
},
|
},
|
||||||
size: rng.gen_range(
|
size: rng.gen_range(STARFIELD_SIZE_MIN..STARFIELD_SIZE_MAX),
|
||||||
galactica_constants::STARFIELD_SIZE_MIN
|
|
||||||
..galactica_constants::STARFIELD_SIZE_MAX,
|
|
||||||
),
|
|
||||||
tint: Vector2 {
|
tint: Vector2 {
|
||||||
x: rng.gen_range(0.0..=1.0),
|
x: rng.gen_range(0.0..=1.0),
|
||||||
y: rng.gen_range(0.0..=1.0),
|
y: rng.gen_range(0.0..=1.0),
|
||||||
|
@ -57,17 +55,17 @@ impl Starfield {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn make_instances(&mut self, aspect: f32) -> Vec<StarfieldInstance> {
|
pub fn make_instances(&mut self, aspect: f32) -> Vec<StarfieldInstance> {
|
||||||
let sz = galactica_constants::STARFIELD_SIZE as f32;
|
let sz = STARFIELD_SIZE as f32;
|
||||||
|
|
||||||
// Compute window size in starfield tiles
|
// Compute window size in starfield tiles
|
||||||
let mut nw_tile: Point2<i32> = {
|
let mut nw_tile: Point2<i32> = {
|
||||||
// Game coordinates (relative to camera) of nw corner of screen.
|
// Game coordinates (relative to camera) of nw corner of screen.
|
||||||
let clip_nw = Point2::from((aspect, 1.0)) * galactica_constants::ZOOM_MAX;
|
let clip_nw = Point2::from((aspect, 1.0)) * ZOOM_MAX;
|
||||||
|
|
||||||
// Parallax correction.
|
// Parallax correction.
|
||||||
// Also, adjust v for mod to work properly
|
// Also, adjust v for mod to work properly
|
||||||
// (v is centered at 0)
|
// (v is centered at 0)
|
||||||
let v: Point2<f32> = clip_nw * galactica_constants::STARFIELD_Z_MIN;
|
let v: Point2<f32> = clip_nw * STARFIELD_Z_MIN;
|
||||||
let v_adj: Point2<f32> = (v.x + (sz / 2.0), v.y + (sz / 2.0)).into();
|
let v_adj: Point2<f32> = (v.x + (sz / 2.0), v.y + (sz / 2.0)).into();
|
||||||
|
|
||||||
#[rustfmt::skip]
|
#[rustfmt::skip]
|
||||||
|
@ -91,10 +89,8 @@ impl Starfield {
|
||||||
|
|
||||||
// Truncate tile grid to buffer size
|
// Truncate tile grid to buffer size
|
||||||
// (The window won't be full of stars if our instance limit is too small)
|
// (The window won't be full of stars if our instance limit is too small)
|
||||||
while ((nw_tile.x * 2 + 1)
|
while ((nw_tile.x * 2 + 1) * (nw_tile.y * 2 + 1) * STARFIELD_COUNT as i32)
|
||||||
* (nw_tile.y * 2 + 1)
|
> STARFIELD_SPRITE_INSTANCE_LIMIT as i32
|
||||||
* galactica_constants::STARFIELD_COUNT as i32)
|
|
||||||
> galactica_constants::STARFIELD_SPRITE_INSTANCE_LIMIT as i32
|
|
||||||
{
|
{
|
||||||
nw_tile -= Vector2::from((1, 1));
|
nw_tile -= Vector2::from((1, 1));
|
||||||
}
|
}
|
||||||
|
@ -119,7 +115,7 @@ impl Starfield {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Enforce starfield limit
|
// Enforce starfield limit
|
||||||
if instances.len() as u64 > galactica_constants::STARFIELD_SPRITE_INSTANCE_LIMIT {
|
if instances.len() as u64 > STARFIELD_SPRITE_INSTANCE_LIMIT {
|
||||||
unreachable!("Starfield limit exceeded!")
|
unreachable!("Starfield limit exceeded!")
|
||||||
}
|
}
|
||||||
self.instance_count = instances.len() as u32;
|
self.instance_count = instances.len() as u32;
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
use crate::globaluniform::{AtlasArray, AtlasImageLocation, SpriteData, SpriteDataArray};
|
use crate::globaluniform::{AtlasArray, AtlasImageLocation, SpriteData, SpriteDataArray};
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use bytemuck::Zeroable;
|
use bytemuck::Zeroable;
|
||||||
use galactica_constants::ASSET_CACHE;
|
|
||||||
use galactica_content::Content;
|
use galactica_content::Content;
|
||||||
use galactica_packer::SpriteAtlasImage;
|
use galactica_packer::SpriteAtlasImage;
|
||||||
|
use galactica_util::constants::ASSET_CACHE;
|
||||||
use image::GenericImageView;
|
use image::GenericImageView;
|
||||||
use std::{fs::File, io::Read, num::NonZeroU32, path::Path};
|
use std::{fs::File, io::Read, num::NonZeroU32, path::Path};
|
||||||
use wgpu::BindGroupLayout;
|
use wgpu::BindGroupLayout;
|
||||||
|
|
|
@ -0,0 +1,68 @@
|
||||||
|
use glyphon::{Attrs, Buffer, Color, Family, Metrics, Shaping, TextArea, TextBounds};
|
||||||
|
use std::time::Instant;
|
||||||
|
|
||||||
|
use crate::{datastructs::RenderState, RenderInput};
|
||||||
|
|
||||||
|
pub(super) struct FpsIndicator {
|
||||||
|
buffer: Buffer,
|
||||||
|
last_update: Instant,
|
||||||
|
update_counter: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FpsIndicator {
|
||||||
|
pub fn new(state: &mut RenderState) -> Self {
|
||||||
|
let mut buffer = Buffer::new(&mut state.text_font_system, Metrics::new(12.0, 20.0));
|
||||||
|
buffer.set_size(
|
||||||
|
&mut state.text_font_system,
|
||||||
|
state.window_size.width as f32,
|
||||||
|
state.window_size.height as f32,
|
||||||
|
);
|
||||||
|
buffer.shape_until_scroll(&mut state.text_font_system);
|
||||||
|
|
||||||
|
Self {
|
||||||
|
buffer,
|
||||||
|
last_update: Instant::now(),
|
||||||
|
update_counter: 0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FpsIndicator {
|
||||||
|
pub fn update(&mut self, input: &RenderInput, state: &mut RenderState) {
|
||||||
|
// Update once every n frames
|
||||||
|
if self.update_counter > 0 {
|
||||||
|
self.update_counter -= 1;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
self.update_counter = 100;
|
||||||
|
|
||||||
|
self.buffer.set_text(
|
||||||
|
&mut state.text_font_system,
|
||||||
|
&format!(
|
||||||
|
"Game: {:.02?}\nPhys: {:.02?}\nRender: {:.02?}",
|
||||||
|
1.0 / input.timing.galaxy,
|
||||||
|
1.0 / input.timing.physics,
|
||||||
|
1.0 / (self.last_update.elapsed().as_secs_f32() / 100.0)
|
||||||
|
),
|
||||||
|
Attrs::new().family(Family::SansSerif),
|
||||||
|
Shaping::Basic,
|
||||||
|
);
|
||||||
|
self.last_update = Instant::now();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_textarea(&self) -> TextArea {
|
||||||
|
TextArea {
|
||||||
|
buffer: &self.buffer,
|
||||||
|
left: 10.0,
|
||||||
|
top: 400.0,
|
||||||
|
scale: 1.0,
|
||||||
|
bounds: TextBounds {
|
||||||
|
left: 10,
|
||||||
|
top: 400,
|
||||||
|
right: 300,
|
||||||
|
bottom: 800,
|
||||||
|
},
|
||||||
|
default_color: Color::rgb(255, 255, 255),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,32 @@
|
||||||
|
use glyphon::TextArea;
|
||||||
|
|
||||||
|
use crate::{datastructs::RenderState, RenderInput};
|
||||||
|
|
||||||
|
use super::{fpsindicator::FpsIndicator, radar::Radar, status::Status};
|
||||||
|
|
||||||
|
pub struct UiManager {
|
||||||
|
radar: Radar,
|
||||||
|
status: Status,
|
||||||
|
fps: FpsIndicator,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl UiManager {
|
||||||
|
pub fn new(state: &mut RenderState) -> Self {
|
||||||
|
Self {
|
||||||
|
radar: Radar::new(),
|
||||||
|
status: Status::new(),
|
||||||
|
fps: FpsIndicator::new(state),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Draw all ui elements
|
||||||
|
pub fn draw(&mut self, input: &RenderInput, state: &mut RenderState) {
|
||||||
|
self.radar.draw(input, state);
|
||||||
|
self.status.draw(input, state);
|
||||||
|
self.fps.update(input, state);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_textareas(&self) -> impl Iterator<Item = TextArea> {
|
||||||
|
(0..1).map(|_| self.fps.get_textarea())
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,6 @@
|
||||||
|
mod fpsindicator;
|
||||||
|
mod manager;
|
||||||
|
mod radar;
|
||||||
|
mod status;
|
||||||
|
|
||||||
|
pub use manager::UiManager;
|
|
@ -1,20 +1,23 @@
|
||||||
//! GPUState routines for drawing HUD elements
|
|
||||||
|
|
||||||
use std::f32::consts::TAU;
|
|
||||||
|
|
||||||
use cgmath::{Deg, InnerSpace, Point2, Rad, Vector2};
|
use cgmath::{Deg, InnerSpace, Point2, Rad, Vector2};
|
||||||
use galactica_systemsim::util;
|
use galactica_systemsim::util;
|
||||||
|
use galactica_util::constants::UI_SPRITE_INSTANCE_LIMIT;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
vertexbuffer::{
|
datastructs::RenderState,
|
||||||
types::{RadialBarInstance, UiInstance},
|
vertexbuffer::{types::UiInstance, BufferObject},
|
||||||
BufferObject,
|
PositionAnchor, RenderInput,
|
||||||
},
|
|
||||||
GPUState, PositionAnchor, RenderState,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
impl GPUState {
|
pub(super) struct Radar {}
|
||||||
pub(super) fn hud_add_radar(&mut self, state: &RenderState) {
|
|
||||||
|
impl Radar {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Radar {
|
||||||
|
pub fn draw(&mut self, input: &RenderInput, state: &mut RenderState) {
|
||||||
let radar_range = 4000.0;
|
let radar_range = 4000.0;
|
||||||
let radar_size = 300.0;
|
let radar_size = 300.0;
|
||||||
let hide_range = 0.85;
|
let hide_range = 0.85;
|
||||||
|
@ -22,40 +25,40 @@ impl GPUState {
|
||||||
let system_object_scale = 1.0 / 600.0;
|
let system_object_scale = 1.0 / 600.0;
|
||||||
let ship_scale = 1.0 / 10.0;
|
let ship_scale = 1.0 / 10.0;
|
||||||
|
|
||||||
let player_world_object = state.systemsim.get_ship(state.player_data).unwrap();
|
let player_world_object = input.systemsim.get_ship(input.player_data).unwrap();
|
||||||
let player_body = state
|
let player_body = input
|
||||||
.systemsim
|
.systemsim
|
||||||
.get_rigid_body(player_world_object.rigid_body)
|
.get_rigid_body(player_world_object.rigid_body)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
let player_position = util::rigidbody_position(player_body);
|
let player_position = util::rigidbody_position(player_body);
|
||||||
let planet_sprite = state.content.get_sprite_handle("ui::planetblip");
|
let planet_sprite = input.content.get_sprite_handle("ui::planetblip");
|
||||||
let ship_sprite = state.content.get_sprite_handle("ui::shipblip");
|
let ship_sprite = input.content.get_sprite_handle("ui::shipblip");
|
||||||
let arrow_sprite = state.content.get_sprite_handle("ui::centerarrow");
|
let arrow_sprite = input.content.get_sprite_handle("ui::centerarrow");
|
||||||
|
|
||||||
// Enforce buffer limit
|
// Enforce buffer limit
|
||||||
if self.vertex_buffers.ui_counter as u64 > galactica_constants::UI_SPRITE_INSTANCE_LIMIT {
|
if state.vertex_buffers.ui_counter as u64 > UI_SPRITE_INSTANCE_LIMIT {
|
||||||
// TODO: no panic, handle this better.
|
// TODO: no panic, handle this better.
|
||||||
panic!("UI limit exceeded!")
|
panic!("UI limit exceeded!")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Push this object's instance
|
// Push this object's instance
|
||||||
self.queue.write_buffer(
|
state.queue.write_buffer(
|
||||||
&self.vertex_buffers.ui.instances,
|
&state.vertex_buffers.ui.instances,
|
||||||
UiInstance::SIZE * self.vertex_buffers.ui_counter,
|
UiInstance::SIZE * state.vertex_buffers.ui_counter,
|
||||||
bytemuck::cast_slice(&[UiInstance {
|
bytemuck::cast_slice(&[UiInstance {
|
||||||
anchor: PositionAnchor::NwNw.to_int(),
|
anchor: PositionAnchor::NwNw.to_int(),
|
||||||
position: [10.0, -10.0],
|
position: [10.0, -10.0],
|
||||||
angle: 0.0,
|
angle: 0.0,
|
||||||
size: radar_size,
|
size: radar_size,
|
||||||
color: [1.0, 1.0, 1.0, 1.0],
|
color: [1.0, 1.0, 1.0, 1.0],
|
||||||
sprite_index: state.content.get_sprite_handle("ui::radar").get_index(),
|
sprite_index: input.content.get_sprite_handle("ui::radar").get_index(),
|
||||||
}]),
|
}]),
|
||||||
);
|
);
|
||||||
self.vertex_buffers.ui_counter += 1;
|
state.vertex_buffers.ui_counter += 1;
|
||||||
|
|
||||||
// Draw system objects
|
// Draw system objects
|
||||||
let system = state.content.get_system(state.current_system);
|
let system = input.content.get_system(input.current_system);
|
||||||
for o in &system.objects {
|
for o in &system.objects {
|
||||||
let size = (o.size / o.pos.z) / (radar_range * system_object_scale);
|
let size = (o.size / o.pos.z) / (radar_range * system_object_scale);
|
||||||
let p = Point2 {
|
let p = Point2 {
|
||||||
|
@ -74,17 +77,15 @@ impl GPUState {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Enforce buffer limit
|
// Enforce buffer limit
|
||||||
if self.vertex_buffers.ui_counter as u64
|
if state.vertex_buffers.ui_counter as u64 > UI_SPRITE_INSTANCE_LIMIT {
|
||||||
> galactica_constants::UI_SPRITE_INSTANCE_LIMIT
|
|
||||||
{
|
|
||||||
// TODO: no panic, handle this better.
|
// TODO: no panic, handle this better.
|
||||||
panic!("UI limit exceeded!")
|
panic!("UI limit exceeded!")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Push this object's instance
|
// Push this object's instance
|
||||||
self.queue.write_buffer(
|
state.queue.write_buffer(
|
||||||
&self.vertex_buffers.ui.instances,
|
&state.vertex_buffers.ui.instances,
|
||||||
UiInstance::SIZE * self.vertex_buffers.ui_counter,
|
UiInstance::SIZE * state.vertex_buffers.ui_counter,
|
||||||
bytemuck::cast_slice(&[UiInstance {
|
bytemuck::cast_slice(&[UiInstance {
|
||||||
anchor: PositionAnchor::NwC.to_int(),
|
anchor: PositionAnchor::NwC.to_int(),
|
||||||
position: (Point2 {
|
position: (Point2 {
|
||||||
|
@ -98,25 +99,25 @@ impl GPUState {
|
||||||
sprite_index: planet_sprite.get_index(),
|
sprite_index: planet_sprite.get_index(),
|
||||||
}]),
|
}]),
|
||||||
);
|
);
|
||||||
self.vertex_buffers.ui_counter += 1;
|
state.vertex_buffers.ui_counter += 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Draw ships
|
// Draw ships
|
||||||
for (s, r) in state.systemsim.iter_ship_body() {
|
for (s, r) in input.systemsim.iter_ship_body() {
|
||||||
// This will be None if this ship is dead.
|
// This will be None if this ship is dead.
|
||||||
// Stays around while the physics system runs a collapse sequence
|
// Stays around while the physics system runs a collapse sequence
|
||||||
let color = match state.data.get_ship(s.data_handle) {
|
let color = match input.data.get_ship(s.data_handle) {
|
||||||
None => {
|
None => {
|
||||||
// TODO: configurable
|
// TODO: configurable
|
||||||
[0.2, 0.2, 0.2, 1.0]
|
[0.2, 0.2, 0.2, 1.0]
|
||||||
}
|
}
|
||||||
Some(data) => {
|
Some(data) => {
|
||||||
let c = state.content.get_faction(data.get_faction()).color;
|
let c = input.content.get_faction(data.get_faction()).color;
|
||||||
[c[0], c[1], c[2], 1.0]
|
[c[0], c[1], c[2], 1.0]
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
let ship = state.content.get_ship(s.data_handle.content_handle());
|
let ship = input.content.get_ship(s.data_handle.content_handle());
|
||||||
let size = (ship.size * ship.sprite.aspect) * ship_scale;
|
let size = (ship.size * ship.sprite.aspect) * ship_scale;
|
||||||
let p = util::rigidbody_position(r);
|
let p = util::rigidbody_position(r);
|
||||||
let d = (p - player_position) / radar_range;
|
let d = (p - player_position) / radar_range;
|
||||||
|
@ -135,17 +136,15 @@ impl GPUState {
|
||||||
|
|
||||||
// Enforce buffer limit
|
// Enforce buffer limit
|
||||||
// TODO: cleaner solution. don't do this everywhere.
|
// TODO: cleaner solution. don't do this everywhere.
|
||||||
if self.vertex_buffers.ui_counter as u64
|
if state.vertex_buffers.ui_counter as u64 > UI_SPRITE_INSTANCE_LIMIT {
|
||||||
> galactica_constants::UI_SPRITE_INSTANCE_LIMIT
|
|
||||||
{
|
|
||||||
// TODO: no panic, handle this better.
|
// TODO: no panic, handle this better.
|
||||||
panic!("UI limit exceeded!")
|
panic!("UI limit exceeded!")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Push this object's instance
|
// Push this object's instance
|
||||||
self.queue.write_buffer(
|
state.queue.write_buffer(
|
||||||
&self.vertex_buffers.ui.instances,
|
&state.vertex_buffers.ui.instances,
|
||||||
UiInstance::SIZE * self.vertex_buffers.ui_counter,
|
UiInstance::SIZE * state.vertex_buffers.ui_counter,
|
||||||
bytemuck::cast_slice(&[UiInstance {
|
bytemuck::cast_slice(&[UiInstance {
|
||||||
anchor: PositionAnchor::NwC.to_int(),
|
anchor: PositionAnchor::NwC.to_int(),
|
||||||
position: position.into(),
|
position: position.into(),
|
||||||
|
@ -155,33 +154,31 @@ impl GPUState {
|
||||||
sprite_index: ship_sprite.get_index(),
|
sprite_index: ship_sprite.get_index(),
|
||||||
}]),
|
}]),
|
||||||
);
|
);
|
||||||
self.vertex_buffers.ui_counter += 1;
|
state.vertex_buffers.ui_counter += 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Draw viewport frame
|
// Draw viewport frame
|
||||||
let d = Vector2 {
|
let d = Vector2 {
|
||||||
x: (state.camera_zoom / 2.0) * self.window_aspect,
|
x: (input.camera_zoom / 2.0) * state.window_aspect,
|
||||||
y: state.camera_zoom / 2.0,
|
y: input.camera_zoom / 2.0,
|
||||||
} / radar_range;
|
} / radar_range;
|
||||||
let m = d.magnitude();
|
let m = d.magnitude();
|
||||||
let d = d * (radar_size / 2.0);
|
let d = d * (radar_size / 2.0);
|
||||||
let color = [0.3, 0.3, 0.3, 1.0];
|
let color = [0.3, 0.3, 0.3, 1.0];
|
||||||
if m < 0.8 {
|
if m < 0.8 {
|
||||||
let sprite = state.content.get_sprite_handle("ui::radarframe");
|
let sprite = input.content.get_sprite_handle("ui::radarframe");
|
||||||
let size = 7.0f32.min((0.8 - m) * 70.0);
|
let size = 7.0f32.min((0.8 - m) * 70.0);
|
||||||
|
|
||||||
// Enforce buffer limit (this section adds four items)
|
// Enforce buffer limit (this section adds four items)
|
||||||
if self.vertex_buffers.ui_counter as u64 + 4
|
if state.vertex_buffers.ui_counter as u64 + 4 > UI_SPRITE_INSTANCE_LIMIT {
|
||||||
> galactica_constants::UI_SPRITE_INSTANCE_LIMIT
|
|
||||||
{
|
|
||||||
// TODO: no panic, handle this better.
|
// TODO: no panic, handle this better.
|
||||||
panic!("UI limit exceeded!")
|
panic!("UI limit exceeded!")
|
||||||
}
|
}
|
||||||
|
|
||||||
self.queue.write_buffer(
|
state.queue.write_buffer(
|
||||||
&self.vertex_buffers.ui.instances,
|
&state.vertex_buffers.ui.instances,
|
||||||
UiInstance::SIZE * self.vertex_buffers.ui_counter,
|
UiInstance::SIZE * state.vertex_buffers.ui_counter,
|
||||||
bytemuck::cast_slice(&[UiInstance {
|
bytemuck::cast_slice(&[UiInstance {
|
||||||
anchor: PositionAnchor::NwNw.to_int(),
|
anchor: PositionAnchor::NwNw.to_int(),
|
||||||
position: Point2 {
|
position: Point2 {
|
||||||
|
@ -195,11 +192,11 @@ impl GPUState {
|
||||||
sprite_index: sprite.get_index(),
|
sprite_index: sprite.get_index(),
|
||||||
}]),
|
}]),
|
||||||
);
|
);
|
||||||
self.vertex_buffers.ui_counter += 1;
|
state.vertex_buffers.ui_counter += 1;
|
||||||
|
|
||||||
self.queue.write_buffer(
|
state.queue.write_buffer(
|
||||||
&self.vertex_buffers.ui.instances,
|
&state.vertex_buffers.ui.instances,
|
||||||
UiInstance::SIZE * self.vertex_buffers.ui_counter,
|
UiInstance::SIZE * state.vertex_buffers.ui_counter,
|
||||||
bytemuck::cast_slice(&[UiInstance {
|
bytemuck::cast_slice(&[UiInstance {
|
||||||
anchor: PositionAnchor::NwSw.to_int(),
|
anchor: PositionAnchor::NwSw.to_int(),
|
||||||
position: Point2 {
|
position: Point2 {
|
||||||
|
@ -213,11 +210,11 @@ impl GPUState {
|
||||||
sprite_index: sprite.get_index(),
|
sprite_index: sprite.get_index(),
|
||||||
}]),
|
}]),
|
||||||
);
|
);
|
||||||
self.vertex_buffers.ui_counter += 1;
|
state.vertex_buffers.ui_counter += 1;
|
||||||
|
|
||||||
self.queue.write_buffer(
|
state.queue.write_buffer(
|
||||||
&self.vertex_buffers.ui.instances,
|
&state.vertex_buffers.ui.instances,
|
||||||
UiInstance::SIZE * self.vertex_buffers.ui_counter,
|
UiInstance::SIZE * state.vertex_buffers.ui_counter,
|
||||||
bytemuck::cast_slice(&[UiInstance {
|
bytemuck::cast_slice(&[UiInstance {
|
||||||
anchor: PositionAnchor::NwSe.to_int(),
|
anchor: PositionAnchor::NwSe.to_int(),
|
||||||
position: Point2 {
|
position: Point2 {
|
||||||
|
@ -231,11 +228,11 @@ impl GPUState {
|
||||||
sprite_index: sprite.get_index(),
|
sprite_index: sprite.get_index(),
|
||||||
}]),
|
}]),
|
||||||
);
|
);
|
||||||
self.vertex_buffers.ui_counter += 1;
|
state.vertex_buffers.ui_counter += 1;
|
||||||
|
|
||||||
self.queue.write_buffer(
|
state.queue.write_buffer(
|
||||||
&self.vertex_buffers.ui.instances,
|
&state.vertex_buffers.ui.instances,
|
||||||
UiInstance::SIZE * self.vertex_buffers.ui_counter,
|
UiInstance::SIZE * state.vertex_buffers.ui_counter,
|
||||||
bytemuck::cast_slice(&[UiInstance {
|
bytemuck::cast_slice(&[UiInstance {
|
||||||
anchor: PositionAnchor::NwNe.to_int(),
|
anchor: PositionAnchor::NwNe.to_int(),
|
||||||
position: Point2 {
|
position: Point2 {
|
||||||
|
@ -249,7 +246,7 @@ impl GPUState {
|
||||||
sprite_index: sprite.get_index(),
|
sprite_index: sprite.get_index(),
|
||||||
}]),
|
}]),
|
||||||
);
|
);
|
||||||
self.vertex_buffers.ui_counter += 1;
|
state.vertex_buffers.ui_counter += 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Arrow to center of system
|
// Arrow to center of system
|
||||||
|
@ -263,15 +260,14 @@ impl GPUState {
|
||||||
y: radar_size / -2.0 - 10.0,
|
y: radar_size / -2.0 - 10.0,
|
||||||
} + ((q.normalize() * 0.865) * (radar_size / 2.0));
|
} + ((q.normalize() * 0.865) * (radar_size / 2.0));
|
||||||
|
|
||||||
if self.vertex_buffers.ui_counter as u64 > galactica_constants::UI_SPRITE_INSTANCE_LIMIT
|
if state.vertex_buffers.ui_counter as u64 > UI_SPRITE_INSTANCE_LIMIT {
|
||||||
{
|
|
||||||
// TODO: no panic, handle this better.
|
// TODO: no panic, handle this better.
|
||||||
panic!("UI limit exceeded!")
|
panic!("UI limit exceeded!")
|
||||||
}
|
}
|
||||||
|
|
||||||
self.queue.write_buffer(
|
state.queue.write_buffer(
|
||||||
&self.vertex_buffers.ui.instances,
|
&state.vertex_buffers.ui.instances,
|
||||||
UiInstance::SIZE * self.vertex_buffers.ui_counter,
|
UiInstance::SIZE * state.vertex_buffers.ui_counter,
|
||||||
bytemuck::cast_slice(&[UiInstance {
|
bytemuck::cast_slice(&[UiInstance {
|
||||||
anchor: PositionAnchor::NwC.to_int(),
|
anchor: PositionAnchor::NwC.to_int(),
|
||||||
position: position.into(),
|
position: position.into(),
|
||||||
|
@ -281,75 +277,7 @@ impl GPUState {
|
||||||
sprite_index: arrow_sprite.get_index(),
|
sprite_index: arrow_sprite.get_index(),
|
||||||
}]),
|
}]),
|
||||||
);
|
);
|
||||||
self.vertex_buffers.ui_counter += 1;
|
state.vertex_buffers.ui_counter += 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) fn hud_add_status(&mut self, state: &RenderState) {
|
|
||||||
if self.vertex_buffers.ui_counter as u64 > galactica_constants::UI_SPRITE_INSTANCE_LIMIT {
|
|
||||||
// TODO: no panic, handle this better.
|
|
||||||
panic!("UI limit exceeded!")
|
|
||||||
}
|
|
||||||
|
|
||||||
let player_world_object = state.systemsim.get_ship(state.player_data).unwrap();
|
|
||||||
|
|
||||||
let data = state
|
|
||||||
.data
|
|
||||||
.get_ship(player_world_object.data_handle)
|
|
||||||
.unwrap();
|
|
||||||
let max_shields = data.get_outfits().get_shield_strength();
|
|
||||||
let current_shields = data.get_shields();
|
|
||||||
let current_hull = data.get_hull();
|
|
||||||
let max_hull = state.content.get_ship(data.get_content()).hull;
|
|
||||||
|
|
||||||
self.queue.write_buffer(
|
|
||||||
&self.vertex_buffers.ui.instances,
|
|
||||||
UiInstance::SIZE * self.vertex_buffers.ui_counter,
|
|
||||||
bytemuck::cast_slice(&[UiInstance {
|
|
||||||
anchor: PositionAnchor::NeNe.to_int(),
|
|
||||||
position: [-10.0, -10.0],
|
|
||||||
angle: 0.0,
|
|
||||||
size: 200.0,
|
|
||||||
color: [1.0, 1.0, 1.0, 1.0],
|
|
||||||
sprite_index: state.content.get_sprite_handle("ui::status").get_index(),
|
|
||||||
}]),
|
|
||||||
);
|
|
||||||
self.vertex_buffers.ui_counter += 1;
|
|
||||||
|
|
||||||
// We add two items here, so +2
|
|
||||||
if self.vertex_buffers.radialbar_counter as u64 + 2
|
|
||||||
> galactica_constants::RADIALBAR_SPRITE_INSTANCE_LIMIT
|
|
||||||
{
|
|
||||||
// TODO: no panic, handle this better.
|
|
||||||
panic!("Radialbar limit exceeded!")
|
|
||||||
}
|
|
||||||
|
|
||||||
self.queue.write_buffer(
|
|
||||||
&self.vertex_buffers.radialbar.instances,
|
|
||||||
RadialBarInstance::SIZE * self.vertex_buffers.radialbar_counter,
|
|
||||||
bytemuck::cast_slice(&[RadialBarInstance {
|
|
||||||
position: [-19.0, -19.0],
|
|
||||||
anchor: PositionAnchor::NeNe.to_int(),
|
|
||||||
diameter: 182.0,
|
|
||||||
stroke: 5.0,
|
|
||||||
color: [0.3, 0.6, 0.8, 1.0],
|
|
||||||
angle: (current_shields / max_shields) * TAU,
|
|
||||||
}]),
|
|
||||||
);
|
|
||||||
self.vertex_buffers.radialbar_counter += 1;
|
|
||||||
|
|
||||||
self.queue.write_buffer(
|
|
||||||
&self.vertex_buffers.radialbar.instances,
|
|
||||||
RadialBarInstance::SIZE * self.vertex_buffers.radialbar_counter,
|
|
||||||
bytemuck::cast_slice(&[RadialBarInstance {
|
|
||||||
position: [-27.0, -27.0],
|
|
||||||
anchor: PositionAnchor::NeNe.to_int(),
|
|
||||||
diameter: 166.0,
|
|
||||||
stroke: 5.0,
|
|
||||||
color: [0.8, 0.7, 0.5, 1.0],
|
|
||||||
angle: (current_hull / max_hull) * TAU,
|
|
||||||
}]),
|
|
||||||
);
|
|
||||||
self.vertex_buffers.radialbar_counter += 1;
|
|
||||||
}
|
|
||||||
}
|
}
|
|
@ -0,0 +1,87 @@
|
||||||
|
use std::f32::consts::TAU;
|
||||||
|
|
||||||
|
use galactica_util::constants::{RADIALBAR_SPRITE_INSTANCE_LIMIT, UI_SPRITE_INSTANCE_LIMIT};
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
datastructs::RenderState,
|
||||||
|
vertexbuffer::{
|
||||||
|
types::{RadialBarInstance, UiInstance},
|
||||||
|
BufferObject,
|
||||||
|
},
|
||||||
|
PositionAnchor, RenderInput,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub(super) struct Status {}
|
||||||
|
impl Status {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Status {
|
||||||
|
pub fn draw(&mut self, input: &RenderInput, state: &mut RenderState) {
|
||||||
|
if state.vertex_buffers.ui_counter as u64 > UI_SPRITE_INSTANCE_LIMIT {
|
||||||
|
// TODO: no panic, handle this better.
|
||||||
|
panic!("UI limit exceeded!")
|
||||||
|
}
|
||||||
|
|
||||||
|
let player_world_object = input.systemsim.get_ship(input.player_data).unwrap();
|
||||||
|
|
||||||
|
let data = input
|
||||||
|
.data
|
||||||
|
.get_ship(player_world_object.data_handle)
|
||||||
|
.unwrap();
|
||||||
|
let max_shields = data.get_outfits().get_shield_strength();
|
||||||
|
let current_shields = data.get_shields();
|
||||||
|
let current_hull = data.get_hull();
|
||||||
|
let max_hull = input.content.get_ship(data.get_content()).hull;
|
||||||
|
|
||||||
|
state.queue.write_buffer(
|
||||||
|
&state.vertex_buffers.ui.instances,
|
||||||
|
UiInstance::SIZE * state.vertex_buffers.ui_counter,
|
||||||
|
bytemuck::cast_slice(&[UiInstance {
|
||||||
|
anchor: PositionAnchor::NeNe.to_int(),
|
||||||
|
position: [-10.0, -10.0],
|
||||||
|
angle: 0.0,
|
||||||
|
size: 200.0,
|
||||||
|
color: [1.0, 1.0, 1.0, 1.0],
|
||||||
|
sprite_index: input.content.get_sprite_handle("ui::status").get_index(),
|
||||||
|
}]),
|
||||||
|
);
|
||||||
|
state.vertex_buffers.ui_counter += 1;
|
||||||
|
|
||||||
|
// We add two items here, so +2
|
||||||
|
if state.vertex_buffers.radialbar_counter as u64 + 2 > RADIALBAR_SPRITE_INSTANCE_LIMIT {
|
||||||
|
// TODO: no panic, handle this better.
|
||||||
|
panic!("Radialbar limit exceeded!")
|
||||||
|
}
|
||||||
|
|
||||||
|
state.queue.write_buffer(
|
||||||
|
&state.vertex_buffers.radialbar.instances,
|
||||||
|
RadialBarInstance::SIZE * state.vertex_buffers.radialbar_counter,
|
||||||
|
bytemuck::cast_slice(&[RadialBarInstance {
|
||||||
|
position: [-19.0, -19.0],
|
||||||
|
anchor: PositionAnchor::NeNe.to_int(),
|
||||||
|
diameter: 182.0,
|
||||||
|
stroke: 5.0,
|
||||||
|
color: [0.3, 0.6, 0.8, 1.0],
|
||||||
|
angle: (current_shields / max_shields) * TAU,
|
||||||
|
}]),
|
||||||
|
);
|
||||||
|
state.vertex_buffers.radialbar_counter += 1;
|
||||||
|
|
||||||
|
state.queue.write_buffer(
|
||||||
|
&state.vertex_buffers.radialbar.instances,
|
||||||
|
RadialBarInstance::SIZE * state.vertex_buffers.radialbar_counter,
|
||||||
|
bytemuck::cast_slice(&[RadialBarInstance {
|
||||||
|
position: [-27.0, -27.0],
|
||||||
|
anchor: PositionAnchor::NeNe.to_int(),
|
||||||
|
diameter: 166.0,
|
||||||
|
stroke: 5.0,
|
||||||
|
color: [0.8, 0.7, 0.5, 1.0],
|
||||||
|
angle: (current_hull / max_hull) * TAU,
|
||||||
|
}]),
|
||||||
|
);
|
||||||
|
state.vertex_buffers.radialbar_counter += 1;
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,5 +1,5 @@
|
||||||
[package]
|
[package]
|
||||||
name = "galactica-constants"
|
name = "galactica-util"
|
||||||
description = "Compile-time parameters for Galactica"
|
description = "Compile-time parameters for Galactica"
|
||||||
categories = { workspace = true }
|
categories = { workspace = true }
|
||||||
keywords = { workspace = true }
|
keywords = { workspace = true }
|
|
@ -1,6 +1,5 @@
|
||||||
#![warn(missing_docs)]
|
|
||||||
|
|
||||||
//! Compile-time parameters
|
//! Compile-time parameters
|
||||||
|
|
||||||
// TODO: many of these should be moved to a config file or cli option
|
// TODO: many of these should be moved to a config file or cli option
|
||||||
|
|
||||||
/// Minimum zoom level
|
/// Minimum zoom level
|
|
@ -0,0 +1,6 @@
|
||||||
|
#![warn(missing_docs)]
|
||||||
|
|
||||||
|
//! Various utilities
|
||||||
|
|
||||||
|
pub mod constants;
|
||||||
|
pub mod timing;
|
|
@ -0,0 +1,44 @@
|
||||||
|
//! Keep track of the time we spent in each part of the game loop.
|
||||||
|
use std::time::Instant;
|
||||||
|
|
||||||
|
/// Utility struct.
|
||||||
|
/// Keeps track of the time we spent in each part of the game loop.
|
||||||
|
pub struct Timing {
|
||||||
|
timer: Option<Instant>,
|
||||||
|
|
||||||
|
/// The time we spent simulating game state
|
||||||
|
pub galaxy: f32,
|
||||||
|
|
||||||
|
/// The time we spent simulating physics
|
||||||
|
pub physics: f32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Timing {
|
||||||
|
/// Create a new timing struct
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self {
|
||||||
|
timer: None,
|
||||||
|
galaxy: f32::NAN,
|
||||||
|
physics: f32::NAN,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Start the timer
|
||||||
|
pub fn start(&mut self) {
|
||||||
|
self.timer = Some(Instant::now());
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Clear timer and record galaxy simulation time.
|
||||||
|
/// Assumes timer has been started
|
||||||
|
pub fn mark_galaxy(&mut self) {
|
||||||
|
self.galaxy = self.timer.unwrap().elapsed().as_secs_f32();
|
||||||
|
self.timer = None;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Clear timer and record physics simulation time
|
||||||
|
/// Asumes timer has been started
|
||||||
|
pub fn mark_physics(&mut self) {
|
||||||
|
self.physics = self.timer.unwrap().elapsed().as_secs_f32();
|
||||||
|
self.timer = None;
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue