Compare commits
No commits in common. "6a11f7cbb7b2d70e5a1599be6ffa8166bee04067" and "25aa902e0247bda0ce67e55bd1df6753eb270dac" have entirely different histories.
6a11f7cbb7
...
25aa902e02
25 changed files with 241 additions and 1384 deletions
180
Cargo.lock
generated
180
Cargo.lock
generated
|
@ -56,15 +56,6 @@ dependencies = [
|
||||||
"zerocopy",
|
"zerocopy",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "aho-corasick"
|
|
||||||
version = "1.1.3"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916"
|
|
||||||
dependencies = [
|
|
||||||
"memchr",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "allocator-api2"
|
name = "allocator-api2"
|
||||||
version = "0.2.16"
|
version = "0.2.16"
|
||||||
|
@ -947,18 +938,6 @@ dependencies = [
|
||||||
"windows-sys 0.52.0",
|
"windows-sys 0.52.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "fixed-timestep-rotating-model"
|
|
||||||
version = "0.1.0"
|
|
||||||
dependencies = [
|
|
||||||
"anyhow",
|
|
||||||
"async-std",
|
|
||||||
"fps_counter",
|
|
||||||
"lyra-engine",
|
|
||||||
"rand 0.8.5",
|
|
||||||
"tracing",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "flate2"
|
name = "flate2"
|
||||||
version = "1.0.28"
|
version = "1.0.28"
|
||||||
|
@ -1008,15 +987,6 @@ dependencies = [
|
||||||
"percent-encoding",
|
"percent-encoding",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "fps_counter"
|
|
||||||
version = "3.0.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "ff23a4d90ba4b859f370ee3c12ca3b1ca80d8ee144b279e135f6852cdadd6dd6"
|
|
||||||
dependencies = [
|
|
||||||
"instant",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "fsevent-sys"
|
name = "fsevent-sys"
|
||||||
version = "4.1.0"
|
version = "4.1.0"
|
||||||
|
@ -1117,19 +1087,6 @@ dependencies = [
|
||||||
"byteorder",
|
"byteorder",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "generator"
|
|
||||||
version = "0.7.5"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "5cc16584ff22b460a382b7feec54b23d2908d858152e5739a120b949293bd74e"
|
|
||||||
dependencies = [
|
|
||||||
"cc",
|
|
||||||
"libc",
|
|
||||||
"log",
|
|
||||||
"rustversion",
|
|
||||||
"windows 0.48.0",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "generic-array"
|
name = "generic-array"
|
||||||
version = "0.14.7"
|
version = "0.14.7"
|
||||||
|
@ -1790,19 +1747,6 @@ dependencies = [
|
||||||
"value-bag",
|
"value-bag",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "loom"
|
|
||||||
version = "0.7.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "7e045d70ddfbc984eacfa964ded019534e8f6cbf36f6410aee0ed5cefa5a9175"
|
|
||||||
dependencies = [
|
|
||||||
"cfg-if",
|
|
||||||
"generator",
|
|
||||||
"scoped-tls",
|
|
||||||
"tracing",
|
|
||||||
"tracing-subscriber",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "lyra-ecs"
|
name = "lyra-ecs"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
|
@ -1854,15 +1798,12 @@ dependencies = [
|
||||||
"lyra-resource",
|
"lyra-resource",
|
||||||
"lyra-scene",
|
"lyra-scene",
|
||||||
"quote",
|
"quote",
|
||||||
"rustc-hash",
|
|
||||||
"syn 2.0.51",
|
"syn 2.0.51",
|
||||||
"thiserror",
|
"thiserror",
|
||||||
"tracing",
|
"tracing",
|
||||||
"tracing-appender",
|
"tracing-appender",
|
||||||
"tracing-log 0.1.4",
|
"tracing-log 0.1.4",
|
||||||
"tracing-subscriber",
|
"tracing-subscriber",
|
||||||
"tracing-tracy",
|
|
||||||
"unique",
|
|
||||||
"uuid",
|
"uuid",
|
||||||
"wgpu",
|
"wgpu",
|
||||||
"winit",
|
"winit",
|
||||||
|
@ -1973,27 +1914,6 @@ dependencies = [
|
||||||
"libc",
|
"libc",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "many-lights"
|
|
||||||
version = "0.1.0"
|
|
||||||
dependencies = [
|
|
||||||
"anyhow",
|
|
||||||
"async-std",
|
|
||||||
"fps_counter",
|
|
||||||
"lyra-engine",
|
|
||||||
"rand 0.8.5",
|
|
||||||
"tracing",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "matchers"
|
|
||||||
version = "0.1.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "8263075bb86c5a1b1427b5ae862e8889656f126e9f77c484496e8b47cf5c5558"
|
|
||||||
dependencies = [
|
|
||||||
"regex-automata 0.1.10",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "memchr"
|
name = "memchr"
|
||||||
version = "2.7.1"
|
version = "2.7.1"
|
||||||
|
@ -2735,50 +2655,6 @@ dependencies = [
|
||||||
"bitflags 1.3.2",
|
"bitflags 1.3.2",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "regex"
|
|
||||||
version = "1.10.4"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "c117dbdfde9c8308975b6a18d71f3f385c89461f7b3fb054288ecf2a2058ba4c"
|
|
||||||
dependencies = [
|
|
||||||
"aho-corasick",
|
|
||||||
"memchr",
|
|
||||||
"regex-automata 0.4.6",
|
|
||||||
"regex-syntax 0.8.3",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "regex-automata"
|
|
||||||
version = "0.1.10"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132"
|
|
||||||
dependencies = [
|
|
||||||
"regex-syntax 0.6.29",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "regex-automata"
|
|
||||||
version = "0.4.6"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "86b83b8b9847f9bf95ef68afb0b8e6cdb80f498442f5179a29fad448fcc1eaea"
|
|
||||||
dependencies = [
|
|
||||||
"aho-corasick",
|
|
||||||
"memchr",
|
|
||||||
"regex-syntax 0.8.3",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "regex-syntax"
|
|
||||||
version = "0.6.29"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "regex-syntax"
|
|
||||||
version = "0.8.3"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "adad44e29e4c806119491a7f06f03de4d1af22c3a680dd47f1e6e179439d1f56"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "remove_dir_all"
|
name = "remove_dir_all"
|
||||||
version = "0.5.3"
|
version = "0.5.3"
|
||||||
|
@ -2882,12 +2758,6 @@ dependencies = [
|
||||||
"base64 0.21.5",
|
"base64 0.21.5",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "rustversion"
|
|
||||||
version = "1.0.15"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "80af6f9131f277a45a3fba6ce8e2258037bb0477a67e610d3c1fe046ab31de47"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ryu"
|
name = "ryu"
|
||||||
version = "1.0.16"
|
version = "1.0.16"
|
||||||
|
@ -3485,49 +3355,14 @@ version = "0.3.18"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "ad0f048c97dbd9faa9b7df56362b8ebcaa52adb06b498c050d2f4e32f90a7a8b"
|
checksum = "ad0f048c97dbd9faa9b7df56362b8ebcaa52adb06b498c050d2f4e32f90a7a8b"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"matchers",
|
|
||||||
"nu-ansi-term",
|
"nu-ansi-term",
|
||||||
"once_cell",
|
|
||||||
"regex",
|
|
||||||
"sharded-slab",
|
"sharded-slab",
|
||||||
"smallvec",
|
"smallvec",
|
||||||
"thread_local",
|
"thread_local",
|
||||||
"tracing",
|
|
||||||
"tracing-core",
|
"tracing-core",
|
||||||
"tracing-log 0.2.0",
|
"tracing-log 0.2.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "tracing-tracy"
|
|
||||||
version = "0.11.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "6024d04f84a69fd0d1dc1eee3a2b070bd246530a0582f9982ae487cb6c703614"
|
|
||||||
dependencies = [
|
|
||||||
"tracing-core",
|
|
||||||
"tracing-subscriber",
|
|
||||||
"tracy-client",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "tracy-client"
|
|
||||||
version = "0.17.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "59fb931a64ff88984f86d3e9bcd1ae8843aa7fe44dd0f8097527bc172351741d"
|
|
||||||
dependencies = [
|
|
||||||
"loom",
|
|
||||||
"once_cell",
|
|
||||||
"tracy-client-sys",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "tracy-client-sys"
|
|
||||||
version = "0.22.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "9d104d610dfa9dd154535102cc9c6164ae1fa37842bc2d9e83f9ac82b0ae0882"
|
|
||||||
dependencies = [
|
|
||||||
"cc",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "try-lock"
|
name = "try-lock"
|
||||||
version = "0.2.5"
|
version = "0.2.5"
|
||||||
|
@ -3579,12 +3414,6 @@ version = "0.2.4"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c"
|
checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "unique"
|
|
||||||
version = "0.9.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "d360722e1f3884f5b14d332185f02ff111f771f0c76a313268fe6af1409aba96"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "url"
|
name = "url"
|
||||||
version = "2.5.0"
|
version = "2.5.0"
|
||||||
|
@ -3974,15 +3803,6 @@ dependencies = [
|
||||||
"windows-targets 0.42.2",
|
"windows-targets 0.42.2",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "windows"
|
|
||||||
version = "0.48.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "e686886bc078bc1b0b600cac0147aadb815089b6e4da64016cbd754b6342700f"
|
|
||||||
dependencies = [
|
|
||||||
"windows-targets 0.48.5",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows"
|
name = "windows"
|
||||||
version = "0.52.0"
|
version = "0.52.0"
|
||||||
|
|
|
@ -10,12 +10,11 @@ members = [
|
||||||
"lyra-ecs",
|
"lyra-ecs",
|
||||||
"lyra-reflect",
|
"lyra-reflect",
|
||||||
"lyra-scripting",
|
"lyra-scripting",
|
||||||
"lyra-game", "lyra-math", "lyra-scene", "examples/many-lights", "examples/fixed-timestep-rotating-model"]
|
"lyra-game", "lyra-math", "lyra-scene"]
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
scripting = ["dep:lyra-scripting"]
|
scripting = ["dep:lyra-scripting"]
|
||||||
lua_scripting = ["scripting", "lyra-scripting/lua"]
|
lua_scripting = ["scripting", "lyra-scripting/lua"]
|
||||||
tracy = ["lyra-game/tracy"]
|
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
lyra-game = { path = "lyra-game" }
|
lyra-game = { path = "lyra-game" }
|
||||||
|
|
|
@ -1,22 +0,0 @@
|
||||||
[package]
|
|
||||||
name = "fixed-timestep-rotating-model"
|
|
||||||
version = "0.1.0"
|
|
||||||
edition = "2021"
|
|
||||||
|
|
||||||
[dependencies]
|
|
||||||
lyra-engine = { path = "../../", features = ["tracy"] }
|
|
||||||
anyhow = "1.0.75"
|
|
||||||
async-std = "1.12.0"
|
|
||||||
tracing = "0.1.37"
|
|
||||||
rand = "0.8.5"
|
|
||||||
fps_counter = "3.0.0"
|
|
||||||
|
|
||||||
[target.x86_64-unknown-linux-gnu]
|
|
||||||
linker = "/usr/bin/clang"
|
|
||||||
rustflags = ["-Clink-arg=-fuse-ld=lld", "-Clink-arg=-Wl,--no-rosegment"]
|
|
||||||
|
|
||||||
[profile.dev]
|
|
||||||
opt-level = 1
|
|
||||||
|
|
||||||
[profile.release]
|
|
||||||
debug = true
|
|
|
@ -1,241 +0,0 @@
|
||||||
use std::ptr::NonNull;
|
|
||||||
|
|
||||||
use lyra_engine::{
|
|
||||||
assets::{gltf::Gltf, ResourceManager},
|
|
||||||
ecs::{
|
|
||||||
query::{Res, ResMut, View},
|
|
||||||
system::{BatchedSystem, Criteria, CriteriaSchedule, IntoSystem},
|
|
||||||
World,
|
|
||||||
},
|
|
||||||
game::Game,
|
|
||||||
input::{
|
|
||||||
Action, ActionHandler, ActionKind, ActionMapping, ActionMappingId, ActionSource,
|
|
||||||
InputActionPlugin, KeyCode, LayoutId, MouseAxis, MouseInput,
|
|
||||||
},
|
|
||||||
math::{self, Transform, Vec3},
|
|
||||||
render::light::directional::DirectionalLight,
|
|
||||||
scene::{
|
|
||||||
CameraComponent, FreeFlyCamera, FreeFlyCameraPlugin, WorldTransform,
|
|
||||||
ACTLBL_LOOK_LEFT_RIGHT, ACTLBL_LOOK_ROLL, ACTLBL_LOOK_UP_DOWN,
|
|
||||||
ACTLBL_MOVE_FORWARD_BACKWARD, ACTLBL_MOVE_LEFT_RIGHT, ACTLBL_MOVE_UP_DOWN,
|
|
||||||
},
|
|
||||||
DeltaTime,
|
|
||||||
};
|
|
||||||
use tracing::info;
|
|
||||||
|
|
||||||
#[async_std::main]
|
|
||||||
async fn main() {
|
|
||||||
let action_handler_plugin = |game: &mut Game| {
|
|
||||||
let action_handler = ActionHandler::builder()
|
|
||||||
.add_layout(LayoutId::from(0))
|
|
||||||
.add_action(ACTLBL_MOVE_FORWARD_BACKWARD, Action::new(ActionKind::Axis))
|
|
||||||
.add_action(ACTLBL_MOVE_LEFT_RIGHT, Action::new(ActionKind::Axis))
|
|
||||||
.add_action(ACTLBL_MOVE_UP_DOWN, Action::new(ActionKind::Axis))
|
|
||||||
.add_action(ACTLBL_LOOK_LEFT_RIGHT, Action::new(ActionKind::Axis))
|
|
||||||
.add_action(ACTLBL_LOOK_UP_DOWN, Action::new(ActionKind::Axis))
|
|
||||||
.add_action(ACTLBL_LOOK_ROLL, Action::new(ActionKind::Axis))
|
|
||||||
.add_action("Debug", Action::new(ActionKind::Button))
|
|
||||||
.add_mapping(
|
|
||||||
ActionMapping::builder(LayoutId::from(0), ActionMappingId::from(0))
|
|
||||||
.bind(
|
|
||||||
ACTLBL_MOVE_FORWARD_BACKWARD,
|
|
||||||
&[
|
|
||||||
ActionSource::Keyboard(KeyCode::W).into_binding_modifier(1.0),
|
|
||||||
ActionSource::Keyboard(KeyCode::S).into_binding_modifier(-1.0),
|
|
||||||
],
|
|
||||||
)
|
|
||||||
.bind(
|
|
||||||
ACTLBL_MOVE_LEFT_RIGHT,
|
|
||||||
&[
|
|
||||||
ActionSource::Keyboard(KeyCode::A).into_binding_modifier(-1.0),
|
|
||||||
ActionSource::Keyboard(KeyCode::D).into_binding_modifier(1.0),
|
|
||||||
],
|
|
||||||
)
|
|
||||||
.bind(
|
|
||||||
ACTLBL_MOVE_UP_DOWN,
|
|
||||||
&[
|
|
||||||
ActionSource::Keyboard(KeyCode::C).into_binding_modifier(1.0),
|
|
||||||
ActionSource::Keyboard(KeyCode::Z).into_binding_modifier(-1.0),
|
|
||||||
],
|
|
||||||
)
|
|
||||||
.bind(
|
|
||||||
ACTLBL_LOOK_LEFT_RIGHT,
|
|
||||||
&[
|
|
||||||
ActionSource::Mouse(MouseInput::Axis(MouseAxis::X)).into_binding(),
|
|
||||||
ActionSource::Keyboard(KeyCode::Left).into_binding_modifier(-1.0),
|
|
||||||
ActionSource::Keyboard(KeyCode::Right).into_binding_modifier(1.0),
|
|
||||||
],
|
|
||||||
)
|
|
||||||
.bind(
|
|
||||||
ACTLBL_LOOK_UP_DOWN,
|
|
||||||
&[
|
|
||||||
ActionSource::Mouse(MouseInput::Axis(MouseAxis::Y)).into_binding(),
|
|
||||||
ActionSource::Keyboard(KeyCode::Up).into_binding_modifier(-1.0),
|
|
||||||
ActionSource::Keyboard(KeyCode::Down).into_binding_modifier(1.0),
|
|
||||||
],
|
|
||||||
)
|
|
||||||
.bind(
|
|
||||||
ACTLBL_LOOK_ROLL,
|
|
||||||
&[
|
|
||||||
ActionSource::Keyboard(KeyCode::E).into_binding_modifier(-1.0),
|
|
||||||
ActionSource::Keyboard(KeyCode::Q).into_binding_modifier(1.0),
|
|
||||||
],
|
|
||||||
)
|
|
||||||
.bind(
|
|
||||||
"Debug",
|
|
||||||
&[ActionSource::Keyboard(KeyCode::B).into_binding()],
|
|
||||||
)
|
|
||||||
.finish(),
|
|
||||||
)
|
|
||||||
.finish();
|
|
||||||
|
|
||||||
let world = game.world_mut();
|
|
||||||
world.add_resource(action_handler);
|
|
||||||
game.with_plugin(InputActionPlugin);
|
|
||||||
};
|
|
||||||
|
|
||||||
Game::initialize()
|
|
||||||
.await
|
|
||||||
.with_plugin(lyra_engine::DefaultPlugins)
|
|
||||||
.with_plugin(setup_scene_plugin)
|
|
||||||
.with_plugin(action_handler_plugin)
|
|
||||||
//.with_plugin(camera_debug_plugin)
|
|
||||||
.with_plugin(FreeFlyCameraPlugin)
|
|
||||||
.run()
|
|
||||||
.await;
|
|
||||||
}
|
|
||||||
|
|
||||||
fn setup_scene_plugin(game: &mut Game) {
|
|
||||||
let world = game.world_mut();
|
|
||||||
let resman = world.get_resource_mut::<ResourceManager>();
|
|
||||||
let camera_gltf = resman
|
|
||||||
.request::<Gltf>("../assets/AntiqueCamera.glb")
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
camera_gltf.wait_recurse_dependencies_load();
|
|
||||||
let camera_mesh = &camera_gltf.data_ref().unwrap().scenes[0];
|
|
||||||
drop(resman);
|
|
||||||
|
|
||||||
world.spawn((
|
|
||||||
camera_mesh.clone(),
|
|
||||||
WorldTransform::default(),
|
|
||||||
Transform::from_xyz(0.0, -5.0, 0.0),
|
|
||||||
));
|
|
||||||
|
|
||||||
{
|
|
||||||
let mut light_tran = Transform::from_xyz(1.5, 2.5, 0.0);
|
|
||||||
light_tran.scale = Vec3::new(0.5, 0.5, 0.5);
|
|
||||||
light_tran.rotate_x(math::Angle::Degrees(-45.0));
|
|
||||||
light_tran.rotate_y(math::Angle::Degrees(25.0));
|
|
||||||
world.spawn((
|
|
||||||
DirectionalLight {
|
|
||||||
enabled: true,
|
|
||||||
color: Vec3::ONE,
|
|
||||||
intensity: 0.15, //..Default::default()
|
|
||||||
},
|
|
||||||
light_tran,
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut camera = CameraComponent::new_3d();
|
|
||||||
camera.transform.translation += math::Vec3::new(0.0, 0.0, 1.5);
|
|
||||||
world.spawn((camera, FreeFlyCamera::default()));
|
|
||||||
|
|
||||||
let fps_counter = |mut counter: ResMut<fps_counter::FPSCounter>,
|
|
||||||
delta: Res<DeltaTime>| -> anyhow::Result<()> {
|
|
||||||
let tick = counter.tick();
|
|
||||||
|
|
||||||
info!("FPS: {}, frame time: {}", tick, **delta);
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
};
|
|
||||||
|
|
||||||
world.add_resource(fps_counter::FPSCounter::new());
|
|
||||||
|
|
||||||
let rotate_system = |dt: Res<DeltaTime>, view: View<&mut Transform>| -> anyhow::Result<()> {
|
|
||||||
const SPEED: f32 = 4.0;
|
|
||||||
let dt = **dt;
|
|
||||||
|
|
||||||
for mut transform in view.iter() {
|
|
||||||
info!("rotation: {:?}", transform.rotation);
|
|
||||||
transform.rotate_y(math::Angle::Degrees(SPEED * dt));
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
};
|
|
||||||
|
|
||||||
let mut sys = BatchedSystem::new();
|
|
||||||
sys.with_criteria(FixedTimestep::new(60));
|
|
||||||
sys.with_system(rotate_system.into_system());
|
|
||||||
sys.with_system(fps_counter.into_system());
|
|
||||||
|
|
||||||
game.with_system("fixed_timestep", sys, &[]);
|
|
||||||
}
|
|
||||||
|
|
||||||
struct FixedTimestep {
|
|
||||||
max_tps: u32,
|
|
||||||
fixed_time: f32,
|
|
||||||
accumulator: f32,
|
|
||||||
old_dt: Option<DeltaTime>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[allow(dead_code)]
|
|
||||||
impl FixedTimestep {
|
|
||||||
pub fn new(max_tps: u32) -> Self {
|
|
||||||
Self {
|
|
||||||
max_tps,
|
|
||||||
fixed_time: Self::calc_fixed_time(max_tps),
|
|
||||||
accumulator: 0.0,
|
|
||||||
old_dt: None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn calc_fixed_time(max_tps: u32) -> f32 {
|
|
||||||
1.0 / max_tps as f32
|
|
||||||
}
|
|
||||||
|
|
||||||
fn set_tps(&mut self, tps: u32) {
|
|
||||||
self.max_tps = tps;
|
|
||||||
self.fixed_time = Self::calc_fixed_time(tps);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn tps(&self) -> u32 {
|
|
||||||
self.max_tps
|
|
||||||
}
|
|
||||||
|
|
||||||
fn fixed_time(&self) -> f32 {
|
|
||||||
self.fixed_time
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Criteria for FixedTimestep {
|
|
||||||
fn can_run(&mut self, mut world: NonNull<World>, check_count: u32) -> CriteriaSchedule {
|
|
||||||
let world = unsafe { world.as_mut() };
|
|
||||||
if check_count == 0 {
|
|
||||||
let delta_time = world.get_resource::<DeltaTime>();
|
|
||||||
self.accumulator += **delta_time;
|
|
||||||
}
|
|
||||||
|
|
||||||
if self.accumulator >= self.fixed_time {
|
|
||||||
self.accumulator -= self.fixed_time;
|
|
||||||
return CriteriaSchedule::YesAndLoop;
|
|
||||||
}
|
|
||||||
|
|
||||||
CriteriaSchedule::No
|
|
||||||
}
|
|
||||||
|
|
||||||
fn modify_world(&mut self, mut world: NonNull<World>) {
|
|
||||||
let world = unsafe { world.as_mut() };
|
|
||||||
self.old_dt = world.try_get_resource().map(|r| *r);
|
|
||||||
|
|
||||||
world.add_resource(DeltaTime::from(self.fixed_time));
|
|
||||||
}
|
|
||||||
|
|
||||||
fn undo_world_modifications(&mut self, mut world: NonNull<World>) {
|
|
||||||
let world = unsafe { world.as_mut() };
|
|
||||||
world.add_resource(
|
|
||||||
self.old_dt
|
|
||||||
.expect("DeltaTime resource was somehow never got from the world"),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,22 +0,0 @@
|
||||||
[package]
|
|
||||||
name = "many-lights"
|
|
||||||
version = "0.1.0"
|
|
||||||
edition = "2021"
|
|
||||||
|
|
||||||
[dependencies]
|
|
||||||
lyra-engine = { path = "../../", features = ["tracy"] }
|
|
||||||
anyhow = "1.0.75"
|
|
||||||
async-std = "1.12.0"
|
|
||||||
tracing = "0.1.37"
|
|
||||||
rand = "0.8.5"
|
|
||||||
fps_counter = "3.0.0"
|
|
||||||
|
|
||||||
[target.x86_64-unknown-linux-gnu]
|
|
||||||
linker = "/usr/bin/clang"
|
|
||||||
rustflags = ["-Clink-arg=-fuse-ld=lld", "-Clink-arg=-Wl,--no-rosegment"]
|
|
||||||
|
|
||||||
[profile.dev]
|
|
||||||
opt-level = 1
|
|
||||||
|
|
||||||
[profile.release]
|
|
||||||
debug = true
|
|
|
@ -1,189 +0,0 @@
|
||||||
use lyra_engine::{assets::{gltf::Gltf, ResourceManager}, ecs::query::{Res, ResMut, View}, game::Game, input::{Action, ActionHandler, ActionKind, ActionMapping, ActionMappingId, ActionSource, InputActionPlugin, KeyCode, LayoutId, MouseAxis, MouseInput}, math::{self, Quat, Transform, Vec3}, render::light::{directional::DirectionalLight, PointLight}, scene::{CameraComponent, FreeFlyCamera, FreeFlyCameraPlugin, ACTLBL_LOOK_LEFT_RIGHT, ACTLBL_LOOK_ROLL, ACTLBL_LOOK_UP_DOWN, ACTLBL_MOVE_FORWARD_BACKWARD, ACTLBL_MOVE_LEFT_RIGHT, ACTLBL_MOVE_UP_DOWN}, DeltaTime};
|
|
||||||
use rand::Rng;
|
|
||||||
use tracing::info;
|
|
||||||
|
|
||||||
const MAX_POINT_LIGHT_RANGE: f32 = 1.0;
|
|
||||||
const MIN_POINT_LIGHT_RANGE: f32 = 0.5;
|
|
||||||
const POINT_LIGHT_MAX_INTENSITY: f32 = 1.0;
|
|
||||||
const POINT_LIGHT_MIN_INTENSITY: f32 = 0.3;
|
|
||||||
const POINT_LIGHT_CUBE_SCALE: f32 = 0.2;
|
|
||||||
const POINT_LIGHT_NUM: u32 = 500;
|
|
||||||
|
|
||||||
const POINT_LIGHT_MAX_X: f32 = 9.0;
|
|
||||||
const POINT_LIGHT_MIN_X: f32 = -9.0;
|
|
||||||
|
|
||||||
const POINT_LIGHT_MAX_Y: f32 = 3.0;
|
|
||||||
const POINT_LIGHT_MIN_Y: f32 = 0.5;
|
|
||||||
|
|
||||||
const POINT_LIGHT_MAX_Z: f32 = 4.0;
|
|
||||||
const POINT_LIGHT_MIN_Z: f32 = -5.0;
|
|
||||||
|
|
||||||
#[async_std::main]
|
|
||||||
async fn main() {
|
|
||||||
|
|
||||||
let action_handler_plugin = |game: &mut Game| {
|
|
||||||
let action_handler = ActionHandler::builder()
|
|
||||||
.add_layout(LayoutId::from(0))
|
|
||||||
|
|
||||||
.add_action(ACTLBL_MOVE_FORWARD_BACKWARD, Action::new(ActionKind::Axis))
|
|
||||||
.add_action(ACTLBL_MOVE_LEFT_RIGHT, Action::new(ActionKind::Axis))
|
|
||||||
.add_action(ACTLBL_MOVE_UP_DOWN, Action::new(ActionKind::Axis))
|
|
||||||
.add_action(ACTLBL_LOOK_LEFT_RIGHT, Action::new(ActionKind::Axis))
|
|
||||||
.add_action(ACTLBL_LOOK_UP_DOWN, Action::new(ActionKind::Axis))
|
|
||||||
.add_action(ACTLBL_LOOK_ROLL, Action::new(ActionKind::Axis))
|
|
||||||
.add_action("Debug", Action::new(ActionKind::Button))
|
|
||||||
|
|
||||||
.add_mapping(ActionMapping::builder(LayoutId::from(0), ActionMappingId::from(0))
|
|
||||||
.bind(ACTLBL_MOVE_FORWARD_BACKWARD, &[
|
|
||||||
ActionSource::Keyboard(KeyCode::W).into_binding_modifier(1.0),
|
|
||||||
ActionSource::Keyboard(KeyCode::S).into_binding_modifier(-1.0)
|
|
||||||
])
|
|
||||||
.bind(ACTLBL_MOVE_LEFT_RIGHT, &[
|
|
||||||
ActionSource::Keyboard(KeyCode::A).into_binding_modifier(-1.0),
|
|
||||||
ActionSource::Keyboard(KeyCode::D).into_binding_modifier(1.0)
|
|
||||||
])
|
|
||||||
.bind(ACTLBL_MOVE_UP_DOWN, &[
|
|
||||||
ActionSource::Keyboard(KeyCode::C).into_binding_modifier(1.0),
|
|
||||||
ActionSource::Keyboard(KeyCode::Z).into_binding_modifier(-1.0)
|
|
||||||
])
|
|
||||||
.bind(ACTLBL_LOOK_LEFT_RIGHT, &[
|
|
||||||
ActionSource::Mouse(MouseInput::Axis(MouseAxis::X)).into_binding(),
|
|
||||||
ActionSource::Keyboard(KeyCode::Left).into_binding_modifier(-1.0),
|
|
||||||
ActionSource::Keyboard(KeyCode::Right).into_binding_modifier(1.0),
|
|
||||||
])
|
|
||||||
.bind(ACTLBL_LOOK_UP_DOWN, &[
|
|
||||||
ActionSource::Mouse(MouseInput::Axis(MouseAxis::Y)).into_binding(),
|
|
||||||
ActionSource::Keyboard(KeyCode::Up).into_binding_modifier(-1.0),
|
|
||||||
ActionSource::Keyboard(KeyCode::Down).into_binding_modifier(1.0),
|
|
||||||
])
|
|
||||||
.bind(ACTLBL_LOOK_ROLL, &[
|
|
||||||
ActionSource::Keyboard(KeyCode::E).into_binding_modifier(-1.0),
|
|
||||||
ActionSource::Keyboard(KeyCode::Q).into_binding_modifier(1.0),
|
|
||||||
])
|
|
||||||
.bind("Debug", &[
|
|
||||||
ActionSource::Keyboard(KeyCode::B).into_binding(),
|
|
||||||
])
|
|
||||||
.finish()
|
|
||||||
).finish();
|
|
||||||
|
|
||||||
let world = game.world_mut();
|
|
||||||
world.add_resource(action_handler);
|
|
||||||
game.with_plugin(InputActionPlugin);
|
|
||||||
};
|
|
||||||
|
|
||||||
Game::initialize().await
|
|
||||||
.with_plugin(lyra_engine::DefaultPlugins)
|
|
||||||
.with_plugin(setup_scene_plugin)
|
|
||||||
.with_plugin(action_handler_plugin)
|
|
||||||
.with_plugin(camera_debug_plugin)
|
|
||||||
.with_plugin(FreeFlyCameraPlugin)
|
|
||||||
.run().await;
|
|
||||||
}
|
|
||||||
|
|
||||||
fn setup_scene_plugin(game: &mut Game) {
|
|
||||||
let fps_counter = |mut counter: ResMut<fps_counter::FPSCounter>, delta: Res<DeltaTime>| -> anyhow::Result<()> {
|
|
||||||
let tick = counter.tick();
|
|
||||||
|
|
||||||
info!("FPS: {}, frame time: {}", tick, **delta);
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
};
|
|
||||||
game.with_system("fps_counter", fps_counter, &[]);
|
|
||||||
|
|
||||||
let world = game.world_mut();
|
|
||||||
world.add_resource(fps_counter::FPSCounter::new());
|
|
||||||
|
|
||||||
let resman = world.get_resource_mut::<ResourceManager>();
|
|
||||||
let cube_gltf = resman.request::<Gltf>("../assets/texture-sep/texture-sep.gltf").unwrap();
|
|
||||||
|
|
||||||
cube_gltf.wait_recurse_dependencies_load();
|
|
||||||
let cube_mesh = &cube_gltf.data_ref()
|
|
||||||
.unwrap().meshes[0];
|
|
||||||
|
|
||||||
let sponza_model = resman.request::<Gltf>("../assets/sponza/Sponza.gltf").unwrap();
|
|
||||||
drop(resman);
|
|
||||||
|
|
||||||
sponza_model.wait_recurse_dependencies_load();
|
|
||||||
let sponza_scene = &sponza_model.data_ref()
|
|
||||||
.unwrap().scenes[0];
|
|
||||||
|
|
||||||
world.spawn((
|
|
||||||
sponza_scene.clone(),
|
|
||||||
Transform::from_xyz(0.0, 0.0, 0.0),
|
|
||||||
));
|
|
||||||
|
|
||||||
{
|
|
||||||
let mut light_tran = Transform::from_xyz(1.5, 2.5, 0.0);
|
|
||||||
light_tran.scale = Vec3::new(0.5, 0.5, 0.5);
|
|
||||||
light_tran.rotate_x(math::Angle::Degrees(-45.0));
|
|
||||||
light_tran.rotate_y(math::Angle::Degrees(25.0));
|
|
||||||
world.spawn((
|
|
||||||
DirectionalLight {
|
|
||||||
enabled: true,
|
|
||||||
color: Vec3::ONE,
|
|
||||||
intensity: 0.15
|
|
||||||
//..Default::default()
|
|
||||||
},
|
|
||||||
light_tran,
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
let x_range = POINT_LIGHT_MIN_X..POINT_LIGHT_MAX_X;
|
|
||||||
let y_range = POINT_LIGHT_MIN_Y..POINT_LIGHT_MAX_Y;
|
|
||||||
let z_range = POINT_LIGHT_MIN_Z..POINT_LIGHT_MAX_Z;
|
|
||||||
|
|
||||||
let mut rand = rand::thread_rng();
|
|
||||||
let mut rand_vec3 = || -> Vec3 {
|
|
||||||
let x = rand.gen_range(x_range.clone());
|
|
||||||
let y = rand.gen_range(y_range.clone());
|
|
||||||
let z = rand.gen_range(z_range.clone());
|
|
||||||
|
|
||||||
Vec3::new(x, y, z)
|
|
||||||
};
|
|
||||||
|
|
||||||
let mut rand = rand::thread_rng();
|
|
||||||
for _ in 0..POINT_LIGHT_NUM {
|
|
||||||
let range = rand.gen_range(MIN_POINT_LIGHT_RANGE..MAX_POINT_LIGHT_RANGE);
|
|
||||||
let intensity = rand.gen_range(POINT_LIGHT_MIN_INTENSITY..POINT_LIGHT_MAX_INTENSITY);
|
|
||||||
let color = rand_vec3().normalize();
|
|
||||||
|
|
||||||
world.spawn((
|
|
||||||
PointLight {
|
|
||||||
enabled: true,
|
|
||||||
color,
|
|
||||||
intensity,
|
|
||||||
range,
|
|
||||||
..Default::default()
|
|
||||||
},
|
|
||||||
Transform::new(
|
|
||||||
rand_vec3(),
|
|
||||||
Quat::IDENTITY,
|
|
||||||
Vec3::new(POINT_LIGHT_CUBE_SCALE, POINT_LIGHT_CUBE_SCALE, POINT_LIGHT_CUBE_SCALE),
|
|
||||||
),
|
|
||||||
cube_mesh.clone(),
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut camera = CameraComponent::new_3d();
|
|
||||||
// these values were taken by manually positioning the camera in the scene.
|
|
||||||
camera.transform = Transform::new(
|
|
||||||
Vec3::new(-10.0, 0.94, -0.28),
|
|
||||||
Quat::from_xyzw(0.03375484, -0.7116095, 0.0342693, 0.70092666),
|
|
||||||
Vec3::ONE
|
|
||||||
);
|
|
||||||
world.spawn(( camera, FreeFlyCamera::default() ));
|
|
||||||
}
|
|
||||||
|
|
||||||
fn camera_debug_plugin(game: &mut Game) {
|
|
||||||
let sys = |handler: Res<ActionHandler>, view: View<&mut CameraComponent>| -> anyhow::Result<()> {
|
|
||||||
if handler.was_action_just_pressed("Debug") {
|
|
||||||
for mut cam in view.into_iter() {
|
|
||||||
cam.debug = !cam.debug;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
};
|
|
||||||
|
|
||||||
game.with_system("camera_debug_trigger", sys, &[]);
|
|
||||||
}
|
|
|
@ -7,7 +7,6 @@ struct FixedTimestep {
|
||||||
max_tps: u32,
|
max_tps: u32,
|
||||||
fixed_time: f32,
|
fixed_time: f32,
|
||||||
accumulator: f32,
|
accumulator: f32,
|
||||||
old_dt: Option<DeltaTime>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
|
@ -17,7 +16,6 @@ impl FixedTimestep {
|
||||||
max_tps,
|
max_tps,
|
||||||
fixed_time: Self::calc_fixed_time(max_tps),
|
fixed_time: Self::calc_fixed_time(max_tps),
|
||||||
accumulator: 0.0,
|
accumulator: 0.0,
|
||||||
old_dt: None,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -54,21 +52,6 @@ impl Criteria for FixedTimestep {
|
||||||
|
|
||||||
CriteriaSchedule::No
|
CriteriaSchedule::No
|
||||||
}
|
}
|
||||||
|
|
||||||
fn modify_world(&mut self, mut world: NonNull<World>) {
|
|
||||||
let world = unsafe { world.as_mut() };
|
|
||||||
self.old_dt = world.try_get_resource().map(|r| *r);
|
|
||||||
|
|
||||||
world.add_resource(DeltaTime::from(self.fixed_time));
|
|
||||||
}
|
|
||||||
|
|
||||||
fn undo_world_modifications(&mut self, mut world: NonNull<World>) {
|
|
||||||
let world = unsafe { world.as_mut() };
|
|
||||||
world.add_resource(
|
|
||||||
self.old_dt
|
|
||||||
.expect("DeltaTime resource was somehow never got from the world"),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
|
|
|
@ -5,7 +5,7 @@ use crate::{query::{AsQuery, Query}, Archetype, World};
|
||||||
/// This means that entities that `Q` fetches are skipped, and entities that
|
/// This means that entities that `Q` fetches are skipped, and entities that
|
||||||
/// `Q` does not fetch are not skipped.
|
/// `Q` does not fetch are not skipped.
|
||||||
///
|
///
|
||||||
/// ```nobuild
|
/// ```rust,nobuild
|
||||||
/// // Iterate over entities that has a transform, and are not the origin of a `ChildOf` relationship.
|
/// // Iterate over entities that has a transform, and are not the origin of a `ChildOf` relationship.
|
||||||
/// for (en, pos, _) in world
|
/// for (en, pos, _) in world
|
||||||
/// .view::<(Entities, &Transform, Not<Has<RelationOriginComponent<ChildOf>>>)>()
|
/// .view::<(Entities, &Transform, Not<Has<RelationOriginComponent<ChildOf>>>)>()
|
||||||
|
|
|
@ -36,7 +36,7 @@ impl<'a, Q1: Query, Q2: Query> Fetch<'a> for OrFetch<'a, Q1, Q2> {
|
||||||
///
|
///
|
||||||
/// This checks if `Q1` can fetch before checking `Q2`.
|
/// This checks if `Q1` can fetch before checking `Q2`.
|
||||||
///
|
///
|
||||||
/// ```nobuild
|
/// ```rust,nobuild
|
||||||
/// for (en, pos, _) in world
|
/// for (en, pos, _) in world
|
||||||
/// .view::<(Entities, &Transform, Or<Has<Mesh>, Has<Scene>>)>()
|
/// .view::<(Entities, &Transform, Or<Has<Mesh>, Has<Scene>>)>()
|
||||||
/// .iter()
|
/// .iter()
|
||||||
|
|
|
@ -27,10 +27,6 @@ mod world;
|
||||||
#[allow(unused_imports)]
|
#[allow(unused_imports)]
|
||||||
pub use world::*;
|
pub use world::*;
|
||||||
|
|
||||||
mod optional;
|
|
||||||
#[allow(unused_imports)]
|
|
||||||
pub use optional::*;
|
|
||||||
|
|
||||||
pub mod dynamic;
|
pub mod dynamic;
|
||||||
|
|
||||||
pub mod filter;
|
pub mod filter;
|
||||||
|
|
|
@ -1,76 +0,0 @@
|
||||||
use crate::{Archetype, World};
|
|
||||||
|
|
||||||
use super::{AsQuery, Fetch, Query};
|
|
||||||
|
|
||||||
#[derive(Default)]
|
|
||||||
pub struct OptionalFetcher<'a, Q: AsQuery> {
|
|
||||||
fetcher: Option<<Q::Query as Query>::Fetch<'a>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a, Q: AsQuery> Fetch<'a> for OptionalFetcher<'a, Q> {
|
|
||||||
type Item = Option<<Q::Query as Query>::Item<'a>>;
|
|
||||||
|
|
||||||
fn dangling() -> Self {
|
|
||||||
unreachable!()
|
|
||||||
}
|
|
||||||
|
|
||||||
unsafe fn get_item(&mut self, entity: crate::ArchetypeEntityId) -> Self::Item {
|
|
||||||
self.fetcher.as_mut()
|
|
||||||
.map(|f| f.get_item(entity))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn can_visit_item(&mut self, entity: crate::ArchetypeEntityId) -> bool {
|
|
||||||
self.fetcher.as_mut()
|
|
||||||
.map(|f| f.can_visit_item(entity))
|
|
||||||
.unwrap_or(true)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Default)]
|
|
||||||
pub struct Optional<Q: AsQuery> {
|
|
||||||
query: Q::Query,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<Q: AsQuery> Copy for Optional<Q> { }
|
|
||||||
|
|
||||||
impl<Q: AsQuery> Clone for Optional<Q> {
|
|
||||||
fn clone(&self) -> Self {
|
|
||||||
Self { query: self.query.clone() }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<Q: AsQuery> Query for Optional<Q> {
|
|
||||||
type Item<'a> = Option<<Q::Query as Query>::Item<'a>>;
|
|
||||||
|
|
||||||
type Fetch<'a> = OptionalFetcher<'a, Q>;
|
|
||||||
|
|
||||||
fn new() -> Self {
|
|
||||||
Optional {
|
|
||||||
query: Q::Query::new(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn can_visit_archetype(&self, _: &Archetype) -> bool {
|
|
||||||
true
|
|
||||||
}
|
|
||||||
|
|
||||||
unsafe fn fetch<'a>(&self, world: &'a World, arch: &'a Archetype, tick: crate::Tick) -> Self::Fetch<'a> {
|
|
||||||
let fetcher = if self.query.can_visit_archetype(arch) {
|
|
||||||
Some(self.query.fetch(world, arch, tick))
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
};
|
|
||||||
|
|
||||||
OptionalFetcher {
|
|
||||||
fetcher,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<Q: AsQuery> AsQuery for Optional<Q> {
|
|
||||||
type Query = Self;
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<Q: AsQuery> AsQuery for Option<Q> {
|
|
||||||
type Query = Optional<Q>;
|
|
||||||
}
|
|
|
@ -1,9 +1,9 @@
|
||||||
use lyra_ecs::World;
|
use lyra_ecs::World;
|
||||||
use tracing::{debug_span, instrument};
|
use tracing::debug_span;
|
||||||
|
|
||||||
use crate::Access;
|
use crate::Access;
|
||||||
|
|
||||||
use super::{Criteria, GraphExecutorError, IntoSystem, System};
|
use super::{System, Criteria, IntoSystem};
|
||||||
|
|
||||||
/// A system that executes a batch of systems in order that they were given.
|
/// A system that executes a batch of systems in order that they were given.
|
||||||
/// You can optionally add criteria that must pass before the systems are
|
/// You can optionally add criteria that must pass before the systems are
|
||||||
|
@ -13,7 +13,6 @@ pub struct BatchedSystem {
|
||||||
systems: Vec<Box<dyn System>>,
|
systems: Vec<Box<dyn System>>,
|
||||||
criteria: Vec<Box<dyn Criteria>>,
|
criteria: Vec<Box<dyn Criteria>>,
|
||||||
criteria_checks: u32,
|
criteria_checks: u32,
|
||||||
did_run: bool,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl BatchedSystem {
|
impl BatchedSystem {
|
||||||
|
@ -48,7 +47,6 @@ impl System for BatchedSystem {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[instrument(skip(self, world))]
|
|
||||||
fn execute(&mut self, world: std::ptr::NonNull<World>) -> anyhow::Result<()> {
|
fn execute(&mut self, world: std::ptr::NonNull<World>) -> anyhow::Result<()> {
|
||||||
let mut can_run = true;
|
let mut can_run = true;
|
||||||
let mut check_again = false;
|
let mut check_again = false;
|
||||||
|
@ -71,26 +69,13 @@ impl System for BatchedSystem {
|
||||||
}
|
}
|
||||||
|
|
||||||
if can_run {
|
if can_run {
|
||||||
for criteria in self.criteria.iter_mut() {
|
|
||||||
criteria.modify_world(world);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (idx, system) in self.systems.iter_mut().enumerate() {
|
for (idx, system) in self.systems.iter_mut().enumerate() {
|
||||||
let span = debug_span!("batch", system=idx);
|
let sys_span = debug_span!("batch", system=tracing::field::Empty);
|
||||||
let _e = span.enter();
|
sys_span.record("system", idx);
|
||||||
|
let _e = sys_span.enter();
|
||||||
|
|
||||||
system.execute(world)?;
|
system.execute(world)?;
|
||||||
|
|
||||||
/* let deferred_span = debug_span!("deferred_exec");
|
|
||||||
let _e = deferred_span.enter();
|
|
||||||
|
|
||||||
if let Err(e) = system.execute_deferred(world)
|
|
||||||
.map_err(|e| GraphExecutorError::Command(e)) {
|
|
||||||
return Err(e.into());
|
|
||||||
} */
|
|
||||||
}
|
}
|
||||||
|
|
||||||
self.did_run = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if check_again {
|
if check_again {
|
||||||
|
@ -103,25 +88,8 @@ impl System for BatchedSystem {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[instrument(skip(self, world))]
|
fn execute_deferred(&mut self, _: std::ptr::NonNull<World>) -> anyhow::Result<()> {
|
||||||
fn execute_deferred(&mut self, world: std::ptr::NonNull<World>) -> anyhow::Result<()> {
|
todo!()
|
||||||
if self.did_run {
|
|
||||||
for (idx, system) in self.systems.iter_mut().enumerate() {
|
|
||||||
let span = debug_span!("batch", system=idx);
|
|
||||||
let _e = span.enter();
|
|
||||||
|
|
||||||
system.execute_deferred(world)
|
|
||||||
.map_err(|e| GraphExecutorError::Command(e))?;
|
|
||||||
}
|
|
||||||
|
|
||||||
for criteria in self.criteria.iter_mut() {
|
|
||||||
criteria.undo_world_modifications(world);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
self.did_run = false;
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -26,17 +26,13 @@ pub trait Criteria {
|
||||||
/// * `world` - The ecs world.
|
/// * `world` - The ecs world.
|
||||||
/// * `check_count` - The amount of times the Criteria has been checked this tick.
|
/// * `check_count` - The amount of times the Criteria has been checked this tick.
|
||||||
fn can_run(&mut self, world: NonNull<World>, check_count: u32) -> CriteriaSchedule;
|
fn can_run(&mut self, world: NonNull<World>, check_count: u32) -> CriteriaSchedule;
|
||||||
|
}
|
||||||
/// Modify the world after the [`Criteria`] in the system batch allows the systems to run.
|
|
||||||
///
|
impl<F> Criteria for F
|
||||||
/// This can be great if this Criteria limits the execution of systems based off of resources.
|
where F: FnMut(&mut World, u32) -> CriteriaSchedule
|
||||||
/// A `FixedTimestep` criteria would use this to replace the [`DeltaTime`] resource in the
|
{
|
||||||
/// world to match the timestep time.
|
fn can_run(&mut self, mut world: NonNull<World>, check_count: u32) -> CriteriaSchedule {
|
||||||
fn modify_world(&mut self, world: NonNull<World>);
|
let world_mut = unsafe { world.as_mut() };
|
||||||
|
self(world_mut, check_count)
|
||||||
/// Undo modifications to the world after the systems in the batch have been executed.
|
}
|
||||||
///
|
|
||||||
/// The `FixedTimestep` criteria (see docs for [`Criteria::modify_world`]) uses this
|
|
||||||
/// to replace the [`DeltaTime`] resource with its original value before it was replaced.
|
|
||||||
fn undo_world_modifications(&mut self, world: NonNull<World>);
|
|
||||||
}
|
}
|
|
@ -1,6 +1,6 @@
|
||||||
use std::{collections::{HashMap, VecDeque, HashSet}, ptr::NonNull};
|
use std::{collections::{HashMap, VecDeque, HashSet}, ptr::NonNull};
|
||||||
|
|
||||||
use tracing::{debug_span, info_span, instrument};
|
use tracing::{debug_span, info_span};
|
||||||
|
|
||||||
use super::System;
|
use super::System;
|
||||||
|
|
||||||
|
@ -60,7 +60,6 @@ impl GraphExecutor {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Executes the systems in the graph
|
/// Executes the systems in the graph
|
||||||
#[instrument(skip(self, world_ptr, stop_on_error))]
|
|
||||||
pub fn execute(&mut self, mut world_ptr: NonNull<World>, stop_on_error: bool)
|
pub fn execute(&mut self, mut world_ptr: NonNull<World>, stop_on_error: bool)
|
||||||
-> Result<Vec<GraphExecutorError>, GraphExecutorError> {
|
-> Result<Vec<GraphExecutorError>, GraphExecutorError> {
|
||||||
let mut stack = VecDeque::new();
|
let mut stack = VecDeque::new();
|
||||||
|
@ -72,11 +71,13 @@ impl GraphExecutor {
|
||||||
|
|
||||||
let mut possible_errors = Vec::new();
|
let mut possible_errors = Vec::new();
|
||||||
|
|
||||||
|
let sys_span = info_span!("graph_exec", system=tracing::field::Empty);
|
||||||
|
|
||||||
while let Some(node) = stack.pop_front() {
|
while let Some(node) = stack.pop_front() {
|
||||||
let system = self.systems.get_mut(node.as_str()).unwrap();
|
let system = self.systems.get_mut(node.as_str()).unwrap();
|
||||||
|
|
||||||
let span = info_span!("graph_exec", system=system.name.clone());
|
sys_span.record("system", system.name.clone());
|
||||||
let _e = span.enter();
|
let _e = sys_span.enter();
|
||||||
|
|
||||||
if let Err(e) = system.system.execute(world_ptr)
|
if let Err(e) = system.system.execute(world_ptr)
|
||||||
.map_err(|e| GraphExecutorError::SystemError(node, e)) {
|
.map_err(|e| GraphExecutorError::SystemError(node, e)) {
|
||||||
|
|
|
@ -128,39 +128,22 @@ impl World {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Insert a component bundle into an existing entity.
|
/// Insert a bundle into an existing entity. If the components are already existing on the
|
||||||
///
|
/// entity, they will be updated, else the entity will be moved to a different Archetype
|
||||||
/// If the components are already existing on the entity, they will be updated, else the
|
/// that can store the entity. That may involve creating a new Archetype.
|
||||||
/// entity will be moved to a different Archetype that can store the entity. That may
|
|
||||||
/// involve creating a new Archetype.
|
|
||||||
pub fn insert<B>(&mut self, entity: Entity, bundle: B)
|
pub fn insert<B>(&mut self, entity: Entity, bundle: B)
|
||||||
where
|
where
|
||||||
B: Bundle
|
B: Bundle
|
||||||
{
|
{
|
||||||
|
// TODO: If the entity already has the components in `bundle`, update the values of the
|
||||||
|
// components with the bundle.
|
||||||
|
|
||||||
let tick = self.tick();
|
let tick = self.tick();
|
||||||
|
|
||||||
let record = self.entities.entity_record(entity).unwrap();
|
let record = self.entities.entity_record(entity).unwrap();
|
||||||
let current_arch = self.archetypes.get(&record.id).unwrap();
|
let current_arch = self.archetypes.get(&record.id).unwrap();
|
||||||
let current_arch_len = current_arch.len();
|
let current_arch_len = current_arch.len();
|
||||||
|
|
||||||
let mut contains_all = true;
|
|
||||||
for id in bundle.type_ids() {
|
|
||||||
contains_all = contains_all && current_arch.get_column(id).is_some();
|
|
||||||
}
|
|
||||||
|
|
||||||
if contains_all {
|
|
||||||
let current_arch = self.archetypes.get_mut(&record.id).unwrap();
|
|
||||||
let entry_idx = *current_arch.entity_indexes()
|
|
||||||
.get(&entity).unwrap();
|
|
||||||
|
|
||||||
bundle.take(|ptr, id, _info| {
|
|
||||||
let col = current_arch.get_column_mut(id).unwrap();
|
|
||||||
unsafe { col.set_at(entry_idx.0 as _, ptr, tick) };
|
|
||||||
});
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// contains the type ids for the old component columns + the ids for the new components
|
// contains the type ids for the old component columns + the ids for the new components
|
||||||
let mut combined_column_types: Vec<DynTypeId> = current_arch.columns.iter().map(|c| c.info.type_id()).collect();
|
let mut combined_column_types: Vec<DynTypeId> = current_arch.columns.iter().map(|c| c.info.type_id()).collect();
|
||||||
combined_column_types.extend(bundle.type_ids());
|
combined_column_types.extend(bundle.type_ids());
|
||||||
|
@ -244,7 +227,7 @@ impl World {
|
||||||
/// A method used for debugging implementation details of the ECS.
|
/// A method used for debugging implementation details of the ECS.
|
||||||
///
|
///
|
||||||
/// Here's an example of the output:
|
/// Here's an example of the output:
|
||||||
/// ```nobuild
|
/// ```
|
||||||
/// Entities
|
/// Entities
|
||||||
/// 1 in archetype 0 at 0
|
/// 1 in archetype 0 at 0
|
||||||
/// 0 in archetype 1 at 0
|
/// 0 in archetype 1 at 0
|
||||||
|
@ -288,7 +271,7 @@ impl World {
|
||||||
/// the contents of entities inside the archetypes.
|
/// the contents of entities inside the archetypes.
|
||||||
///
|
///
|
||||||
/// Below is a template of the output:
|
/// Below is a template of the output:
|
||||||
/// ```nobuild
|
/// ```
|
||||||
/// Entities
|
/// Entities
|
||||||
/// %ENTITY_ID% in archetype %ARCHETYPE_ID% at %INDEX%
|
/// %ENTITY_ID% in archetype %ARCHETYPE_ID% at %INDEX%
|
||||||
/// Arch ID -- %ARCHETYPE_LEN% entities
|
/// Arch ID -- %ARCHETYPE_LEN% entities
|
||||||
|
@ -635,6 +618,8 @@ mod tests {
|
||||||
|
|
||||||
insert_and_assert(&mut world, e1, v2s[0], v3s[0]);
|
insert_and_assert(&mut world, e1, v2s[0], v3s[0]);
|
||||||
println!("Entity 1 is good");
|
println!("Entity 1 is good");
|
||||||
|
assert_eq!(world.archetypes.len(), 1);
|
||||||
|
println!("Empty archetype was removed");
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -670,21 +655,4 @@ mod tests {
|
||||||
assert!(tick >= world_tick);
|
assert!(tick >= world_tick);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Tests replacing components using World::insert
|
|
||||||
#[test]
|
|
||||||
fn entity_insert_replace() {
|
|
||||||
let mut world = World::new();
|
|
||||||
let first = world.spawn((Vec2::new(10.0, 10.0),));
|
|
||||||
let second = world.spawn((Vec2::new(5.0, 5.0),));
|
|
||||||
|
|
||||||
world.insert(first, Vec2::new(50.0, 50.0));
|
|
||||||
|
|
||||||
let pos = world.view_one::<&mut Vec2>(first).get().unwrap();
|
|
||||||
assert_eq!(*pos, Vec2::new(50.0, 50.0));
|
|
||||||
drop(pos);
|
|
||||||
|
|
||||||
let pos = world.view_one::<&mut Vec2>(second).get().unwrap();
|
|
||||||
assert_eq!(*pos, Vec2::new(5.0, 5.0));
|
|
||||||
}
|
|
||||||
}
|
}
|
|
@ -11,17 +11,14 @@ lyra-math = { path = "../lyra-math" }
|
||||||
lyra-scene = { path = "../lyra-scene" }
|
lyra-scene = { path = "../lyra-scene" }
|
||||||
|
|
||||||
winit = "0.28.1"
|
winit = "0.28.1"
|
||||||
wgpu = "0.15.1"
|
|
||||||
|
|
||||||
tracing = "0.1.37"
|
tracing = "0.1.37"
|
||||||
tracing-subscriber = { version = "0.3.16", features = [ "tracing-log" ] }
|
tracing-subscriber = { version = "0.3.16", features = [ "tracing-log" ] }
|
||||||
tracing-log = "0.1.3"
|
tracing-log = "0.1.3"
|
||||||
tracing-appender = "0.2.2"
|
tracing-appender = "0.2.2"
|
||||||
tracing-tracy = { version = "0.11.0", optional = true }
|
wgpu = "0.15.1"
|
||||||
|
|
||||||
async-std = { version = "1.12.0", features = [ "unstable", "attributes" ] }
|
async-std = { version = "1.12.0", features = [ "unstable", "attributes" ] }
|
||||||
cfg-if = "1"
|
cfg-if = "1"
|
||||||
bytemuck = { version = "1.12", features = [ "derive", "min_const_generics" ] }
|
bytemuck = { version = "1.12", features = [ "derive" ] }
|
||||||
image = { version = "0.24", default-features = false, features = ["png", "jpeg"] }
|
image = { version = "0.24", default-features = false, features = ["png", "jpeg"] }
|
||||||
anyhow = "1.0"
|
anyhow = "1.0"
|
||||||
instant = "0.1"
|
instant = "0.1"
|
||||||
|
@ -33,8 +30,3 @@ quote = "1.0.29"
|
||||||
uuid = { version = "1.5.0", features = ["v4", "fast-rng"] }
|
uuid = { version = "1.5.0", features = ["v4", "fast-rng"] }
|
||||||
itertools = "0.11.0"
|
itertools = "0.11.0"
|
||||||
thiserror = "1.0.56"
|
thiserror = "1.0.56"
|
||||||
unique = "0.9.1"
|
|
||||||
rustc-hash = "1.1.0"
|
|
||||||
|
|
||||||
[features]
|
|
||||||
tracy = ["dep:tracing-tracy"]
|
|
||||||
|
|
|
@ -4,15 +4,9 @@ use lyra_reflect::Reflect;
|
||||||
|
|
||||||
use crate::{plugin::Plugin, game::GameStages};
|
use crate::{plugin::Plugin, game::GameStages};
|
||||||
|
|
||||||
#[derive(Clone, Copy, Component, Default, Reflect)]
|
#[derive(Clone, Component, Default, Reflect)]
|
||||||
pub struct DeltaTime(f32, #[reflect(skip)] Option<Instant>);
|
pub struct DeltaTime(f32, #[reflect(skip)] Option<Instant>);
|
||||||
|
|
||||||
impl From<f32> for DeltaTime {
|
|
||||||
fn from(value: f32) -> Self {
|
|
||||||
DeltaTime(value, None)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl std::ops::Deref for DeltaTime {
|
impl std::ops::Deref for DeltaTime {
|
||||||
type Target = f32;
|
type Target = f32;
|
||||||
|
|
||||||
|
|
|
@ -3,7 +3,7 @@ use std::{sync::Arc, collections::VecDeque, ptr::NonNull};
|
||||||
use async_std::task::block_on;
|
use async_std::task::block_on;
|
||||||
|
|
||||||
use lyra_ecs::{World, system::{System, IntoSystem}};
|
use lyra_ecs::{World, system::{System, IntoSystem}};
|
||||||
use tracing::{error, info, Level};
|
use tracing::{info, error, Level};
|
||||||
use tracing_appender::non_blocking;
|
use tracing_appender::non_blocking;
|
||||||
use tracing_subscriber::{
|
use tracing_subscriber::{
|
||||||
layer::SubscriberExt,
|
layer::SubscriberExt,
|
||||||
|
@ -344,22 +344,15 @@ impl Game {
|
||||||
pub async fn run(&mut self) {
|
pub async fn run(&mut self) {
|
||||||
// init logging
|
// init logging
|
||||||
let (stdout_layer, _stdout_nb) = non_blocking(std::io::stdout());
|
let (stdout_layer, _stdout_nb) = non_blocking(std::io::stdout());
|
||||||
{
|
tracing_subscriber::registry()
|
||||||
let t = tracing_subscriber::registry()
|
.with(fmt::layer().with_writer(stdout_layer))
|
||||||
.with(fmt::layer().with_writer(stdout_layer));
|
.with(filter::Targets::new()
|
||||||
|
|
||||||
#[cfg(feature = "tracy")]
|
|
||||||
let t = t.with(tracing_tracy::TracyLayer::default());
|
|
||||||
|
|
||||||
t.with(filter::Targets::new()
|
|
||||||
// done by prefix, so it includes all lyra subpackages
|
// done by prefix, so it includes all lyra subpackages
|
||||||
.with_target("lyra", Level::DEBUG)
|
.with_target("lyra", Level::DEBUG)
|
||||||
.with_target("wgpu", Level::WARN)
|
.with_target("wgpu", Level::WARN)
|
||||||
.with_target("winit", Level::DEBUG)
|
.with_target("winit", Level::DEBUG)
|
||||||
.with_default(Level::INFO))
|
.with_default(Level::INFO))
|
||||||
.init();
|
.init();
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
let world = self.world.take().unwrap_or_default();
|
let world = self.world.take().unwrap_or_default();
|
||||||
|
|
||||||
|
|
|
@ -1,292 +0,0 @@
|
||||||
use std::{alloc::Layout, cmp, marker::PhantomData, mem};
|
|
||||||
|
|
||||||
use std::{alloc, ptr};
|
|
||||||
use unique::Unique;
|
|
||||||
|
|
||||||
/// A [`Vec`] with its elements aligned to a runtime alignment value.
|
|
||||||
pub struct AVec<T> {
|
|
||||||
buf: Unique<u8>,
|
|
||||||
cap: usize,
|
|
||||||
len: usize,
|
|
||||||
align: usize,
|
|
||||||
_marker: PhantomData<T>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T> AVec<T> {
|
|
||||||
// Tiny Vecs are dumb. Skip to:
|
|
||||||
// - 8 if the element size is 1, because any heap allocators are likely
|
|
||||||
// to round up a request of less than 8 bytes to at least 8 bytes.
|
|
||||||
// - 4 if elements are moderate-sized (<= 1 KiB).
|
|
||||||
// - 1 otherwise, to avoid wasting too much space for very short Vecs.
|
|
||||||
//
|
|
||||||
// Taken from Rust's standard library RawVec
|
|
||||||
pub(crate) const MIN_NON_ZERO_CAP: usize = if mem::size_of::<T>() == 1 {
|
|
||||||
8
|
|
||||||
} else if mem::size_of::<T>() <= 1024 {
|
|
||||||
4
|
|
||||||
} else {
|
|
||||||
1
|
|
||||||
};
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub fn new(alignment: usize) -> Self {
|
|
||||||
debug_assert!(mem::size_of::<T>() > 0, "ZSTs not yet supported");
|
|
||||||
|
|
||||||
Self {
|
|
||||||
buf: Unique::dangling(),
|
|
||||||
cap: 0,
|
|
||||||
len: 0,
|
|
||||||
align: alignment,
|
|
||||||
_marker: PhantomData
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Constructs a new, empty `AVec` with at least the specified capacity.
|
|
||||||
///
|
|
||||||
/// The aligned vector will be able to hold at least `capacity` elements without reallocating.
|
|
||||||
/// This method may allocate for more elements than `capacity`. If `capacity` is zero,
|
|
||||||
/// the vector will not allocate.
|
|
||||||
///
|
|
||||||
/// # Panics
|
|
||||||
///
|
|
||||||
/// Panics if the capacity exceeds `usize::MAX` bytes.
|
|
||||||
#[inline]
|
|
||||||
pub fn with_capacity(alignment: usize, capacity: usize) -> Self {
|
|
||||||
let mut s = Self::new(alignment);
|
|
||||||
|
|
||||||
if capacity > 0 {
|
|
||||||
unsafe {
|
|
||||||
s.grow_amortized(0, capacity);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
s
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Calculates the size of the 'slot' for a single **aligned** item.
|
|
||||||
#[inline(always)]
|
|
||||||
fn slot_size(&self) -> usize {
|
|
||||||
let a = self.align - 1;
|
|
||||||
mem::align_of::<T>() + (a) & !a
|
|
||||||
}
|
|
||||||
|
|
||||||
/// # Panics
|
|
||||||
///
|
|
||||||
/// Panics if the new capacity exceeds `usize::MAX` bytes.
|
|
||||||
#[inline]
|
|
||||||
unsafe fn grow_amortized(&mut self, len: usize, additional: usize) {
|
|
||||||
debug_assert!(additional > 0);
|
|
||||||
|
|
||||||
let required_cap = len.checked_add(additional)
|
|
||||||
.expect("Capacity overflow");
|
|
||||||
|
|
||||||
let cap = cmp::max(self.cap * 2, required_cap);
|
|
||||||
let cap = cmp::max(Self::MIN_NON_ZERO_CAP, cap);
|
|
||||||
|
|
||||||
let new_layout = Layout::from_size_align_unchecked(cap * self.slot_size(), self.align);
|
|
||||||
|
|
||||||
let ptr = alloc::alloc(new_layout);
|
|
||||||
self.buf = Unique::new_unchecked(ptr);
|
|
||||||
self.cap = cap;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// # Panics
|
|
||||||
///
|
|
||||||
/// Panics if the new capacity exceeds `usize::MAX` bytes.
|
|
||||||
#[inline]
|
|
||||||
unsafe fn grow_exact(&mut self, len: usize, additional: usize) {
|
|
||||||
debug_assert!(additional > 0);
|
|
||||||
|
|
||||||
let cap = len.checked_add(additional)
|
|
||||||
.expect("Capacity overflow");
|
|
||||||
|
|
||||||
let new_layout = Layout::from_size_align_unchecked(cap * self.slot_size(), self.align);
|
|
||||||
|
|
||||||
let ptr = alloc::alloc(new_layout);
|
|
||||||
self.buf = Unique::new_unchecked(ptr);
|
|
||||||
self.cap = cap;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Reserves capacity for at least `additional` more elements.
|
|
||||||
///
|
|
||||||
/// The collection may reserve more space to speculatively avoid frequent reallocations.
|
|
||||||
/// After calling `reserve`, capacity will be greater than or equal to
|
|
||||||
/// `self.len() + additional`. Does nothing if capacity is already sufficient.
|
|
||||||
///
|
|
||||||
/// # Panics
|
|
||||||
///
|
|
||||||
/// Panics if the new capacity exceeds `usize::MAX` bytes.
|
|
||||||
#[inline]
|
|
||||||
pub fn reserve(&mut self, additional: usize) {
|
|
||||||
debug_assert!(additional > 0);
|
|
||||||
|
|
||||||
let remaining = self.capacity().wrapping_sub(self.len);
|
|
||||||
|
|
||||||
if additional > remaining {
|
|
||||||
unsafe { self.grow_amortized(self.len, additional) };
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Reserves capacity for `additional` more elements.
|
|
||||||
///
|
|
||||||
/// Unlike [`reserve`], this will not over-allocate to speculatively avoid frequent
|
|
||||||
/// reallocations. After calling `reserve_exact`, capacity will be equal to
|
|
||||||
/// `self.len() + additional`. Does nothing if the capacity is already sufficient.
|
|
||||||
///
|
|
||||||
/// Prefer [`reserve`] if future insertions are expected.
|
|
||||||
///
|
|
||||||
/// # Panics
|
|
||||||
///
|
|
||||||
/// Panics if the new capacity exceeds `usize::MAX` bytes.
|
|
||||||
#[inline]
|
|
||||||
pub fn reserve_exact(&mut self, additional: usize) {
|
|
||||||
let remaining = self.capacity().wrapping_sub(self.len);
|
|
||||||
|
|
||||||
if additional > remaining {
|
|
||||||
unsafe { self.grow_exact(self.len, additional) };
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Appends an element to the back of the collection.
|
|
||||||
///
|
|
||||||
/// # Panics
|
|
||||||
///
|
|
||||||
/// Panics if the new capacity exceeds `usize::MAX` bytes.
|
|
||||||
#[inline]
|
|
||||||
pub fn push(&mut self, val: T) {
|
|
||||||
if self.len == self.cap {
|
|
||||||
self.reserve(self.slot_size());
|
|
||||||
}
|
|
||||||
|
|
||||||
unsafe {
|
|
||||||
// SAFETY: the length is ensured to be less than the capacity.
|
|
||||||
self.set_at_unchecked(self.len, val);
|
|
||||||
}
|
|
||||||
|
|
||||||
self.len += 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Sets an element at position `idx` within the vector to `val`.
|
|
||||||
///
|
|
||||||
/// # Unsafe
|
|
||||||
///
|
|
||||||
/// If `self.len > idx`, bytes past the length of the vector will be written to, potentially
|
|
||||||
/// also writing past the capacity of the vector.
|
|
||||||
#[inline(always)]
|
|
||||||
unsafe fn set_at_unchecked(&mut self, idx: usize, val: T) {
|
|
||||||
let ptr = self.buf
|
|
||||||
.as_ptr()
|
|
||||||
.add(idx * self.slot_size());
|
|
||||||
|
|
||||||
std::ptr::write(ptr.cast::<T>(), val);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Sets an element at position `idx` within the vector to `val`.
|
|
||||||
///
|
|
||||||
/// # Panics
|
|
||||||
///
|
|
||||||
/// Panics if `idx >= self.len`.
|
|
||||||
#[inline(always)]
|
|
||||||
pub fn set_at(&mut self, idx: usize, val: T) {
|
|
||||||
assert!(self.len > idx);
|
|
||||||
|
|
||||||
unsafe {
|
|
||||||
self.set_at_unchecked(idx, val);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Shortens the vector, keeping the first `len` elements and dropping the rest.
|
|
||||||
///
|
|
||||||
/// If `len` is greater or equal to the vector’s current length, this has no effect.
|
|
||||||
#[inline]
|
|
||||||
pub fn truncate(&mut self, len: usize) {
|
|
||||||
if len > self.len {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
unsafe {
|
|
||||||
// drop each element past the new length
|
|
||||||
for i in len..self.len {
|
|
||||||
let ptr = self.buf.as_ptr()
|
|
||||||
.add(i * self.slot_size())
|
|
||||||
.cast::<T>();
|
|
||||||
|
|
||||||
ptr::drop_in_place(ptr);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
self.len = len;
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline(always)]
|
|
||||||
pub fn as_ptr(&self) -> *const u8 {
|
|
||||||
self.buf.as_ptr()
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline(always)]
|
|
||||||
pub fn as_mut_ptr(&self) -> *mut u8 {
|
|
||||||
self.buf.as_ptr()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns the alignment of the elements in the vector.
|
|
||||||
#[inline(always)]
|
|
||||||
pub fn align(&self) -> usize {
|
|
||||||
self.align
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns the length of the vector.
|
|
||||||
#[inline(always)]
|
|
||||||
pub fn len(&self) -> usize {
|
|
||||||
self.len
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns the capacity of the vector.
|
|
||||||
///
|
|
||||||
/// The capacity is the amount of elements that the vector can store without reallocating.
|
|
||||||
#[inline(always)]
|
|
||||||
pub fn capacity(&self) -> usize {
|
|
||||||
self.cap
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: Clone> AVec<T> {
|
|
||||||
/// Resized the `AVec` in-place so that `len` is equal to `new_len`.
|
|
||||||
///
|
|
||||||
/// If `new_len` is greater than `len`, the `AVec` is extended by the difference, and
|
|
||||||
/// each additional slot is filled with `value`. If `new_len` is less than `len`,
|
|
||||||
/// the `AVec` will be truncated by to be `new_len`
|
|
||||||
///
|
|
||||||
/// This method requires `T` to implement [`Clone`] in order to clone the passed value.
|
|
||||||
///
|
|
||||||
/// # Panics
|
|
||||||
///
|
|
||||||
/// Panics if the new capacity exceeds `usize::MAX` bytes.
|
|
||||||
#[inline]
|
|
||||||
pub fn resize(&mut self, new_len: usize, value: T) {
|
|
||||||
if new_len > self.len {
|
|
||||||
self.reserve(new_len - self.len);
|
|
||||||
|
|
||||||
unsafe {
|
|
||||||
let mut ptr = self.buf
|
|
||||||
.as_ptr().add(self.len * self.slot_size());
|
|
||||||
|
|
||||||
// write all elements besides the last one
|
|
||||||
for _ in 1..new_len {
|
|
||||||
std::ptr::write(ptr.cast::<T>(), value.clone());
|
|
||||||
ptr = ptr.add(self.slot_size());
|
|
||||||
self.len += 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if new_len > 0 {
|
|
||||||
// the last element can be written without cloning
|
|
||||||
std::ptr::write(ptr.cast::<T>(), value.clone());
|
|
||||||
self.len += 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
self.len = new_len;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
self.truncate(new_len);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,7 +1,6 @@
|
||||||
use std::{borrow::Cow, mem, rc::Rc};
|
use std::{borrow::Cow, mem, rc::Rc};
|
||||||
|
|
||||||
use glam::UVec2;
|
use glam::UVec2;
|
||||||
use tracing::instrument;
|
|
||||||
use wgpu::{util::DeviceExt, ComputePipeline};
|
use wgpu::{util::DeviceExt, ComputePipeline};
|
||||||
use winit::dpi::PhysicalSize;
|
use winit::dpi::PhysicalSize;
|
||||||
|
|
||||||
|
@ -203,7 +202,6 @@ impl LightCullCompute {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[instrument(skip(self))]
|
|
||||||
pub fn update_screen_size(&mut self, size: PhysicalSize<u32>) {
|
pub fn update_screen_size(&mut self, size: PhysicalSize<u32>) {
|
||||||
self.screen_size_buffer.write_buffer(&self.queue, 0,
|
self.screen_size_buffer.write_buffer(&self.queue, 0,
|
||||||
&[UVec2::new(size.width, size.height)]);
|
&[UVec2::new(size.width, size.height)]);
|
||||||
|
@ -214,9 +212,7 @@ impl LightCullCompute {
|
||||||
self.light_indices_grid = Self::create_grid(&self.device, self.workgroup_size);
|
self.light_indices_grid = Self::create_grid(&self.device, self.workgroup_size);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[instrument(skip(self, camera_buffers, lights_buffers, depth_texture))]
|
|
||||||
pub fn compute(&mut self, camera_buffers: &BufferWrapper, lights_buffers: &LightUniformBuffers, depth_texture: &RenderTexture) {
|
pub fn compute(&mut self, camera_buffers: &BufferWrapper, lights_buffers: &LightUniformBuffers, depth_texture: &RenderTexture) {
|
||||||
self.cleanup();
|
|
||||||
let mut encoder = self.device.create_command_encoder(&wgpu::CommandEncoderDescriptor {
|
let mut encoder = self.device.create_command_encoder(&wgpu::CommandEncoderDescriptor {
|
||||||
label: Some("LightCullCompute"),
|
label: Some("LightCullCompute"),
|
||||||
});
|
});
|
||||||
|
@ -238,7 +234,8 @@ impl LightCullCompute {
|
||||||
}
|
}
|
||||||
|
|
||||||
self.queue.submit(std::iter::once(encoder.finish()));
|
self.queue.submit(std::iter::once(encoder.finish()));
|
||||||
//self.device.poll(wgpu::Maintain::Wait);
|
self.device.poll(wgpu::Maintain::Wait);
|
||||||
|
self.cleanup();
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn cleanup(&mut self) {
|
pub fn cleanup(&mut self) {
|
||||||
|
|
|
@ -13,4 +13,3 @@ pub mod window;
|
||||||
pub mod transform_buffer_storage;
|
pub mod transform_buffer_storage;
|
||||||
pub mod light;
|
pub mod light;
|
||||||
pub mod light_cull_compute;
|
pub mod light_cull_compute;
|
||||||
pub mod avec;
|
|
|
@ -1,17 +1,18 @@
|
||||||
use std::collections::{VecDeque, HashSet};
|
use std::collections::{HashMap, VecDeque, HashSet};
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use std::borrow::Cow;
|
use std::borrow::Cow;
|
||||||
|
|
||||||
use glam::Vec3;
|
use glam::Vec3;
|
||||||
|
use instant::Instant;
|
||||||
use itertools::izip;
|
use itertools::izip;
|
||||||
use lyra_ecs::query::filter::{Has, Not, Or};
|
use lyra_ecs::query::filter::{Has, Not, Or};
|
||||||
use lyra_ecs::relation::{ChildOf, RelationOriginComponent};
|
use lyra_ecs::relation::{ChildOf, RelationOriginComponent};
|
||||||
use lyra_ecs::{Component, Entity};
|
use lyra_ecs::{Entity, Tick};
|
||||||
use lyra_ecs::query::{Entities, Res, TickOf};
|
use lyra_ecs::query::{Entities, TickOf};
|
||||||
use lyra_ecs::World;
|
use lyra_ecs::World;
|
||||||
use lyra_scene::{SceneGraph, WorldTransform};
|
use lyra_scene::{SceneGraph, WorldTransform};
|
||||||
use tracing::{debug, instrument, warn};
|
use tracing::{debug, warn};
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
use wgpu::{BindGroupLayout, Limits};
|
use wgpu::{BindGroupLayout, Limits};
|
||||||
use wgpu::util::DeviceExt;
|
use wgpu::util::DeviceExt;
|
||||||
|
@ -21,7 +22,6 @@ use crate::math::Transform;
|
||||||
use crate::render::material::MaterialUniform;
|
use crate::render::material::MaterialUniform;
|
||||||
use crate::render::render_buffer::BufferWrapperBuilder;
|
use crate::render::render_buffer::BufferWrapperBuilder;
|
||||||
use crate::scene::CameraComponent;
|
use crate::scene::CameraComponent;
|
||||||
use crate::DeltaTime;
|
|
||||||
|
|
||||||
use super::camera::{RenderCamera, CameraUniform};
|
use super::camera::{RenderCamera, CameraUniform};
|
||||||
use super::desc_buf_lay::DescVertexBufferLayout;
|
use super::desc_buf_lay::DescVertexBufferLayout;
|
||||||
|
@ -67,10 +67,12 @@ struct MeshBufferStorage {
|
||||||
//transform_index: TransformBufferIndices,
|
//transform_index: TransformBufferIndices,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, Component)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct InterpTransform {
|
pub struct CachedTransform {
|
||||||
last_transform: Transform,
|
last_updated_at: Option<Instant>,
|
||||||
alpha: f32,
|
cached_at: Instant,
|
||||||
|
to_transform: Transform,
|
||||||
|
from_transform: Transform,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct BasicRenderer {
|
pub struct BasicRenderer {
|
||||||
|
@ -83,12 +85,13 @@ pub struct BasicRenderer {
|
||||||
|
|
||||||
pub clear_color: wgpu::Color,
|
pub clear_color: wgpu::Color,
|
||||||
|
|
||||||
pub render_pipelines: rustc_hash::FxHashMap<u64, Arc<FullRenderPipeline>>,
|
pub render_pipelines: HashMap<u64, Arc<FullRenderPipeline>>,
|
||||||
pub render_jobs: VecDeque<RenderJob>,
|
pub render_jobs: VecDeque<RenderJob>,
|
||||||
|
|
||||||
mesh_buffers: rustc_hash::FxHashMap<uuid::Uuid, MeshBufferStorage>, // TODO: clean up left over buffers from deleted entities/components
|
mesh_buffers: HashMap<uuid::Uuid, MeshBufferStorage>, // TODO: clean up left over buffers from deleted entities/components
|
||||||
material_buffers: rustc_hash::FxHashMap<uuid::Uuid, Rc<Material>>,
|
material_buffers: HashMap<uuid::Uuid, Rc<Material>>,
|
||||||
entity_meshes: rustc_hash::FxHashMap<Entity, uuid::Uuid>,
|
entity_meshes: HashMap<Entity, uuid::Uuid>,
|
||||||
|
entity_last_transforms: HashMap<Entity, CachedTransform>,
|
||||||
|
|
||||||
transform_buffers: TransformBuffers,
|
transform_buffers: TransformBuffers,
|
||||||
|
|
||||||
|
@ -110,7 +113,6 @@ pub struct BasicRenderer {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl BasicRenderer {
|
impl BasicRenderer {
|
||||||
#[instrument(skip(window))]
|
|
||||||
pub async fn create_with_window(window: Arc<Window>) -> BasicRenderer {
|
pub async fn create_with_window(window: Arc<Window>) -> BasicRenderer {
|
||||||
let size = window.inner_size();
|
let size = window.inner_size();
|
||||||
|
|
||||||
|
@ -166,7 +168,7 @@ impl BasicRenderer {
|
||||||
format: surface_format,
|
format: surface_format,
|
||||||
width: size.width,
|
width: size.width,
|
||||||
height: size.height,
|
height: size.height,
|
||||||
present_mode: wgpu::PresentMode::Immediate,
|
present_mode,
|
||||||
alpha_mode: surface_caps.alpha_modes[0],
|
alpha_mode: surface_caps.alpha_modes[0],
|
||||||
view_formats: vec![],
|
view_formats: vec![],
|
||||||
};
|
};
|
||||||
|
@ -220,11 +222,11 @@ impl BasicRenderer {
|
||||||
b: 0.3,
|
b: 0.3,
|
||||||
a: 1.0,
|
a: 1.0,
|
||||||
},
|
},
|
||||||
render_pipelines: Default::default(),
|
render_pipelines: HashMap::new(),
|
||||||
render_jobs: Default::default(),
|
render_jobs: VecDeque::new(),
|
||||||
mesh_buffers: Default::default(),
|
mesh_buffers: HashMap::new(),
|
||||||
material_buffers: Default::default(),
|
material_buffers: HashMap::new(),
|
||||||
entity_meshes: Default::default(),
|
entity_meshes: HashMap::new(),
|
||||||
|
|
||||||
render_limits,
|
render_limits,
|
||||||
transform_buffers,
|
transform_buffers,
|
||||||
|
@ -235,6 +237,7 @@ impl BasicRenderer {
|
||||||
bgl_texture,
|
bgl_texture,
|
||||||
default_texture,
|
default_texture,
|
||||||
depth_buffer_texture: depth_texture,
|
depth_buffer_texture: depth_texture,
|
||||||
|
entity_last_transforms: HashMap::new(),
|
||||||
|
|
||||||
light_buffers: light_uniform_buffers,
|
light_buffers: light_uniform_buffers,
|
||||||
material_buffer: mat_buffer,
|
material_buffer: mat_buffer,
|
||||||
|
@ -242,7 +245,7 @@ impl BasicRenderer {
|
||||||
};
|
};
|
||||||
|
|
||||||
// create the default pipelines
|
// create the default pipelines
|
||||||
let mut pipelines = rustc_hash::FxHashMap::default();
|
let mut pipelines = HashMap::new();
|
||||||
pipelines.insert(0, Arc::new(FullRenderPipeline::new(&s.device, &s.config, &shader,
|
pipelines.insert(0, Arc::new(FullRenderPipeline::new(&s.device, &s.config, &shader,
|
||||||
vec![super::vertex::Vertex::desc(),],
|
vec![super::vertex::Vertex::desc(),],
|
||||||
vec![&s.bgl_texture, &s.transform_buffers.bindgroup_layout,
|
vec![&s.bgl_texture, &s.transform_buffers.bindgroup_layout,
|
||||||
|
@ -257,7 +260,6 @@ impl BasicRenderer {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Checks if the mesh buffers in the GPU need to be updated.
|
/// Checks if the mesh buffers in the GPU need to be updated.
|
||||||
#[instrument(skip(self, _entity, meshh))]
|
|
||||||
fn check_mesh_buffers(&mut self, _entity: Entity, meshh: &ResHandle<Mesh>) {
|
fn check_mesh_buffers(&mut self, _entity: Entity, meshh: &ResHandle<Mesh>) {
|
||||||
let mesh_uuid = meshh.uuid();
|
let mesh_uuid = meshh.uuid();
|
||||||
|
|
||||||
|
@ -297,7 +299,6 @@ impl BasicRenderer {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[instrument(skip(self, mesh))]
|
|
||||||
fn create_vertex_index_buffers(&mut self, mesh: &Mesh) -> (BufferStorage, Option<(wgpu::IndexFormat, BufferStorage)>) {
|
fn create_vertex_index_buffers(&mut self, mesh: &Mesh) -> (BufferStorage, Option<(wgpu::IndexFormat, BufferStorage)>) {
|
||||||
let positions = mesh.position().unwrap();
|
let positions = mesh.position().unwrap();
|
||||||
let tex_coords: Vec<glam::Vec2> = mesh.tex_coords().cloned()
|
let tex_coords: Vec<glam::Vec2> = mesh.tex_coords().cloned()
|
||||||
|
@ -347,7 +348,6 @@ impl BasicRenderer {
|
||||||
( vertex_buffer, indices )
|
( vertex_buffer, indices )
|
||||||
}
|
}
|
||||||
|
|
||||||
#[instrument(skip(self, mesh))]
|
|
||||||
fn create_mesh_buffers(&mut self, mesh: &Mesh) -> MeshBufferStorage {
|
fn create_mesh_buffers(&mut self, mesh: &Mesh) -> MeshBufferStorage {
|
||||||
let (vertex_buffer, buffer_indices) = self.create_vertex_index_buffers(mesh);
|
let (vertex_buffer, buffer_indices) = self.create_vertex_index_buffers(mesh);
|
||||||
|
|
||||||
|
@ -374,7 +374,6 @@ impl BasicRenderer {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Processes the mesh for the renderer, storing and creating buffers as needed. Returns true if a new mesh was processed.
|
/// Processes the mesh for the renderer, storing and creating buffers as needed. Returns true if a new mesh was processed.
|
||||||
#[instrument(skip(self, transform, mesh, entity))]
|
|
||||||
fn process_mesh(&mut self, entity: Entity, transform: Transform, mesh: &Mesh, mesh_uuid: Uuid) -> bool {
|
fn process_mesh(&mut self, entity: Entity, transform: Transform, mesh: &Mesh, mesh_uuid: Uuid) -> bool {
|
||||||
let _ = transform;
|
let _ = transform;
|
||||||
/* if self.transform_buffers.should_expand() {
|
/* if self.transform_buffers.should_expand() {
|
||||||
|
@ -394,79 +393,68 @@ impl BasicRenderer {
|
||||||
true
|
true
|
||||||
} else { false }
|
} else { false }
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
impl Renderer for BasicRenderer {
|
fn interpolate_transforms(&mut self, now: Instant, last_epoch: Tick, entity: Entity, transform: &Transform, transform_epoch: Tick) -> Transform {
|
||||||
#[instrument(skip(self, main_world))]
|
let cached = match self.entity_last_transforms.get_mut(&entity) {
|
||||||
fn prepare(&mut self, main_world: &mut World) {
|
Some(last) if transform_epoch == last_epoch => {
|
||||||
let last_epoch = main_world.current_tick();
|
last.from_transform = last.to_transform;
|
||||||
let mut alive_entities = HashSet::new();
|
last.to_transform = *transform;
|
||||||
|
last.last_updated_at = Some(last.cached_at);
|
||||||
|
last.cached_at = now;
|
||||||
|
|
||||||
let view = main_world.view_iter::<(
|
last.clone()
|
||||||
Entities,
|
|
||||||
&Transform,
|
|
||||||
TickOf<Transform>,
|
|
||||||
Or<
|
|
||||||
(&MeshHandle, TickOf<MeshHandle>),
|
|
||||||
(&SceneHandle, TickOf<SceneHandle>)
|
|
||||||
>,
|
|
||||||
Option<&mut InterpTransform>,
|
|
||||||
Res<DeltaTime>,
|
|
||||||
)>();
|
|
||||||
|
|
||||||
// used to store InterpTransform components to add to entities later
|
|
||||||
let mut component_queue: Vec<(Entity, InterpTransform)> = vec![];
|
|
||||||
|
|
||||||
for (
|
|
||||||
entity,
|
|
||||||
transform,
|
|
||||||
_transform_epoch,
|
|
||||||
(
|
|
||||||
mesh_pair,
|
|
||||||
scene_pair
|
|
||||||
),
|
|
||||||
interp_tran,
|
|
||||||
delta_time,
|
|
||||||
) in view
|
|
||||||
{
|
|
||||||
alive_entities.insert(entity);
|
|
||||||
|
|
||||||
let interp_transform = match interp_tran {
|
|
||||||
Some(mut interp_transform) => {
|
|
||||||
// found in https://youtu.be/YJB1QnEmlTs?t=472
|
|
||||||
interp_transform.alpha = 1.0 - interp_transform.alpha.powf(**delta_time);
|
|
||||||
|
|
||||||
interp_transform.last_transform = interp_transform.last_transform.lerp(*transform, interp_transform.alpha);
|
|
||||||
interp_transform.last_transform
|
|
||||||
},
|
},
|
||||||
|
Some(last) => last.clone(),
|
||||||
None => {
|
None => {
|
||||||
let interp = InterpTransform {
|
let cached = CachedTransform {
|
||||||
last_transform: *transform,
|
last_updated_at: None,
|
||||||
alpha: 0.5,
|
cached_at: now,
|
||||||
|
from_transform: *transform,
|
||||||
|
to_transform: *transform,
|
||||||
};
|
};
|
||||||
component_queue.push((entity, interp));
|
self.entity_last_transforms.insert(entity, cached.clone());
|
||||||
|
cached
|
||||||
*transform
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let fixed_time = match cached.last_updated_at {
|
||||||
|
Some(last_updated_at) => cached.cached_at - last_updated_at,
|
||||||
|
None => now - cached.cached_at
|
||||||
|
}.as_secs_f32();
|
||||||
|
let accumulator = (now - cached.cached_at).as_secs_f32();
|
||||||
|
let alpha = accumulator / fixed_time;
|
||||||
|
|
||||||
|
cached.from_transform.lerp(cached.to_transform, alpha)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Renderer for BasicRenderer {
|
||||||
|
fn prepare(&mut self, main_world: &mut World) {
|
||||||
|
let last_epoch = main_world.current_tick();
|
||||||
|
let now_inst = Instant::now();
|
||||||
|
let mut alive_entities = HashSet::new();
|
||||||
|
|
||||||
|
let view = main_world.view_iter::<(Entities, &Transform, TickOf<Transform>,
|
||||||
|
Or<(&MeshHandle, TickOf<MeshHandle>), (&SceneHandle, TickOf<SceneHandle>)>)>();
|
||||||
|
|
||||||
|
for (entity, transform, transform_epoch, (mesh_pair, scene_pair)) in view {
|
||||||
|
alive_entities.insert(entity);
|
||||||
|
|
||||||
if let Some((mesh_han, mesh_epoch)) = mesh_pair {
|
if let Some((mesh_han, mesh_epoch)) = mesh_pair {
|
||||||
|
let interop_pos = self.interpolate_transforms(now_inst, last_epoch, entity, &transform, transform_epoch);
|
||||||
|
|
||||||
if let Some(mesh) = mesh_han.data_ref() {
|
if let Some(mesh) = mesh_han.data_ref() {
|
||||||
// if process mesh did not just create a new mesh, and the epoch
|
// if process mesh did not just create a new mesh, and the epoch
|
||||||
// shows that the scene has changed, verify that the mesh buffers
|
// shows that the scene has changed, verify that the mesh buffers
|
||||||
// dont need to be resent to the gpu.
|
// dont need to be resent to the gpu.
|
||||||
if !self.process_mesh(entity, interp_transform, &*mesh, mesh_han.uuid())
|
if !self.process_mesh(entity, interop_pos, &*mesh, mesh_han.uuid())
|
||||||
&& mesh_epoch == last_epoch {
|
&& mesh_epoch == last_epoch {
|
||||||
self.check_mesh_buffers(entity, &mesh_han);
|
self.check_mesh_buffers(entity, &mesh_han);
|
||||||
}
|
}
|
||||||
|
|
||||||
if self.transform_buffers.needs_expand() {
|
|
||||||
self.transform_buffers.expand_buffers(&self.device);
|
|
||||||
}
|
|
||||||
|
|
||||||
let group = TransformGroup::EntityRes(entity, mesh_han.uuid());
|
let group = TransformGroup::EntityRes(entity, mesh_han.uuid());
|
||||||
let transform_id = self.transform_buffers.update_or_push(&self.device, &self.queue, &self.render_limits,
|
let transform_id = self.transform_buffers.update_or_push(&self.queue, &self.render_limits,
|
||||||
group, interp_transform.calculate_mat4(), glam::Mat3::from_quat(interp_transform.rotation));
|
group, || ( interop_pos.calculate_mat4(), glam::Mat3::from_quat(interop_pos.rotation) ));
|
||||||
|
|
||||||
let material = mesh.material.as_ref().unwrap()
|
let material = mesh.material.as_ref().unwrap()
|
||||||
.data_ref().unwrap();
|
.data_ref().unwrap();
|
||||||
|
@ -478,14 +466,14 @@ impl Renderer for BasicRenderer {
|
||||||
|
|
||||||
if let Some((scene_han, scene_epoch)) = scene_pair {
|
if let Some((scene_han, scene_epoch)) = scene_pair {
|
||||||
if let Some(scene) = scene_han.data_ref() {
|
if let Some(scene) = scene_han.data_ref() {
|
||||||
if scene_epoch == last_epoch {
|
|
||||||
let view = scene.world().view::<(Entities, &mut WorldTransform, &Transform, Not<Has<RelationOriginComponent<ChildOf>>>)>();
|
let view = scene.world().view::<(Entities, &mut WorldTransform, &Transform, Not<Has<RelationOriginComponent<ChildOf>>>)>();
|
||||||
lyra_scene::system_update_world_transforms(scene.world(), view).unwrap();
|
lyra_scene::system_update_world_transforms(scene.world(), view).unwrap();
|
||||||
}
|
|
||||||
|
let interpo_pos = self.interpolate_transforms(now_inst, last_epoch, entity, &transform, transform_epoch);
|
||||||
|
|
||||||
for (mesh_han, pos) in scene.world().view_iter::<(&MeshHandle, &WorldTransform)>() {
|
for (mesh_han, pos) in scene.world().view_iter::<(&MeshHandle, &WorldTransform)>() {
|
||||||
if let Some(mesh) = mesh_han.data_ref() {
|
if let Some(mesh) = mesh_han.data_ref() {
|
||||||
let mesh_interpo = interp_transform + **pos;
|
let mesh_interpo = interpo_pos + **pos;
|
||||||
|
|
||||||
// if process mesh did not just create a new mesh, and the epoch
|
// if process mesh did not just create a new mesh, and the epoch
|
||||||
// shows that the scene has changed, verify that the mesh buffers
|
// shows that the scene has changed, verify that the mesh buffers
|
||||||
|
@ -495,14 +483,10 @@ impl Renderer for BasicRenderer {
|
||||||
self.check_mesh_buffers(entity, &mesh_han);
|
self.check_mesh_buffers(entity, &mesh_han);
|
||||||
}
|
}
|
||||||
|
|
||||||
if self.transform_buffers.needs_expand() {
|
|
||||||
self.transform_buffers.expand_buffers(&self.device);
|
|
||||||
}
|
|
||||||
|
|
||||||
let scene_mesh_group = TransformGroup::Res(scene_han.uuid(), mesh_han.uuid());
|
let scene_mesh_group = TransformGroup::Res(scene_han.uuid(), mesh_han.uuid());
|
||||||
let group = TransformGroup::OwnedGroup(entity, scene_mesh_group.into());
|
let group = TransformGroup::OwnedGroup(entity, scene_mesh_group.into());
|
||||||
let transform_id = self.transform_buffers.update_or_push(&self.device, &self.queue, &self.render_limits,
|
let transform_id = self.transform_buffers.update_or_push(&self.queue, &self.render_limits,
|
||||||
group, mesh_interpo.calculate_mat4(), glam::Mat3::from_quat(mesh_interpo.rotation) );
|
group, || ( mesh_interpo.calculate_mat4(), glam::Mat3::from_quat(mesh_interpo.rotation) ));
|
||||||
|
|
||||||
let material = mesh.material.as_ref().unwrap()
|
let material = mesh.material.as_ref().unwrap()
|
||||||
.data_ref().unwrap();
|
.data_ref().unwrap();
|
||||||
|
@ -515,12 +499,8 @@ impl Renderer for BasicRenderer {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (en, interp) in component_queue {
|
|
||||||
main_world.insert(en, interp);
|
|
||||||
}
|
|
||||||
|
|
||||||
// collect dead entities
|
// collect dead entities
|
||||||
self.transform_buffers.send_to_gpu(&self.queue);
|
self.transform_buffers.tick();
|
||||||
|
|
||||||
// when buffer storage length does not match the amount of iterated entities,
|
// when buffer storage length does not match the amount of iterated entities,
|
||||||
// remove all dead entities, and their buffers, if they weren't iterated over
|
// remove all dead entities, and their buffers, if they weren't iterated over
|
||||||
|
@ -532,9 +512,10 @@ impl Renderer for BasicRenderer {
|
||||||
self.mesh_buffers.retain(|u, _| !removed_entities.contains(u));
|
self.mesh_buffers.retain(|u, _| !removed_entities.contains(u));
|
||||||
}
|
}
|
||||||
|
|
||||||
// update camera uniform
|
|
||||||
if let Some(camera) = main_world.view_iter::<&mut CameraComponent>().next() {
|
if let Some(camera) = main_world.view_iter::<&mut CameraComponent>().next() {
|
||||||
let uniform = self.inuse_camera.calc_view_projection(&camera);
|
let uniform = self.inuse_camera.calc_view_projection(&camera);
|
||||||
|
//let pos = camera.transform.translation;
|
||||||
|
//let uniform = CameraUniform::new(view_mat, *view_proj, pos);
|
||||||
self.camera_buffer.write_buffer(&self.queue, 0, &[uniform]);
|
self.camera_buffer.write_buffer(&self.queue, 0, &[uniform]);
|
||||||
} else {
|
} else {
|
||||||
warn!("Missing camera!");
|
warn!("Missing camera!");
|
||||||
|
@ -543,7 +524,6 @@ impl Renderer for BasicRenderer {
|
||||||
self.light_buffers.update_lights(&self.queue, last_epoch, main_world);
|
self.light_buffers.update_lights(&self.queue, last_epoch, main_world);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[instrument(skip(self))]
|
|
||||||
fn render(&mut self) -> Result<(), wgpu::SurfaceError> {
|
fn render(&mut self) -> Result<(), wgpu::SurfaceError> {
|
||||||
let output = self.surface.get_current_texture()?;
|
let output = self.surface.get_current_texture()?;
|
||||||
let view = output.texture.create_view(&wgpu::TextureViewDescriptor::default());
|
let view = output.texture.create_view(&wgpu::TextureViewDescriptor::default());
|
||||||
|
@ -613,7 +593,7 @@ impl Renderer for BasicRenderer {
|
||||||
// Get the bindgroup for job's transform and bind to it using an offset.
|
// Get the bindgroup for job's transform and bind to it using an offset.
|
||||||
let bindgroup = self.transform_buffers.bind_group(job.transform_id);
|
let bindgroup = self.transform_buffers.bind_group(job.transform_id);
|
||||||
let offset = self.transform_buffers.buffer_offset(job.transform_id);
|
let offset = self.transform_buffers.buffer_offset(job.transform_id);
|
||||||
render_pass.set_bind_group(1, bindgroup, &[ offset, ]);
|
render_pass.set_bind_group(1, bindgroup, &[ offset, offset, ]);
|
||||||
|
|
||||||
render_pass.set_bind_group(2, &self.camera_buffer.bindgroup(), &[]);
|
render_pass.set_bind_group(2, &self.camera_buffer.bindgroup(), &[]);
|
||||||
render_pass.set_bind_group(3, &self.light_buffers.bind_group_pair.bindgroup, &[]);
|
render_pass.set_bind_group(3, &self.light_buffers.bind_group_pair.bindgroup, &[]);
|
||||||
|
@ -644,7 +624,6 @@ impl Renderer for BasicRenderer {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[instrument(skip(self))]
|
|
||||||
fn on_resize(&mut self, new_size: winit::dpi::PhysicalSize<u32>) {
|
fn on_resize(&mut self, new_size: winit::dpi::PhysicalSize<u32>) {
|
||||||
if new_size.width > 0 && new_size.height > 0 {
|
if new_size.width > 0 && new_size.height > 0 {
|
||||||
self.size = new_size;
|
self.size = new_size;
|
||||||
|
|
|
@ -21,11 +21,6 @@ struct VertexOutput {
|
||||||
@location(2) world_normal: vec3<f32>,
|
@location(2) world_normal: vec3<f32>,
|
||||||
}
|
}
|
||||||
|
|
||||||
struct TransformData {
|
|
||||||
transform: mat4x4<f32>,
|
|
||||||
normal_matrix: mat4x4<f32>,
|
|
||||||
}
|
|
||||||
|
|
||||||
struct CameraUniform {
|
struct CameraUniform {
|
||||||
view: mat4x4<f32>,
|
view: mat4x4<f32>,
|
||||||
inverse_projection: mat4x4<f32>,
|
inverse_projection: mat4x4<f32>,
|
||||||
|
@ -56,7 +51,9 @@ struct Lights {
|
||||||
};
|
};
|
||||||
|
|
||||||
@group(1) @binding(0)
|
@group(1) @binding(0)
|
||||||
var<uniform> u_model_transform_data: TransformData;
|
var<uniform> u_model_transform: mat4x4<f32>;
|
||||||
|
@group(1) @binding(1)
|
||||||
|
var<uniform> u_model_normal_matrix: mat4x4<f32>;
|
||||||
|
|
||||||
@group(2) @binding(0)
|
@group(2) @binding(0)
|
||||||
var<uniform> u_camera: CameraUniform;
|
var<uniform> u_camera: CameraUniform;
|
||||||
|
@ -71,14 +68,13 @@ fn vs_main(
|
||||||
var out: VertexOutput;
|
var out: VertexOutput;
|
||||||
|
|
||||||
out.tex_coords = model.tex_coords;
|
out.tex_coords = model.tex_coords;
|
||||||
out.clip_position = u_camera.view_projection * u_model_transform_data.transform * vec4<f32>(model.position, 1.0);
|
out.clip_position = u_camera.view_projection * u_model_transform * vec4<f32>(model.position, 1.0);
|
||||||
|
|
||||||
// the normal mat is actually only a mat3x3, but there's a bug in wgpu: https://github.com/gfx-rs/wgpu-rs/issues/36
|
// the normal mat is actually only a mat3x3, but there's a bug in wgpu: https://github.com/gfx-rs/wgpu-rs/issues/36
|
||||||
let normal_mat4 = u_model_transform_data.normal_matrix;
|
let normal_mat = mat3x3(u_model_normal_matrix[0].xyz, u_model_normal_matrix[1].xyz, u_model_normal_matrix[2].xyz);
|
||||||
let normal_mat = mat3x3(normal_mat4[0].xyz, normal_mat4[1].xyz, normal_mat4[2].xyz);
|
|
||||||
out.world_normal = normalize(normal_mat * model.normal, );
|
out.world_normal = normalize(normal_mat * model.normal, );
|
||||||
|
|
||||||
var world_position: vec4<f32> = u_model_transform_data.transform * vec4<f32>(model.position, 1.0);
|
var world_position: vec4<f32> = u_model_transform * vec4<f32>(model.position, 1.0);
|
||||||
out.world_position = world_position.xyz;
|
out.world_position = world_position.xyz;
|
||||||
|
|
||||||
return out;
|
return out;
|
||||||
|
|
|
@ -1,14 +1,11 @@
|
||||||
use std::{collections::{HashMap, VecDeque}, hash::{BuildHasher, DefaultHasher, Hash, Hasher, RandomState}, num::NonZeroU64};
|
use std::{collections::{HashMap, VecDeque}, hash::{BuildHasher, DefaultHasher, Hash, Hasher, RandomState}, num::NonZeroU64};
|
||||||
|
|
||||||
use lyra_ecs::Entity;
|
use lyra_ecs::Entity;
|
||||||
use tracing::instrument;
|
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
use wgpu::Limits;
|
use wgpu::Limits;
|
||||||
|
|
||||||
use std::mem;
|
use std::mem;
|
||||||
|
|
||||||
use crate::render::avec::AVec;
|
|
||||||
|
|
||||||
/// A group id created from a [`TransformGroup`].
|
/// A group id created from a [`TransformGroup`].
|
||||||
///
|
///
|
||||||
/// This is mainly created so that [`TransformGroup::OwnedGroup`] can use another group inside of it.
|
/// This is mainly created so that [`TransformGroup::OwnedGroup`] can use another group inside of it.
|
||||||
|
@ -69,10 +66,8 @@ pub struct TransformIndex {
|
||||||
struct BufferEntry {
|
struct BufferEntry {
|
||||||
pub len: usize,
|
pub len: usize,
|
||||||
pub bindgroup: wgpu::BindGroup,
|
pub bindgroup: wgpu::BindGroup,
|
||||||
pub buffer: wgpu::Buffer,
|
pub transform_buffer: wgpu::Buffer,
|
||||||
transforms: AVec<TransformNormalMatPair>,
|
pub normal_buffer: wgpu::Buffer,
|
||||||
//pub normal_buffer: wgpu::Buffer,
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A HashMap that caches values for reuse.
|
/// A HashMap that caches values for reuse.
|
||||||
|
@ -163,12 +158,10 @@ impl<K: Hash + Eq + PartialEq + Clone, V: Clone, S: BuildHasher> CachedValMap<K,
|
||||||
/// update, and retrieve the transforms.
|
/// update, and retrieve the transforms.
|
||||||
pub struct TransformBuffers {
|
pub struct TransformBuffers {
|
||||||
pub bindgroup_layout: wgpu::BindGroupLayout,
|
pub bindgroup_layout: wgpu::BindGroupLayout,
|
||||||
//groups: CachedValMap<TransformGroupId, TransformIndex>,
|
groups: CachedValMap<TransformGroupId, TransformIndex>,
|
||||||
//groups: SlotMap<TransformGroupId, TransformIndex>,
|
|
||||||
entries: Vec<BufferEntry>,
|
entries: Vec<BufferEntry>,
|
||||||
limits: wgpu::Limits,
|
limits: wgpu::Limits,
|
||||||
max_transform_count: usize,
|
max_transform_count: usize,
|
||||||
next_index: usize,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TransformBuffers {
|
impl TransformBuffers {
|
||||||
|
@ -187,16 +180,26 @@ impl TransformBuffers {
|
||||||
},
|
},
|
||||||
count: None,
|
count: None,
|
||||||
},
|
},
|
||||||
|
wgpu::BindGroupLayoutEntry {
|
||||||
|
binding: 1,
|
||||||
|
visibility: wgpu::ShaderStages::VERTEX,
|
||||||
|
ty: wgpu::BindingType::Buffer {
|
||||||
|
ty: wgpu::BufferBindingType::Uniform,
|
||||||
|
has_dynamic_offset: true,
|
||||||
|
min_binding_size: None,
|
||||||
|
},
|
||||||
|
count: None,
|
||||||
|
}
|
||||||
],
|
],
|
||||||
label: Some("transform_bind_group_layout"),
|
label: Some("transform_bind_group_layout"),
|
||||||
});
|
});
|
||||||
|
|
||||||
let mut s = Self {
|
let mut s = Self {
|
||||||
bindgroup_layout,
|
bindgroup_layout,
|
||||||
|
groups: Default::default(),
|
||||||
entries: Default::default(),
|
entries: Default::default(),
|
||||||
max_transform_count: (limits.max_uniform_buffer_binding_size) as usize / (limits.min_uniform_buffer_offset_alignment as usize), //(mem::size_of::<glam::Mat4>()),
|
max_transform_count: (limits.max_uniform_buffer_binding_size / 2) as usize / (mem::size_of::<glam::Mat4>()),
|
||||||
limits,
|
limits,
|
||||||
next_index: 0,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// create the first uniform buffer
|
// create the first uniform buffer
|
||||||
|
@ -205,59 +208,70 @@ impl TransformBuffers {
|
||||||
s
|
s
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Write the transform buffers to the gpu.
|
/// Update an existing transform in the buffers.
|
||||||
///
|
///
|
||||||
/// This uses [`wgpu::Queue::write_buffer`], so the write is not immediately submitted,
|
/// # Panics
|
||||||
/// and instead enqueued internally to happen at the start of the next submit() call.
|
/// Panics if the `entity_group` is not already inside of the buffers.
|
||||||
pub fn send_to_gpu(&mut self, queue: &wgpu::Queue) {
|
pub fn update_transform(&mut self, queue: &wgpu::Queue, limits: &Limits, entity_group: TransformGroup, transform: glam::Mat4, normal_matrix: glam::Mat3) -> TransformIndex {
|
||||||
self.next_index = 0;
|
let index = *self.groups.get(entity_group.into())
|
||||||
|
.expect("Use 'push_transform' for new entities");
|
||||||
|
let entry = self.entries.get_mut(index.entry_index).unwrap();
|
||||||
|
|
||||||
for entry in &mut self.entries {
|
let normal_matrix = glam::Mat4::from_mat3(normal_matrix);
|
||||||
entry.len = 0;
|
|
||||||
|
|
||||||
let p = entry.transforms.as_ptr();
|
// write the transform and normal to the end of the transform
|
||||||
let bytes = unsafe { std::slice::from_raw_parts(p as *const u8, entry.transforms.len() * entry.transforms.align()) };
|
let offset = Self::get_buffer_offset(limits, index) as _;
|
||||||
|
queue.write_buffer(&entry.transform_buffer, offset, bytemuck::bytes_of(&transform));
|
||||||
|
queue.write_buffer(&entry.normal_buffer, offset, bytemuck::bytes_of(&normal_matrix));
|
||||||
|
|
||||||
queue.write_buffer(&entry.buffer, 0, bytes);
|
index
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Push a new transform into the buffers.
|
||||||
|
pub fn push_transform(&mut self, queue: &wgpu::Queue, limits: &Limits, entity_group: TransformGroup, transform: glam::Mat4, normal_matrix: glam::Mat3) -> TransformIndex {
|
||||||
|
self.groups.insert(entity_group.into(), || {
|
||||||
|
// this closure is only called when there are no values that can be reused,
|
||||||
|
// so we get a brand new index at the end of the last entry in the chain.
|
||||||
|
let last = self.entries.last_mut().unwrap();
|
||||||
|
|
||||||
|
// ensure the gpu buffer is not overflown
|
||||||
|
debug_assert!(last.len < self.max_transform_count,
|
||||||
|
"Transform buffer is filled and 'next_indices' was not incremented! \
|
||||||
|
Was a new buffer created?");
|
||||||
|
|
||||||
|
let tidx = last.len;
|
||||||
|
last.len += 1;
|
||||||
|
|
||||||
|
TransformIndex {
|
||||||
|
entry_index: self.entries.len() - 1,
|
||||||
|
transform_index: tidx
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
self.update_transform(queue, limits, entity_group, transform, normal_matrix)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Collect the dead transforms and prepare self to check next time.
|
||||||
|
pub fn tick(&mut self) {
|
||||||
|
self.groups.update();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns a boolean indicating if the buffer contains this group.
|
||||||
|
pub fn contains(&self, group: TransformGroup) -> bool {
|
||||||
|
self.groups.contains(group.into())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Update an existing transform group or if its not existing yet, pushes it to the buffer.
|
/// Update an existing transform group or if its not existing yet, pushes it to the buffer.
|
||||||
///
|
///
|
||||||
/// Returns: the index that the transform is at in the buffers.
|
/// Returns: the index that the transform is at in the buffers.
|
||||||
#[instrument(skip(self, device, queue, limits, group, transform, normal_matrix))]
|
pub fn update_or_push<F>(&mut self, queue: &wgpu::Queue, limits: &Limits, group: TransformGroup, transform_fn: F) -> TransformIndex
|
||||||
#[inline(always)]
|
where F: Fn() -> (glam::Mat4, glam::Mat3)
|
||||||
pub fn update_or_push(&mut self, device: &wgpu::Device, queue: &wgpu::Queue, limits: &Limits, group: TransformGroup, transform: glam::Mat4, normal_matrix: glam::Mat3) -> TransformIndex
|
|
||||||
{
|
{
|
||||||
// maybe will be used at some point again
|
let (transform, normal_matrix) = transform_fn();
|
||||||
let _ = (queue, limits, group);
|
if self.contains(group) {
|
||||||
|
self.update_transform(queue, limits, group, transform, normal_matrix)
|
||||||
let normal_matrix = glam::Mat4::from_mat3(normal_matrix);
|
} else {
|
||||||
|
self.push_transform(queue, limits, group, transform, normal_matrix)
|
||||||
let index = self.next_index;
|
|
||||||
self.next_index += 1;
|
|
||||||
|
|
||||||
// the index of the entry to put the transform into
|
|
||||||
let entry_index = index / self.max_transform_count;
|
|
||||||
// the index of the transform in the buffer
|
|
||||||
let transform_index = index % self.max_transform_count;
|
|
||||||
|
|
||||||
if entry_index >= self.entries.len() {
|
|
||||||
self.expand_buffers(device);
|
|
||||||
}
|
|
||||||
|
|
||||||
let entry = self.entries.get_mut(entry_index).unwrap();
|
|
||||||
|
|
||||||
// write the transform and normal to the end of the transform
|
|
||||||
entry.transforms.set_at(transform_index, TransformNormalMatPair {
|
|
||||||
transform,
|
|
||||||
normal_mat: normal_matrix,
|
|
||||||
});
|
|
||||||
entry.len += 1;
|
|
||||||
|
|
||||||
TransformIndex {
|
|
||||||
entry_index: 0,
|
|
||||||
transform_index: index,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -265,7 +279,6 @@ impl TransformBuffers {
|
||||||
///
|
///
|
||||||
/// This object has a chain of uniform buffers, when the buffers are expanded, a new
|
/// This object has a chain of uniform buffers, when the buffers are expanded, a new
|
||||||
/// "chain-link" is created.
|
/// "chain-link" is created.
|
||||||
#[instrument(skip(self, device))]
|
|
||||||
pub fn expand_buffers(&mut self, device: &wgpu::Device) {
|
pub fn expand_buffers(&mut self, device: &wgpu::Device) {
|
||||||
let limits = device.limits();
|
let limits = device.limits();
|
||||||
let max_buffer_sizes = self.max_transform_count as u64 * limits.min_uniform_buffer_offset_alignment as u64;
|
let max_buffer_sizes = self.max_transform_count as u64 * limits.min_uniform_buffer_offset_alignment as u64;
|
||||||
|
@ -279,9 +292,21 @@ impl TransformBuffers {
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
let tran_stride = mem::size_of::<TransformNormalMatPair>();
|
let normal_mat_buffer = device.create_buffer(
|
||||||
|
&wgpu::BufferDescriptor {
|
||||||
|
label: Some(&format!("B_NormalMatrix_{}", self.entries.len())),
|
||||||
|
usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST,
|
||||||
|
size: max_buffer_sizes,
|
||||||
|
mapped_at_creation: false,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
let bindgroup = device.create_bind_group(&wgpu::BindGroupDescriptor {
|
let tran_stride = mem::size_of::<glam::Mat4>();
|
||||||
|
// although a normal matrix only needs to be a mat3, there's a weird issue with
|
||||||
|
// misalignment from wgpu or spirv-cross: https://github.com/gfx-rs/wgpu-rs/issues/36
|
||||||
|
let norm_stride = mem::size_of::<glam::Mat4>();
|
||||||
|
|
||||||
|
let transform_bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {
|
||||||
layout: &self.bindgroup_layout,
|
layout: &self.bindgroup_layout,
|
||||||
entries: &[
|
entries: &[
|
||||||
wgpu::BindGroupEntry {
|
wgpu::BindGroupEntry {
|
||||||
|
@ -294,34 +319,42 @@ impl TransformBuffers {
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
|
wgpu::BindGroupEntry {
|
||||||
|
binding: 1,
|
||||||
|
resource: wgpu::BindingResource::Buffer(
|
||||||
|
wgpu::BufferBinding {
|
||||||
|
buffer: &normal_mat_buffer,
|
||||||
|
offset: 0,
|
||||||
|
size: Some(NonZeroU64::new(norm_stride as u64).unwrap())
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
],
|
],
|
||||||
label: Some("BG_Transforms"),
|
label: Some("BG_Transforms"),
|
||||||
});
|
});
|
||||||
|
|
||||||
let mut transforms = AVec::new(limits.min_uniform_buffer_offset_alignment as _);
|
|
||||||
transforms.resize(self.max_transform_count, TransformNormalMatPair {
|
|
||||||
transform: glam::Mat4::IDENTITY,
|
|
||||||
normal_mat: glam::Mat4::IDENTITY,
|
|
||||||
});
|
|
||||||
|
|
||||||
let entry = BufferEntry {
|
let entry = BufferEntry {
|
||||||
bindgroup,
|
bindgroup: transform_bind_group,
|
||||||
buffer: transform_buffer,
|
transform_buffer,
|
||||||
|
normal_buffer: normal_mat_buffer,
|
||||||
len: 0,
|
len: 0,
|
||||||
|
|
||||||
transforms,
|
|
||||||
};
|
};
|
||||||
self.entries.push(entry);
|
self.entries.push(entry);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the bind group for the transform index.
|
/// Returns the bind group for the transform index.
|
||||||
#[inline(always)]
|
|
||||||
pub fn bind_group(&self, transform_id: TransformIndex) -> &wgpu::BindGroup {
|
pub fn bind_group(&self, transform_id: TransformIndex) -> &wgpu::BindGroup {
|
||||||
let entry_index = transform_id.transform_index / self.max_transform_count;
|
let entry = self.entries.get(transform_id.entry_index).unwrap();
|
||||||
let entry = self.entries.get(entry_index).unwrap();
|
|
||||||
&entry.bindgroup
|
&entry.bindgroup
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Get the buffer offset for a transform using wgpu limits.
|
||||||
|
///
|
||||||
|
/// If its possible to borrow immutably, use [`TransformBuffers::buffer_offset`].
|
||||||
|
fn get_buffer_offset(limits: &wgpu::Limits, transform_index: TransformIndex) -> u32 {
|
||||||
|
transform_index.transform_index as u32 * limits.min_uniform_buffer_offset_alignment as u32
|
||||||
|
}
|
||||||
|
|
||||||
/// Returns the offset of the transform inside the bind group buffer.
|
/// Returns the offset of the transform inside the bind group buffer.
|
||||||
///
|
///
|
||||||
/// ```nobuild
|
/// ```nobuild
|
||||||
|
@ -329,21 +362,8 @@ impl TransformBuffers {
|
||||||
/// let offset = transform_buffers.buffer_offset(job.transform_id);
|
/// let offset = transform_buffers.buffer_offset(job.transform_id);
|
||||||
/// render_pass.set_bind_group(1, bindgroup, &[ offset, offset, ]);
|
/// render_pass.set_bind_group(1, bindgroup, &[ offset, offset, ]);
|
||||||
/// ```
|
/// ```
|
||||||
#[inline(always)]
|
|
||||||
pub fn buffer_offset(&self, transform_index: TransformIndex) -> u32 {
|
pub fn buffer_offset(&self, transform_index: TransformIndex) -> u32 {
|
||||||
//Self::get_buffer_offset(&self.limits, transform_index)
|
Self::get_buffer_offset(&self.limits, transform_index)
|
||||||
let transform_index = transform_index.transform_index % self.max_transform_count;
|
|
||||||
let t = transform_index as u32 * self.limits.min_uniform_buffer_offset_alignment as u32;
|
|
||||||
//debug!("offset: {t}");
|
|
||||||
t
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns a boolean indicating if the buffers need to be expanded
|
|
||||||
pub fn needs_expand(&self) -> bool {
|
|
||||||
false
|
|
||||||
/* self.entries.last()
|
|
||||||
.map(|entry| entry.len >= self.max_transform_count)
|
|
||||||
.unwrap_or(false) */
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
use std::{hash::{Hash, DefaultHasher, Hasher}, collections::{HashMap, HashSet, VecDeque}, ptr::NonNull, fmt::Debug};
|
use std::{hash::{Hash, DefaultHasher, Hasher}, collections::{HashMap, HashSet, VecDeque}, ptr::NonNull, fmt::Debug};
|
||||||
|
|
||||||
use lyra_ecs::{system::{GraphExecutor, GraphExecutorError, System}, World};
|
use lyra_ecs::{system::{GraphExecutor, GraphExecutorError, System}, World};
|
||||||
use tracing::{info_span, instrument};
|
use tracing::info_span;
|
||||||
|
|
||||||
#[derive(thiserror::Error, Debug)]
|
#[derive(thiserror::Error, Debug)]
|
||||||
pub enum StagedExecutorError {
|
pub enum StagedExecutorError {
|
||||||
|
@ -112,7 +112,6 @@ impl StagedExecutor {
|
||||||
/// Execute the staged systems in order.
|
/// Execute the staged systems in order.
|
||||||
///
|
///
|
||||||
/// If `stop_on_error` is false but errors are encountered, those errors will be returned in a Vec.
|
/// If `stop_on_error` is false but errors are encountered, those errors will be returned in a Vec.
|
||||||
#[instrument(skip(self, world, stop_on_error))]
|
|
||||||
pub fn execute(&mut self, world: NonNull<World>, stop_on_error: bool) -> Result<Vec<StagedExecutorError>, StagedExecutorError> {
|
pub fn execute(&mut self, world: NonNull<World>, stop_on_error: bool) -> Result<Vec<StagedExecutorError>, StagedExecutorError> {
|
||||||
let mut stack = VecDeque::new();
|
let mut stack = VecDeque::new();
|
||||||
let mut visited = HashSet::new();
|
let mut visited = HashSet::new();
|
||||||
|
@ -121,14 +120,13 @@ impl StagedExecutor {
|
||||||
self.topological_sort(&mut stack, &mut visited, node)?;
|
self.topological_sort(&mut stack, &mut visited, node)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
//let stage_span = info_span!("stage_exec", stage=tracing::field::Empty);
|
let stage_span = info_span!("stage_exec", stage=tracing::field::Empty);
|
||||||
|
|
||||||
let mut errors = vec![];
|
let mut errors = vec![];
|
||||||
while let Some(node) = stack.pop_front() {
|
while let Some(node) = stack.pop_front() {
|
||||||
let stage = self.stages.get_mut(&node).unwrap();
|
let stage = self.stages.get_mut(&node).unwrap();
|
||||||
|
|
||||||
let stage_span = info_span!("stage_exec", stage=stage.name.clone());
|
stage_span.record("stage", stage.name.clone());
|
||||||
//stage_span.record("stage", stage.name.clone());
|
|
||||||
let _e = stage_span.enter();
|
let _e = stage_span.enter();
|
||||||
|
|
||||||
if let Err(e) = stage.exec.execute(world, stop_on_error) {
|
if let Err(e) = stage.exec.execute(world, stop_on_error) {
|
||||||
|
|
Loading…
Add table
Reference in a new issue