diff --git a/.drone.yml b/.drone.yml index 04d40ada..7b046469 100644 --- a/.drone.yml +++ b/.drone.yml @@ -2,11 +2,6 @@ kind: pipeline name: default steps: -- name: format - image: truedoctor/rust-wasm:nightly-2020-02-28 - pull: if-not-exists - commands: - - nix-shell --run 'cargo make check-format' --arg inCI true # check formatting for all projects - name: build image: truedoctor/rust-wasm:nightly-2020-02-28 pull: if-not-exists @@ -39,6 +34,11 @@ steps: path: /tmp/demo commands: - cp -r client /tmp/demo +- name: format + image: truedoctor/rust-wasm:nightly-2020-02-28 + pull: if-not-exists + commands: + - nix-shell --run 'cargo make check-format' --arg inCI true # check formatting for all projects volumes: - name: demo diff --git a/.gitignore b/.gitignore index 34af0419..224f07a9 100644 --- a/.gitignore +++ b/.gitignore @@ -31,3 +31,6 @@ gen/ # Direnv state-directory .direnv/ + +# IntelliJ +ratatosk.iml diff --git a/Cargo.lock b/Cargo.lock index f262f6a8..8f6554b5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -40,9 +40,9 @@ checksum = "f8aac770f1885fd7e387acedd76065302551364496e46b3dd00860b2f8359b9d" [[package]] name = "backtrace" -version = "0.3.45" +version = "0.3.46" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad235dabf00f36301792cfe82499880ba54c6486be094d1047b02bacb67c14e8" +checksum = "b1e692897359247cc6bb902933361652380af0f1b7651ae5c5013407f30e109e" dependencies = [ "backtrace-sys", "cfg-if", @@ -52,9 +52,9 @@ dependencies = [ [[package]] name = "backtrace-sys" -version = "0.1.34" +version = "0.1.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca797db0057bae1a7aa2eef3283a874695455cecf08a43bfb8507ee0ebc1ed69" +checksum = "7de8aba10a69c8e8d7622c5710229485ec32e9d55fdad160ea559c086fdcd118" dependencies = [ "cc", "libc", @@ -103,14 +103,14 @@ dependencies = [ "rask-engine", "serde", "serde_json", - "spine_tiny", + "spine_tiny 0.1.0 (git+http://github.com/tomaka/spine-rs)", ] [[package]] name = "bumpalo" -version = "3.2.0" +version = "3.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f359dc14ff8911330a51ef78022d376f25ed00248912803b58f00cb1c27f742" +checksum = "12ae9db68ad7fac5fe51304d20f016c911539251075a214f8e663babefa35187" [[package]] name = "byte-tools" @@ -206,6 +206,26 @@ dependencies = [ "winapi 0.3.8", ] +[[package]] +name = "const_env" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3e9e4f72c6e3398ca6da372abd9affd8f89781fe728869bbf986206e9af9627e" +dependencies = [ + "const_env_impl", +] + +[[package]] +name = "const_env_impl" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a4f51209740b5e1589e702b3044cdd4562cef41b6da404904192ffffb852d62" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "cookie" version = "0.12.0" @@ -308,9 +328,9 @@ dependencies = [ [[package]] name = "deflate" -version = "0.8.3" +version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "050ef6de42a33903b30a7497b76b40d3d58691d4d3eec355348c122444a388f0" +checksum = "e7e5d2a2273fed52a7f947ee55b092c4057025d7a3e04e5ecdbd25d6c3fb1bd7" dependencies = [ "adler32", "byteorder", @@ -522,9 +542,9 @@ dependencies = [ [[package]] name = "hermit-abi" -version = "0.1.8" +version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1010591b26bbfe835e9faeabeb11866061cc7dcebffd56ad7d0942d0e61aefd8" +checksum = "725cf19794cf90aa94e65050cb4191ff5d8fa87a498383774c47b332e3af952e" dependencies = [ "libc", ] @@ -625,9 +645,9 @@ dependencies = [ [[package]] name = "image" -version = "0.23.2" +version = "0.23.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9062b90712d25bc6bb165d110aa59c6b47c849246e341e7b86a98daff9d49f60" +checksum = "bfc5483f8d5afd3653b38a196c52294dcb239c3e1a5bade1990353ea13bcf387" dependencies = [ "bytemuck", "byteorder", @@ -723,9 +743,9 @@ checksum = "dea0c0405123bba743ee3f91f49b1c7cfb684eef0da0a50110f758ccf24cdff0" [[package]] name = "lock_api" -version = "0.3.3" +version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "79b2de95ecb4691949fea4716ca53cdbcfccb2c612e19644a8bad05edcf9f47b" +checksum = "c4da24a77a3d8a6d4862d95f72e6fdb9c09a643ecdb402d754004a557f2bec75" dependencies = [ "scopeguard", ] @@ -928,9 +948,9 @@ checksum = "2839e79665f131bdb5782e51f2c6c9599c133c6098982a54c794358bf432529c" [[package]] name = "openssl" -version = "0.10.28" +version = "0.10.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "973293749822d7dd6370d6da1e523b0d1db19f06c459134c658b2a4261378b52" +checksum = "cee6d85f4cb4c4f59a6a85d5b68a233d280c82e29e822913b9c8b129fbf20bdd" dependencies = [ "bitflags", "cfg-if", @@ -948,9 +968,9 @@ checksum = "77af24da69f9d9341038eba93a073b1fdaaa1b788221b00a69bce9e762cb32de" [[package]] name = "openssl-sys" -version = "0.9.54" +version = "0.9.55" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1024c0a59774200a555087a6da3f253a9095a5f344e353b212ac4c8b8e450986" +checksum = "7717097d810a0f2e2323f9e5d11e71608355e24828410b55b9d4f18aa5f9a5d8" dependencies = [ "autocfg 1.0.0", "cc", @@ -1005,9 +1025,9 @@ checksum = "05da548ad6865900e60eaba7f589cc0783590a92e940c26953ff81ddbab2d677" [[package]] name = "png" -version = "0.16.1" +version = "0.16.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46060468187c21c00ffa2a920690b29997d7fd543f5a4d400461e4a7d4fccde8" +checksum = "910f09135b1ed14bb16be445a8c23ddf0777eca485fbfc7cee00d81fecab158a" dependencies = [ "bitflags", "crc32fast", @@ -1023,9 +1043,9 @@ checksum = "74490b50b9fbe561ac330df47c08f3f33073d2d00c150f719147d7c54522fa1b" [[package]] name = "proc-macro2" -version = "1.0.9" +version = "1.0.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c09721c6781493a2a492a96b5a5bf19b65917fe6728884e7c44dd0c60ca3435" +checksum = "df246d292ff63439fea9bc8c0a270bed0e390d5ebd4db4ba15aba81111b5abe3" dependencies = [ "unicode-xid", ] @@ -1202,6 +1222,11 @@ dependencies = [ [[package]] name = "rask-engine" version = "0.2.0" +dependencies = [ + "image", + "lazy_static", + "spine_tiny 0.1.0 (git+https://github.com/reeFridge/spine-rs)", +] [[package]] name = "rask-server" @@ -1222,6 +1247,7 @@ version = "0.1.0" dependencies = [ "fern", "js-sys", + "lazy_static", "log", "rand 0.7.3", "rask-engine", @@ -1248,6 +1274,7 @@ dependencies = [ name = "rask-wasm-shared" version = "0.1.0" dependencies = [ + "const_env", "fern", "image", "js-sys", @@ -1300,9 +1327,9 @@ checksum = "2439c63f3f6139d1b57529d16bc3b8bb855230c8efcc5d3a896c8bea7c3b1e84" [[package]] name = "regex" -version = "1.3.5" +version = "1.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8900ebc1363efa7ea1c399ccc32daed870b4002651e0bed86e72d501ebbe0048" +checksum = "7f6946991529684867e47d86474e3a6d0c0ab9b82d5821e314b1ede31fa3a4b3" dependencies = [ "regex-syntax", ] @@ -1362,6 +1389,12 @@ version = "0.1.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4c691c0e608126e00913e33f0ccf3727d5fc84573623b8d65b2df340b5201783" +[[package]] +name = "rustc-hex" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ceb8ce7a5e520de349e1fa172baeba4a9e8d5ef06c47471863530bc4972ee1e" + [[package]] name = "rustc-serialize" version = "0.3.24" @@ -1385,9 +1418,9 @@ checksum = "535622e6be132bccd223f4bb2b8ac8d53cda3c7a6394944d3b2b33fb974f9d76" [[package]] name = "schannel" -version = "0.1.17" +version = "0.1.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "507a9e6e8ffe0a4e0ebb9a10293e62fdf7657c06f1b8bb07a8fcf697d2abf295" +checksum = "039c25b130bd8c1321ee2d7de7fde2659fa9c2744e4bb29711cfc852ea53cd19" dependencies = [ "lazy_static", "winapi 0.3.8", @@ -1407,21 +1440,22 @@ checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" [[package]] name = "security-framework" -version = "0.4.1" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97bbedbe81904398b6ebb054b3e912f99d55807125790f3198ac990d98def5b0" +checksum = "572dfa3a0785509e7a44b5b4bebcf94d41ba34e9ed9eb9df722545c3b3c4144a" dependencies = [ "bitflags", "core-foundation", "core-foundation-sys", + "libc", "security-framework-sys", ] [[package]] name = "security-framework-sys" -version = "0.4.1" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "06fd2f23e31ef68dd2328cc383bd493142e46107a3a0e24f7d734e3f3b80fe4c" +checksum = "8ddb15a5fec93b7021b8a9e96009c5d8d51c15673569f7c0f6b7204e5b7b404f" dependencies = [ "core-foundation-sys", "libc", @@ -1444,18 +1478,18 @@ checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" [[package]] name = "serde" -version = "1.0.105" +version = "1.0.106" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e707fbbf255b8fc8c3b99abb91e7257a622caeb20a9818cbadbeeede4e0932ff" +checksum = "36df6ac6412072f67cf767ebbde4133a5b2e88e76dc6187fa7104cd16f783399" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.105" +version = "1.0.106" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac5d00fc561ba2724df6758a17de23df5914f20e41cb00f94d5b7ae42fffaff8" +checksum = "9e549e3abf4fb8621bd1609f11dfc9f5e50320802273b12f3811a67e6716ea6c" dependencies = [ "proc-macro2", "quote", @@ -1464,9 +1498,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.48" +version = "1.0.51" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9371ade75d4c2d6cb154141b9752cf3781ec9c05e0e5cf35060e1e70ee7b9c25" +checksum = "da07b57ee2623368351e9a0488bb0b261322a15a6e0ae53e243cbdc0f4208da9" dependencies = [ "itoa", "ryu", @@ -1514,9 +1548,9 @@ dependencies = [ [[package]] name = "smallvec" -version = "1.2.0" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c2fb2ec9bcd216a5b0d0ccf31ab17b5ed1d627960edff65bbe95d3ce221cefc" +checksum = "05720e22615919e4734f6a99ceae50d00226c3c5aca406e102ebc33298214e0a" [[package]] name = "spin" @@ -1533,6 +1567,17 @@ dependencies = [ "rustc-serialize", ] +[[package]] +name = "spine_tiny" +version = "0.1.0" +source = "git+https://github.com/reeFridge/spine-rs#280ae6cbbe24cdfb8374adac583b755395321ac4" +dependencies = [ + "rustc-hex", + "serde", + "serde_derive", + "serde_json", +] + [[package]] name = "string" version = "0.2.1" @@ -1550,9 +1595,9 @@ checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a" [[package]] name = "syn" -version = "1.0.16" +version = "1.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "123bd9499cfb380418d509322d7a6d52e5315f064fe4b3ad18a53d6b92c07859" +checksum = "0df0eb663f387145cab623dea85b09c2c5b4b0aef44e945d928e682fce71bb03" dependencies = [ "proc-macro2", "quote", @@ -1794,7 +1839,7 @@ version = "0.1.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5479532badd04e128284890390c1e876ef7a993d0570b3597ae43dfa1d59afa4" dependencies = [ - "smallvec 1.2.0", + "smallvec 1.3.0", ] [[package]] diff --git a/client/Makefile.toml b/client/Makefile.toml index e89d6d09..5da74f85 100644 --- a/client/Makefile.toml +++ b/client/Makefile.toml @@ -11,7 +11,7 @@ args = [ "fmt", "--all" ] [tasks.logic] workspace = false -env = { STACK_SIZE = "134217728", CARGO_TARGET_DIR = "../target/logic", CRATE = "logic" } +env = { CARGO_TARGET_DIR = "../target/logic", CRATE = "logic" } script = [ "cargo make crate-build wasm-logic", "cargo make wasm logic", @@ -19,7 +19,7 @@ script = [ [tasks.graphics] workspace = false -env = { STACK_SIZE = "4194304", CARGO_TARGET_DIR = "../target/graphics", CRATE = "graphics" } +env = { CARGO_TARGET_DIR = "../target/graphics", CRATE = "graphics" } script = [ "cargo make crate-build wasm-graphics", "cargo make wasm graphics", @@ -27,10 +27,14 @@ script = [ [tasks.crate-build] workspace = false -command = "cargo" -condition = { env_set = [ "STACK_SIZE", "CARGO_TARGET_DIR" ] } -env = { "RUSTFLAGS" = "-Clink-arg=--stack-first -Clink-arg=--no-entry -Clink-arg=--allow-undefined -Clink-arg=--strip-all -Clink-arg=--export-dynamic -Clink-arg=--import-memory -Clink-arg=--shared-memory -Clink-arg=--max-memory=1073741824 -Clink-arg=--threads -Clink-arg=-zstack-size=${STACK_SIZE} -Clink-arg=--export=__wasm_init_memory -Clink-arg=--no-check-features -Clink-arg=--export=__wasm_init_tls -Clink-arg=--export=__tls_size -Ctarget-feature=+atomics,+bulk-memory" } -args = [ "make", "exec-${BUILD_ENV}", "--", "build", "-p", "rask-${@}", "--target", "wasm32-unknown-unknown" ] +condition = { env_set = [ "CARGO_TARGET_DIR" ] } +env = { "RUSTFLAGS" = "-Clink-arg=--no-entry -Clink-arg=--allow-undefined -Clink-arg=--strip-all -Clink-arg=--export-dynamic -Clink-arg=--import-memory -Clink-arg=--shared-memory -Clink-arg=--threads -Clink-arg=--export=__wasm_init_memory -Clink-arg=--no-check-features -Clink-arg=--export=__wasm_init_tls -Clink-arg=--export=__tls_size -Ctarget-feature=+atomics,+bulk-memory" } +script = [ + "touch build.rs #triggers the build.rs to be rebuild every time", + "cargo make exec-${BUILD_ENV} -- build -p rask-${@} --target wasm32-unknown-unknown", + "mkdir -p gen", + "mv $(find $CARGO_TARGET_DIR/ -name mem.json) gen/", +] # TODO cargo doesn't seem to have a post-build hook but it might be nice # to determine if a rebuild happened (otherwise we don't need to rerun wasm-bindgen). diff --git a/client/build.rs b/client/build.rs new file mode 100644 index 00000000..905bcf9b --- /dev/null +++ b/client/build.rs @@ -0,0 +1,102 @@ +use std::fs::File; +use std::io::prelude::*; + +#[allow(non_snake_case)] +const fn KiB(n: u32) -> u32 { + n * 1024 +} +#[allow(non_snake_case)] +const fn MiB(n: u32) -> u32 { + n * KiB(1024) +} +/// align the given address to the next 32bit +const fn align32_up(n: u32) -> u32 { + (n + 3) & !3 +} + +const WORKER_NAME_VAR: &'static str = "CRATE"; + +/// Reserved memory +const MAX_MEORY: u32 = MiB(512); + +/// The first page of memory is reserved +const STACK_ALIGNMENT: u32 = 1024 * 63; + +/// The size of the stack. Its start is at address 0 +const GRAPHICS_STACK_SIZE: u32 = MiB(4); +const GRAPHICS_HEAP_SIZE: u32 = MiB(1); + +/// The size of the Allocator structures +const ALLOCATOR_SIZE: u32 = MiB(1); + +/// Size of the internal resource library. +/// This determines the highest available id. +const RESOURCE_TABLE_SIZE: u32 = KiB(1); + +/// Size of the message queue used to communicate between main.js and the logic thread +/// Its address must be exported to javascript. +const MESSAGE_QUEUE_SIZE: u32 = 64; + +/// The address memory synchronization area. +/// It contains data needed for synchronization between main thread and logic thread. +/// This address must be exported to javascript. +const SYNCHRONIZATION_MEMORY_SIZE: u32 = 32; + +/// Number of sprites to store in the double buffer +const BUFFER_SIZE: u32 = KiB(1); + +fn main() -> std::io::Result<()> { + println!("{:#?}", std::env::vars().collect::>()); + let name = std::env::var(WORKER_NAME_VAR); + let is_logic = match name { + Ok(worker) if &worker == "logic" => true, + Ok(worker) if &worker == "graphics" => false, + Ok(key) => panic!( + "{} is no valid value for {}. Possible values are logic and graphics", + key, WORKER_NAME_VAR, + ), + Err(std::env::VarError::NotPresent) => { + panic!("{} is not defined in the environment.", WORKER_NAME_VAR) + } + Err(err) => panic!("env var parsing failed (\"{:?}\")", err), + }; + + let graphics_stack = align32_up(STACK_ALIGNMENT + GRAPHICS_STACK_SIZE); + let alloc = align32_up(graphics_stack); + let graphics_heap = align32_up(alloc + ALLOCATOR_SIZE); + let sync = align32_up(alloc + GRAPHICS_HEAP_SIZE); + let table = align32_up(sync + SYNCHRONIZATION_MEMORY_SIZE); + let buffer = align32_up(table + RESOURCE_TABLE_SIZE); + let queue = align32_up(buffer + BUFFER_SIZE); + let logic_heap = align32_up(queue + MESSAGE_QUEUE_SIZE); + + println!("cargo:rustc-env=GRAPHICS_STACK={}", graphics_stack); + println!("cargo:rustc-env=ALLOCATOR={}", alloc); + println!("cargo:rustc-env=GRAPHICS_HEAP={}", graphics_heap); + println!("cargo:rustc-env=SYNCHRONIZATION_MEMORY={}", sync); + println!("cargo:rustc-env=RESOURCE_TABLE={}", table); + println!("cargo:rustc-env=RESOURCE_TABLE_SIZE={}", buffer - table); + println!("cargo:rustc-env=DOUBLE_BUFFER={}", buffer); + println!("cargo:rustc-env=DOUBLE_BUFFER_SIZE={}", queue - buffer); + println!("cargo:rustc-env=MESSAGE_QUEUE={}", queue); + println!("cargo:rustc-env=MESSAGE_QUEUE_SIZE={}", logic_heap - queue); + println!("cargo:rustc-env=LOGIC_HEAP={}", logic_heap); + + if !is_logic { + println!("cargo:rustc-cdylib-link-arg=--stack-first"); + println!( + "cargo:rustc-cdylib-link-arg=-zstack-size={}", + graphics_stack + ); + }; + println!("cargo:rustc-cdylib-link-arg=--max-memory={}", MAX_MEORY); + + let out_dir = std::env::var("OUT_DIR").unwrap(); + let mut file = File::create(format!("{}/mem.json", out_dir))?; + write!( + &mut file, + "{{max_memory:{},queue_start:{},sync_area:{}}}", + MAX_MEORY, queue, sync + )?; + Ok(()) +} diff --git a/client/double-buffer/Cargo.toml b/client/double-buffer/Cargo.toml deleted file mode 100644 index 3ca866c4..00000000 --- a/client/double-buffer/Cargo.toml +++ /dev/null @@ -1,11 +0,0 @@ -[package] -name = "double-buffer" -version = "0.1.0" -authors = ["natrixaeria"] -edition = "2018" - -[lib] -crate-type = ["rlib"] - -[profile.release] -lto = true diff --git a/client/double-buffer/src/lib.rs b/client/double-buffer/src/lib.rs deleted file mode 100644 index 474dbb2c..00000000 --- a/client/double-buffer/src/lib.rs +++ /dev/null @@ -1,110 +0,0 @@ -use std::fmt::Debug; - -pub trait Element: Clone + Sized + Default + Debug {} -type Flag = u8; - -impl Element for T {} - -#[derive(Debug)] -pub struct DoubleBuffer { - reading_at: Flag, - provided: Flag, - buffer: [T; 2], -} - -#[derive(Debug)] -pub struct ReaderBufferView<'a, T: Element> { - ptr: &'a mut DoubleBuffer, - read_pos: u8, -} - -#[derive(Debug)] -pub struct WriterBufferView<'a, T: Element> { - ptr: &'a mut DoubleBuffer, - write_pos: u8, -} - -impl DoubleBuffer { - pub fn new() -> Self { - DoubleBuffer { - reading_at: 0, - provided: 0, - buffer: [ - T::default(), - T::default() - ]} - } - - pub fn borrow_reader<'a>(&'a mut self) -> Option> { - match (self.get_reading_at(), self.get_provided()) { - (0, 0) => None, - (0, p) => { - let mut x = p; - self.set_reading_at(x); - while x != p { - x = p; - self.set_reading_at(x); - }; - Some(ReaderBufferView { ptr: self, read_pos: x - 1 }) - }, - (c, p) => panic!("invalid state ({},{}) for consumer reached", c, p), - } - } - - pub fn borrow_writer<'a>(&'a mut self) -> WriterBufferView<'a, T> { - let write_pos = match (self.get_reading_at(), self.get_provided()) { - (0, 0) => 0, - (0, y) => 2 - y, - (y, x) => if x == y { 2 - y } else { self.set_provided(y); y - 1 }, - }; - WriterBufferView { ptr: self, write_pos } - } - - #[inline(never)] - #[no_mangle] - fn set_reading_at(&mut self, reading_at: Flag) { - self.reading_at = reading_at; - } - - #[inline(never)] - #[no_mangle] - fn get_reading_at(&mut self) -> Flag { - self.reading_at - } - - #[inline(never)] - #[no_mangle] - fn set_provided(&mut self, provided: Flag) { - self.provided = provided; - } - - #[inline(never)] - #[no_mangle] - fn get_provided(&mut self) -> Flag { - self.provided - } -} - -impl<'a, T: Element> ReaderBufferView<'a, T> { - pub fn get(&self) -> &T { - &self.ptr.buffer[self.read_pos as usize] - } -} - -impl<'a, T: Element> WriterBufferView<'a, T> { - pub fn set(&mut self, data: T) { - self.ptr.buffer[self.write_pos as usize] = data; - } -} - -impl<'a, T: Element> std::ops::Drop for ReaderBufferView<'a, T> { - fn drop(&mut self) { - self.ptr.set_reading_at(0); - } -} - -impl<'a, T: Element> std::ops::Drop for WriterBufferView<'a, T> { - fn drop(&mut self) { - self.ptr.set_provided(self.write_pos + 1); - } -} diff --git a/client/double-buffer/tests/integration.rs b/client/double-buffer/tests/integration.rs deleted file mode 100644 index b736316a..00000000 --- a/client/double-buffer/tests/integration.rs +++ /dev/null @@ -1,21 +0,0 @@ -use double_buffer::DoubleBuffer; - -#[test] -fn test_initial_empty() { - let mut db: DoubleBuffer = DoubleBuffer::new(); - - assert!(db.borrow_reader().is_none()); -} - -#[test] -fn test_simple_read_write() { - let mut db = DoubleBuffer::new(); - { - let mut writer = db.borrow_writer(); - writer.set(42); - } - { - let reader = db.borrow_reader().unwrap(); - assert_eq!(*reader.get(), 42); - } -} diff --git a/client/graphics/Cargo.toml b/client/graphics/Cargo.toml index 4acd72dc..12f56003 100644 --- a/client/graphics/Cargo.toml +++ b/client/graphics/Cargo.toml @@ -17,6 +17,7 @@ log = "0.4" js-sys = "=0.3.36" fern = "0.5.9" rand = "0.7" +lazy_static = "1.4" [dependencies.rask-wasm-shared] version = "0.1.0" diff --git a/client/graphics/build.rs b/client/graphics/build.rs new file mode 120000 index 00000000..10238032 --- /dev/null +++ b/client/graphics/build.rs @@ -0,0 +1 @@ +../build.rs \ No newline at end of file diff --git a/client/graphics/src/context.rs b/client/graphics/src/context.rs index 9db72893..a45513d8 100644 --- a/client/graphics/src/context.rs +++ b/client/graphics/src/context.rs @@ -1,7 +1,19 @@ use crate::graphics::WebGl; use crate::render::Render; +use lazy_static::lazy_static; +use rask_engine::resources::{GetStore, ResourceTable}; use rask_wasm_shared::error::ClientError; -use rask_wasm_shared::sprite::{Animation, Frame}; +use rask_wasm_shared::mem; +use rask_wasm_shared::sprite::Frame; + +lazy_static! { + pub static ref RESOURCE_TABLE: ResourceTable = unsafe { + let mut table = + ResourceTable::from_memory(mem::RESOURCE_TABLE, mem::RESOURCE_TABLE_ELEMENT_COUNT); + table.clear(); + table + }; +} pub struct Context { render: Render, @@ -13,8 +25,7 @@ impl Context { } pub fn render(&mut self) -> Result<(), ClientError> { - self.render - .render(rask_wasm_shared::mem::shared_heap().animations()) + self.render.render() } } diff --git a/client/graphics/src/graphics.rs b/client/graphics/src/graphics.rs index d4fcf370..922d63c8 100644 --- a/client/graphics/src/graphics.rs +++ b/client/graphics/src/graphics.rs @@ -1,10 +1,10 @@ use crate::shader::Program; use rask_engine::math; use rask_engine::math::Mat3; +use rask_engine::resources::texture::{ColorType, Texture}; use rask_wasm_shared::error::ClientError; use rask_wasm_shared::sprite::TextureId; use rask_wasm_shared::state::State; -use rask_wasm_shared::texture::{ColorType, Texture}; use wasm_bindgen::prelude::*; use wasm_bindgen::JsCast; use web_sys::WebGl2RenderingContext as Gl2; @@ -69,9 +69,10 @@ pub trait GraphicsApi: Sized { fn start_frame(&mut self, color: &[f32; 3]) -> Result<(), ClientError>; fn end_frame(&self) -> Result<(), ClientError>; - fn draw_rect(&self, pos: &math::Vec2, mat: &Mat3, tex: u32) -> Result<(), ClientError>; - fn upload_texture(&mut self, texture: &mut Texture, n: u32) -> Result<(), ClientError>; + fn draw_rect(&self, mat: &Mat3, tex: u32) -> Result<(), ClientError>; + fn upload_texture(&mut self, texture: &Texture, n: u32) -> Result<(), ClientError>; fn resize_texture_pool(&mut self, n: u32) -> Result<(), ClientError>; + fn grow_texture_pool(&mut self, n: u32) -> Result<(), ClientError>; fn ok(&self) -> Result<(), Self::GraphicsError>; } @@ -208,7 +209,7 @@ impl GraphicsApi for WebGl { }) } - fn upload_texture(&mut self, texture: &mut Texture, n: u32) -> Result<(), ClientError> { + fn upload_texture(&mut self, texture: &Texture, n: u32) -> Result<(), ClientError> { log::debug!( "uploading texture (id {}, {}x{})", n, @@ -218,13 +219,13 @@ impl GraphicsApi for WebGl { let handle = WebGlApiTexture::new(&self.gl)?; self.gl.active_texture(Gl2::TEXTURE0); handle.bind(&self.gl); - if let ColorType::Rgb8 = texture.colortype() { + if let ColorType::Rgb8 = texture.color_type() { // TODO: copy RGB buffer to RGBA return Err(ClientError::ResourceError(format!( "RGB not yet implemented" ))); } - let (internalformat, format) = Self::colorformat(texture.colortype())?; + let (internalformat, format) = Self::colorformat(texture.color_type())?; let (w, h) = texture.dimension(); self.gl .tex_image_2d_with_i32_and_i32_and_i32_and_format_and_type_and_opt_u8_array( @@ -256,6 +257,12 @@ impl GraphicsApi for WebGl { Ok(()) } + fn grow_texture_pool(&mut self, n: u32) -> Result<(), ClientError> { + Ok(self + .texture_handles + .resize(self.texture_handles.len() + n as usize, None)) + } + fn ok(&self) -> Result<(), Self::GraphicsError> { Self::_ok(&self.gl) } @@ -272,13 +279,13 @@ impl GraphicsApi for WebGl { self.fb.render_pass_1(&self.gl); self.gl .viewport(0, 0, self.width as i32, self.height as i32); - self.draw_rect_notexture(&math::Vec2::new(0.0, 0.0), &-Mat3::identity())?; + self.draw_rect_notexture(&-Mat3::identity())?; Ok(()) } - fn draw_rect(&self, pos: &math::Vec2, mat: &Mat3, tex: TextureId) -> Result<(), ClientError> { + fn draw_rect(&self, mat: &Mat3, tex: TextureId) -> Result<(), ClientError> { self.bind_texture(tex); - self.draw_rect_notexture(pos, mat) + self.draw_rect_notexture(mat) } } @@ -348,11 +355,10 @@ impl WebGl { } } - fn draw_rect_notexture(&self, pos: &math::Vec2, mat: &Mat3) -> Result<(), ClientError> { + fn draw_rect_notexture(&self, mat: &Mat3) -> Result<(), ClientError> { self.prog.upload_fransformation(&self.gl, mat); self.prog.upload_texture_id(&self.gl, 0); - self.gl - .vertex_attrib2fv_with_f32_array(1, &[pos.x(), pos.y()]); + // TODO Fix self.gl.vertex_attrib2fv_with_f32_array(1, &[pos.x(), pos.y()]); self.gl.draw_arrays(Gl2::TRIANGLES, 0, 6); Ok(()) } diff --git a/client/graphics/src/render.rs b/client/graphics/src/render.rs index 7ebdced3..ebe42e25 100644 --- a/client/graphics/src/render.rs +++ b/client/graphics/src/render.rs @@ -1,10 +1,12 @@ +use crate::context::RESOURCE_TABLE; use crate::graphics::GraphicsApi; use rask_engine::math::Mat3; +use rask_engine::resources::GetStore; +use rask_engine::resources::Texture; use rask_wasm_shared::error::ClientError; use rask_wasm_shared::get_double_buffer; -use rask_wasm_shared::sprite::{Animation, Frame, Sprite}; +use rask_wasm_shared::sprite::{Frame, Sprite}; use rask_wasm_shared::state::State; -use rask_wasm_shared::texture::Texture; pub struct Render { graphics: T, @@ -22,65 +24,38 @@ impl Render { }) } - pub fn render(&mut self, animations: &[Animation]) -> Result<(), ClientError> { + pub fn render(&mut self) -> Result<(), ClientError> { self.graphics .ok() .map_err(|e| ClientError::WebGlError(format!("WebGl2 error: {}", e)))?; - if rask_wasm_shared::mem::shared_heap().get_texture_notify() { - rask_wasm_shared::mem::shared_heap().unset_texture_notify(); - self.update_textures()?; - } self.graphics.start_frame(&[0.8, 0.05, 0.55])?; - if self.draw_sprites(animations)? { + if self.draw_sprites()? { self.frame_nr += 1; } self.graphics.end_frame() } - pub fn update_textures(&mut self) -> Result<(), ClientError> { - if let Some(textures) = rask_wasm_shared::mem::shared_heap().textures_mut() { - let n = textures.len() as u32; - self.graphics.resize_texture_pool(n)?; - if n > self.texture_count { - for (i, texture) in textures - .iter_mut() - .skip(self.texture_count as usize) - .enumerate() - { - self.graphics.upload_texture(texture, i as u32)? - } - } - self.texture_count = n; - } + pub fn upload_texture(&mut self, id: u32) -> Result<(), ClientError> { + let texture = unsafe { RESOURCE_TABLE.get(id as usize)? }; + //self.graphics.grow_texture_pool(1); + self.graphics.upload_texture(texture, id)?; + self.texture_count += 1; Ok(()) } - pub fn draw_sprites(&mut self, animations: &[Animation]) -> Result { + pub fn draw_sprites(&mut self) -> Result { if let Some(state) = get_double_buffer().borrow_reader() { - for sprite in state.get().sprites().iter() { + let sprites = state.get().sprites(); + self.graphics.resize_texture_pool(sprites.len() as u32)?; + self.texture_count = 0; + for sprite in sprites { //log::debug!("draw sprite: {:?}", sprite); - self.draw_sprite(sprite, animations)?; + self.upload_texture(sprite.tex_id)?; + self.graphics.draw_rect(&sprite.transform, sprite.tex_id)?; } Ok(true) } else { Ok(false) } } - - pub fn draw_sprite( - &mut self, - sprite: &Sprite, - animations: &[Animation], - ) -> Result<(), ClientError> { - let frame = sprite - .get_frame(animations) - .ok_or(ClientError::ResourceError( - "could not get animation frame".to_owned(), - ))?; - for transformation in frame.transformations().iter() { - self.graphics - .draw_rect(&sprite.pos, transformation, sprite.tex_id)?; - } - Ok(()) - } } diff --git a/client/logic/build.rs b/client/logic/build.rs new file mode 120000 index 00000000..10238032 --- /dev/null +++ b/client/logic/build.rs @@ -0,0 +1 @@ +../build.rs \ No newline at end of file diff --git a/client/logic/src/game_context.rs b/client/logic/src/game_context.rs index 0410b292..154b3f26 100644 --- a/client/logic/src/game_context.rs +++ b/client/logic/src/game_context.rs @@ -1,6 +1,8 @@ use rask_engine::math; +use rask_engine::resources::{GetStore, ResourceTable, Texture}; use rask_wasm_shared::error::ClientError; use rask_wasm_shared::get_double_buffer; +use rask_wasm_shared::mem::{RESOURCE_TABLE, RESOURCE_TABLE_ELEMENT_COUNT}; use rask_wasm_shared::sprite::*; use rask_wasm_shared::state::State; @@ -10,6 +12,7 @@ const IMAGE2_DATA: &[u8] = include_bytes!("../../res/thief.png"); pub struct GameContext { state: State, tick_nr: u64, + resource_table: ResourceTable, } impl GameContext { @@ -17,6 +20,9 @@ impl GameContext { Ok(Self { state: State::default(), tick_nr: 0, + resource_table: unsafe { + ResourceTable::from_memory(RESOURCE_TABLE, RESOURCE_TABLE_ELEMENT_COUNT) + }, }) } @@ -28,55 +34,16 @@ impl GameContext { pub fn tick(&mut self) -> Result<(), ClientError> { if self.state.sprites().is_empty() { - self.state - .append_sprite(&Sprite::new(math::Vec2::new(0.0, 0.0), 3, 0, 0)); - self.state - .append_sprite(&Sprite::new(math::Vec2::new(0.0, 0.0), 2, 0, 0)); - self.state - .append_sprite(&Sprite::new(math::Vec2::new(0.3, 0.3), 0, 0, 1)); - self.state - .append_sprite(&Sprite::new(math::Vec2::new(0.0, 0.0), 1, 0, 1)); - self.state - .append_sprite(&Sprite::new(math::Vec2::new(0.0, -0.6), 0, 0, 1)); - self.state - .append_sprite(&Sprite::new(math::Vec2::new(-0.6, 0.6), 1, 0, 1)); + self.state.append_sprite(&Sprite::default()); - let shared_heap = rask_wasm_shared::mem::shared_heap(); - *shared_heap.animations_mut() = vec![ - Animation::new(vec![ - Frame::new(vec![rask_engine::math::Mat3::scaling(0.4, 0.4)]), - Frame::new(vec![ - rask_engine::math::Mat3::scaling(0.4, 0.4) - * rask_engine::math::Mat3::translation(0.5, 0.0) - * rask_engine::math::Mat3::rotation(6.0), - ]), - ]), - Animation::new(vec![ - Frame::new(vec![rask_engine::math::Mat3::scaling(0.4, 0.4)]), - Frame::new(vec![rask_engine::math::Mat3::scaling(0.6, 0.2)]), - ]), - Animation::new(vec![Frame::new(vec![rask_engine::math::Mat3::scaling( - 9.0 / 16.0, - 1.0, - )])]), - Animation::new(vec![Frame::new(vec![rask_engine::math::Mat3::identity()])]), - ]; - - *shared_heap.textures_mut() = Some(vec![ - rask_wasm_shared::texture::Texture::from_png_stream(IMAGE1_DATA)?, - rask_wasm_shared::texture::Texture::from_png_stream(IMAGE2_DATA)?, - ]); - shared_heap.set_texture_notify(); - } - - let animations = rask_wasm_shared::mem::shared_heap().animations(); - for sprite in self.state.sprites_mut().iter_mut() { - if (self.tick_nr % 10) == 9 { - sprite - .next_frame(animations) - .ok_or(ClientError::ResourceError(format!("invalid animation id")))?; + unsafe { + self.resource_table + .store(Texture::from_png_stream(IMAGE1_DATA)?, 0)?; + self.resource_table + .store(Texture::from_png_stream(IMAGE2_DATA)?, 1)?; } } + self.push_state()?; self.tick_nr += 1; Ok(()) diff --git a/client/shared/Cargo.toml b/client/shared/Cargo.toml index 3dc49be9..768f5bb6 100644 --- a/client/shared/Cargo.toml +++ b/client/shared/Cargo.toml @@ -18,6 +18,7 @@ js-sys = "=0.3.36" fern = "0.5.9" rand = "0.7" image = "0.23" +const_env = "0.1" [dependencies.rask-engine] version = "0.2.0" diff --git a/client/shared/src/alloc.rs b/client/shared/src/alloc.rs index cc4a09be..42e0a33c 100644 --- a/client/shared/src/alloc.rs +++ b/client/shared/src/alloc.rs @@ -1,6 +1,8 @@ +use settings::AllocSettings; +use std::alloc::{GlobalAlloc, Layout}; + pub mod settings { use crate::mem::*; - use std::alloc::{GlobalAlloc, Layout}; pub trait AllocSettings { fn allocator_addr() -> usize; @@ -11,26 +13,23 @@ pub mod settings { impl AllocSettings for Logic { fn allocator_addr() -> usize { - ALLOCATOR_AREA_START + ALLOCATOR } fn allocation_start_address() -> isize { - LOGIC_ALLOCATION_AREA_START as isize + LOGIC_HEAP as isize } } impl AllocSettings for Graphics { fn allocator_addr() -> usize { - ALLOCATOR_AREA_START + std::mem::size_of::() + ALLOCATOR + std::mem::size_of::() } fn allocation_start_address() -> isize { - GRAPHICS_ALLOCATION_AREA_START as isize + GRAPHICS_HEAP as isize } } } -use settings::AllocSettings; -use std::alloc::{GlobalAlloc, Layout}; - pub trait MutableAlloc { unsafe fn alloc(&mut self, layout: Layout) -> *mut u8; unsafe fn dealloc(&mut self, ptr: *mut u8, layout: Layout); diff --git a/client/shared/src/double_buffer.rs b/client/shared/src/double_buffer.rs index 786578b8..bf767c19 100644 --- a/client/shared/src/double_buffer.rs +++ b/client/shared/src/double_buffer.rs @@ -72,22 +72,18 @@ impl DoubleBuffer { } } - #[inline(always)] pub extern "C" fn set_reading_at(&mut self, reading_at: Flag) { unsafe { mem::atomic_write_u8(&mut self.reading_at, reading_at) } } - #[inline(always)] pub extern "C" fn get_reading_at(&self) -> Flag { unsafe { mem::atomic_read_u8(&self.reading_at) } } - #[inline(always)] pub extern "C" fn set_provided(&mut self, provided: Flag) { unsafe { mem::atomic_write_u8(&mut self.provided, provided) } } - #[inline(always)] pub extern "C" fn get_provided(&self) -> Flag { unsafe { mem::atomic_read_u8(&self.provided) } } diff --git a/client/shared/src/error.rs b/client/shared/src/error.rs index 67eced27..539625f2 100644 --- a/client/shared/src/error.rs +++ b/client/shared/src/error.rs @@ -25,6 +25,7 @@ pub enum ClientError { WebSocketError(JsValue), WebGlError(String), ResourceError(String), + EngineError(String), } fn jsvalue_to_string(v: &JsValue) -> String { @@ -45,7 +46,9 @@ impl std::fmt::Display for ClientError { ClientError::JsValueError(e) | ClientError::WebSocketError(e) => { write!(f, "{}", jsvalue_to_string(e)) } - ClientError::ResourceError(e) | ClientError::WebGlError(e) => write!(f, "{}", e), + ClientError::ResourceError(e) + | ClientError::EngineError(e) + | ClientError::WebGlError(e) => write!(f, "{}", e), } } } @@ -55,3 +58,8 @@ impl From for ClientError { ClientError::JsValueError(error) } } +impl From for ClientError { + fn from(error: rask_engine::error::EngineError) -> Self { + ClientError::EngineError(format!("{}", error)) + } +} diff --git a/client/shared/src/lib.rs b/client/shared/src/lib.rs index b2159c42..ea163cd5 100644 --- a/client/shared/src/lib.rs +++ b/client/shared/src/lib.rs @@ -7,10 +7,10 @@ pub mod error; pub mod mem; pub mod sprite; pub mod state; -pub mod texture; pub mod wasm_log; pub use error::*; pub use mem::*; +//pub use rask_engine::resources::texture; pub use wee_alloc; diff --git a/client/shared/src/mem.rs b/client/shared/src/mem.rs index 5c69ad9a..2f7891ae 100644 --- a/client/shared/src/mem.rs +++ b/client/shared/src/mem.rs @@ -1,46 +1,64 @@ -use crate::state::State; type Buffer = crate::double_buffer::DoubleBuffer; -use crate::{sprite::*, texture::*}; - -const fn KiB(n: usize) -> usize { - n * 1024 -} -const fn MiB(n: usize) -> usize { - n * KiB(1024) -} +use crate::double_buffer::DoubleBuffer; +use crate::sprite::Sprite; +use crate::state::State; +use const_env::from_env; +use rask_engine::resources::{Resource, ResourceTable}; +use std::mem::size_of; -const STACK_ALIGNMENT: usize = 1024 * 63; -const MESSAGE_QUEUE_LENGTH: usize = 32; +#[from_env] +/// The position of the stack. +pub const GRAPHIC_STACK: usize = 0; -/// The size of the stack. Its start is at address 0 -pub const GRAPHIC_STACK_SIZE: usize = MiB(4) + STACK_ALIGNMENT; +#[from_env] +/// The address of the Allocator structures +pub const ALLOCATOR: usize = 0; -/// The address of the Allocator structures (size: 1MiB) -pub const ALLOCATOR_AREA_START: usize = GRAPHIC_STACK_SIZE; +#[from_env] +/// The graphics heap address +pub const GRAPHICS_HEAP: usize = 0; +#[from_env] /// The address memory synchronization area. (size: 1MiB) /// It contains data needed for synchronization between main thread and logic thread. /// This address must currently be 0x50fc00. /// On change you have to modify the corresponding js file. -pub const SYNCHRONIZATION_MEMORY_START: usize = ALLOCATOR_AREA_START + MiB(1); - +pub const SYNCHRONIZATION_MEMORY: usize = 0; + +#[from_env] +/// Address of the internal resource library. +pub const RESOURCE_TABLE: usize = 0; +#[from_env] +pub const RESOURCE_TABLE_SIZE: usize = 0; +pub const RESOURCE_TABLE_ELEMENT_COUNT: usize = (RESOURCE_TABLE_SIZE as i64 + - size_of::() as i64) as usize + / size_of::(); + +#[from_env] /// The address of the double buffer (size: target dependent) -pub const SHARED_BUFFER_AREA_START: usize = - SYNCHRONIZATION_MEMORY_START + core::mem::size_of::(); - +pub const DOUBLE_BUFFER: usize = 0; +#[from_env] +pub const DOUBLE_BUFFER_SIZE: usize = 0; +pub const DOUBLE_BUFFER_ELEMENT_COUNT: usize = + (DOUBLE_BUFFER_SIZE as i64 - size_of::>() as i64) as usize + / size_of::(); + +#[from_env] +/// Address of the event queue +pub const MESSAGE_QUEUE: usize = 0; +#[from_env] +pub const MESSAGE_QUEUE_SIZE: usize = 0; +pub const MESSAGE_QUEUE_ELEMENT_COUNT: usize = (MESSAGE_QUEUE_SIZE as i64 + - size_of::>() as i64) as usize + / size_of::>(); + +#[from_env] /// The logic heap address (size: 32MiB) -pub const LOGIC_ALLOCATION_AREA_START: usize = - SHARED_BUFFER_AREA_START + core::mem::size_of::(); - -/// The graphics heap address (size: 32MiB) -pub const GRAPHICS_ALLOCATION_AREA_START: usize = LOGIC_ALLOCATION_AREA_START + MiB(32); - -/// The start of unbounded shared memory (size: unbounded) -pub const SHARED_ALLOCATION_AREA_START: usize = GRAPHICS_ALLOCATION_AREA_START + MiB(32); +pub const LOGIC_HEAP: usize = 0; pub fn get_double_buffer() -> &'static mut Buffer { - unsafe { &mut *(SHARED_BUFFER_AREA_START as *mut Buffer) } + unsafe { &mut *(DOUBLE_BUFFER as *mut Buffer) } } #[repr(align(4))] @@ -48,15 +66,17 @@ pub struct SynchronizationMemory { /// time elapsed since logic thread initialisation in milliseconds pub elapsed_ms: i32, last_elapsed_ms: i32, + pub mouse_x: f32, + pub mouse_y: f32, } impl SynchronizationMemory { pub unsafe fn get() -> &'static Self { - &*(SYNCHRONIZATION_MEMORY_START as *const Self) + &*(SYNCHRONIZATION_MEMORY as *const Self) } pub unsafe fn get_mut() -> &'static mut Self { - &mut *(SYNCHRONIZATION_MEMORY_START as *mut Self) + &mut *(SYNCHRONIZATION_MEMORY as *mut Self) } pub fn wait_for_main_thread_notify(&mut self) { @@ -82,14 +102,15 @@ impl MessageQueueElement { fn get_writing(&self) -> u8 { unsafe { atomic_read_u8(&self.writing) } } - fn read(&mut self) -> Option { self.set_reading(1); if self.get_writing() == 0 { let e = self.payload.clone(); self.set_reading(0); Some(e) - } else { None } + } else { + None + } } } @@ -99,12 +120,12 @@ pub struct MessageQueue { writer_index: u32, /// the index of the next element to be read reader_index: u32, - _phantom: core::marker::PhantomData + _phantom: core::marker::PhantomData, } impl MessageQueue { pub fn length() -> usize { - MESSAGE_QUEUE_LENGTH + MESSAGE_QUEUE_ELEMENT_COUNT } fn mem_size() -> usize { @@ -112,7 +133,12 @@ impl MessageQueue { } unsafe fn get_mut(&mut self, n: usize) -> Option<&mut MessageQueueElement> { - core::slice::from_raw_parts_mut((self as *mut Self as *mut u8).offset(core::mem::size_of::() as isize) as *mut MessageQueueElement, Self::length()).get_mut(n) + core::slice::from_raw_parts_mut( + (self as *mut Self as *mut u8).offset(core::mem::size_of::() as isize) + as *mut MessageQueueElement, + Self::length(), + ) + .get_mut(n) } pub fn pop(&mut self) -> Option { @@ -126,47 +152,6 @@ impl MessageQueue { } } -pub struct SharedHeap { - last_addr: u32, - animations: Vec, - texture_notify: bool, - textures: Option>, -} - -impl SharedHeap { - pub fn animations_mut(&mut self) -> &mut Vec { - &mut self.animations - } - - pub fn animations(&self) -> &Vec { - &self.animations - } - - pub fn unset_texture_notify(&mut self) { - self.texture_notify = false - } - - pub fn set_texture_notify(&mut self) { - self.texture_notify = true - } - - pub fn get_texture_notify(&mut self) -> bool { - self.texture_notify - } - - pub fn textures_mut(&mut self) -> &mut Option> { - &mut self.textures - } - - pub fn textures(&self) -> &Option> { - &self.textures - } -} - -pub fn shared_heap() -> &'static mut SharedHeap { - unsafe { &mut *(SHARED_ALLOCATION_AREA_START as *mut SharedHeap) } -} - extern "C" { #[link_name = "llvm.wasm.atomic.wait.i32"] /// see https://github.com/WebAssembly/threads/blob/master/proposals/threads/Overview.md#wait-and-notify-operators diff --git a/client/shared/src/sprite.rs b/client/shared/src/sprite.rs index b7111c4e..b446e46b 100644 --- a/client/shared/src/sprite.rs +++ b/client/shared/src/sprite.rs @@ -6,70 +6,22 @@ pub type TextureId = u32; #[derive(Clone, Copy, Debug)] pub struct Sprite { - pub pos: math::Vec2, - pub animation_id: AnimationId, - pub frame_id: FrameId, + pub transform: math::Mat3, pub tex_id: TextureId, } impl Default for Sprite { fn default() -> Self { Self { - pos: math::Vec2::new(0.0, 0.0), - animation_id: 0, - frame_id: 0, + transform: math::Mat3::new(0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0), tex_id: 0, } } } impl Sprite { - pub fn get_animation<'a>(&self, animations: &'a [Animation]) -> Option<&'a Animation> { - animations.get(self.animation_id as usize) - } - - pub fn get_frame<'a>(&self, animations: &'a [Animation]) -> Option<&'a Frame> { - self.get_animation(animations)? - .frames - .get(self.frame_id as usize) - } - - pub fn new( - pos: math::Vec2, - animation_id: AnimationId, - frame_id: FrameId, - tex_id: TextureId, - ) -> Self { - Self { - pos, - animation_id, - frame_id, - tex_id, - } - } - - pub fn next_frame(&mut self, animations: &[Animation]) -> Option { - self.frame_id = self.get_animation(animations)?.next(self.frame_id); - Some(self.frame_id) - } -} - -#[derive(Debug)] -pub struct Animation { - frames: Vec, -} - -impl Animation { - pub fn new(frames: Vec) -> Self { - Self { frames } - } - - pub fn next(&self, frame_id: FrameId) -> FrameId { - match self.frames.len() as u32 { - 0 => 0, - len if len - 1 <= frame_id => (frame_id + 1) % len, - _ => frame_id + 1, - } + pub fn new(transform: math::Mat3, tex_id: TextureId) -> Self { + Self { transform, tex_id } } } diff --git a/rask-engine/Cargo.toml b/rask-engine/Cargo.toml index 89a78e5c..be7c26df 100644 --- a/rask-engine/Cargo.toml +++ b/rask-engine/Cargo.toml @@ -5,3 +5,8 @@ authors = ["konsumlamm ", "Dennis Kobert for AABox { } } -impl PartialEq for AABox { - fn eq(&self, other: &Self) -> bool { - self.pos == other.pos && self.size == other.size - } -} - -impl Eq for AABox {} - /// A rotated box. -#[derive(Clone, Copy, Debug)] +#[derive(Clone, Copy, Debug, Eq, PartialEq)] pub struct RBox { /// The origin. pub pos: Vec2, @@ -68,9 +61,10 @@ pub struct RBox { impl RBox { /// Creates a new rotated box from a position, an orientation and a width. - pub fn new(pos: Vec2, orientation: Vec2, width: f32) -> Self { - let scale = width / orientation.norm(); - let orth = Vec2::new(orientation.x(), -orientation.y()) / scale; + // v2 has the same direction as v1 rotated to the left by 90° + pub fn new(pos: Vec2, orientation: Vec2, height: f32) -> Self { + let scale = height / orientation.norm(); + let orth = Vec2::new(-orientation.y(), orientation.x()) * scale; Self { pos, v1: orientation, @@ -115,10 +109,21 @@ impl ops::SubAssign for RBox { } } -impl PartialEq for RBox { - fn eq(&self, other: &Self) -> bool { - self.pos == other.pos && self.v1 == other.v1 && self.v2 == other.v2 +impl From for RBox { + fn from(aabox: AABox) -> Self { + Self { + pos: aabox.pos, + v1: Vec2::new(aabox.size.x(), 0.0), + v2: Vec2::new(0.0, aabox.size.y()), + } } } -impl Eq for RBox {} +impl From<&spine::skeleton::SRT> for RBox { + fn from(srt: &spine::skeleton::SRT) -> RBox { + let pos = srt.transform([-1.0, -1.0]).into(); + let v1 = Vec2::from(srt.transform([1.0, -1.0])) - pos; + let v2 = Vec2::from(srt.transform([-1.0, 1.0])) - pos; + RBox { pos, v1, v2 } + } +} diff --git a/rask-engine/src/collide.rs b/rask-engine/src/collide.rs index e64c313e..a220691e 100644 --- a/rask-engine/src/collide.rs +++ b/rask-engine/src/collide.rs @@ -3,12 +3,108 @@ use crate::boxes::{AABox, RBox}; use crate::math::Vec2; +use core::ops::Range; + +// For information on the SAT, see: http://www.dyn4j.org/2010/01/sat/. /// A trait for objects that can collide with other objects. pub trait Collide { fn collides(&self, other: &Rhs) -> bool; } +fn left_under(v1: Vec2, v2: Vec2) -> bool { + v1.x() < v2.x() && v1.y() < v2.y() +} + +#[derive(Debug)] +struct Projection { + min: f32, + max: f32, +} + +impl Collide for Projection { + fn collides(&self, other: &Self) -> bool { + self.max >= other.min && self.min <= other.max + } +} + +fn project(rbox: &RBox, axis: &Vec2) -> Projection { + // the vertices of rbox without rbox.pos + let vertices = [ + rbox.pos + rbox.v1, + rbox.pos + rbox.v2, + rbox.pos + rbox.v1 + rbox.v2, + ]; + // project each vertex onto axis + vertices.iter().fold( + { + let p = axis.dot(rbox.pos); + Projection { min: p, max: p } + }, + |Projection { min, max }, vertex| { + let p = axis.dot(*vertex); + if p < min { + Projection { min: p, max } + } else if p > max { + Projection { min, max: p } + } else { + Projection { min, max } + } + }, + ) +} + +/// Calculate the bound in a line segment that collides an AABox projected onto an axis. +/// `bound` is a tuple of the start and ending point of the AABB. +/// `pos` is a component of the position vector of the line segment. +/// `direction` is a component of the direction vector of the line segment. +fn calculate_aabox_rbox_component_bounds( + bound: Range, + pos: f32, + direction: f32, +) -> (f32, f32) { + if direction == 0.0 { + return (0.0, 1.0); + } + // get bounds of s by transforming "g(s) = pos + s * direction" + // and applying the inequation g(s) >= bound.start and g(s) <= bound.end + let (s1, s2) = ( + (bound.start - pos) / direction, + (bound.end - pos) / direction, + ); + // if direction is negative, you have to switch the values + if direction > 0.0 { + (s1, s2) + } else { + (s2, s1) + } +} + +/// Test for collision between an AABox and an edge of a rbox +fn collide_aabox_rbox_segment( + xbound: Range, + ybound: Range, + pos: Vec2, + direction: Vec2, +) -> bool { + let sbound1 = calculate_aabox_rbox_component_bounds(xbound, pos.x(), direction.x()); + if sbound1.0 > sbound1.1 { + return false; + } + let sbound2 = calculate_aabox_rbox_component_bounds(ybound, pos.y(), direction.y()); + if sbound2.0 > sbound2.1 { + return false; + } + let (sbound1, sbound2) = (sbound1.0..sbound1.1, sbound2.0..sbound2.1); + + sbound1.end >= sbound2.start + && sbound1.start <= sbound2.end + && sbound1.end >= 0.0 + && sbound2.end >= 0.0 + && sbound1.start <= 1.0 + && sbound2.start <= 1.0 +} + impl Collide for Vec2 { fn collides(&self, other: &Self) -> bool { self == other @@ -17,100 +113,73 @@ impl Collide for Vec2 { impl Collide for AABox { fn collides(&self, other: &Vec2) -> bool { - self.pos < *other && *other < self.pos + self.size + left_under(self.pos, *other) && left_under(*other, self.pos + self.size) } } impl Collide for AABox { fn collides(&self, other: &Self) -> bool { - self.pos.x() < other.pos.x() + other.size.x() - && other.pos.x() < self.pos.x() + self.size.x() - && self.pos.y() < other.pos.y() + other.size.y() - && other.pos.y() < self.pos.y() + self.size.y() + left_under(self.pos, other.pos + other.size) && left_under(other.pos, self.pos + self.size) } } impl Collide for RBox { fn collides(&self, other: &Vec2) -> bool { - let v1_diff = *other + self.v1 * (-self.v1.dot(*other - self.pos) / self.v1.norm2()); - let v2_diff = *other + self.v2 * (-self.v2.dot(*other - self.pos) / self.v2.norm2()); - - let v1_dist = ((v1_diff - self.pos) / self.v2).x(); - let v2_dist = ((v2_diff - self.pos) / self.v1).x(); - 0.0 <= v1_dist && v1_dist <= 1.0 && 0.0 <= v2_dist && v2_dist <= 1.0 - //v1_diff < self.pos + self.v2 && self.pos < v1_diff - //&& v2_diff < self.pos + self.v1 && self.pos < v2_diff + let v1_proj = project(self, &self.v1); + let p1 = other.dot(self.v1); + let v2_proj = project(self, &self.v2); + let p2 = other.dot(self.v2); + v1_proj.min <= p1 && v1_proj.max >= p1 && v2_proj.min <= p2 && v2_proj.max >= p2 + } +} + +impl Collide for spine::skeleton::SRT { + fn collides(&self, other: &Vec2) -> bool { + let rbox: RBox = self.into(); + rbox.collides(other) + } +} + +impl Collide for spine::skeleton::SRT { + fn collides(&self, other: &AABox) -> bool { + let rbox: RBox = self.into(); + rbox.collides(other) } } impl Collide for RBox { fn collides(&self, other: &AABox) -> bool { - let other_size = other.pos + other.size; - - // project points onto a orthogonal line - let v1_diff = other.pos + self.v1 * (-self.v1.dot(other.pos - self.pos) / self.v1.norm2()); - let v2_diff = other.pos + self.v2 * (-self.v2.dot(other.pos) / self.v2.norm2()); - let v1_diff_size = - other_size + self.v1 * (-self.v1.dot(other_size - self.pos) / self.v1.norm2()); - let v2_diff_size = - other_size + self.v2 * (-self.v2.dot(other_size - self.pos) / self.v2.norm2()); - - // calculate the norm - let v1_dist = (v1_diff - self.pos) / self.v2; - let v2_dist = (v2_diff - self.pos) / self.v1; - let v1_dist_size = (v1_diff_size - self.pos) / self.v2; - let v2_dist_size = (v2_diff_size - self.pos) / self.v1; - - let v1_dist = if v1_dist.x().is_finite() { - v1_dist.x() - } else { - v1_dist.y() - }; - let v2_dist = if v2_dist.x().is_finite() { - v2_dist.x() - } else { - v2_dist.y() - }; - let v1_dist_size = if v1_dist_size.x().is_finite() { - v1_dist_size.x() - } else { - v1_dist_size.y() - }; - let v2_dist_size = if v2_dist_size.x().is_finite() { - v2_dist_size.x() - } else { - v2_dist_size.y() - }; - - let minx = f32::min( - self.pos.x(), - f32::min((self.pos + self.v1).x(), (self.pos + self.v2).x()), - ); - let maxx = f32::max( - self.pos.x(), - f32::max((self.pos + self.v1).x(), (self.pos + self.v2).x()), - ); - let miny = f32::min( - self.pos.y(), - f32::min((self.pos + self.v1).y(), (self.pos + self.v2).y()), - ); - let maxy = f32::max( - self.pos.y(), - f32::max((self.pos + self.v1).y(), (self.pos + self.v2).y()), - ); - - 0.0 <= v1_dist_size - && v1_dist <= 1.0 - && 0.0 <= v2_dist_size - && v2_dist <= 1.0 - && other.pos.x() <= maxx - && minx <= other.pos.x() + other.size.x() - && other.pos.y() <= maxy - && miny <= other.pos.y() + other.size.y() + let xbound = other.pos.x()..other.pos.x() + other.size.x(); + let ybound = other.pos.y()..other.pos.y() + other.size.y(); + let edges = [ + (self.pos, self.v1), + (self.pos, self.v2), + (self.pos + self.v1, self.v2), + (self.pos + self.v2, self.v1), + ]; + collide_aabox_rbox_segment(xbound.clone(), ybound.clone(), self.pos, self.v1) + || collide_aabox_rbox_segment(xbound.clone(), ybound.clone(), self.pos, self.v2) + || collide_aabox_rbox_segment( + xbound.clone(), + ybound.clone(), + self.pos + self.v1, + self.v2, + ) + || collide_aabox_rbox_segment(xbound, ybound, self.pos + self.v2, self.v1) + } +} + +impl Collide for RBox { + fn collides(&self, other: &Self) -> bool { + // using the SAT + // TODO: optimization: remove duplicate axes + let axes = [self.v1, self.v2, other.v1, other.v2]; + axes.iter() + .all(|axis| project(self, axis).collides(&project(other, axis))) } } -impl> Collide for Vec { +impl> Collide for [T] { fn collides(&self, other: &S) -> bool { self.iter().any(|x| x.collides(other)) } diff --git a/rask-engine/src/error.rs b/rask-engine/src/error.rs new file mode 100644 index 00000000..e5740925 --- /dev/null +++ b/rask-engine/src/error.rs @@ -0,0 +1,38 @@ +use std::error::Error; +use std::fmt::{self, Display}; + +/// The error type used by the game engine. +#[derive(Debug)] +pub enum EngineError { + ResourceType(String), + ResourceIndex(String), + MathError(String), + Misc(String), +} + +impl Display for EngineError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self { + EngineError::ResourceType(e) => write!(f, "ResourceError: {}", e), + EngineError::ResourceIndex(e) => write!(f, "ResourceError: {}", e), + EngineError::Misc(e) => write!(f, "EngineError: {}", e), + EngineError::MathError(e) => write!(f, "MathError: {}", e), + } + } +} + +impl Error for EngineError {} + +macro_rules! derive_from { + ($type:ty, $kind:ident) => { + impl From<$type> for EngineError { + fn from(error: $type) -> Self { + EngineError::$kind(format!("{}", error)) + } + } + }; +} + +derive_from!(&str, Misc); +derive_from!(String, Misc); +derive_from!(image::error::ImageError, ResourceType); diff --git a/rask-engine/src/lib.rs b/rask-engine/src/lib.rs index 3e911b44..1c5dead9 100644 --- a/rask-engine/src/lib.rs +++ b/rask-engine/src/lib.rs @@ -4,4 +4,10 @@ pub mod boxes; pub mod collide; +pub mod error; pub mod math; +pub mod resources; +pub mod world; + +#[doc(inline)] +pub use error::EngineError; diff --git a/rask-engine/src/math/mat3.rs b/rask-engine/src/math/mat3.rs index e93e85ce..52e02047 100644 --- a/rask-engine/src/math/mat3.rs +++ b/rask-engine/src/math/mat3.rs @@ -221,6 +221,8 @@ impl Mat3 { /// (a b c) /// (d e f) /// (g h i) + #[allow(clippy::too_many_arguments)] + #[allow(clippy::many_single_char_names)] pub const fn new( a: f32, b: f32, diff --git a/rask-engine/src/math/vec2.rs b/rask-engine/src/math/vec2.rs index 1c68caf9..0d0cfbc7 100644 --- a/rask-engine/src/math/vec2.rs +++ b/rask-engine/src/math/vec2.rs @@ -1,4 +1,4 @@ -use core::cmp::Ordering; +use core::iter::{once, Chain, Once}; use core::ops; use crate::math::EPSILON; @@ -112,20 +112,6 @@ impl ops::DivAssign for Vec2 { } } -impl PartialOrd for Vec2 { - fn partial_cmp(&self, other: &Self) -> Option { - if self == other { - Some(Ordering::Equal) - } else if self.x <= other.x && self.y <= other.y { - Some(Ordering::Less) - } else if self.x >= other.x && self.y >= other.y { - Some(Ordering::Greater) - } else { - None - } - } -} - impl PartialEq for Vec2 { fn eq(&self, other: &Self) -> bool { f32::abs(self.x - other.x) < EPSILON && f32::abs(self.y - other.y) < EPSILON @@ -146,6 +132,29 @@ impl From for (f32, f32) { } } +impl From<[f32; 2]> for Vec2 { + fn from([x, y]: [f32; 2]) -> Self { + Self::new(x, y) + } +} + +impl From for [f32; 2] { + fn from(vec: Vec2) -> Self { + [vec.x(), vec.y()] + } +} + +pub type IntoIter = Chain, Once>; + +impl IntoIterator for Vec2 { + type Item = f32; + type IntoIter = IntoIter; + + fn into_iter(self) -> Self::IntoIter { + once(self.x).chain(once(self.y)) + } +} + impl Vec2 { /// Creates a new `Vec2` from x and y coordinates. pub const fn new(x: f32, y: f32) -> Self { diff --git a/rask-engine/src/math/vec3.rs b/rask-engine/src/math/vec3.rs index 8e1fb160..200b68fd 100644 --- a/rask-engine/src/math/vec3.rs +++ b/rask-engine/src/math/vec3.rs @@ -1,4 +1,4 @@ -use core::cmp::Ordering; +use core::iter::{once, Chain, Once}; use core::ops; use crate::math::EPSILON; @@ -114,20 +114,6 @@ impl ops::DivAssign for Vec3 { } } -impl PartialOrd for Vec3 { - fn partial_cmp(&self, other: &Self) -> Option { - if self == other { - Some(Ordering::Equal) - } else if self.x <= other.x && self.y <= other.y && self.z <= other.z { - Some(Ordering::Less) - } else if self.x >= other.x && self.y >= other.y && self.z >= other.z { - Some(Ordering::Greater) - } else { - None - } - } -} - impl PartialEq for Vec3 { fn eq(&self, other: &Self) -> bool { f32::abs(self.x - other.x) < EPSILON @@ -150,6 +136,29 @@ impl From for (f32, f32, f32) { } } +impl From<[f32; 3]> for Vec3 { + fn from([x, y, z]: [f32; 3]) -> Self { + Self::new(x, y, z) + } +} + +impl From for [f32; 3] { + fn from(vec: Vec3) -> Self { + [vec.x(), vec.y(), vec.z()] + } +} + +pub type IntoIter = Chain, Chain, Once>>; + +impl IntoIterator for Vec3 { + type Item = f32; + type IntoIter = IntoIter; + + fn into_iter(self) -> Self::IntoIter { + once(self.x).chain(once(self.y).chain(once(self.z))) + } +} + impl Vec3 { /// Creates a new `Vec3` from x and y coordinates. pub const fn new(x: f32, y: f32, z: f32) -> Self { @@ -195,4 +204,9 @@ impl Vec3 { pub fn normalized(self) -> Self { self / self.norm() } + + /// Returns this `Vec3` as a `Vec2`, disregarding the z component. + pub fn into_vec2(self) -> super::Vec2 { + super::Vec2::new(self.x(), self.y()) + } } diff --git a/rask-engine/src/math_simd/mat2.rs b/rask-engine/src/math_simd/mat2.rs deleted file mode 100644 index 710c0daf..00000000 --- a/rask-engine/src/math_simd/mat2.rs +++ /dev/null @@ -1,128 +0,0 @@ -use std::{fmt, ops}; - -use packed_simd::{f32x2, shuffle}; - -use crate::math_simd::{Vec2, EPSILON}; - -#[derive(Clone, Copy)] -pub struct Mat2(f32x2, f32x2); - -impl ops::Add for Mat2 { - type Output = Self; - - fn add(self, other: Self) -> Self::Output { - Self(self.0 + other.0, self.1 + other.1) - } -} - -impl ops::AddAssign for Mat2 { - fn add_assign(&mut self, other: Self) { - self.0 += other.0; - self.1 += other.1; - } -} - -impl ops::Sub for Mat2 { - type Output = Self; - - fn sub(self, other: Self) -> Self::Output { - Self(self.0 - other.0, self.1 - other.1) - } -} - -impl ops::SubAssign for Mat2 { - fn sub_assign(&mut self, other: Self) { - self.0 -= other.0; - self.1 -= other.1; - } -} - -impl ops::Neg for Mat2 { - type Output = Self; - - fn neg(self) -> Self::Output { - Self(-self.0, -self.1) - } -} - -impl ops::Mul for Mat2 { - type Output = Self; - - fn mul(self, scale: f32) -> Self::Output { - Self(self.0 * scale, self.1 * scale) - } -} - -impl ops::MulAssign for Mat2 { - fn mul_assign(&mut self, scale: f32) { - self.0 *= scale; - self.1 *= scale; - } -} - -impl ops::Mul for Mat2 { - type Output = Vec2; - - fn mul(self, other: Vec2) -> Self::Output { - Vec2(self.0 * other.0.extract(0) + self.1 * other.0.extract(1)) - } -} -/* -impl ops::Mul for Mat2 { - type Output = Mat2; - - fn mul(self, other: Self) -> Self::Output {} -} - -impl ops::MulAssign for Mat2 { - fn mul_assign(&mut self, other: Self) {} -} -*/ -impl ops::Div for Mat2 { - type Output = Self; - - fn div(self, scale: f32) -> Self::Output { - Self(self.0 / scale, self.1 / scale) - } -} - -impl ops::DivAssign for Mat2 { - fn div_assign(&mut self, scale: f32) { - self.0 /= scale; - self.1 /= scale; - } -} - -impl PartialEq for Mat2 { - fn eq(&self, other: &Self) -> bool { - (self.0 - other.0).abs().lt(f32x2::splat(EPSILON)).all() - && (self.1 - other.1).abs().lt(f32x2::splat(EPSILON)).all() - } -} - -impl Eq for Mat2 {} - -impl Mat2 { - pub fn new(a: f32, b: f32, c: f32, d: f32) -> Self { - Self(f32x2::new(a, c), f32x2::new(b, d)) - } - - pub fn from_vec2(v1: Vec2, v2: Vec2) -> Self { - Self(v1.0, v2.0) - } - - pub fn zero() -> Self { - Self(f32x2::splat(0.0), f32x2::splat(0.0)) - } - - pub fn identity() -> Self { - Self(f32x2::new(1.0, 0.0), f32x2::new(0.0, 1.0)) - } - - pub fn transpose(self) -> Self { - Self( - shuffle!(self.0, self.1, [0, 2]), - shuffle!(self.0, self.1, [1, 3]), - ) - } -} diff --git a/rask-engine/src/math_simd/mod.rs b/rask-engine/src/math_simd/mod.rs deleted file mode 100644 index 87807e3a..00000000 --- a/rask-engine/src/math_simd/mod.rs +++ /dev/null @@ -1,11 +0,0 @@ -//! Note: this requires `packed_simd = "0.3.3"` as dependency in Cargo.toml to compile -//! This module is supposed to have the same types and functions as the math module, but is using -//! SIMD to achieve that. However, currently the module is not exposed. - -pub mod mat2; -pub mod vec2; - -pub use mat2::Mat2; -pub use vec2::Vec2; - -pub const EPSILON: f32 = 1e-8; diff --git a/rask-engine/src/math_simd/vec2.rs b/rask-engine/src/math_simd/vec2.rs deleted file mode 100644 index 1ac3319a..00000000 --- a/rask-engine/src/math_simd/vec2.rs +++ /dev/null @@ -1,176 +0,0 @@ -use std::cmp::Ordering; -use std::{fmt, ops}; - -use packed_simd::f32x2; - -use crate::math_simd::EPSILON; - -#[derive(Clone, Copy)] -pub struct Vec2(pub(super) f32x2); - -impl fmt::Debug for Vec2 { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!( - f, - "Vec2 {{ x: {:?}, y: {:?} }}", - self.0.extract(0), - self.0.extract(1) - ) - } -} - -impl ops::Add for Vec2 { - type Output = Self; - - fn add(self, other: Self) -> Self::Output { - Self(self.0 + other.0) - } -} - -impl ops::AddAssign for Vec2 { - fn add_assign(&mut self, other: Self) { - self.0 += other.0; - } -} - -impl ops::Sub for Vec2 { - type Output = Self; - - fn sub(self, other: Self) -> Self::Output { - Self(self.0 - other.0) - } -} - -impl ops::SubAssign for Vec2 { - fn sub_assign(&mut self, other: Self) { - self.0 -= other.0; - } -} - -impl ops::Neg for Vec2 { - type Output = Self; - - fn neg(self) -> Self::Output { - Self(-self.0) - } -} - -impl ops::Mul for Vec2 { - type Output = Self; - - fn mul(self, scale: f32) -> Self::Output { - Self(self.0 * scale) - } -} - -impl ops::MulAssign for Vec2 { - fn mul_assign(&mut self, scale: f32) { - self.0 *= scale; - } -} - -impl ops::Mul for Vec2 { - type Output = Self; - - fn mul(self, other: Self) -> Self::Output { - Self(self.0 * other.0) - } -} - -impl ops::MulAssign for Vec2 { - fn mul_assign(&mut self, other: Self) { - self.0 *= other.0; - } -} - -impl ops::Div for Vec2 { - type Output = Self; - - fn div(self, scale: f32) -> Self::Output { - Self(self.0 / scale) - } -} - -impl ops::DivAssign for Vec2 { - fn div_assign(&mut self, scale: f32) { - self.0 /= scale; - } -} - -impl ops::Div for Vec2 { - type Output = Self; - - fn div(self, other: Self) -> Self::Output { - Self(self.0 / other.0) - } -} - -impl ops::DivAssign for Vec2 { - fn div_assign(&mut self, other: Self) { - self.0 /= other.0; - } -} - -impl PartialOrd for Vec2 { - fn partial_cmp(&self, other: &Self) -> Option { - if self == other { - Some(Ordering::Equal) - } else if self.0.le(other.0).all() { - Some(Ordering::Less) - } else if self.0.ge(other.0).all() { - Some(Ordering::Greater) - } else { - None - } - } -} - -impl PartialEq for Vec2 { - fn eq(&self, other: &Self) -> bool { - (self.0 - other.0).abs().lt(f32x2::splat(EPSILON)).all() - } -} - -impl Eq for Vec2 {} - -impl Vec2 { - /// Creates a new Vec2 from x and y coordinates. - pub fn new(x: f32, y: f32) -> Self { - Self(f32x2::new(x, y)) - } - - /// Returns the zero vector. - pub fn zero() -> Self { - Self(f32x2::splat(0.0)) - } - - /// Returns the x coordinate. - pub fn x(self) -> f32 { - self.0.extract(0) - } - - /// Returns the y coordinate. - pub fn y(self) -> f32 { - self.0.extract(1) - } - - /// Returns the dot product. - pub fn dot(self, other: Self) -> f32 { - (self.0 * other.0).sum() - } - - /// Returns the square of the euclidean norm of the vector. - pub fn norm2(self) -> f32 { - self.dot(self) - } - - /// Returns the euclidean norm of the vector. - pub fn norm(self) -> f32 { - self.norm2().sqrt() - } - - /// Returns a normalized version of the vector, that is, a vector that points in the same direction, but has norm 1. - pub fn normalized(self) -> Self { - self / self.norm() - } -} diff --git a/rask-engine/src/resources/library.rs b/rask-engine/src/resources/library.rs new file mode 100644 index 00000000..1a64615b --- /dev/null +++ b/rask-engine/src/resources/library.rs @@ -0,0 +1,73 @@ +use super::Resource; +use crate::EngineError; + +/// The library is used to store and retrieve resources. +pub struct ResourceTable(&'static mut [Resource]); + +macro_rules! get_store { + ($type: ty, $enum_type: ident) => { + impl GetStore<$type> for ResourceTable { + unsafe fn get(&self, id: usize) -> Result<&$type, EngineError> { + self.index_check(id)?; + match &self.0[id] { + Resource::$enum_type(value) => Ok(&value), + _ => Err(EngineError::ResourceType("Wrong resource type".into())), + } + } + unsafe fn store(&mut self, data: $type, id: usize) -> Result<(), EngineError> { + self.index_check(id)?; + Ok(self.0[id] = Resource::$enum_type(data)) + } + } + }; +} + +pub trait GetStore { + /// Retrieve a resource from the library. + /// + /// # Safety + /// + /// The function is not thread safe. + unsafe fn get(&self, id: usize) -> Result<&T, EngineError>; + /// Store a resource to the library + /// + /// # Safety + /// + /// The function is not thread safe. + unsafe fn store(&mut self, data: T, id: usize) -> Result<(), EngineError>; +} + +impl ResourceTable { + /// Create a new library at a specific position in memory. + /// + /// # Safety + /// + /// The function is safe as long as the memory from memory_offset to memory_offset + CATALOG_SIZE * sizeof(Resource) + pub unsafe fn from_memory(memory_offset: usize, catalog_size: usize) -> Self { + ResourceTable(core::slice::from_raw_parts_mut( + memory_offset as *mut Resource, + catalog_size, + )) + } + + pub fn clear(&mut self) { + for i in 0..self.0.len() { + self.0[i] = Resource::None; + } + } + + fn index_check(&self, id: usize) -> Result<(), EngineError> { + if id >= self.0.len() { + return Err(EngineError::ResourceIndex(format!( + "The requested resource index: {} is out ouf range, the max id is {}", + id, + self.0.len() - 1 + ))); + } + Ok(()) + } +} + +get_store!(super::Texture, Texture); +get_store!(spine::skeleton::Skeleton, Skeleton); +get_store!(super::Sound, Sound); diff --git a/rask-engine/src/resources/mod.rs b/rask-engine/src/resources/mod.rs new file mode 100644 index 00000000..a6454f7f --- /dev/null +++ b/rask-engine/src/resources/mod.rs @@ -0,0 +1,38 @@ +/*! +The resource management system for the ratatosk game engine. + +# Example + +``` +use lazy_static::lazy_static; +# use rask_engine::resources::*; + +lazy_static! { + static ref TABLE: ResourceTable = unsafe { ResourceTable::from_memory(0, 0) }; +} + +fn test() { + unsafe { + let _texture: &Texture = TABLE.get(0).unwrap(); + } +} +``` +*/ + +mod library; +pub mod sound; +pub mod texture; + +#[doc(inline)] +pub use library::*; +#[doc(inline)] +pub use sound::Sound; +#[doc(inline)] +pub use texture::Texture; + +pub enum Resource { + None, + Texture(Texture), + Sound(Sound), + Skeleton(spine::skeleton::Skeleton), +} diff --git a/rask-engine/src/resources/sound.rs b/rask-engine/src/resources/sound.rs new file mode 100644 index 00000000..f5d0b8a8 --- /dev/null +++ b/rask-engine/src/resources/sound.rs @@ -0,0 +1 @@ +pub struct Sound {} diff --git a/client/shared/src/texture.rs b/rask-engine/src/resources/texture.rs similarity index 58% rename from client/shared/src/texture.rs rename to rask-engine/src/resources/texture.rs index 90f98163..35d68586 100644 --- a/client/shared/src/texture.rs +++ b/rask-engine/src/resources/texture.rs @@ -1,7 +1,8 @@ -use crate::error::ClientError; +use std::convert::TryInto; + use image::{png::PngDecoder, ImageDecoder}; -use std::convert::TryInto; +use crate::error::EngineError; pub use image::ColorType; @@ -9,30 +10,27 @@ pub struct Texture { raw_data: Vec, w: u32, h: u32, - colortype: ColorType, + color_type: ColorType, } impl Texture { - pub fn from_png_stream(r: R) -> Result { - let decoder = PngDecoder::new(r) - .map_err(|e| ClientError::ResourceError(format!("png image reading error: {}", e)))?; + pub fn from_png_stream(r: R) -> Result { + let decoder = PngDecoder::new(r)?; let (w, h) = decoder.dimensions(); - let e = |_| ClientError::ResourceError(format!("invalid image resolution")); + let e = |_| EngineError::ResourceType("invalid image resolution".to_owned()); let (w, h) = (w.try_into().map_err(e)?, h.try_into().map_err(e)?); let colortype = decoder.color_type(); let mut bytes = vec![0; w as usize * h as usize * colortype.bytes_per_pixel() as usize]; - decoder - .read_image(&mut bytes) - .map_err(|e| ClientError::ResourceError(format!("png image decoding error: {}", e)))?; + decoder.read_image(&mut bytes)?; Ok(Self { raw_data: bytes, w, h, - colortype, + color_type: colortype, }) } @@ -40,11 +38,11 @@ impl Texture { (self.w, self.h) } - pub fn colortype(&self) -> ColorType { - self.colortype + pub fn color_type(&self) -> ColorType { + self.color_type } - pub fn raw(&self) -> &Vec { + pub fn raw(&self) -> &[u8] { &self.raw_data } diff --git a/rask-engine/src/world.rs b/rask-engine/src/world.rs new file mode 100644 index 00000000..5c4b04c0 --- /dev/null +++ b/rask-engine/src/world.rs @@ -0,0 +1,52 @@ +use crate::math::Vec2; + +const GRAVITY: Vec2 = Vec2::new(0.0, -9.81); + +pub trait Move { + fn gravity() -> bool; + fn position(&mut self) -> &mut Vec2; + fn velocity(&mut self) -> &mut Vec2; + + fn update(&mut self, dt: f32) { + if Self::gravity() { + *self.velocity() += dt * GRAVITY; + } + let vel = *self.velocity(); + *self.position() += dt * vel; + } +} + +pub struct World { + players: Vec, + entities: Vec, + ground: Ground, + background: Background, +} + +pub struct Player { + position: Vec2, + velocity: Vec2, + skeleton: Skeleton, +} + +impl Move for Player { + fn gravity() -> bool { + true + } + + fn position(&mut self) -> &mut Vec2 { + &mut self.position + } + + fn velocity(&mut self) -> &mut Vec2 { + &mut self.velocity + } +} + +pub struct Entity; + +pub struct Skeleton; + +pub struct Ground; + +pub struct Background; diff --git a/rask-engine/tests/collide_test.rs b/rask-engine/tests/collide_test.rs index f1f261e6..13d5bfde 100644 --- a/rask-engine/tests/collide_test.rs +++ b/rask-engine/tests/collide_test.rs @@ -76,7 +76,7 @@ fn test_collide_rbox_dot() { let a = Vec2::new(1.0, 1.0); let b = Vec2::new(1.0, 1.0); let c = Vec2::new(1.0, -1.0); - let aa_box = RBox { + let rbox = RBox { pos: a, v1: b, v2: c, @@ -84,7 +84,7 @@ fn test_collide_rbox_dot() { let c = Vec2::new(1.6, 0.6); - assert!(aa_box.collides(&c)); + assert!(rbox.collides(&c)); } #[test] @@ -92,7 +92,7 @@ fn test_not_collide_rbox_dot() { let a = Vec2::new(1.0, 1.0); let b = Vec2::new(1.0, 1.0); let c = Vec2::new(1.0, -1.0); - let aa_box = RBox { + let rbox = RBox { pos: a, v1: b, v2: c, @@ -100,7 +100,7 @@ fn test_not_collide_rbox_dot() { let c = Vec2::new(1.4, 0.4); - assert!(!(aa_box.collides(&c))); + assert!(!(rbox.collides(&c))); } #[test] @@ -108,52 +108,52 @@ fn test_collide_rbox_aabox_intersecting() { let a = Vec2::new(1.0, 2.5); let b = Vec2::new(0.0, 2.5); let c = Vec2::new(3.0, 0.5); - let aa_box = RBox { + let box1 = RBox { pos: a, v1: b, v2: c, }; let a = Vec2::new(2.0, 3.5); let b = Vec2::new(3.0, 7.5); - let bb_box = AABox { pos: a, size: b }; + let box2 = AABox { pos: a, size: b }; - assert!(aa_box.collides(&bb_box)); + assert!(box1.collides(&box2)); } #[test] fn test_collide_rbox_aabox_edges_touch() { let a = Vec2::new(4.0, 5.5); let b = Vec2::new(1.0, 7.5); - let aa_box = RBox::new(a, b, 3.9); + let box1 = RBox::new(a, b, 3.9); let a = Vec2::new(0.0, 0.5); let b = Vec2::new(4.0, 5.0); - let bb_box = AABox { pos: a, size: b }; + let box2 = AABox { pos: a, size: b }; - assert!(aa_box.collides(&bb_box)); + assert!(box1.collides(&box2)); } #[test] fn test_collide_rbox_aabox_crossed() { let a = Vec2::new(2.0, 0.5); let b = Vec2::new(1.0, 7.5); - let aa_box = RBox::new(a, b, 3.9); + let box1 = RBox::new(a, b, 3.9); let a = Vec2::new(0.0, 4.5); let b = Vec2::new(15.0, 1.5); - let bb_box = AABox { pos: a, size: b }; + let box2 = AABox { pos: a, size: b }; - assert!(aa_box.collides(&bb_box)); + assert!(box1.collides(&box2)); } #[test] fn test_not_collide_rbox_aabox_next_to() { let a = Vec2::new(2.0, 0.5); let b = Vec2::new(1.0, 7.5); - let aa_box = RBox::new(a, b, 3.9); + let box1 = RBox::new(a, b, 3.9); let a = Vec2::new(5.0, 40.5); let b = Vec2::new(15.0, 1.5); - let bb_box = AABox { pos: a, size: b }; + let box2 = AABox { pos: a, size: b }; - assert!(!aa_box.collides(&bb_box)); + assert!(!box1.collides(&box2)); } #[test] @@ -161,14 +161,14 @@ fn test_not_collide_rbox_aabox() { let a = Vec2::new(1.0, 1.0); let b = Vec2::new(0.0, 1.0); let c = Vec2::new(1.0, 0.0); - let aa_box = RBox { + let box1 = RBox { pos: a, v1: b, v2: c, }; let a = Vec2::new(3.0, 3.5); let b = Vec2::new(3.0, 7.5); - let bb_box = AABox { pos: a, size: b }; + let box2 = AABox { pos: a, size: b }; - assert!(!(aa_box.collides(&bb_box))); + assert!(!(box1.collides(&box2))); } diff --git a/rask-engine/tests/vec2.rs b/rask-engine/tests/vec2.rs index c5ca2585..41de20b9 100644 --- a/rask-engine/tests/vec2.rs +++ b/rask-engine/tests/vec2.rs @@ -79,38 +79,6 @@ fn test_div_vec2() { assert_eq!(a / b, Vec2::new(-4.2, 3.75)); } -#[test] -fn test_less_vec2() { - let a = Vec2::new(1.0, 7.5); - let b = Vec2::new(-3.0, 2.5); - - assert!(b < a); -} - -#[test] -fn test_less_vec2_fail() { - let a = Vec2::new(1.0, 7.5); - let b = Vec2::new(3.0, 2.5); - - assert!(!(a < b)); -} - -#[test] -fn test_greater_vec2() { - let a = Vec2::new(1.0, 7.5); - let b = Vec2::new(-3.0, 2.5); - - assert!(a > b); -} - -#[test] -fn test_greater_vec2_fail() { - let a = Vec2::new(1.0, 7.5); - let b = Vec2::new(3.0, 2.5); - - assert!(!(a > b)); -} - #[test] fn test_norm_vec2() { let a = Vec2::new(3.0, 4.0); diff --git a/rask-engine/tests/vec3.rs b/rask-engine/tests/vec3.rs index 69389f68..d4ba3da2 100644 --- a/rask-engine/tests/vec3.rs +++ b/rask-engine/tests/vec3.rs @@ -78,38 +78,6 @@ fn test_div_vec3() { assert_eq!(a / b, Vec3::new(-4.2, 3.75, 16.0)); } -#[test] -fn test_less_vec3() { - let a = Vec3::new(1.0, 50.0, 7.5); - let b = Vec3::new(-3.0, 1.0, 2.5); - - assert!(b < a); -} - -#[test] -fn test_less_vec3_fail() { - let a = Vec3::new(1.0, 67.0, 7.5); - let b = Vec3::new(3.0, -2.0, 2.5); - - assert!(!(a < b)); -} - -#[test] -fn test_greater_vec3() { - let a = Vec3::new(1.0, 4.0, 7.5); - let b = Vec3::new(-3.0, 1.0, 2.5); - - assert!(a > b); -} - -#[test] -fn test_greater_vec3_fail() { - let a = Vec3::new(1.0, 54.0, 7.5); - let b = Vec3::new(3.0, 5.0, 2.5); - - assert!(!(a > b)); -} - #[test] fn test_norm_vec3() { let a = Vec3::new(13.0, 4.0, 16.0);