mirror of
https://github.com/eliasstepanik/big_space_with_trim.git
synced 2026-01-11 00:08:27 +00:00
Bevy 0.16 (#46)
# Objective - Working branch to target all fixes for bevy 0.16 Co-authored-by: Zachary Harrold <zac@harrold.com.au>
This commit is contained in:
parent
9bae63e4b4
commit
44ff1f32de
12
.github/workflows/rust.yml
vendored
12
.github/workflows/rust.yml
vendored
@ -29,7 +29,7 @@ jobs:
|
|||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v3
|
||||||
- uses: dtolnay/rust-toolchain@stable
|
- uses: dtolnay/rust-toolchain@stable
|
||||||
- uses: Swatinem/rust-cache@v2.7.0
|
- uses: Swatinem/rust-cache@v2.7.0
|
||||||
- run: cargo check --features=all --all-targets
|
- run: cargo check --all-features --all-targets
|
||||||
|
|
||||||
check-no-defaults:
|
check-no-defaults:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
@ -38,7 +38,7 @@ jobs:
|
|||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v3
|
||||||
- uses: dtolnay/rust-toolchain@stable
|
- uses: dtolnay/rust-toolchain@stable
|
||||||
- uses: Swatinem/rust-cache@v2.7.0
|
- uses: Swatinem/rust-cache@v2.7.0
|
||||||
- run: cargo check --no-default-features --all-targets
|
- run: cargo check --no-default-features --all-targets --features=libm
|
||||||
|
|
||||||
clippy:
|
clippy:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
@ -48,7 +48,7 @@ jobs:
|
|||||||
- uses: dtolnay/rust-toolchain@stable
|
- uses: dtolnay/rust-toolchain@stable
|
||||||
- uses: Swatinem/rust-cache@v2.7.0
|
- uses: Swatinem/rust-cache@v2.7.0
|
||||||
- run: rustup component add clippy
|
- run: rustup component add clippy
|
||||||
- run: cargo clippy --features=all --all-targets -- -D warnings
|
- run: cargo clippy --all-features --all-targets -- -D warnings
|
||||||
|
|
||||||
doc:
|
doc:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
@ -57,7 +57,7 @@ jobs:
|
|||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v3
|
||||||
- uses: dtolnay/rust-toolchain@stable
|
- uses: dtolnay/rust-toolchain@stable
|
||||||
- uses: Swatinem/rust-cache@v2.7.0
|
- uses: Swatinem/rust-cache@v2.7.0
|
||||||
- run: cargo doc --features=all --no-deps
|
- run: cargo doc --all-features --no-deps
|
||||||
env:
|
env:
|
||||||
RUSTDOCFLAGS: -D warnings
|
RUSTDOCFLAGS: -D warnings
|
||||||
|
|
||||||
@ -68,7 +68,7 @@ jobs:
|
|||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v3
|
||||||
- uses: dtolnay/rust-toolchain@stable
|
- uses: dtolnay/rust-toolchain@stable
|
||||||
- uses: Swatinem/rust-cache@v2.7.0
|
- uses: Swatinem/rust-cache@v2.7.0
|
||||||
- run: cargo test --features=all
|
- run: cargo test --all-features
|
||||||
|
|
||||||
doctest:
|
doctest:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
@ -77,4 +77,4 @@ jobs:
|
|||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v3
|
||||||
- uses: dtolnay/rust-toolchain@stable
|
- uses: dtolnay/rust-toolchain@stable
|
||||||
- uses: Swatinem/rust-cache@v2.7.0
|
- uses: Swatinem/rust-cache@v2.7.0
|
||||||
- run: cargo test --features=all --doc
|
- run: cargo test --all-features --doc
|
||||||
@ -2,6 +2,12 @@
|
|||||||
|
|
||||||
## UNRELEASED
|
## UNRELEASED
|
||||||
|
|
||||||
|
### New: `no_std` Support
|
||||||
|
|
||||||
|
Thanks to `bushrat011899`'s efforts upstream and in this crate, it is now possible to use the plugin without the rust standard library. This is particularly useful when targeting embedded or console targets.
|
||||||
|
|
||||||
|
## v0.9.0 - 2024-12-23
|
||||||
|
|
||||||
### New: `GridCell` Spatial Hashing
|
### New: `GridCell` Spatial Hashing
|
||||||
|
|
||||||
Spatial hashing makes fast spatial queries and neighbor lookups possible. This release adds the `GridHashMap`, an automatically updated map of the entities in each grid cell. This makes it possible to query things like:
|
Spatial hashing makes fast spatial queries and neighbor lookups possible. This release adds the `GridHashMap`, an automatically updated map of the entities in each grid cell. This makes it possible to query things like:
|
||||||
@ -37,5 +43,4 @@ The newly added types follow this pattern:
|
|||||||
- `GridPartition`: Group of adjacent grid cells.
|
- `GridPartition`: Group of adjacent grid cells.
|
||||||
- `GridPartitionMap`: A map for finding independent partitions of entities.
|
- `GridPartitionMap`: A map for finding independent partitions of entities.
|
||||||
|
|
||||||
|
|
||||||
It should now be more clear how all of the `Grid` types are related to each other.
|
It should now be more clear how all of the `Grid` types are related to each other.
|
||||||
127
Cargo.toml
127
Cargo.toml
@ -9,42 +9,57 @@ repository = "https://github.com/aevyrie/big_space"
|
|||||||
documentation = "https://docs.rs/crate/big_space/latest"
|
documentation = "https://docs.rs/crate/big_space/latest"
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = []
|
default = ["std"]
|
||||||
all = ["debug", "camera"] # Can't use all-features, integer type features are incompatible.
|
debug = ["std", "bevy_gizmos", "bevy_color"]
|
||||||
debug = ["bevy_gizmos", "bevy_color"]
|
camera = ["std", "bevy_render", "bevy_time", "bevy_input"]
|
||||||
camera = ["bevy_render", "bevy_time", "bevy_input"]
|
|
||||||
i8 = []
|
i8 = []
|
||||||
i16 = []
|
i16 = []
|
||||||
i32 = []
|
i32 = []
|
||||||
i64 = []
|
i64 = []
|
||||||
i128 = []
|
i128 = []
|
||||||
|
|
||||||
[dependencies]
|
std = [
|
||||||
tracing = "0.1" # Less deps than pulling in bevy_log
|
"bevy_app/std",
|
||||||
smallvec = "1.13.2" # Already used by bevy in commands
|
"bevy_ecs/std",
|
||||||
bevy_app = { version = "0.15.0", default-features = false }
|
"bevy_math/std",
|
||||||
bevy_ecs = { version = "0.15.0", default-features = true }
|
"bevy_reflect/std",
|
||||||
bevy_hierarchy = { version = "0.15.0", default-features = false }
|
"bevy_tasks/std",
|
||||||
bevy_math = { version = "0.15.0", default-features = false }
|
"bevy_transform/std",
|
||||||
bevy_reflect = { version = "0.15.0", default-features = false }
|
"bevy_utils/std",
|
||||||
bevy_tasks = { version = "0.15.0", default-features = false }
|
"bevy_platform_support/std",
|
||||||
bevy_transform = { version = "0.15.0", default-features = false, features = [
|
"bevy_color?/std",
|
||||||
"bevy-support",
|
"bevy_input?/std",
|
||||||
] }
|
"bevy_time?/std",
|
||||||
bevy_utils = { version = "0.15.0", default-features = false }
|
]
|
||||||
# Optional
|
libm = ["bevy_math/libm", "dep:libm"]
|
||||||
bevy_color = { version = "0.15.0", default-features = false, optional = true }
|
|
||||||
bevy_gizmos = { version = "0.15.0", default-features = false, optional = true }
|
|
||||||
bevy_render = { version = "0.15.0", default-features = false, optional = true }
|
|
||||||
bevy_input = { version = "0.15.0", default-features = false, optional = true }
|
|
||||||
bevy_time = { version = "0.15.0", default-features = false, optional = true }
|
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
tracing = { version = "0.1", default-features = false } # Less deps than pulling in bevy_log
|
||||||
|
smallvec = { version = "1.13.2", default-features = false } # Already used by bevy in commands
|
||||||
|
bevy_app = { version = "0.16.0-rc.3", default-features = false, features = ["bevy_reflect"] }
|
||||||
|
bevy_ecs = { version = "0.16.0-rc.3", default-features = false }
|
||||||
|
bevy_math = { version = "0.16.0-rc.3", default-features = false }
|
||||||
|
bevy_reflect = { version = "0.16.0-rc.3", default-features = false, features = ["glam"] }
|
||||||
|
bevy_tasks = { version = "0.16.0-rc.3", default-features = false }
|
||||||
|
bevy_transform = { version = "0.16.0-rc.3", default-features = false, features = [
|
||||||
|
"bevy-support",
|
||||||
|
"bevy_reflect",
|
||||||
|
] }
|
||||||
|
bevy_utils = { version = "0.16.0-rc.3", default-features = false }
|
||||||
|
bevy_platform_support = { version = "0.16.0-rc.3", default-features = false, features = ["alloc"] }
|
||||||
|
# Optional
|
||||||
|
bevy_color = { version = "0.16.0-rc.3", default-features = false, optional = true }
|
||||||
|
bevy_gizmos = { version = "0.16.0-rc.3", default-features = false, optional = true }
|
||||||
|
bevy_render = { version = "0.16.0-rc.3", default-features = false, optional = true }
|
||||||
|
bevy_input = { version = "0.16.0-rc.3", default-features = false, optional = true }
|
||||||
|
bevy_time = { version = "0.16.0-rc.3", default-features = false, optional = true }
|
||||||
|
libm = { version = "0.2", default-features = false, optional = true }
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
big_space = { path = "", features = ["debug", "camera"] }
|
bevy = { version = "0.16.0-rc.3", default-features = false, features = [
|
||||||
bevy = { version = "0.15.0", default-features = false, features = [
|
|
||||||
"bevy_scene",
|
"bevy_scene",
|
||||||
"bevy_asset",
|
"bevy_asset",
|
||||||
|
"bevy_color",
|
||||||
"bevy_gltf",
|
"bevy_gltf",
|
||||||
"bevy_winit",
|
"bevy_winit",
|
||||||
"default_font",
|
"default_font",
|
||||||
@ -61,7 +76,36 @@ noise = "0.9"
|
|||||||
turborand = "0.10"
|
turborand = "0.10"
|
||||||
criterion = "0.5"
|
criterion = "0.5"
|
||||||
bytemuck = "1.20"
|
bytemuck = "1.20"
|
||||||
bevy_hanabi = "0.14"
|
# bevy_hanabi = "0.14" # TODO: Update
|
||||||
|
|
||||||
|
[lints.clippy]
|
||||||
|
doc_markdown = "warn"
|
||||||
|
manual_let_else = "warn"
|
||||||
|
match_same_arms = "warn"
|
||||||
|
redundant_closure_for_method_calls = "warn"
|
||||||
|
redundant_else = "warn"
|
||||||
|
semicolon_if_nothing_returned = "warn"
|
||||||
|
type_complexity = "allow"
|
||||||
|
undocumented_unsafe_blocks = "warn"
|
||||||
|
unwrap_or_default = "warn"
|
||||||
|
|
||||||
|
ptr_as_ptr = "warn"
|
||||||
|
ptr_cast_constness = "warn"
|
||||||
|
ref_as_ptr = "warn"
|
||||||
|
|
||||||
|
# see: https://github.com/bevyengine/bevy/pull/15375#issuecomment-2366966219
|
||||||
|
too_long_first_doc_paragraph = "allow"
|
||||||
|
|
||||||
|
std_instead_of_core = "warn"
|
||||||
|
std_instead_of_alloc = "warn"
|
||||||
|
alloc_instead_of_core = "warn"
|
||||||
|
|
||||||
|
[lints.rust]
|
||||||
|
missing_docs = "warn"
|
||||||
|
unexpected_cfgs = { level = "warn", check-cfg = ['cfg(docsrs_dep)'] }
|
||||||
|
unsafe_code = "deny"
|
||||||
|
unsafe_op_in_unsafe_fn = "warn"
|
||||||
|
unused_qualifications = "warn"
|
||||||
|
|
||||||
[[bench]]
|
[[bench]]
|
||||||
name = "benchmarks"
|
name = "benchmarks"
|
||||||
@ -70,18 +114,19 @@ harness = false
|
|||||||
[[example]]
|
[[example]]
|
||||||
name = "debug"
|
name = "debug"
|
||||||
path = "examples/debug.rs"
|
path = "examples/debug.rs"
|
||||||
|
required-features = ["debug"]
|
||||||
doc-scrape-examples = false
|
doc-scrape-examples = false
|
||||||
|
|
||||||
[[example]]
|
[[example]]
|
||||||
name = "demo"
|
name = "demo"
|
||||||
path = "examples/demo.rs"
|
path = "examples/demo.rs"
|
||||||
required-features = ["i128"]
|
required-features = ["i128", "camera", "debug"]
|
||||||
doc-scrape-examples = false
|
doc-scrape-examples = false
|
||||||
|
|
||||||
|
|
||||||
[[example]]
|
[[example]]
|
||||||
name = "error_child"
|
name = "error_child"
|
||||||
path = "examples/error_child.rs"
|
path = "examples/error_child.rs"
|
||||||
|
required-features = ["camera", "debug"]
|
||||||
doc-scrape-examples = false
|
doc-scrape-examples = false
|
||||||
|
|
||||||
[[example]]
|
[[example]]
|
||||||
@ -92,25 +137,41 @@ doc-scrape-examples = false
|
|||||||
[[example]]
|
[[example]]
|
||||||
name = "infinite"
|
name = "infinite"
|
||||||
path = "examples/infinite.rs"
|
path = "examples/infinite.rs"
|
||||||
required-features = ["i8"]
|
required-features = ["i8", "camera", "debug"]
|
||||||
doc-scrape-examples = false
|
doc-scrape-examples = false
|
||||||
|
|
||||||
[[example]]
|
[[example]]
|
||||||
name = "minimal"
|
name = "minimal"
|
||||||
path = "examples/minimal.rs"
|
path = "examples/minimal.rs"
|
||||||
|
required-features = ["camera"]
|
||||||
doc-scrape-examples = false
|
doc-scrape-examples = false
|
||||||
|
|
||||||
[[example]]
|
# TODO: Uncomment once bevy_hanabi is updated
|
||||||
name = "particles"
|
# [[example]]
|
||||||
path = "examples/particles.rs"
|
# name = "particles"
|
||||||
doc-scrape-examples = false
|
# path = "examples/particles.rs"
|
||||||
|
# doc-scrape-examples = false
|
||||||
|
|
||||||
[[example]]
|
[[example]]
|
||||||
name = "planets"
|
name = "planets"
|
||||||
path = "examples/planets.rs"
|
path = "examples/planets.rs"
|
||||||
|
required-features = ["camera"]
|
||||||
|
doc-scrape-examples = false
|
||||||
|
|
||||||
|
[[example]]
|
||||||
|
name = "small_scale"
|
||||||
|
path = "examples/small_scale.rs"
|
||||||
|
required-features = ["camera", "debug"]
|
||||||
|
doc-scrape-examples = false
|
||||||
|
|
||||||
|
[[example]]
|
||||||
|
name = "spatial_hash"
|
||||||
|
path = "examples/spatial_hash.rs"
|
||||||
|
required-features = ["camera"]
|
||||||
doc-scrape-examples = false
|
doc-scrape-examples = false
|
||||||
|
|
||||||
[[example]]
|
[[example]]
|
||||||
name = "split_screen"
|
name = "split_screen"
|
||||||
path = "examples/split_screen.rs"
|
path = "examples/split_screen.rs"
|
||||||
|
required-features = ["camera", "debug"]
|
||||||
doc-scrape-examples = false
|
doc-scrape-examples = false
|
||||||
|
|||||||
12
README.md
12
README.md
@ -5,7 +5,7 @@
|
|||||||
<img src="https://raw.githubusercontent.com/aevyrie/big_space/refs/heads/main/assets/bigspacebanner.svg" width="80%">
|
<img src="https://raw.githubusercontent.com/aevyrie/big_space/refs/heads/main/assets/bigspacebanner.svg" width="80%">
|
||||||
|
|
||||||
Huge worlds, high performance, no dependencies, ecosystem compatibility. [Read the docs](https://docs.rs/big_space)
|
Huge worlds, high performance, no dependencies, ecosystem compatibility. [Read the docs](https://docs.rs/big_space)
|
||||||
|
|
||||||
[](https://crates.io/crates/big_space)
|
[](https://crates.io/crates/big_space)
|
||||||
[](https://docs.rs/big_space)
|
[](https://docs.rs/big_space)
|
||||||
[](https://github.com/aevyrie/big_space/actions/workflows/rust.yml)
|
[](https://github.com/aevyrie/big_space/actions/workflows/rust.yml)
|
||||||
@ -18,11 +18,10 @@ Huge worlds, high performance, no dependencies, ecosystem compatibility. [Read t
|
|||||||
- Uses `Transform`, making it compatible with most of the Bevy ecosystem.
|
- Uses `Transform`, making it compatible with most of the Bevy ecosystem.
|
||||||
- No added dependencies.
|
- No added dependencies.
|
||||||
- Absolute coordinates without drift, unlike camera-relative or periodic recentering solutions.
|
- Absolute coordinates without drift, unlike camera-relative or periodic recentering solutions.
|
||||||
- Chunks the world into integer grids, from `i8` up to `i128`.
|
- Chunks the world into nestable integer grids, from `i8` up to `i128`.
|
||||||
- Grids can be nested.
|
|
||||||
- Spatial hashing for fast grid cell lookups and neighbor search.
|
- Spatial hashing for fast grid cell lookups and neighbor search.
|
||||||
- Spatial partitioning to group sets of disconnected entities.
|
- Spatial partitioning to group sets of connected cells.
|
||||||
- 3-5x faster than Bevy's transform propagation for wide hierarchies.
|
- Great performance scaling and parallelism with massive entity counts.
|
||||||
- 👉 [Extensive documentation you should read.](https://docs.rs/big_space)
|
- 👉 [Extensive documentation you should read.](https://docs.rs/big_space)
|
||||||
|
|
||||||

|

|
||||||
@ -40,7 +39,8 @@ https://github.com/user-attachments/assets/9ce5283f-7d48-47dc-beef-9a7626858ed4
|
|||||||
## Bevy Version Support
|
## Bevy Version Support
|
||||||
|
|
||||||
| bevy | big_space |
|
| bevy | big_space |
|
||||||
| ---- | --------- |
|
|------|-----------|
|
||||||
|
| 0.16 | 0.10 |
|
||||||
| 0.15 | 0.8, 0.9 |
|
| 0.15 | 0.8, 0.9 |
|
||||||
| 0.14 | 0.7 |
|
| 0.14 | 0.7 |
|
||||||
| 0.13 | 0.5, 0.6 |
|
| 0.13 | 0.5, 0.6 |
|
||||||
|
|||||||
@ -1,9 +1,11 @@
|
|||||||
|
//! `big_space` benchmarks.
|
||||||
#![allow(clippy::type_complexity)]
|
#![allow(clippy::type_complexity)]
|
||||||
|
#![allow(missing_docs)]
|
||||||
|
|
||||||
use bevy::prelude::*;
|
use bevy::prelude::*;
|
||||||
use big_space::prelude::*;
|
use big_space::prelude::*;
|
||||||
|
use core::{iter::repeat_with, ops::Neg};
|
||||||
use criterion::{black_box, criterion_group, criterion_main, Criterion};
|
use criterion::{black_box, criterion_group, criterion_main, Criterion};
|
||||||
use std::{iter::repeat_with, ops::Neg};
|
|
||||||
use turborand::prelude::*;
|
use turborand::prelude::*;
|
||||||
|
|
||||||
criterion_group!(
|
criterion_group!(
|
||||||
@ -52,7 +54,7 @@ fn deep_hierarchy(c: &mut Criterion) {
|
|||||||
fn translate(mut transforms: Query<&mut Transform>) {
|
fn translate(mut transforms: Query<&mut Transform>) {
|
||||||
transforms.iter_mut().for_each(|mut transform| {
|
transforms.iter_mut().for_each(|mut transform| {
|
||||||
transform.translation += Vec3::ONE;
|
transform.translation += Vec3::ONE;
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut app = App::new();
|
let mut app = App::new();
|
||||||
@ -91,7 +93,7 @@ fn wide_hierarchy(c: &mut Criterion) {
|
|||||||
fn translate(mut transforms: Query<&mut Transform>) {
|
fn translate(mut transforms: Query<&mut Transform>) {
|
||||||
transforms.iter_mut().for_each(|mut transform| {
|
transforms.iter_mut().for_each(|mut transform| {
|
||||||
transform.translation += Vec3::ONE;
|
transform.translation += Vec3::ONE;
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut app = App::new();
|
let mut app = App::new();
|
||||||
@ -126,9 +128,9 @@ fn spatial_hashing(c: &mut Criterion) {
|
|||||||
let rng = Rng::with_seed(342525);
|
let rng = Rng::with_seed(342525);
|
||||||
let values: Vec<_> = repeat_with(|| {
|
let values: Vec<_> = repeat_with(|| {
|
||||||
[
|
[
|
||||||
rng.i64(-HALF_WIDTH..=HALF_WIDTH),
|
rng.i64(-HALF_WIDTH..=HALF_WIDTH) as GridPrecision,
|
||||||
rng.i64(-HALF_WIDTH..=HALF_WIDTH),
|
rng.i64(-HALF_WIDTH..=HALF_WIDTH) as GridPrecision,
|
||||||
rng.i64(-HALF_WIDTH..=HALF_WIDTH),
|
rng.i64(-HALF_WIDTH..=HALF_WIDTH) as GridPrecision,
|
||||||
]
|
]
|
||||||
})
|
})
|
||||||
.take(N_SPAWN)
|
.take(N_SPAWN)
|
||||||
@ -143,7 +145,7 @@ fn spatial_hashing(c: &mut Criterion) {
|
|||||||
fn translate(mut cells: Query<&mut GridCell>) {
|
fn translate(mut cells: Query<&mut GridCell>) {
|
||||||
cells.iter_mut().take(N_MOVE).for_each(|mut cell| {
|
cells.iter_mut().take(N_MOVE).for_each(|mut cell| {
|
||||||
*cell += GridCell::ONE;
|
*cell += GridCell::ONE;
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut app = App::new();
|
let mut app = App::new();
|
||||||
@ -204,7 +206,7 @@ fn spatial_hashing(c: &mut Criterion) {
|
|||||||
// });
|
// });
|
||||||
// });
|
// });
|
||||||
|
|
||||||
fn setup_uniform<const HALF_EXTENT: i64>(mut commands: Commands) {
|
fn setup_uniform<const HALF_EXTENT: GridPrecision>(mut commands: Commands) {
|
||||||
commands.spawn_big_space(Grid::new(1.0, 0.0), |root| {
|
commands.spawn_big_space(Grid::new(1.0, 0.0), |root| {
|
||||||
for x in HALF_EXTENT.neg()..HALF_EXTENT {
|
for x in HALF_EXTENT.neg()..HALF_EXTENT {
|
||||||
for y in HALF_EXTENT.neg()..HALF_EXTENT {
|
for y in HALF_EXTENT.neg()..HALF_EXTENT {
|
||||||
@ -226,7 +228,8 @@ fn spatial_hashing(c: &mut Criterion) {
|
|||||||
let parent = app
|
let parent = app
|
||||||
.world_mut()
|
.world_mut()
|
||||||
.query_filtered::<Entity, With<BigSpace>>()
|
.query_filtered::<Entity, With<BigSpace>>()
|
||||||
.single(app.world());
|
.single(app.world())
|
||||||
|
.unwrap();
|
||||||
let spatial_map = app.world().resource::<GridHashMap>();
|
let spatial_map = app.world().resource::<GridHashMap>();
|
||||||
let hash = GridHash::__new_manual(parent, &GridCell { x: 0, y: 0, z: 0 });
|
let hash = GridHash::__new_manual(parent, &GridCell { x: 0, y: 0, z: 0 });
|
||||||
let entry = spatial_map.get(&hash).unwrap();
|
let entry = spatial_map.get(&hash).unwrap();
|
||||||
@ -254,7 +257,8 @@ fn spatial_hashing(c: &mut Criterion) {
|
|||||||
let parent = app
|
let parent = app
|
||||||
.world_mut()
|
.world_mut()
|
||||||
.query_filtered::<Entity, With<BigSpace>>()
|
.query_filtered::<Entity, With<BigSpace>>()
|
||||||
.single(app.world());
|
.single(app.world())
|
||||||
|
.unwrap();
|
||||||
let spatial_map = app.world().resource::<GridHashMap>();
|
let spatial_map = app.world().resource::<GridHashMap>();
|
||||||
let hash = GridHash::__new_manual(parent, &GridCell { x: 0, y: 0, z: 0 });
|
let hash = GridHash::__new_manual(parent, &GridCell { x: 0, y: 0, z: 0 });
|
||||||
let entry = spatial_map.get(&hash).unwrap();
|
let entry = spatial_map.get(&hash).unwrap();
|
||||||
@ -288,9 +292,9 @@ fn hash_filtering(c: &mut Criterion) {
|
|||||||
let rng = Rng::with_seed(342525);
|
let rng = Rng::with_seed(342525);
|
||||||
let values: Vec<_> = repeat_with(|| {
|
let values: Vec<_> = repeat_with(|| {
|
||||||
[
|
[
|
||||||
rng.i64(-HALF_WIDTH..=HALF_WIDTH),
|
rng.i64(-HALF_WIDTH..=HALF_WIDTH) as GridPrecision,
|
||||||
rng.i64(-HALF_WIDTH..=HALF_WIDTH),
|
rng.i64(-HALF_WIDTH..=HALF_WIDTH) as GridPrecision,
|
||||||
rng.i64(-HALF_WIDTH..=HALF_WIDTH),
|
rng.i64(-HALF_WIDTH..=HALF_WIDTH) as GridPrecision,
|
||||||
]
|
]
|
||||||
})
|
})
|
||||||
.take(N_ENTITIES)
|
.take(N_ENTITIES)
|
||||||
|
|||||||
@ -1,3 +1,4 @@
|
|||||||
|
//! Demonstrates debugging visualization for `big_space` components.
|
||||||
#![allow(clippy::type_complexity)]
|
#![allow(clippy::type_complexity)]
|
||||||
|
|
||||||
use bevy::{color::palettes, prelude::*};
|
use bevy::{color::palettes, prelude::*};
|
||||||
@ -6,9 +7,9 @@ use big_space::prelude::*;
|
|||||||
fn main() {
|
fn main() {
|
||||||
App::new()
|
App::new()
|
||||||
.add_plugins((
|
.add_plugins((
|
||||||
DefaultPlugins,
|
DefaultPlugins.build().disable::<TransformPlugin>(),
|
||||||
BigSpacePlugin::default(),
|
BigSpacePlugin::default(),
|
||||||
big_space::debug::FloatingOriginDebugPlugin::default(),
|
FloatingOriginDebugPlugin::default(),
|
||||||
))
|
))
|
||||||
.add_systems(Startup, setup)
|
.add_systems(Startup, setup)
|
||||||
.add_systems(Update, (movement, rotation))
|
.add_systems(Update, (movement, rotation))
|
||||||
@ -26,7 +27,7 @@ fn movement(
|
|||||||
Query<&mut Transform, With<Mover<3>>>,
|
Query<&mut Transform, With<Mover<3>>>,
|
||||||
Query<&mut Transform, With<Mover<4>>>,
|
Query<&mut Transform, With<Mover<4>>>,
|
||||||
)>,
|
)>,
|
||||||
) {
|
) -> Result {
|
||||||
let delta_translation = |offset: f32, scale: f32| -> Vec3 {
|
let delta_translation = |offset: f32, scale: f32| -> Vec3 {
|
||||||
let t_1 = time.elapsed_secs() * 0.1 + offset;
|
let t_1 = time.elapsed_secs() * 0.1 + offset;
|
||||||
let dt = time.delta_secs() * 0.1;
|
let dt = time.delta_secs() * 0.1;
|
||||||
@ -38,10 +39,12 @@ fn movement(
|
|||||||
p1 - p0
|
p1 - p0
|
||||||
};
|
};
|
||||||
|
|
||||||
q.p0().single_mut().translation += delta_translation(20.0, 1.0);
|
q.p0().single_mut()?.translation += delta_translation(20.0, 1.0);
|
||||||
q.p1().single_mut().translation += delta_translation(251.0, 1.0);
|
q.p1().single_mut()?.translation += delta_translation(251.0, 1.0);
|
||||||
q.p2().single_mut().translation += delta_translation(812.0, 1.0);
|
q.p2().single_mut()?.translation += delta_translation(812.0, 1.0);
|
||||||
q.p3().single_mut().translation += delta_translation(863.0, 0.4);
|
q.p3().single_mut()?.translation += delta_translation(863.0, 0.4);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Component)]
|
#[derive(Component)]
|
||||||
|
|||||||
@ -1,3 +1,5 @@
|
|||||||
|
//! Demonstrates using the plugin over a wide range of scales, from protons to the universe.
|
||||||
|
|
||||||
use bevy::{
|
use bevy::{
|
||||||
color::palettes,
|
color::palettes,
|
||||||
prelude::*,
|
prelude::*,
|
||||||
@ -13,10 +15,10 @@ use big_space::{
|
|||||||
fn main() {
|
fn main() {
|
||||||
App::new()
|
App::new()
|
||||||
.add_plugins((
|
.add_plugins((
|
||||||
DefaultPlugins,
|
DefaultPlugins.build().disable::<TransformPlugin>(),
|
||||||
BigSpacePlugin::default(),
|
BigSpacePlugin::default(),
|
||||||
FloatingOriginDebugPlugin::default(),
|
FloatingOriginDebugPlugin::default(),
|
||||||
big_space::camera::CameraControllerPlugin::default(),
|
CameraControllerPlugin::default(),
|
||||||
))
|
))
|
||||||
.insert_resource(ClearColor(Color::BLACK))
|
.insert_resource(ClearColor(Color::BLACK))
|
||||||
.add_systems(Startup, (setup, ui_setup))
|
.add_systems(Startup, (setup, ui_setup))
|
||||||
@ -79,10 +81,10 @@ fn setup(
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Component, Reflect)]
|
#[derive(Component, Reflect)]
|
||||||
pub struct BigSpaceDebugText;
|
struct BigSpaceDebugText;
|
||||||
|
|
||||||
#[derive(Component, Reflect)]
|
#[derive(Component, Reflect)]
|
||||||
pub struct FunFactText;
|
struct FunFactText;
|
||||||
|
|
||||||
fn ui_setup(mut commands: Commands) {
|
fn ui_setup(mut commands: Commands) {
|
||||||
commands.spawn((
|
commands.spawn((
|
||||||
@ -125,13 +127,11 @@ fn highlight_nearest_sphere(
|
|||||||
cameras: Query<&CameraController>,
|
cameras: Query<&CameraController>,
|
||||||
objects: Query<&GlobalTransform>,
|
objects: Query<&GlobalTransform>,
|
||||||
mut gizmos: Gizmos,
|
mut gizmos: Gizmos,
|
||||||
) {
|
) -> Result {
|
||||||
let Some((entity, _)) = cameras.single().nearest_object() else {
|
let Some((entity, _)) = cameras.single()?.nearest_object() else {
|
||||||
return;
|
return Ok(());
|
||||||
};
|
|
||||||
let Ok(transform) = objects.get(entity) else {
|
|
||||||
return;
|
|
||||||
};
|
};
|
||||||
|
let transform = objects.get(entity)?;
|
||||||
// Ignore rotation due to panicking in gizmos, as of bevy 0.13
|
// Ignore rotation due to panicking in gizmos, as of bevy 0.13
|
||||||
let (scale, _, translation) = transform.to_scale_rotation_translation();
|
let (scale, _, translation) = transform.to_scale_rotation_translation();
|
||||||
gizmos
|
gizmos
|
||||||
@ -141,6 +141,7 @@ fn highlight_nearest_sphere(
|
|||||||
Color::Srgba(palettes::basic::RED),
|
Color::Srgba(palettes::basic::RED),
|
||||||
)
|
)
|
||||||
.resolution(128);
|
.resolution(128);
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(clippy::type_complexity)]
|
#[allow(clippy::type_complexity)]
|
||||||
@ -155,8 +156,8 @@ fn ui_text_system(
|
|||||||
origin: Query<(Entity, GridTransformReadOnly), With<FloatingOrigin>>,
|
origin: Query<(Entity, GridTransformReadOnly), With<FloatingOrigin>>,
|
||||||
camera: Query<&CameraController>,
|
camera: Query<&CameraController>,
|
||||||
objects: Query<&Transform, With<Mesh3d>>,
|
objects: Query<&Transform, With<Mesh3d>>,
|
||||||
) {
|
) -> Result {
|
||||||
let (origin_entity, origin_pos) = origin.single();
|
let (origin_entity, origin_pos) = origin.single()?;
|
||||||
let translation = origin_pos.transform.translation;
|
let translation = origin_pos.transform.translation;
|
||||||
|
|
||||||
let grid_text = format!(
|
let grid_text = format!(
|
||||||
@ -170,7 +171,7 @@ fn ui_text_system(
|
|||||||
);
|
);
|
||||||
|
|
||||||
let Some(grid) = grids.parent_grid(origin_entity) else {
|
let Some(grid) = grids.parent_grid(origin_entity) else {
|
||||||
return;
|
return Ok(());
|
||||||
};
|
};
|
||||||
|
|
||||||
let real_position = grid.grid_position_double(origin_pos.cell, origin_pos.transform);
|
let real_position = grid.grid_position_double(origin_pos.cell, origin_pos.transform);
|
||||||
@ -183,7 +184,7 @@ fn ui_text_system(
|
|||||||
real_position.x as f32, real_position.y as f32, real_position.z as f32
|
real_position.x as f32, real_position.y as f32, real_position.z as f32
|
||||||
);
|
);
|
||||||
|
|
||||||
let velocity = camera.single().velocity();
|
let velocity = camera.single()?.velocity();
|
||||||
let speed = velocity.0.length() / time.delta_secs_f64();
|
let speed = velocity.0.length() / time.delta_secs_f64();
|
||||||
let camera_text = if speed > 3.0e8 {
|
let camera_text = if speed > 3.0e8 {
|
||||||
format!("Speed: {:.0e} * speed of light", speed / 3.0e8)
|
format!("Speed: {:.0e} * speed of light", speed / 3.0e8)
|
||||||
@ -191,8 +192,8 @@ fn ui_text_system(
|
|||||||
format!("Speed: {:.2e} m/s", speed)
|
format!("Speed: {:.2e} m/s", speed)
|
||||||
};
|
};
|
||||||
|
|
||||||
let (nearest_text, fact_text) = if let Some(nearest) = camera.single().nearest_object() {
|
let (nearest_text, fact_text) = if let Some(nearest) = camera.single()?.nearest_object() {
|
||||||
let dia = objects.get(nearest.0).unwrap().scale.max_element();
|
let dia = objects.get(nearest.0)?.scale.max_element();
|
||||||
let (fact_dia, fact) = closest(dia);
|
let (fact_dia, fact) = closest(dia);
|
||||||
let dist = nearest.1;
|
let dist = nearest.1;
|
||||||
let multiple = dia / fact_dia;
|
let multiple = dia / fact_dia;
|
||||||
@ -206,13 +207,15 @@ fn ui_text_system(
|
|||||||
("".into(), "".into())
|
("".into(), "".into())
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut debug_text = debug_text.single_mut();
|
let mut debug_text = debug_text.single_mut()?;
|
||||||
|
|
||||||
debug_text.0.0 = format!(
|
debug_text.0.0 = format!(
|
||||||
"{grid_text}\n{translation_text}\n\n{real_position_f64_text}\n{real_position_f32_text}\n\n{camera_text}\n{nearest_text}"
|
"{grid_text}\n{translation_text}\n\n{real_position_f64_text}\n{real_position_f32_text}\n\n{camera_text}\n{nearest_text}"
|
||||||
);
|
);
|
||||||
|
|
||||||
fun_text.single_mut().0 = fact_text
|
fun_text.single_mut()?.0 = fact_text;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn closest<'a>(diameter: f32) -> (f32, &'a str) {
|
fn closest<'a>(diameter: f32) -> (f32, &'a str) {
|
||||||
@ -262,10 +265,8 @@ fn cursor_grab_system(
|
|||||||
mut cam: ResMut<CameraInput>,
|
mut cam: ResMut<CameraInput>,
|
||||||
btn: Res<ButtonInput<MouseButton>>,
|
btn: Res<ButtonInput<MouseButton>>,
|
||||||
key: Res<ButtonInput<KeyCode>>,
|
key: Res<ButtonInput<KeyCode>>,
|
||||||
) {
|
) -> Result {
|
||||||
let Some(mut window) = windows.get_single_mut().ok() else {
|
let mut window = windows.single_mut()?;
|
||||||
return;
|
|
||||||
};
|
|
||||||
|
|
||||||
if btn.just_pressed(MouseButton::Left) {
|
if btn.just_pressed(MouseButton::Left) {
|
||||||
window.cursor_options.grab_mode = CursorGrabMode::Locked;
|
window.cursor_options.grab_mode = CursorGrabMode::Locked;
|
||||||
@ -280,4 +281,6 @@ fn cursor_grab_system(
|
|||||||
// window.mode = WindowMode::Windowed;
|
// window.mode = WindowMode::Windowed;
|
||||||
cam.defaults_disabled = true;
|
cam.defaults_disabled = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|||||||
@ -10,7 +10,10 @@ use big_space::prelude::*;
|
|||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
App::new()
|
App::new()
|
||||||
.add_plugins((DefaultPlugins, BigSpacePlugin::default()))
|
.add_plugins((
|
||||||
|
DefaultPlugins.build().disable::<TransformPlugin>(),
|
||||||
|
BigSpacePlugin::default(),
|
||||||
|
))
|
||||||
.add_systems(Startup, (setup_scene, setup_ui))
|
.add_systems(Startup, (setup_scene, setup_ui))
|
||||||
.add_systems(Update, (rotator_system, toggle_plugin))
|
.add_systems(Update, (rotator_system, toggle_plugin))
|
||||||
.run();
|
.run();
|
||||||
@ -22,8 +25,8 @@ fn main() {
|
|||||||
/// floating point error when we disable this plugin.
|
/// floating point error when we disable this plugin.
|
||||||
///
|
///
|
||||||
/// This plugin can function much further from the origin without any issues. Try setting this to:
|
/// This plugin can function much further from the origin without any issues. Try setting this to:
|
||||||
/// 10_000_000_000_000_000 with the default i64 feature, or
|
/// `10_000_000_000_000_000` with the default i64 feature, or
|
||||||
/// 10_000_000_000_000_000_000_000_000_000_000_000_000 with the i128 feature.
|
/// `10_000_000_000_000_000_000_000_000_000_000_000_000` with the i128 feature.
|
||||||
const DISTANCE: GridPrecision = 2_000_000;
|
const DISTANCE: GridPrecision = 2_000_000;
|
||||||
|
|
||||||
/// Move the floating origin back to the "true" origin when the user presses the spacebar to emulate
|
/// Move the floating origin back to the "true" origin when the user presses the spacebar to emulate
|
||||||
@ -35,14 +38,16 @@ fn toggle_plugin(
|
|||||||
mut text: Query<&mut Text>,
|
mut text: Query<&mut Text>,
|
||||||
mut disabled: Local<bool>,
|
mut disabled: Local<bool>,
|
||||||
mut floating_origin: Query<(Entity, &mut GridCell), With<FloatingOrigin>>,
|
mut floating_origin: Query<(Entity, &mut GridCell), With<FloatingOrigin>>,
|
||||||
) {
|
) -> Result {
|
||||||
if input.just_pressed(KeyCode::Space) {
|
if input.just_pressed(KeyCode::Space) {
|
||||||
*disabled = !*disabled;
|
*disabled = !*disabled;
|
||||||
}
|
}
|
||||||
|
|
||||||
let this_grid = grids.parent_grid(floating_origin.single().0).unwrap();
|
let this_grid = grids
|
||||||
|
.parent_grid(floating_origin.single().unwrap().0)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
let mut origin_cell = floating_origin.single_mut().1;
|
let mut origin_cell = floating_origin.single_mut()?.1;
|
||||||
let index_max = DISTANCE / this_grid.cell_edge_length() as GridPrecision;
|
let index_max = DISTANCE / this_grid.cell_edge_length() as GridPrecision;
|
||||||
let increment = index_max / 100;
|
let increment = index_max / 100;
|
||||||
|
|
||||||
@ -73,14 +78,16 @@ fn toggle_plugin(
|
|||||||
.as_bytes()
|
.as_bytes()
|
||||||
.rchunks(3)
|
.rchunks(3)
|
||||||
.rev()
|
.rev()
|
||||||
.map(std::str::from_utf8)
|
.map(core::str::from_utf8)
|
||||||
.collect::<Result<Vec<&str>, _>>()
|
.collect::<Result<Vec<&str>, _>>()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.join(",") // separator
|
.join(",") // separator
|
||||||
};
|
};
|
||||||
|
|
||||||
text.single_mut().0 =
|
text.single_mut()?.0 =
|
||||||
format!("Press Spacebar to toggle: {msg}\nCamera distance to floating origin: {}\nMesh distance from origin: {}", thousands(dist), thousands(DISTANCE))
|
format!("Press Spacebar to toggle: {msg}\nCamera distance to floating origin: {}\nMesh distance from origin: {}", thousands(dist), thousands(DISTANCE));
|
||||||
|
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Component)]
|
#[derive(Component)]
|
||||||
|
|||||||
@ -1,15 +1,14 @@
|
|||||||
//! This example demonstrates error accumulating from parent to children in nested grids.
|
//! This example demonstrates error accumulating from parent to children in nested grids.
|
||||||
use bevy::{math::DVec3, prelude::*};
|
use bevy::{color::palettes, math::DVec3, prelude::*};
|
||||||
use bevy_color::palettes;
|
|
||||||
use big_space::prelude::*;
|
use big_space::prelude::*;
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
App::new()
|
App::new()
|
||||||
.add_plugins((
|
.add_plugins((
|
||||||
DefaultPlugins,
|
DefaultPlugins.build().disable::<TransformPlugin>(),
|
||||||
BigSpacePlugin::default(),
|
BigSpacePlugin::default(),
|
||||||
big_space::camera::CameraControllerPlugin::default(),
|
CameraControllerPlugin::default(),
|
||||||
big_space::debug::FloatingOriginDebugPlugin::default(),
|
FloatingOriginDebugPlugin::default(),
|
||||||
))
|
))
|
||||||
.add_systems(Startup, setup_scene)
|
.add_systems(Startup, setup_scene)
|
||||||
.run();
|
.run();
|
||||||
@ -86,7 +85,7 @@ fn setup_scene(
|
|||||||
..default()
|
..default()
|
||||||
}),
|
}),
|
||||||
FloatingOrigin,
|
FloatingOrigin,
|
||||||
big_space::camera::CameraController::default() // Built-in camera controller
|
CameraController::default() // Built-in camera controller
|
||||||
.with_speed_bounds([10e-18, 10e35])
|
.with_speed_bounds([10e-18, 10e35])
|
||||||
.with_smoothness(0.9, 0.8)
|
.with_smoothness(0.9, 0.8)
|
||||||
.with_speed(1.0),
|
.with_speed(1.0),
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
//! Big spaces are infinite, looping back on themselves smoothly.
|
//! Big spaces are infinite, looping back on themselves smoothly. This example requires the use of
|
||||||
|
//! the `i8` feature, because a small world is needed to be able to see the "edge".
|
||||||
|
|
||||||
use bevy::prelude::*;
|
use bevy::prelude::*;
|
||||||
use big_space::prelude::*;
|
use big_space::prelude::*;
|
||||||
@ -6,10 +7,10 @@ use big_space::prelude::*;
|
|||||||
fn main() {
|
fn main() {
|
||||||
App::new()
|
App::new()
|
||||||
.add_plugins((
|
.add_plugins((
|
||||||
DefaultPlugins,
|
DefaultPlugins.build().disable::<TransformPlugin>(),
|
||||||
BigSpacePlugin::default(),
|
BigSpacePlugin::default(),
|
||||||
FloatingOriginDebugPlugin::default(), // Draws cell AABBs and grids
|
FloatingOriginDebugPlugin::default(),
|
||||||
big_space::camera::CameraControllerPlugin::default(), // Compatible controller
|
CameraControllerPlugin::default(),
|
||||||
))
|
))
|
||||||
.add_systems(Startup, setup_scene)
|
.add_systems(Startup, setup_scene)
|
||||||
.run();
|
.run();
|
||||||
@ -44,7 +45,7 @@ fn setup_scene(
|
|||||||
Camera3d::default(),
|
Camera3d::default(),
|
||||||
Transform::from_xyz(0.0, 0.0, 10.0),
|
Transform::from_xyz(0.0, 0.0, 10.0),
|
||||||
FloatingOrigin,
|
FloatingOrigin,
|
||||||
big_space::camera::CameraController::default()
|
CameraController::default()
|
||||||
.with_speed(10.)
|
.with_speed(10.)
|
||||||
.with_smoothness(0.99, 0.95),
|
.with_smoothness(0.99, 0.95),
|
||||||
));
|
));
|
||||||
|
|||||||
@ -10,10 +10,10 @@ const BIG_DISTANCE: f64 = 1_000_000_000_000_000_000.0;
|
|||||||
fn main() {
|
fn main() {
|
||||||
App::new()
|
App::new()
|
||||||
.add_plugins((
|
.add_plugins((
|
||||||
DefaultPlugins,
|
DefaultPlugins.build().disable::<TransformPlugin>(),
|
||||||
BigSpacePlugin::default(),
|
BigSpacePlugin::default(),
|
||||||
FloatingOriginDebugPlugin::default(), // Draws cell AABBs and grids
|
FloatingOriginDebugPlugin::default(), // Draws cell AABBs and grids
|
||||||
big_space::camera::CameraControllerPlugin::default(), // Compatible controller
|
CameraControllerPlugin::default(), // Compatible controller
|
||||||
))
|
))
|
||||||
.add_systems(Startup, setup_scene)
|
.add_systems(Startup, setup_scene)
|
||||||
.run();
|
.run();
|
||||||
@ -67,7 +67,7 @@ fn setup_scene(
|
|||||||
Transform::from_translation(cell_offset + Vec3::new(0.0, 0.0, 10.0)),
|
Transform::from_translation(cell_offset + Vec3::new(0.0, 0.0, 10.0)),
|
||||||
grid_cell,
|
grid_cell,
|
||||||
FloatingOrigin,
|
FloatingOrigin,
|
||||||
big_space::camera::CameraController::default(),
|
CameraController::default(),
|
||||||
));
|
));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,4 +1,7 @@
|
|||||||
use std::collections::VecDeque;
|
//! A practical example of a spare ship on a planet, in a solar system, surrounded by stars.
|
||||||
|
extern crate alloc;
|
||||||
|
|
||||||
|
use alloc::collections::VecDeque;
|
||||||
|
|
||||||
use bevy::{
|
use bevy::{
|
||||||
color::palettes,
|
color::palettes,
|
||||||
@ -15,14 +18,15 @@ use turborand::{rng::Rng, TurboRand};
|
|||||||
fn main() {
|
fn main() {
|
||||||
App::new()
|
App::new()
|
||||||
.add_plugins((
|
.add_plugins((
|
||||||
DefaultPlugins,
|
DefaultPlugins.build().disable::<TransformPlugin>(),
|
||||||
BigSpacePlugin::new(true),
|
BigSpacePlugin::new(true),
|
||||||
big_space::camera::CameraControllerPlugin::default(),
|
CameraControllerPlugin::default(),
|
||||||
))
|
))
|
||||||
.insert_resource(ClearColor(Color::BLACK))
|
.insert_resource(ClearColor(Color::BLACK))
|
||||||
.insert_resource(AmbientLight {
|
.insert_resource(AmbientLight {
|
||||||
color: Color::WHITE,
|
color: Color::WHITE,
|
||||||
brightness: 200.0,
|
brightness: 200.0,
|
||||||
|
..Default::default()
|
||||||
})
|
})
|
||||||
.add_systems(Startup, spawn_solar_system)
|
.add_systems(Startup, spawn_solar_system)
|
||||||
.add_systems(
|
.add_systems(
|
||||||
@ -69,11 +73,12 @@ fn rotate(mut rotate_query: Query<(&mut Transform, &Rotates)>) {
|
|||||||
fn lighting(
|
fn lighting(
|
||||||
mut light: Query<(&mut Transform, &mut GlobalTransform), With<PrimaryLight>>,
|
mut light: Query<(&mut Transform, &mut GlobalTransform), With<PrimaryLight>>,
|
||||||
sun: Query<&GlobalTransform, (With<Sun>, Without<PrimaryLight>)>,
|
sun: Query<&GlobalTransform, (With<Sun>, Without<PrimaryLight>)>,
|
||||||
) {
|
) -> Result {
|
||||||
let sun_pos = sun.single().translation();
|
let sun_pos = sun.single()?.translation();
|
||||||
let (mut light_tr, mut light_gt) = light.single_mut();
|
let (mut light_tr, mut light_gt) = light.single_mut()?;
|
||||||
light_tr.look_at(-sun_pos, Vec3::Y);
|
light_tr.look_at(-sun_pos, Vec3::Y);
|
||||||
*light_gt = (*light_tr).into();
|
*light_gt = (*light_tr).into();
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn springy_ship(
|
fn springy_ship(
|
||||||
@ -81,7 +86,7 @@ fn springy_ship(
|
|||||||
mut ship: Query<&mut Transform, With<Spaceship>>,
|
mut ship: Query<&mut Transform, With<Spaceship>>,
|
||||||
mut desired_dir: Local<(Vec3, Quat)>,
|
mut desired_dir: Local<(Vec3, Quat)>,
|
||||||
mut smoothed_rot: Local<VecDeque<Vec3>>,
|
mut smoothed_rot: Local<VecDeque<Vec3>>,
|
||||||
) {
|
) -> Result {
|
||||||
desired_dir.0 = DVec3::new(cam_input.right, cam_input.up, -cam_input.forward).as_vec3()
|
desired_dir.0 = DVec3::new(cam_input.right, cam_input.up, -cam_input.forward).as_vec3()
|
||||||
* (1.0 + cam_input.boost as u8 as f32);
|
* (1.0 + cam_input.boost as u8 as f32);
|
||||||
|
|
||||||
@ -89,7 +94,7 @@ fn springy_ship(
|
|||||||
smoothed_rot.push_front(DVec3::new(cam_input.pitch, cam_input.yaw, cam_input.roll).as_vec3());
|
smoothed_rot.push_front(DVec3::new(cam_input.pitch, cam_input.yaw, cam_input.roll).as_vec3());
|
||||||
let avg_rot = smoothed_rot.iter().sum::<Vec3>() / smoothed_rot.len() as f32;
|
let avg_rot = smoothed_rot.iter().sum::<Vec3>() / smoothed_rot.len() as f32;
|
||||||
|
|
||||||
use std::f32::consts::*;
|
use core::f32::consts::*;
|
||||||
desired_dir.1 = Quat::IDENTITY.slerp(
|
desired_dir.1 = Quat::IDENTITY.slerp(
|
||||||
Quat::from_euler(
|
Quat::from_euler(
|
||||||
EulerRot::XYZ,
|
EulerRot::XYZ,
|
||||||
@ -100,12 +105,14 @@ fn springy_ship(
|
|||||||
0.2,
|
0.2,
|
||||||
) * Quat::from_rotation_y(PI);
|
) * Quat::from_rotation_y(PI);
|
||||||
|
|
||||||
ship.single_mut().translation = ship
|
ship.single_mut()?.translation = ship
|
||||||
.single_mut()
|
.single_mut()?
|
||||||
.translation
|
.translation
|
||||||
.lerp(desired_dir.0 * Vec3::new(0.5, 0.5, -2.0), 0.02);
|
.lerp(desired_dir.0 * Vec3::new(0.5, 0.5, -2.0), 0.02);
|
||||||
|
|
||||||
ship.single_mut().rotation = ship.single_mut().rotation.slerp(desired_dir.1, 0.02);
|
ship.single_mut()?.rotation = ship.single_mut()?.rotation.slerp(desired_dir.1, 0.02);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn spawn_solar_system(
|
fn spawn_solar_system(
|
||||||
@ -218,7 +225,7 @@ fn spawn_solar_system(
|
|||||||
camera.insert((
|
camera.insert((
|
||||||
FloatingOrigin,
|
FloatingOrigin,
|
||||||
Transform::from_translation(cam_pos).looking_to(Vec3::NEG_Z, Vec3::X),
|
Transform::from_translation(cam_pos).looking_to(Vec3::NEG_Z, Vec3::X),
|
||||||
big_space::camera::CameraController::default() // Built-in camera controller
|
CameraController::default() // Built-in camera controller
|
||||||
.with_speed_bounds([0.1, 10e35])
|
.with_speed_bounds([0.1, 10e35])
|
||||||
.with_smoothness(0.98, 0.98)
|
.with_smoothness(0.98, 0.98)
|
||||||
.with_speed(1.0),
|
.with_speed(1.0),
|
||||||
@ -244,7 +251,7 @@ fn spawn_solar_system(
|
|||||||
camera.with_child((
|
camera.with_child((
|
||||||
Spaceship,
|
Spaceship,
|
||||||
SceneRoot(asset_server.load("models/low_poly_spaceship/scene.gltf#Scene0")),
|
SceneRoot(asset_server.load("models/low_poly_spaceship/scene.gltf#Scene0")),
|
||||||
Transform::from_rotation(Quat::from_rotation_y(std::f32::consts::PI)),
|
Transform::from_rotation(Quat::from_rotation_y(core::f32::consts::PI)),
|
||||||
));
|
));
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@ -276,10 +283,8 @@ fn cursor_grab_system(
|
|||||||
mut cam: ResMut<big_space::camera::CameraInput>,
|
mut cam: ResMut<big_space::camera::CameraInput>,
|
||||||
btn: Res<ButtonInput<MouseButton>>,
|
btn: Res<ButtonInput<MouseButton>>,
|
||||||
key: Res<ButtonInput<KeyCode>>,
|
key: Res<ButtonInput<KeyCode>>,
|
||||||
) {
|
) -> Result<()> {
|
||||||
let Some(mut window) = windows.get_single_mut().ok() else {
|
let mut window = windows.single_mut()?;
|
||||||
return;
|
|
||||||
};
|
|
||||||
|
|
||||||
if btn.just_pressed(MouseButton::Right) {
|
if btn.just_pressed(MouseButton::Right) {
|
||||||
window.cursor_options.grab_mode = bevy::window::CursorGrabMode::Locked;
|
window.cursor_options.grab_mode = bevy::window::CursorGrabMode::Locked;
|
||||||
@ -294,4 +299,6 @@ fn cursor_grab_system(
|
|||||||
// window.mode = WindowMode::Windowed;
|
// window.mode = WindowMode::Windowed;
|
||||||
cam.defaults_disabled = true;
|
cam.defaults_disabled = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|||||||
@ -9,6 +9,7 @@
|
|||||||
use bevy::prelude::*;
|
use bevy::prelude::*;
|
||||||
use bevy_math::DVec3;
|
use bevy_math::DVec3;
|
||||||
use big_space::prelude::*;
|
use big_space::prelude::*;
|
||||||
|
use tracing::info;
|
||||||
|
|
||||||
const UNIVERSE_DIA: f64 = 8.8e26; // Diameter of the observable universe
|
const UNIVERSE_DIA: f64 = 8.8e26; // Diameter of the observable universe
|
||||||
const PROTON_DIA: f32 = 1.68e-15; // Diameter of a proton
|
const PROTON_DIA: f32 = 1.68e-15; // Diameter of a proton
|
||||||
@ -16,10 +17,10 @@ const PROTON_DIA: f32 = 1.68e-15; // Diameter of a proton
|
|||||||
fn main() {
|
fn main() {
|
||||||
App::new()
|
App::new()
|
||||||
.add_plugins((
|
.add_plugins((
|
||||||
DefaultPlugins,
|
DefaultPlugins.build().disable::<TransformPlugin>(),
|
||||||
BigSpacePlugin::default(),
|
BigSpacePlugin::default(),
|
||||||
FloatingOriginDebugPlugin::default(), // Draws cell AABBs and grids
|
FloatingOriginDebugPlugin::default(), // Draws cell AABBs and grids
|
||||||
big_space::camera::CameraControllerPlugin::default(), // Compatible controller
|
CameraControllerPlugin::default(), // Compatible controller
|
||||||
))
|
))
|
||||||
.add_systems(Startup, setup_scene)
|
.add_systems(Startup, setup_scene)
|
||||||
.add_systems(Update, (bounce_atoms, toggle_cam_pos))
|
.add_systems(Update, (bounce_atoms, toggle_cam_pos))
|
||||||
@ -75,14 +76,14 @@ fn setup_scene(
|
|||||||
Transform::from_xyz(0.0, 0.0, PROTON_DIA * 2.0),
|
Transform::from_xyz(0.0, 0.0, PROTON_DIA * 2.0),
|
||||||
grid_cell,
|
grid_cell,
|
||||||
FloatingOrigin,
|
FloatingOrigin,
|
||||||
big_space::camera::CameraController::default(),
|
CameraController::default(),
|
||||||
));
|
));
|
||||||
|
|
||||||
// A space ship
|
// A space ship
|
||||||
root_grid.spawn_spatial((
|
root_grid.spawn_spatial((
|
||||||
SceneRoot(asset_server.load("models/low_poly_spaceship/scene.gltf#Scene0")),
|
SceneRoot(asset_server.load("models/low_poly_spaceship/scene.gltf#Scene0")),
|
||||||
Transform::from_xyz(0.0, 0.0, 2.5)
|
Transform::from_xyz(0.0, 0.0, 2.5)
|
||||||
.with_rotation(Quat::from_rotation_y(std::f32::consts::PI)),
|
.with_rotation(Quat::from_rotation_y(core::f32::consts::PI)),
|
||||||
grid_cell,
|
grid_cell,
|
||||||
));
|
));
|
||||||
});
|
});
|
||||||
@ -104,12 +105,15 @@ fn toggle_cam_pos(
|
|||||||
grid: Query<&Grid>,
|
grid: Query<&Grid>,
|
||||||
keyboard: Res<ButtonInput<KeyCode>>,
|
keyboard: Res<ButtonInput<KeyCode>>,
|
||||||
protons: Query<&GlobalTransform, With<Proton>>,
|
protons: Query<&GlobalTransform, With<Proton>>,
|
||||||
) {
|
) -> Result {
|
||||||
if !keyboard.just_pressed(KeyCode::KeyT) {
|
if !keyboard.just_pressed(KeyCode::KeyT) {
|
||||||
return;
|
return Ok(());
|
||||||
}
|
}
|
||||||
*cam.single_mut() = if *toggle {
|
*cam.single_mut()? = if *toggle {
|
||||||
grid.single().translation_to_grid(DVec3::X * UNIVERSE_DIA).0
|
grid.single()
|
||||||
|
.unwrap()
|
||||||
|
.translation_to_grid(DVec3::X * UNIVERSE_DIA)
|
||||||
|
.0
|
||||||
} else {
|
} else {
|
||||||
GridCell::ZERO
|
GridCell::ZERO
|
||||||
};
|
};
|
||||||
@ -120,4 +124,5 @@ fn toggle_cam_pos(
|
|||||||
for proton in &protons {
|
for proton in &protons {
|
||||||
info!("Proton x coord: {}", proton.translation().x);
|
info!("Proton x coord: {}", proton.translation().x);
|
||||||
}
|
}
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,14 +1,14 @@
|
|||||||
use std::hash::Hasher;
|
//! Demonstrates the included optional spatial hashing and partitioning of grid cells.
|
||||||
|
|
||||||
use bevy::{
|
use bevy::{
|
||||||
core_pipeline::{bloom::Bloom, fxaa::Fxaa, tonemapping::Tonemapping},
|
core_pipeline::{bloom::Bloom, tonemapping::Tonemapping},
|
||||||
prelude::*,
|
prelude::*,
|
||||||
};
|
};
|
||||||
use bevy_ecs::entity::EntityHasher;
|
use bevy_ecs::{entity::EntityHasher, relationship::Relationship};
|
||||||
use bevy_math::DVec3;
|
use bevy_math::DVec3;
|
||||||
use big_space::prelude::*;
|
use big_space::prelude::*;
|
||||||
|
use core::hash::Hasher;
|
||||||
use noise::{NoiseFn, Simplex};
|
use noise::{NoiseFn, Simplex};
|
||||||
use smallvec::SmallVec;
|
|
||||||
use turborand::prelude::*;
|
use turborand::prelude::*;
|
||||||
|
|
||||||
// Try bumping this up to really stress test. I'm able to push a million entities with an M3 Max.
|
// Try bumping this up to really stress test. I'm able to push a million entities with an M3 Max.
|
||||||
@ -21,11 +21,11 @@ const PERCENT_STATIC: f32 = 0.99;
|
|||||||
fn main() {
|
fn main() {
|
||||||
App::new()
|
App::new()
|
||||||
.add_plugins((
|
.add_plugins((
|
||||||
DefaultPlugins,
|
DefaultPlugins.build().disable::<TransformPlugin>(),
|
||||||
BigSpacePlugin::default(),
|
BigSpacePlugin::default(),
|
||||||
GridHashPlugin::<()>::default(),
|
GridHashPlugin::<()>::default(),
|
||||||
GridPartitionPlugin::<()>::default(),
|
GridPartitionPlugin::<()>::default(),
|
||||||
big_space::camera::CameraControllerPlugin::default(),
|
CameraControllerPlugin::default(),
|
||||||
))
|
))
|
||||||
.add_systems(Startup, (spawn, setup_ui))
|
.add_systems(Startup, (spawn, setup_ui))
|
||||||
.add_systems(
|
.add_systems(
|
||||||
@ -69,7 +69,7 @@ impl FromWorld for MaterialPresets {
|
|||||||
|
|
||||||
let mut meshes = world.resource_mut::<Assets<Mesh>>();
|
let mut meshes = world.resource_mut::<Assets<Mesh>>();
|
||||||
let sphere = meshes.add(
|
let sphere = meshes.add(
|
||||||
Sphere::new(HALF_WIDTH / (1_000_000_f32).powf(0.33) * 0.5)
|
Sphere::new(HALF_WIDTH / 1_000_000_f32.powf(0.33) * 0.5)
|
||||||
.mesh()
|
.mesh()
|
||||||
.ico(0)
|
.ico(0)
|
||||||
.unwrap(),
|
.unwrap(),
|
||||||
@ -89,10 +89,12 @@ fn draw_partitions(
|
|||||||
partitions: Res<GridPartitionMap>,
|
partitions: Res<GridPartitionMap>,
|
||||||
grids: Query<(&GlobalTransform, &Grid)>,
|
grids: Query<(&GlobalTransform, &Grid)>,
|
||||||
camera: Query<&GridHash, With<Camera>>,
|
camera: Query<&GridHash, With<Camera>>,
|
||||||
) {
|
) -> Result {
|
||||||
|
let camera = camera.single()?;
|
||||||
|
|
||||||
for (id, p) in partitions.iter().take(10_000) {
|
for (id, p) in partitions.iter().take(10_000) {
|
||||||
let Ok((transform, grid)) = grids.get(p.grid()) else {
|
let Ok((transform, grid)) = grids.get(p.grid()) else {
|
||||||
return;
|
return Ok(());
|
||||||
};
|
};
|
||||||
let l = grid.cell_edge_length();
|
let l = grid.cell_edge_length();
|
||||||
|
|
||||||
@ -102,7 +104,7 @@ fn draw_partitions(
|
|||||||
let hue = (f % 360) as f32;
|
let hue = (f % 360) as f32;
|
||||||
|
|
||||||
p.iter()
|
p.iter()
|
||||||
.filter(|hash| *hash != camera.single())
|
.filter(|hash| *hash != camera)
|
||||||
.take(1_000)
|
.take(1_000)
|
||||||
.for_each(|h| {
|
.for_each(|h| {
|
||||||
let center = [h.cell().x as i32, h.cell().y as i32, h.cell().z as i32];
|
let center = [h.cell().x as i32, h.cell().y as i32, h.cell().z as i32];
|
||||||
@ -110,7 +112,7 @@ fn draw_partitions(
|
|||||||
.with_scale(Vec3::splat(l));
|
.with_scale(Vec3::splat(l));
|
||||||
gizmos.cuboid(
|
gizmos.cuboid(
|
||||||
transform.mul_transform(local_trans),
|
transform.mul_transform(local_trans),
|
||||||
Hsla::new(hue, 1.0, 0.5, 0.05),
|
Hsla::new(hue, 1.0, 0.5, 0.2),
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -123,19 +125,20 @@ fn draw_partitions(
|
|||||||
|
|
||||||
gizmos.cuboid(
|
gizmos.cuboid(
|
||||||
transform.mul_transform(local_trans),
|
transform.mul_transform(local_trans),
|
||||||
Hsla::new(hue, 1.0, 0.5, 0.2),
|
Hsla::new(hue, 1.0, 0.5, 0.9),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(clippy::too_many_arguments)]
|
#[allow(clippy::too_many_arguments)]
|
||||||
#[allow(clippy::type_complexity)]
|
#[allow(clippy::type_complexity)]
|
||||||
fn move_player(
|
fn move_player(
|
||||||
time: Res<Time>,
|
time: Res<Time>,
|
||||||
mut _gizmos: Gizmos,
|
mut player: Query<(&mut Transform, &mut GridCell, &ChildOf, &GridHash), With<Player>>,
|
||||||
mut player: Query<(&mut Transform, &mut GridCell, &Parent, &GridHash), With<Player>>,
|
|
||||||
mut non_player: Query<
|
mut non_player: Query<
|
||||||
(&mut Transform, &mut GridCell, &Parent),
|
(&mut Transform, &mut GridCell, &ChildOf),
|
||||||
(Without<Player>, With<NonPlayer>),
|
(Without<Player>, With<NonPlayer>),
|
||||||
>,
|
>,
|
||||||
mut materials: Query<&mut MeshMaterial3d<StandardMaterial>, Without<Player>>,
|
mut materials: Query<&mut MeshMaterial3d<StandardMaterial>, Without<Player>>,
|
||||||
@ -146,11 +149,11 @@ fn move_player(
|
|||||||
mut text: Query<&mut Text>,
|
mut text: Query<&mut Text>,
|
||||||
hash_stats: Res<big_space::timing::SmoothedStat<big_space::timing::GridHashStats>>,
|
hash_stats: Res<big_space::timing::SmoothedStat<big_space::timing::GridHashStats>>,
|
||||||
prop_stats: Res<big_space::timing::SmoothedStat<big_space::timing::PropagationStats>>,
|
prop_stats: Res<big_space::timing::SmoothedStat<big_space::timing::PropagationStats>>,
|
||||||
) {
|
) -> Result {
|
||||||
let n_entities = non_player.iter().len();
|
let n_entities = non_player.iter().len();
|
||||||
for neighbor in neighbors.iter() {
|
for neighbor in neighbors.iter() {
|
||||||
if let Ok(mut material) = materials.get_mut(*neighbor) {
|
if let Ok(mut material) = materials.get_mut(*neighbor) {
|
||||||
**material = material_presets.default.clone_weak();
|
material.set_if_neq(material_presets.default.clone_weak().into());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -170,14 +173,13 @@ fn move_player(
|
|||||||
}
|
}
|
||||||
|
|
||||||
let t = time.elapsed_secs() * 0.01;
|
let t = time.elapsed_secs() * 0.01;
|
||||||
let (mut transform, mut cell, parent, hash) = player.single_mut();
|
let (mut transform, mut cell, parent, hash) = player.single_mut()?;
|
||||||
let absolute_pos = HALF_WIDTH
|
let absolute_pos = HALF_WIDTH
|
||||||
* CELL_WIDTH
|
* CELL_WIDTH
|
||||||
* 0.8
|
* 0.8
|
||||||
* Vec3::new((5.0 * t).sin(), (7.0 * t).cos(), (20.0 * t).sin());
|
* Vec3::new((5.0 * t).sin(), (7.0 * t).cos(), (20.0 * t).sin());
|
||||||
(*cell, transform.translation) = grids
|
(*cell, transform.translation) = grids
|
||||||
.get(parent.get())
|
.get(parent.get())?
|
||||||
.unwrap()
|
|
||||||
.imprecise_translation_to_grid(absolute_pos);
|
.imprecise_translation_to_grid(absolute_pos);
|
||||||
|
|
||||||
neighbors.clear();
|
neighbors.clear();
|
||||||
@ -185,15 +187,8 @@ fn move_player(
|
|||||||
hash_grid.flood(hash, None).entities().for_each(|entity| {
|
hash_grid.flood(hash, None).entities().for_each(|entity| {
|
||||||
neighbors.push(entity);
|
neighbors.push(entity);
|
||||||
if let Ok(mut material) = materials.get_mut(entity) {
|
if let Ok(mut material) = materials.get_mut(entity) {
|
||||||
**material = material_presets.flood.clone_weak();
|
material.set_if_neq(material_presets.flood.clone_weak().into());
|
||||||
}
|
}
|
||||||
|
|
||||||
// let grid = grid.get(entry.grid).unwrap();
|
|
||||||
// let transform = grid.global_transform(
|
|
||||||
// &entry.cell,
|
|
||||||
// &Transform::from_scale(Vec3::splat(grid.cell_edge_length() * 0.99)),
|
|
||||||
// );
|
|
||||||
// gizmos.cuboid(transform, Color::linear_rgba(1.0, 1.0, 1.0, 0.2));
|
|
||||||
});
|
});
|
||||||
|
|
||||||
hash_grid
|
hash_grid
|
||||||
@ -204,11 +199,11 @@ fn move_player(
|
|||||||
.for_each(|entity| {
|
.for_each(|entity| {
|
||||||
neighbors.push(entity);
|
neighbors.push(entity);
|
||||||
if let Ok(mut material) = materials.get_mut(entity) {
|
if let Ok(mut material) = materials.get_mut(entity) {
|
||||||
**material = material_presets.highlight.clone_weak();
|
material.set_if_neq(material_presets.highlight.clone_weak().into());
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
let mut text = text.single_mut();
|
let mut text = text.single_mut()?;
|
||||||
text.0 = format!(
|
text.0 = format!(
|
||||||
"\
|
"\
|
||||||
Controls:
|
Controls:
|
||||||
@ -236,9 +231,8 @@ Total: {: >22.1?}",
|
|||||||
.as_bytes()
|
.as_bytes()
|
||||||
.rchunks(3)
|
.rchunks(3)
|
||||||
.rev()
|
.rev()
|
||||||
.map(std::str::from_utf8)
|
.map(core::str::from_utf8)
|
||||||
.collect::<Result<Vec<&str>, _>>()
|
.collect::<Result<Vec<&str>, _>>()?
|
||||||
.unwrap()
|
|
||||||
.join(","),
|
.join(","),
|
||||||
//
|
//
|
||||||
prop_stats.avg().grid_recentering(),
|
prop_stats.avg().grid_recentering(),
|
||||||
@ -254,6 +248,8 @@ Total: {: >22.1?}",
|
|||||||
//
|
//
|
||||||
prop_stats.avg().total() + hash_stats.avg().total(),
|
prop_stats.avg().total() + hash_stats.avg().total(),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn spawn(mut commands: Commands) {
|
fn spawn(mut commands: Commands) {
|
||||||
@ -267,11 +263,10 @@ fn spawn(mut commands: Commands) {
|
|||||||
},
|
},
|
||||||
Tonemapping::AcesFitted,
|
Tonemapping::AcesFitted,
|
||||||
Transform::from_xyz(0.0, 0.0, HALF_WIDTH * CELL_WIDTH * 2.0),
|
Transform::from_xyz(0.0, 0.0, HALF_WIDTH * CELL_WIDTH * 2.0),
|
||||||
big_space::camera::CameraController::default()
|
CameraController::default()
|
||||||
.with_smoothness(0.98, 0.93)
|
.with_smoothness(0.98, 0.93)
|
||||||
.with_slowing(false)
|
.with_slowing(false)
|
||||||
.with_speed(15.0),
|
.with_speed(15.0),
|
||||||
Fxaa::default(),
|
|
||||||
Bloom::default(),
|
Bloom::default(),
|
||||||
GridCell::new(0, 0, HALF_WIDTH as GridPrecision / 2),
|
GridCell::new(0, 0, HALF_WIDTH as GridPrecision / 2),
|
||||||
))
|
))
|
||||||
@ -287,54 +282,41 @@ fn spawn_spheres(
|
|||||||
mut commands: Commands,
|
mut commands: Commands,
|
||||||
input: Res<ButtonInput<KeyCode>>,
|
input: Res<ButtonInput<KeyCode>>,
|
||||||
material_presets: Res<MaterialPresets>,
|
material_presets: Res<MaterialPresets>,
|
||||||
mut grid: Query<(Entity, &Grid, &mut Children)>,
|
grid: Query<Entity, With<Grid>>,
|
||||||
non_players: Query<(), With<NonPlayer>>,
|
non_players: Query<(), With<NonPlayer>>,
|
||||||
) {
|
) -> Result {
|
||||||
let n_entities = non_players.iter().len().max(1);
|
let n_entities = non_players.iter().len().max(1);
|
||||||
let n_spawn = if input.pressed(KeyCode::KeyG) {
|
let n_spawn = if input.pressed(KeyCode::KeyG) {
|
||||||
n_entities
|
n_entities
|
||||||
} else if input.pressed(KeyCode::KeyF) {
|
} else if input.pressed(KeyCode::KeyF) {
|
||||||
1_000
|
1_000
|
||||||
} else {
|
} else {
|
||||||
return;
|
return Ok(());
|
||||||
};
|
};
|
||||||
|
|
||||||
let (entity, _grid, mut children) = grid.single_mut();
|
let entity = grid.single()?;
|
||||||
let mut dyn_parent = bevy_reflect::DynamicTupleStruct::default();
|
commands.entity(entity).with_children(|builder| {
|
||||||
dyn_parent.insert(entity);
|
for value in sample_noise(n_spawn, &Simplex::new(345612), &Rng::new()) {
|
||||||
let dyn_parent = dyn_parent.as_partial_reflect();
|
|
||||||
|
|
||||||
let new_children = sample_noise(n_spawn, &Simplex::new(345612), &Rng::new())
|
|
||||||
.map(|value| {
|
|
||||||
let hash = GridHash::__new_manual(entity, &GridCell::default());
|
let hash = GridHash::__new_manual(entity, &GridCell::default());
|
||||||
commands
|
builder.spawn((
|
||||||
.spawn((
|
Transform::from_xyz(value.x, value.y, value.z),
|
||||||
Transform::from_xyz(value.x, value.y, value.z),
|
GlobalTransform::default(),
|
||||||
GlobalTransform::default(),
|
GridCell::default(),
|
||||||
GridCell::default(),
|
FastGridHash::from(hash),
|
||||||
FastGridHash::from(hash),
|
hash,
|
||||||
hash,
|
NonPlayer,
|
||||||
NonPlayer,
|
Mesh3d(material_presets.sphere.clone_weak()),
|
||||||
Parent::from_reflect(dyn_parent).unwrap(),
|
MeshMaterial3d(material_presets.default.clone_weak()),
|
||||||
Mesh3d(material_presets.sphere.clone_weak()),
|
bevy_render::view::VisibilityRange {
|
||||||
MeshMaterial3d(material_presets.default.clone_weak()),
|
start_margin: 1.0..5.0,
|
||||||
bevy_render::view::VisibilityRange {
|
end_margin: HALF_WIDTH * CELL_WIDTH * 0.5..HALF_WIDTH * CELL_WIDTH * 0.8,
|
||||||
start_margin: 1.0..5.0,
|
use_aabb: false,
|
||||||
end_margin: HALF_WIDTH * CELL_WIDTH * 0.5..HALF_WIDTH * CELL_WIDTH * 0.8,
|
},
|
||||||
use_aabb: false,
|
bevy_render::view::NoFrustumCulling,
|
||||||
},
|
));
|
||||||
bevy_render::view::NoFrustumCulling,
|
}
|
||||||
))
|
});
|
||||||
.id()
|
Ok(())
|
||||||
})
|
|
||||||
.chain(children.iter().copied())
|
|
||||||
.collect::<SmallVec<[Entity; 8]>>();
|
|
||||||
|
|
||||||
let mut dyn_children = bevy_reflect::DynamicTupleStruct::default();
|
|
||||||
dyn_children.insert(new_children);
|
|
||||||
let dyn_children = dyn_children.as_partial_reflect();
|
|
||||||
|
|
||||||
*children = Children::from_reflect(dyn_children).unwrap();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
@ -343,7 +325,7 @@ fn sample_noise<'a, T: NoiseFn<f64, 3>>(
|
|||||||
noise: &'a T,
|
noise: &'a T,
|
||||||
rng: &'a Rng,
|
rng: &'a Rng,
|
||||||
) -> impl Iterator<Item = Vec3> + use<'a, T> {
|
) -> impl Iterator<Item = Vec3> + use<'a, T> {
|
||||||
std::iter::repeat_with(
|
core::iter::repeat_with(
|
||||||
|| loop {
|
|| loop {
|
||||||
let noise_scale = 0.05 * HALF_WIDTH as f64;
|
let noise_scale = 0.05 * HALF_WIDTH as f64;
|
||||||
let threshold = 0.50;
|
let threshold = 0.50;
|
||||||
@ -390,8 +372,8 @@ fn cursor_grab(
|
|||||||
keyboard: Res<ButtonInput<KeyCode>>,
|
keyboard: Res<ButtonInput<KeyCode>>,
|
||||||
mouse: Res<ButtonInput<MouseButton>>,
|
mouse: Res<ButtonInput<MouseButton>>,
|
||||||
mut windows: Query<&mut Window, With<bevy::window::PrimaryWindow>>,
|
mut windows: Query<&mut Window, With<bevy::window::PrimaryWindow>>,
|
||||||
) {
|
) -> Result {
|
||||||
let mut primary_window = windows.single_mut();
|
let mut primary_window = windows.single_mut()?;
|
||||||
if mouse.just_pressed(MouseButton::Left) {
|
if mouse.just_pressed(MouseButton::Left) {
|
||||||
primary_window.cursor_options.grab_mode = bevy::window::CursorGrabMode::Locked;
|
primary_window.cursor_options.grab_mode = bevy::window::CursorGrabMode::Locked;
|
||||||
primary_window.cursor_options.visible = false;
|
primary_window.cursor_options.visible = false;
|
||||||
@ -400,4 +382,5 @@ fn cursor_grab(
|
|||||||
primary_window.cursor_options.grab_mode = bevy::window::CursorGrabMode::None;
|
primary_window.cursor_options.grab_mode = bevy::window::CursorGrabMode::None;
|
||||||
primary_window.cursor_options.visible = true;
|
primary_window.cursor_options.visible = true;
|
||||||
}
|
}
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,24 +1,24 @@
|
|||||||
//! Demonstrates how a single bevy world can contain multiple big_space hierarchies, each rendered
|
//! Demonstrates how a single bevy world can contain multiple `big_space` hierarchies, each rendered
|
||||||
//! relative to a floating origin inside that big space.
|
//! relative to a floating origin inside that big space.
|
||||||
//!
|
//!
|
||||||
//! This takes the simplest approach, of simply duplicating the worlds and players for each split
|
//! This takes the simplest approach, of simply duplicating the worlds and players for each split
|
||||||
//! screen, and synchronizing the player locations between both.
|
//! screen, and synchronizing the player locations between both.
|
||||||
|
|
||||||
use bevy::{
|
use bevy::{
|
||||||
|
color::palettes,
|
||||||
prelude::*,
|
prelude::*,
|
||||||
render::{camera::Viewport, view::RenderLayers},
|
render::{camera::Viewport, view::RenderLayers},
|
||||||
transform::TransformSystem,
|
transform::TransformSystem,
|
||||||
};
|
};
|
||||||
use bevy_color::palettes;
|
|
||||||
use big_space::prelude::*;
|
use big_space::prelude::*;
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
App::new()
|
App::new()
|
||||||
.add_plugins((
|
.add_plugins((
|
||||||
DefaultPlugins,
|
DefaultPlugins.build().disable::<TransformPlugin>(),
|
||||||
BigSpacePlugin::default(),
|
BigSpacePlugin::default(),
|
||||||
FloatingOriginDebugPlugin::default(),
|
FloatingOriginDebugPlugin::default(),
|
||||||
big_space::camera::CameraControllerPlugin::default(),
|
CameraControllerPlugin::default(),
|
||||||
))
|
))
|
||||||
.add_systems(Startup, setup)
|
.add_systems(Startup, setup)
|
||||||
.add_systems(Update, set_camera_viewports)
|
.add_systems(Update, set_camera_viewports)
|
||||||
@ -60,7 +60,7 @@ fn setup(
|
|||||||
Camera3d::default(),
|
Camera3d::default(),
|
||||||
Transform::from_xyz(1_000_000.0 - 10.0, 100_005.0, 0.0)
|
Transform::from_xyz(1_000_000.0 - 10.0, 100_005.0, 0.0)
|
||||||
.looking_to(Vec3::NEG_X, Vec3::Y),
|
.looking_to(Vec3::NEG_X, Vec3::Y),
|
||||||
big_space::camera::CameraController::default().with_smoothness(0.8, 0.8),
|
CameraController::default().with_smoothness(0.8, 0.8),
|
||||||
RenderLayers::layer(2),
|
RenderLayers::layer(2),
|
||||||
LeftCamera,
|
LeftCamera,
|
||||||
FloatingOrigin,
|
FloatingOrigin,
|
||||||
@ -182,12 +182,14 @@ fn update_cameras(
|
|||||||
Without<RightCamera>,
|
Without<RightCamera>,
|
||||||
),
|
),
|
||||||
>,
|
>,
|
||||||
) {
|
) -> Result {
|
||||||
*left_rep.single_mut().cell = *left.single().cell;
|
*left_rep.single_mut()?.cell = *left.single()?.cell;
|
||||||
*left_rep.single_mut().transform = *left.single().transform;
|
*left_rep.single_mut()?.transform = *left.single()?.transform;
|
||||||
|
|
||||||
*right_rep.single_mut().cell = *right.single().cell;
|
*right_rep.single_mut()?.cell = *right.single()?.cell;
|
||||||
*right_rep.single_mut().transform = *right.single().transform;
|
*right_rep.single_mut()?.transform = *right.single()?.transform;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_camera_viewports(
|
fn set_camera_viewports(
|
||||||
@ -195,13 +197,13 @@ fn set_camera_viewports(
|
|||||||
mut resize_events: EventReader<bevy::window::WindowResized>,
|
mut resize_events: EventReader<bevy::window::WindowResized>,
|
||||||
mut left_camera: Query<&mut Camera, (With<LeftCamera>, Without<RightCamera>)>,
|
mut left_camera: Query<&mut Camera, (With<LeftCamera>, Without<RightCamera>)>,
|
||||||
mut right_camera: Query<&mut Camera, With<RightCamera>>,
|
mut right_camera: Query<&mut Camera, With<RightCamera>>,
|
||||||
) {
|
) -> Result {
|
||||||
// We need to dynamically resize the camera's viewports whenever the window size changes
|
// We need to dynamically resize the camera's viewports whenever the window size changes
|
||||||
// so then each camera always takes up half the screen.
|
// so then each camera always takes up half the screen.
|
||||||
// A resize_event is sent when the window is first created, allowing us to reuse this system for initial setup.
|
// A resize_event is sent when the window is first created, allowing us to reuse this system for initial setup.
|
||||||
for resize_event in resize_events.read() {
|
for resize_event in resize_events.read() {
|
||||||
let window = windows.get(resize_event.window).unwrap();
|
let window = windows.get(resize_event.window)?;
|
||||||
let mut left_camera = left_camera.single_mut();
|
let mut left_camera = left_camera.single_mut()?;
|
||||||
left_camera.viewport = Some(Viewport {
|
left_camera.viewport = Some(Viewport {
|
||||||
physical_position: UVec2::new(0, 0),
|
physical_position: UVec2::new(0, 0),
|
||||||
physical_size: UVec2::new(
|
physical_size: UVec2::new(
|
||||||
@ -211,7 +213,7 @@ fn set_camera_viewports(
|
|||||||
..default()
|
..default()
|
||||||
});
|
});
|
||||||
|
|
||||||
let mut right_camera = right_camera.single_mut();
|
let mut right_camera = right_camera.single_mut()?;
|
||||||
right_camera.viewport = Some(Viewport {
|
right_camera.viewport = Some(Viewport {
|
||||||
physical_position: UVec2::new(window.resolution.physical_width() / 2, 0),
|
physical_position: UVec2::new(window.resolution.physical_width() / 2, 0),
|
||||||
physical_size: UVec2::new(
|
physical_size: UVec2::new(
|
||||||
@ -221,4 +223,6 @@ fn set_camera_viewports(
|
|||||||
..default()
|
..default()
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
//! Component bundles for big_space.
|
//! Component bundles for `big_space`.
|
||||||
|
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
use bevy_ecs::prelude::*;
|
use bevy_ecs::prelude::*;
|
||||||
|
|||||||
@ -3,9 +3,10 @@
|
|||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
use bevy_app::prelude::*;
|
use bevy_app::prelude::*;
|
||||||
use bevy_ecs::prelude::*;
|
use bevy_ecs::prelude::*;
|
||||||
use bevy_hierarchy::prelude::*;
|
|
||||||
use bevy_input::{mouse::MouseMotion, prelude::*};
|
use bevy_input::{mouse::MouseMotion, prelude::*};
|
||||||
use bevy_math::{prelude::*, DQuat, DVec3};
|
use bevy_math::{prelude::*, DQuat, DVec3};
|
||||||
|
use bevy_platform_support::collections::HashSet;
|
||||||
|
use bevy_platform_support::prelude::*;
|
||||||
use bevy_reflect::prelude::*;
|
use bevy_reflect::prelude::*;
|
||||||
use bevy_render::{
|
use bevy_render::{
|
||||||
primitives::Aabb,
|
primitives::Aabb,
|
||||||
@ -13,7 +14,6 @@ use bevy_render::{
|
|||||||
};
|
};
|
||||||
use bevy_time::prelude::*;
|
use bevy_time::prelude::*;
|
||||||
use bevy_transform::{prelude::*, TransformSystem};
|
use bevy_transform::{prelude::*, TransformSystem};
|
||||||
use bevy_utils::HashSet;
|
|
||||||
|
|
||||||
/// Adds the `big_space` camera controller
|
/// Adds the `big_space` camera controller
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
@ -131,7 +131,7 @@ impl Default for CameraController {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// ButtonInput state used to command camera motion. Reset every time the values are read to update
|
/// `ButtonInput` state used to command camera motion. Reset every time the values are read to update
|
||||||
/// the camera. Allows you to map any input to camera motions. Uses aircraft principle axes
|
/// the camera. Allows you to map any input to camera motions. Uses aircraft principle axes
|
||||||
/// conventions.
|
/// conventions.
|
||||||
#[derive(Clone, Debug, Default, Reflect, Resource)]
|
#[derive(Clone, Debug, Default, Reflect, Resource)]
|
||||||
@ -226,7 +226,7 @@ pub fn nearest_objects_in_grid(
|
|||||||
)>,
|
)>,
|
||||||
children: Query<&Children>,
|
children: Query<&Children>,
|
||||||
) {
|
) {
|
||||||
let Ok((cam_entity, mut camera, cam_pos, cam_layer)) = camera.get_single_mut() else {
|
let Ok((cam_entity, mut camera, cam_pos, cam_layer)) = camera.single_mut() else {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
if !camera.slow_near_objects {
|
if !camera.slow_near_objects {
|
||||||
@ -260,7 +260,7 @@ pub fn nearest_objects_in_grid(
|
|||||||
/// Uses [`CameraInput`] state to update the camera position.
|
/// Uses [`CameraInput`] state to update the camera position.
|
||||||
pub fn camera_controller(
|
pub fn camera_controller(
|
||||||
time: Res<Time>,
|
time: Res<Time>,
|
||||||
grids: crate::grid::local_origin::Grids,
|
grids: Grids,
|
||||||
mut input: ResMut<CameraInput>,
|
mut input: ResMut<CameraInput>,
|
||||||
mut camera: Query<(Entity, &mut GridCell, &mut Transform, &mut CameraController)>,
|
mut camera: Query<(Entity, &mut GridCell, &mut Transform, &mut CameraController)>,
|
||||||
) {
|
) {
|
||||||
|
|||||||
@ -1,8 +1,7 @@
|
|||||||
//! Adds `big_space`-specific commands to bevy's `Commands`.
|
//! Adds `big_space`-specific commands to bevy's `Commands`.
|
||||||
|
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
use bevy_ecs::prelude::*;
|
use bevy_ecs::{prelude::*, relationship::RelatedSpawnerCommands};
|
||||||
use bevy_hierarchy::prelude::*;
|
|
||||||
use bevy_transform::prelude::*;
|
use bevy_transform::prelude::*;
|
||||||
use smallvec::SmallVec;
|
use smallvec::SmallVec;
|
||||||
|
|
||||||
@ -177,7 +176,7 @@ impl Drop for GridCommands<'_> {
|
|||||||
let entity = self.entity;
|
let entity = self.entity;
|
||||||
self.commands
|
self.commands
|
||||||
.entity(entity)
|
.entity(entity)
|
||||||
.insert(std::mem::take(&mut self.grid))
|
.insert(core::mem::take(&mut self.grid))
|
||||||
.add_children(&self.children);
|
.add_children(&self.children);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -189,13 +188,13 @@ pub struct SpatialEntityCommands<'a> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> SpatialEntityCommands<'a> {
|
impl<'a> SpatialEntityCommands<'a> {
|
||||||
/// Insert a component on this grid
|
/// Insert a component into this grid.
|
||||||
pub fn insert(&mut self, bundle: impl Bundle) -> &mut Self {
|
pub fn insert(&mut self, bundle: impl Bundle) -> &mut Self {
|
||||||
self.commands.entity(self.entity).insert(bundle);
|
self.commands.entity(self.entity).insert(bundle);
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Removes a `Bundle`` of components from the entity.
|
/// Removes a `Bundle` of components from the entity.
|
||||||
pub fn remove<T>(&mut self) -> &mut Self
|
pub fn remove<T>(&mut self) -> &mut Self
|
||||||
where
|
where
|
||||||
T: Bundle,
|
T: Bundle,
|
||||||
@ -204,8 +203,11 @@ impl<'a> SpatialEntityCommands<'a> {
|
|||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Takes a closure which provides a [`ChildBuilder`].
|
/// Spawns children of this entity (with a [`ChildOf`] relationship) by taking a function that operates on a [`ChildSpawner`].
|
||||||
pub fn with_children(&mut self, spawn_children: impl FnOnce(&mut ChildBuilder)) -> &mut Self {
|
pub fn with_children(
|
||||||
|
&mut self,
|
||||||
|
spawn_children: impl FnOnce(&mut RelatedSpawnerCommands<'_, ChildOf>),
|
||||||
|
) -> &mut Self {
|
||||||
self.commands
|
self.commands
|
||||||
.entity(self.entity)
|
.entity(self.entity)
|
||||||
.with_children(|child_builder| spawn_children(child_builder));
|
.with_children(|child_builder| spawn_children(child_builder));
|
||||||
@ -213,12 +215,16 @@ impl<'a> SpatialEntityCommands<'a> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Spawns the passed bundle and adds it to this entity as a child.
|
/// Spawns the passed bundle and adds it to this entity as a child.
|
||||||
|
///
|
||||||
|
/// For efficient spawning of multiple children, use [`with_children`].
|
||||||
|
///
|
||||||
|
/// [`with_children`]: SpatialEntityCommands::with_children
|
||||||
pub fn with_child<B: Bundle>(&mut self, bundle: B) -> &mut Self {
|
pub fn with_child<B: Bundle>(&mut self, bundle: B) -> &mut Self {
|
||||||
self.commands.entity(self.entity).with_child(bundle);
|
self.commands.entity(self.entity).with_child(bundle);
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the [`Entity``] id of the entity.
|
/// Returns the [`Entity`] id of the entity.
|
||||||
pub fn id(&self) -> Entity {
|
pub fn id(&self) -> Entity {
|
||||||
self.entity
|
self.entity
|
||||||
}
|
}
|
||||||
|
|||||||
10
src/debug.rs
10
src/debug.rs
@ -20,16 +20,16 @@ impl Plugin for FloatingOriginDebugPlugin {
|
|||||||
PostUpdate,
|
PostUpdate,
|
||||||
(update_debug_bounds, update_grid_axes)
|
(update_debug_bounds, update_grid_axes)
|
||||||
.chain()
|
.chain()
|
||||||
.after(bevy_transform::TransformSystem::TransformPropagate),
|
.after(TransformSystem::TransformPropagate),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn setup_gizmos(mut store: ResMut<GizmoConfigStore>) {
|
fn setup_gizmos(mut store: ResMut<GizmoConfigStore>) {
|
||||||
let (config, _) = store.config_mut::<BigSpaceGizmoConfig>();
|
let (config, _) = store.config_mut::<BigSpaceGizmoConfig>();
|
||||||
config.line_perspective = false;
|
config.line.perspective = false;
|
||||||
config.line_joints = GizmoLineJoint::Round(4);
|
config.line.joints = GizmoLineJoint::Round(4);
|
||||||
config.line_width = 1.0;
|
config.line.width = 1.0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Update the rendered debug bounds to only highlight occupied [`GridCell`]s.
|
/// Update the rendered debug bounds to only highlight occupied [`GridCell`]s.
|
||||||
@ -47,7 +47,7 @@ fn update_debug_bounds(
|
|||||||
&Transform::from_scale(Vec3::splat(grid.cell_edge_length() * 0.999)),
|
&Transform::from_scale(Vec3::splat(grid.cell_edge_length() * 0.999)),
|
||||||
);
|
);
|
||||||
if origin.is_none() {
|
if origin.is_none() {
|
||||||
gizmos.cuboid(transform, Color::linear_rgb(0.0, 1.0, 0.0))
|
gizmos.cuboid(transform, Color::linear_rgb(0.0, 1.0, 0.0));
|
||||||
} else {
|
} else {
|
||||||
// gizmos.cuboid(transform, Color::rgba(0.0, 0.0, 1.0, 0.5))
|
// gizmos.cuboid(transform, Color::rgba(0.0, 0.0, 1.0, 0.5))
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,9 +1,8 @@
|
|||||||
//! A floating origin for camera-relative rendering, to maximize precision when converting to f32.
|
//! A floating origin for camera-relative rendering, to maximize precision when converting to f32.
|
||||||
|
|
||||||
use bevy_ecs::prelude::*;
|
use bevy_ecs::prelude::*;
|
||||||
use bevy_hierarchy::prelude::*;
|
use bevy_platform_support::collections::HashMap;
|
||||||
use bevy_reflect::prelude::*;
|
use bevy_reflect::prelude::*;
|
||||||
use bevy_utils::HashMap;
|
|
||||||
|
|
||||||
/// Marks the entity to use as the floating origin.
|
/// Marks the entity to use as the floating origin.
|
||||||
///
|
///
|
||||||
@ -45,13 +44,13 @@ pub struct BigSpace {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl BigSpace {
|
impl BigSpace {
|
||||||
/// Return the this grid's floating origin if it exists and is a descendent of this root.
|
/// Return this grid's floating origin if it exists and is a descendant of this root.
|
||||||
///
|
///
|
||||||
/// `this_entity`: the entity this component belongs to.
|
/// `this_entity`: the entity this component belongs to.
|
||||||
pub(crate) fn validate_floating_origin(
|
pub(crate) fn validate_floating_origin(
|
||||||
&self,
|
&self,
|
||||||
this_entity: Entity,
|
this_entity: Entity,
|
||||||
parents: &Query<&Parent>,
|
parents: &Query<&ChildOf>,
|
||||||
) -> Option<Entity> {
|
) -> Option<Entity> {
|
||||||
let floating_origin = self.floating_origin?;
|
let floating_origin = self.floating_origin?;
|
||||||
let origin_root_entity = parents.iter_ancestors(floating_origin).last()?;
|
let origin_root_entity = parents.iter_ancestors(floating_origin).last()?;
|
||||||
@ -63,10 +62,10 @@ impl BigSpace {
|
|||||||
/// `BigSpace` hierarchy.
|
/// `BigSpace` hierarchy.
|
||||||
pub fn find_floating_origin(
|
pub fn find_floating_origin(
|
||||||
floating_origins: Query<Entity, With<FloatingOrigin>>,
|
floating_origins: Query<Entity, With<FloatingOrigin>>,
|
||||||
parent_query: Query<&Parent>,
|
parent_query: Query<&ChildOf>,
|
||||||
mut big_spaces: Query<(Entity, &mut BigSpace)>,
|
mut big_spaces: Query<(Entity, &mut BigSpace)>,
|
||||||
) {
|
) {
|
||||||
let mut spaces_set = HashMap::new();
|
let mut spaces_set = HashMap::<_, _>::default();
|
||||||
// Reset all floating origin fields, so we know if any are missing.
|
// Reset all floating origin fields, so we know if any are missing.
|
||||||
for (entity, mut space) in &mut big_spaces {
|
for (entity, mut space) in &mut big_spaces {
|
||||||
space.floating_origin = None;
|
space.floating_origin = None;
|
||||||
@ -86,7 +85,7 @@ impl BigSpace {
|
|||||||
tracing::error!(
|
tracing::error!(
|
||||||
"BigSpace {root:#?} has multiple floating origins. There must be exactly one. Resetting this big space and disabling the floating origin to avoid unexpected propagation behavior.",
|
"BigSpace {root:#?} has multiple floating origins. There must be exactly one. Resetting this big space and disabling the floating origin to avoid unexpected propagation behavior.",
|
||||||
);
|
);
|
||||||
space.floating_origin = None
|
space.floating_origin = None;
|
||||||
} else {
|
} else {
|
||||||
space.floating_origin = Some(origin);
|
space.floating_origin = Some(origin);
|
||||||
}
|
}
|
||||||
@ -99,7 +98,7 @@ impl BigSpace {
|
|||||||
.filter(|(_k, v)| **v == 0)
|
.filter(|(_k, v)| **v == 0)
|
||||||
.map(|(k, _v)| k)
|
.map(|(k, _v)| k)
|
||||||
{
|
{
|
||||||
tracing::error!("BigSpace {space:#} has no floating origins. There must be exactly one. Transform propagation will not work until there is a FloatingOrigin in the hierarchy.",)
|
tracing::error!("BigSpace {space:#} has no floating origins. There must be exactly one. Transform propagation will not work until there is a FloatingOrigin in the hierarchy.",);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,12 +1,11 @@
|
|||||||
//! Contains the grid cell implementation
|
//! Contains the grid cell implementation
|
||||||
|
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
use bevy_ecs::prelude::*;
|
use bevy_ecs::{prelude::*, relationship::Relationship};
|
||||||
use bevy_hierarchy::prelude::*;
|
|
||||||
use bevy_math::{DVec3, IVec3};
|
use bevy_math::{DVec3, IVec3};
|
||||||
|
use bevy_platform_support::time::Instant;
|
||||||
use bevy_reflect::prelude::*;
|
use bevy_reflect::prelude::*;
|
||||||
use bevy_transform::prelude::*;
|
use bevy_transform::prelude::*;
|
||||||
use bevy_utils::Instant;
|
|
||||||
|
|
||||||
/// Locates an entity in a cell within its parent's [`Grid`]. The [`Transform`] of an entity with
|
/// Locates an entity in a cell within its parent's [`Grid`]. The [`Transform`] of an entity with
|
||||||
/// this component is a transformation from the center of this cell.
|
/// this component is a transformation from the center of this cell.
|
||||||
@ -84,7 +83,7 @@ impl GridCell {
|
|||||||
pub fn recenter_large_transforms(
|
pub fn recenter_large_transforms(
|
||||||
mut stats: ResMut<crate::timing::PropagationStats>,
|
mut stats: ResMut<crate::timing::PropagationStats>,
|
||||||
grids: Query<&Grid>,
|
grids: Query<&Grid>,
|
||||||
mut changed_transform: Query<(&mut Self, &mut Transform, &Parent), Changed<Transform>>,
|
mut changed_transform: Query<(&mut Self, &mut Transform, &ChildOf), Changed<Transform>>,
|
||||||
) {
|
) {
|
||||||
let start = Instant::now();
|
let start = Instant::now();
|
||||||
changed_transform
|
changed_transform
|
||||||
@ -111,7 +110,7 @@ impl GridCell {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl std::ops::Add for GridCell {
|
impl core::ops::Add for GridCell {
|
||||||
type Output = GridCell;
|
type Output = GridCell;
|
||||||
|
|
||||||
fn add(self, rhs: Self) -> Self::Output {
|
fn add(self, rhs: Self) -> Self::Output {
|
||||||
@ -123,7 +122,7 @@ impl std::ops::Add for GridCell {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl std::ops::Add<IVec3> for GridCell {
|
impl core::ops::Add<IVec3> for GridCell {
|
||||||
type Output = GridCell;
|
type Output = GridCell;
|
||||||
|
|
||||||
fn add(self, rhs: IVec3) -> Self::Output {
|
fn add(self, rhs: IVec3) -> Self::Output {
|
||||||
@ -135,7 +134,7 @@ impl std::ops::Add<IVec3> for GridCell {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl std::ops::Sub for GridCell {
|
impl core::ops::Sub for GridCell {
|
||||||
type Output = GridCell;
|
type Output = GridCell;
|
||||||
|
|
||||||
fn sub(self, rhs: Self) -> Self::Output {
|
fn sub(self, rhs: Self) -> Self::Output {
|
||||||
@ -147,7 +146,7 @@ impl std::ops::Sub for GridCell {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl std::ops::Sub<IVec3> for GridCell {
|
impl core::ops::Sub<IVec3> for GridCell {
|
||||||
type Output = GridCell;
|
type Output = GridCell;
|
||||||
|
|
||||||
fn sub(self, rhs: IVec3) -> Self::Output {
|
fn sub(self, rhs: IVec3) -> Self::Output {
|
||||||
@ -159,7 +158,7 @@ impl std::ops::Sub<IVec3> for GridCell {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl std::ops::Add for &GridCell {
|
impl core::ops::Add for &GridCell {
|
||||||
type Output = GridCell;
|
type Output = GridCell;
|
||||||
|
|
||||||
fn add(self, rhs: Self) -> Self::Output {
|
fn add(self, rhs: Self) -> Self::Output {
|
||||||
@ -167,7 +166,7 @@ impl std::ops::Add for &GridCell {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl std::ops::Add<IVec3> for &GridCell {
|
impl core::ops::Add<IVec3> for &GridCell {
|
||||||
type Output = GridCell;
|
type Output = GridCell;
|
||||||
|
|
||||||
fn add(self, rhs: IVec3) -> Self::Output {
|
fn add(self, rhs: IVec3) -> Self::Output {
|
||||||
@ -175,7 +174,7 @@ impl std::ops::Add<IVec3> for &GridCell {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl std::ops::Sub for &GridCell {
|
impl core::ops::Sub for &GridCell {
|
||||||
type Output = GridCell;
|
type Output = GridCell;
|
||||||
|
|
||||||
fn sub(self, rhs: Self) -> Self::Output {
|
fn sub(self, rhs: Self) -> Self::Output {
|
||||||
@ -183,7 +182,7 @@ impl std::ops::Sub for &GridCell {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl std::ops::Sub<IVec3> for &GridCell {
|
impl core::ops::Sub<IVec3> for &GridCell {
|
||||||
type Output = GridCell;
|
type Output = GridCell;
|
||||||
|
|
||||||
fn sub(self, rhs: IVec3) -> Self::Output {
|
fn sub(self, rhs: IVec3) -> Self::Output {
|
||||||
@ -191,35 +190,35 @@ impl std::ops::Sub<IVec3> for &GridCell {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl std::ops::AddAssign for GridCell {
|
impl core::ops::AddAssign for GridCell {
|
||||||
fn add_assign(&mut self, rhs: Self) {
|
fn add_assign(&mut self, rhs: Self) {
|
||||||
use std::ops::Add;
|
use core::ops::Add;
|
||||||
*self = self.add(rhs);
|
*self = self.add(rhs);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl std::ops::AddAssign<IVec3> for GridCell {
|
impl core::ops::AddAssign<IVec3> for GridCell {
|
||||||
fn add_assign(&mut self, rhs: IVec3) {
|
fn add_assign(&mut self, rhs: IVec3) {
|
||||||
use std::ops::Add;
|
use core::ops::Add;
|
||||||
*self = self.add(rhs);
|
*self = self.add(rhs);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl std::ops::SubAssign for GridCell {
|
impl core::ops::SubAssign for GridCell {
|
||||||
fn sub_assign(&mut self, rhs: Self) {
|
fn sub_assign(&mut self, rhs: Self) {
|
||||||
use std::ops::Sub;
|
use core::ops::Sub;
|
||||||
*self = self.sub(rhs);
|
*self = self.sub(rhs);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl std::ops::SubAssign<IVec3> for GridCell {
|
impl core::ops::SubAssign<IVec3> for GridCell {
|
||||||
fn sub_assign(&mut self, rhs: IVec3) {
|
fn sub_assign(&mut self, rhs: IVec3) {
|
||||||
use std::ops::Sub;
|
use core::ops::Sub;
|
||||||
*self = self.sub(rhs);
|
*self = self.sub(rhs);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl std::ops::Mul<GridPrecision> for GridCell {
|
impl core::ops::Mul<GridPrecision> for GridCell {
|
||||||
type Output = GridCell;
|
type Output = GridCell;
|
||||||
|
|
||||||
fn mul(self, rhs: GridPrecision) -> Self::Output {
|
fn mul(self, rhs: GridPrecision) -> Self::Output {
|
||||||
@ -231,7 +230,7 @@ impl std::ops::Mul<GridPrecision> for GridCell {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl std::ops::Mul<GridPrecision> for &GridCell {
|
impl core::ops::Mul<GridPrecision> for &GridCell {
|
||||||
type Output = GridCell;
|
type Output = GridCell;
|
||||||
|
|
||||||
fn mul(self, rhs: GridPrecision) -> Self::Output {
|
fn mul(self, rhs: GridPrecision) -> Self::Output {
|
||||||
|
|||||||
@ -5,13 +5,14 @@
|
|||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
use bevy_ecs::{
|
use bevy_ecs::{
|
||||||
prelude::*,
|
prelude::*,
|
||||||
|
relationship::Relationship,
|
||||||
system::{
|
system::{
|
||||||
lifetimeless::{Read, Write},
|
lifetimeless::{Read, Write},
|
||||||
SystemParam,
|
SystemParam,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
use bevy_hierarchy::prelude::*;
|
|
||||||
use bevy_math::{prelude::*, DAffine3, DQuat};
|
use bevy_math::{prelude::*, DAffine3, DQuat};
|
||||||
|
use bevy_platform_support::prelude::*;
|
||||||
use bevy_transform::prelude::*;
|
use bevy_transform::prelude::*;
|
||||||
|
|
||||||
pub use inner::LocalFloatingOrigin;
|
pub use inner::LocalFloatingOrigin;
|
||||||
@ -75,7 +76,7 @@ mod inner {
|
|||||||
|
|
||||||
impl LocalFloatingOrigin {
|
impl LocalFloatingOrigin {
|
||||||
/// The grid transform from the local grid, to the floating origin's grid. See
|
/// The grid transform from the local grid, to the floating origin's grid. See
|
||||||
/// [Self::grid_transform].
|
/// [`Self::grid_transform`].
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn grid_transform(&self) -> DAffine3 {
|
pub fn grid_transform(&self) -> DAffine3 {
|
||||||
self.grid_transform
|
self.grid_transform
|
||||||
@ -236,14 +237,14 @@ fn propagate_origin_to_child(
|
|||||||
child_origin_translation_float,
|
child_origin_translation_float,
|
||||||
origin_child_rotation,
|
origin_child_rotation,
|
||||||
);
|
);
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A system param for more easily navigating a hierarchy of [`Grid`]s.
|
/// A system param for more easily navigating a hierarchy of [`Grid`]s.
|
||||||
#[derive(SystemParam)]
|
#[derive(SystemParam)]
|
||||||
pub struct Grids<'w, 's> {
|
pub struct Grids<'w, 's> {
|
||||||
parent: Query<'w, 's, Read<Parent>>,
|
parent: Query<'w, 's, Read<ChildOf>>,
|
||||||
grid_query: Query<'w, 's, (Entity, Read<Grid>, Option<Read<Parent>>)>,
|
grid_query: Query<'w, 's, (Entity, Read<Grid>, Option<Read<ChildOf>>)>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Grids<'_, '_> {
|
impl Grids<'_, '_> {
|
||||||
@ -266,7 +267,7 @@ impl Grids<'_, '_> {
|
|||||||
/// Get the ID of the grid that `this` `Entity` is a child of, if it exists.
|
/// Get the ID of the grid that `this` `Entity` is a child of, if it exists.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn parent_grid_entity(&self, this: Entity) -> Option<Entity> {
|
pub fn parent_grid_entity(&self, this: Entity) -> Option<Entity> {
|
||||||
match self.parent.get(this).map(|parent| **parent) {
|
match self.parent.get(this).map(Relationship::get) {
|
||||||
Err(_) => None,
|
Err(_) => None,
|
||||||
Ok(parent) => match self.grid_query.contains(parent) {
|
Ok(parent) => match self.grid_query.contains(parent) {
|
||||||
true => Some(parent),
|
true => Some(parent),
|
||||||
@ -291,7 +292,7 @@ impl Grids<'_, '_> {
|
|||||||
.iter()
|
.iter()
|
||||||
.filter_map(move |(entity, _, parent)| {
|
.filter_map(move |(entity, _, parent)| {
|
||||||
parent
|
parent
|
||||||
.map(|p| p.get())
|
.map(Relationship::get)
|
||||||
.filter(|parent| *parent == this)
|
.filter(|parent| *parent == this)
|
||||||
.map(|_| entity)
|
.map(|_| entity)
|
||||||
})
|
})
|
||||||
@ -317,9 +318,9 @@ impl Grids<'_, '_> {
|
|||||||
/// A system param for more easily navigating a hierarchy of grids mutably.
|
/// A system param for more easily navigating a hierarchy of grids mutably.
|
||||||
#[derive(SystemParam)]
|
#[derive(SystemParam)]
|
||||||
pub struct GridsMut<'w, 's> {
|
pub struct GridsMut<'w, 's> {
|
||||||
parent: Query<'w, 's, Read<Parent>>,
|
parent: Query<'w, 's, Read<ChildOf>>,
|
||||||
position: Query<'w, 's, (Read<GridCell>, Read<Transform>), With<Grid>>,
|
position: Query<'w, 's, (Read<GridCell>, Read<Transform>), With<Grid>>,
|
||||||
grid_query: Query<'w, 's, (Entity, Write<Grid>, Option<Read<Parent>>)>,
|
grid_query: Query<'w, 's, (Entity, Write<Grid>, Option<Read<ChildOf>>)>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl GridsMut<'_, '_> {
|
impl GridsMut<'_, '_> {
|
||||||
@ -374,7 +375,7 @@ impl GridsMut<'_, '_> {
|
|||||||
/// Get the ID of the grid that `this` `Entity` is a child of, if it exists.
|
/// Get the ID of the grid that `this` `Entity` is a child of, if it exists.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn parent_grid_entity(&self, this: Entity) -> Option<Entity> {
|
pub fn parent_grid_entity(&self, this: Entity) -> Option<Entity> {
|
||||||
match self.parent.get(this).map(|parent| **parent) {
|
match self.parent.get(this).map(Relationship::get) {
|
||||||
Err(_) => None,
|
Err(_) => None,
|
||||||
Ok(parent) => match self.grid_query.contains(parent) {
|
Ok(parent) => match self.grid_query.contains(parent) {
|
||||||
true => Some(parent),
|
true => Some(parent),
|
||||||
@ -399,7 +400,7 @@ impl GridsMut<'_, '_> {
|
|||||||
.iter()
|
.iter()
|
||||||
.filter_map(move |(entity, _, parent)| {
|
.filter_map(move |(entity, _, parent)| {
|
||||||
parent
|
parent
|
||||||
.map(|p| p.get())
|
.map(Relationship::get)
|
||||||
.filter(|parent| *parent == this)
|
.filter(|parent| *parent == this)
|
||||||
.map(|_| entity)
|
.map(|_| entity)
|
||||||
})
|
})
|
||||||
@ -435,9 +436,9 @@ impl LocalFloatingOrigin {
|
|||||||
mut scratch_buffer: Local<Vec<Entity>>,
|
mut scratch_buffer: Local<Vec<Entity>>,
|
||||||
cells: Query<(Entity, Ref<GridCell>)>,
|
cells: Query<(Entity, Ref<GridCell>)>,
|
||||||
roots: Query<(Entity, &BigSpace)>,
|
roots: Query<(Entity, &BigSpace)>,
|
||||||
parents: Query<&Parent>,
|
parents: Query<&ChildOf>,
|
||||||
) {
|
) {
|
||||||
let start = bevy_utils::Instant::now();
|
let start = bevy_platform_support::time::Instant::now();
|
||||||
|
|
||||||
/// The maximum grid tree depth, defensively prevents infinite looping in case there is a
|
/// The maximum grid tree depth, defensively prevents infinite looping in case there is a
|
||||||
/// degenerate hierarchy. It might take a while, but at least it's not forever?
|
/// degenerate hierarchy. It might take a while, but at least it's not forever?
|
||||||
@ -493,7 +494,7 @@ impl LocalFloatingOrigin {
|
|||||||
// child, these do no alias.
|
// child, these do no alias.
|
||||||
for child_grid in scratch_buffer.drain(..) {
|
for child_grid in scratch_buffer.drain(..) {
|
||||||
propagate_origin_to_child(this_grid, &mut grids, child_grid);
|
propagate_origin_to_child(this_grid, &mut grids, child_grid);
|
||||||
grid_stack.push(child_grid) // Push processed child onto the stack
|
grid_stack.push(child_grid); // Push processed child onto the stack
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -507,7 +508,7 @@ impl LocalFloatingOrigin {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
tracing::error!("Reached the maximum grid depth ({MAX_REFERENCE_FRAME_DEPTH}), and exited early to prevent an infinite loop. This might be caused by a degenerate hierarchy.")
|
tracing::error!("Reached the maximum grid depth ({MAX_REFERENCE_FRAME_DEPTH}), and exited early to prevent an infinite loop. This might be caused by a degenerate hierarchy.");
|
||||||
}
|
}
|
||||||
|
|
||||||
stats.local_origin_propagation += start.elapsed();
|
stats.local_origin_propagation += start.elapsed();
|
||||||
@ -550,7 +551,7 @@ mod tests {
|
|||||||
let result = grids.child_grids(child_1).collect::<Vec<_>>();
|
let result = grids.child_grids(child_1).collect::<Vec<_>>();
|
||||||
assert_eq!(result, Vec::new());
|
assert_eq!(result, Vec::new());
|
||||||
|
|
||||||
// Parent
|
// ChildOf
|
||||||
let result = grids.parent_grid_entity(root);
|
let result = grids.parent_grid_entity(root);
|
||||||
assert_eq!(result, None);
|
assert_eq!(result, None);
|
||||||
let result = grids.parent_grid_entity(parent);
|
let result = grids.parent_grid_entity(parent);
|
||||||
@ -575,7 +576,7 @@ mod tests {
|
|||||||
local_floating_origin: LocalFloatingOrigin::new(
|
local_floating_origin: LocalFloatingOrigin::new(
|
||||||
GridCell::new(1_000_000, -1, -1),
|
GridCell::new(1_000_000, -1, -1),
|
||||||
Vec3::ZERO,
|
Vec3::ZERO,
|
||||||
DQuat::from_rotation_z(-std::f64::consts::FRAC_PI_2),
|
DQuat::from_rotation_z(-core::f64::consts::FRAC_PI_2),
|
||||||
),
|
),
|
||||||
..default()
|
..default()
|
||||||
};
|
};
|
||||||
@ -587,7 +588,7 @@ mod tests {
|
|||||||
let child = app
|
let child = app
|
||||||
.world_mut()
|
.world_mut()
|
||||||
.spawn((
|
.spawn((
|
||||||
Transform::from_rotation(Quat::from_rotation_z(std::f32::consts::FRAC_PI_2))
|
Transform::from_rotation(Quat::from_rotation_z(core::f32::consts::FRAC_PI_2))
|
||||||
.with_translation(Vec3::new(1.0, 1.0, 0.0)),
|
.with_translation(Vec3::new(1.0, 1.0, 0.0)),
|
||||||
GridCell::new(1_000_000, 0, 0),
|
GridCell::new(1_000_000, 0, 0),
|
||||||
Grid::default(),
|
Grid::default(),
|
||||||
@ -609,7 +610,7 @@ mod tests {
|
|||||||
assert_eq!(computed_grid, correct_grid);
|
assert_eq!(computed_grid, correct_grid);
|
||||||
|
|
||||||
let computed_rot = child_grid.local_floating_origin.rotation();
|
let computed_rot = child_grid.local_floating_origin.rotation();
|
||||||
let correct_rot = DQuat::from_rotation_z(std::f64::consts::PI);
|
let correct_rot = DQuat::from_rotation_z(core::f64::consts::PI);
|
||||||
let rot_error = computed_rot.angle_between(correct_rot);
|
let rot_error = computed_rot.angle_between(correct_rot);
|
||||||
assert!(rot_error < 1e-10);
|
assert!(rot_error < 1e-10);
|
||||||
|
|
||||||
@ -637,14 +638,14 @@ mod tests {
|
|||||||
let child = app
|
let child = app
|
||||||
.world_mut()
|
.world_mut()
|
||||||
.spawn((
|
.spawn((
|
||||||
Transform::from_rotation(Quat::from_rotation_z(std::f32::consts::FRAC_PI_2))
|
Transform::from_rotation(Quat::from_rotation_z(core::f32::consts::FRAC_PI_2))
|
||||||
.with_translation(Vec3::new(1.0, 1.0, 0.0)),
|
.with_translation(Vec3::new(1.0, 1.0, 0.0)),
|
||||||
GridCell::new(150_000_003_000, 0, 0), // roughly radius of earth orbit
|
GridCell::new(150_000_003_000, 0, 0), // roughly radius of earth orbit
|
||||||
Grid {
|
Grid {
|
||||||
local_floating_origin: LocalFloatingOrigin::new(
|
local_floating_origin: LocalFloatingOrigin::new(
|
||||||
GridCell::new(0, 3_000, 0),
|
GridCell::new(0, 3_000, 0),
|
||||||
Vec3::new(5.0, 5.0, 0.0),
|
Vec3::new(5.0, 5.0, 0.0),
|
||||||
DQuat::from_rotation_z(-std::f64::consts::FRAC_PI_2),
|
DQuat::from_rotation_z(-core::f64::consts::FRAC_PI_2),
|
||||||
),
|
),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
@ -708,7 +709,7 @@ mod tests {
|
|||||||
.world_mut()
|
.world_mut()
|
||||||
.spawn((
|
.spawn((
|
||||||
Transform::default()
|
Transform::default()
|
||||||
.with_rotation(Quat::from_rotation_z(-std::f32::consts::FRAC_PI_2))
|
.with_rotation(Quat::from_rotation_z(-core::f32::consts::FRAC_PI_2))
|
||||||
.with_translation(Vec3::new(3.0, 3.0, 0.0)),
|
.with_translation(Vec3::new(3.0, 3.0, 0.0)),
|
||||||
GridCell::new(0, 0, 0),
|
GridCell::new(0, 0, 0),
|
||||||
Grid::default(),
|
Grid::default(),
|
||||||
@ -729,7 +730,7 @@ mod tests {
|
|||||||
let computed_pos = computed_transform.transform_point3(child_local_point);
|
let computed_pos = computed_transform.transform_point3(child_local_point);
|
||||||
|
|
||||||
let correct_transform = DAffine3::from_rotation_translation(
|
let correct_transform = DAffine3::from_rotation_translation(
|
||||||
DQuat::from_rotation_z(-std::f64::consts::FRAC_PI_2),
|
DQuat::from_rotation_z(-core::f64::consts::FRAC_PI_2),
|
||||||
DVec3::new(2.0, 2.0, 0.0),
|
DVec3::new(2.0, 2.0, 0.0),
|
||||||
);
|
);
|
||||||
let correct_pos = correct_transform.transform_point3(child_local_point);
|
let correct_pos = correct_transform.transform_point3(child_local_point);
|
||||||
|
|||||||
@ -115,9 +115,9 @@ impl Grid {
|
|||||||
return (GridCell::default(), input.as_vec3());
|
return (GridCell::default(), input.as_vec3());
|
||||||
}
|
}
|
||||||
|
|
||||||
let x_r = (x / l).round();
|
let x_r = round(x / l);
|
||||||
let y_r = (y / l).round();
|
let y_r = round(y / l);
|
||||||
let z_r = (z / l).round();
|
let z_r = round(z / l);
|
||||||
let t_x = x - x_r * l;
|
let t_x = x - x_r * l;
|
||||||
let t_y = y - y_r * l;
|
let t_y = y - y_r * l;
|
||||||
let t_z = z - z_r * l;
|
let t_z = z - z_r * l;
|
||||||
@ -165,3 +165,21 @@ impl Grid {
|
|||||||
.into()
|
.into()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn round(x: f64) -> f64 {
|
||||||
|
#[cfg(feature = "libm")]
|
||||||
|
{
|
||||||
|
libm::round(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(all(not(feature = "libm"), feature = "std"))]
|
||||||
|
{
|
||||||
|
x.round()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(all(not(feature = "libm"), not(feature = "std")))]
|
||||||
|
{
|
||||||
|
compile_error!("Must enable the `libm` and/or `std` feature.");
|
||||||
|
f64::NAN
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@ -1,8 +1,7 @@
|
|||||||
//! Logic for propagating transforms through the hierarchy of grids.
|
//! Logic for propagating transforms through the hierarchy of grids.
|
||||||
|
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
use bevy_ecs::prelude::*;
|
use bevy_ecs::{prelude::*, relationship::Relationship};
|
||||||
use bevy_hierarchy::prelude::*;
|
|
||||||
use bevy_reflect::Reflect;
|
use bevy_reflect::Reflect;
|
||||||
use bevy_transform::prelude::*;
|
use bevy_transform::prelude::*;
|
||||||
|
|
||||||
@ -26,13 +25,13 @@ impl Grid {
|
|||||||
Query<(
|
Query<(
|
||||||
Ref<GridCell>,
|
Ref<GridCell>,
|
||||||
Ref<Transform>,
|
Ref<Transform>,
|
||||||
Ref<Parent>,
|
Ref<ChildOf>,
|
||||||
&mut GlobalTransform,
|
&mut GlobalTransform,
|
||||||
)>,
|
)>,
|
||||||
Query<(&Grid, &mut GlobalTransform), With<BigSpace>>,
|
Query<(&Grid, &mut GlobalTransform), With<BigSpace>>,
|
||||||
)>,
|
)>,
|
||||||
) {
|
) {
|
||||||
let start = bevy_utils::Instant::now();
|
let start = bevy_platform_support::time::Instant::now();
|
||||||
|
|
||||||
// Performance note: I've also tried to iterate over each grid's children at once, to avoid
|
// Performance note: I've also tried to iterate over each grid's children at once, to avoid
|
||||||
// the grid and parent lookup, but that made things worse because it prevented dumb
|
// the grid and parent lookup, but that made things worse because it prevented dumb
|
||||||
@ -95,13 +94,13 @@ impl Grid {
|
|||||||
mut commands: Commands,
|
mut commands: Commands,
|
||||||
valid_parent: Query<(), (With<GridCell>, With<GlobalTransform>, With<Children>)>,
|
valid_parent: Query<(), (With<GridCell>, With<GlobalTransform>, With<Children>)>,
|
||||||
unmarked: Query<
|
unmarked: Query<
|
||||||
(Entity, &Parent),
|
(Entity, &ChildOf),
|
||||||
(
|
(
|
||||||
With<Transform>,
|
With<Transform>,
|
||||||
With<GlobalTransform>,
|
With<GlobalTransform>,
|
||||||
Without<GridCell>,
|
Without<GridCell>,
|
||||||
Without<LowPrecisionRoot>,
|
Without<LowPrecisionRoot>,
|
||||||
Or<(Changed<Parent>, Added<Transform>)>,
|
Or<(Changed<ChildOf>, Added<Transform>)>,
|
||||||
),
|
),
|
||||||
>,
|
>,
|
||||||
invalidated: Query<
|
invalidated: Query<
|
||||||
@ -112,13 +111,13 @@ impl Grid {
|
|||||||
Without<Transform>,
|
Without<Transform>,
|
||||||
Without<GlobalTransform>,
|
Without<GlobalTransform>,
|
||||||
With<GridCell>,
|
With<GridCell>,
|
||||||
Without<Parent>,
|
Without<ChildOf>,
|
||||||
)>,
|
)>,
|
||||||
),
|
),
|
||||||
>,
|
>,
|
||||||
has_possibly_invalid_parent: Query<(Entity, &Parent), With<LowPrecisionRoot>>,
|
has_possibly_invalid_parent: Query<(Entity, &ChildOf), With<LowPrecisionRoot>>,
|
||||||
) {
|
) {
|
||||||
let start = bevy_utils::Instant::now();
|
let start = bevy_platform_support::time::Instant::now();
|
||||||
for (entity, parent) in unmarked.iter() {
|
for (entity, parent) in unmarked.iter() {
|
||||||
if valid_parent.contains(parent.get()) {
|
if valid_parent.contains(parent.get()) {
|
||||||
commands.entity(entity).insert(LowPrecisionRoot);
|
commands.entity(entity).insert(LowPrecisionRoot);
|
||||||
@ -150,17 +149,17 @@ impl Grid {
|
|||||||
Or<(With<Grid>, With<GridCell>)>,
|
Or<(With<Grid>, With<GridCell>)>,
|
||||||
),
|
),
|
||||||
>,
|
>,
|
||||||
roots: Query<(Entity, &Parent), With<LowPrecisionRoot>>,
|
roots: Query<(Entity, &ChildOf), With<LowPrecisionRoot>>,
|
||||||
transform_query: Query<
|
transform_query: Query<
|
||||||
(Ref<Transform>, &mut GlobalTransform, Option<&Children>),
|
(Ref<Transform>, &mut GlobalTransform, Option<&Children>),
|
||||||
(
|
(
|
||||||
With<Parent>,
|
With<ChildOf>,
|
||||||
Without<GridCell>, // Used to prove access to GlobalTransform is disjoint
|
Without<GridCell>, // Used to prove access to GlobalTransform is disjoint
|
||||||
Without<Grid>,
|
Without<Grid>,
|
||||||
),
|
),
|
||||||
>,
|
>,
|
||||||
parent_query: Query<
|
parent_query: Query<
|
||||||
(Entity, Ref<Parent>),
|
(Entity, Ref<ChildOf>),
|
||||||
(
|
(
|
||||||
With<Transform>,
|
With<Transform>,
|
||||||
With<GlobalTransform>,
|
With<GlobalTransform>,
|
||||||
@ -169,7 +168,7 @@ impl Grid {
|
|||||||
),
|
),
|
||||||
>,
|
>,
|
||||||
) {
|
) {
|
||||||
let start = bevy_utils::Instant::now();
|
let start = bevy_platform_support::time::Instant::now();
|
||||||
let update_transforms = |low_precision_root, parent_transform: Ref<GlobalTransform>| {
|
let update_transforms = |low_precision_root, parent_transform: Ref<GlobalTransform>| {
|
||||||
// High precision global transforms are change-detected, and are only updated if that
|
// High precision global transforms are change-detected, and are only updated if that
|
||||||
// entity has moved relative to the floating origin's grid cell.
|
// entity has moved relative to the floating origin's grid cell.
|
||||||
@ -186,6 +185,10 @@ impl Grid {
|
|||||||
// other root entities' `propagate_recursive` calls will not conflict with this one.
|
// other root entities' `propagate_recursive` calls will not conflict with this one.
|
||||||
// - Since this is the only place where `transform_query` gets used, there will be no
|
// - Since this is the only place where `transform_query` gets used, there will be no
|
||||||
// conflicting fetches elsewhere.
|
// conflicting fetches elsewhere.
|
||||||
|
#[expect(
|
||||||
|
unsafe_code,
|
||||||
|
reason = "`propagate_recursive()` is unsafe due to its use of `Query::get_unchecked()`."
|
||||||
|
)]
|
||||||
unsafe {
|
unsafe {
|
||||||
Self::propagate_recursive(
|
Self::propagate_recursive(
|
||||||
&parent_transform,
|
&parent_transform,
|
||||||
@ -206,8 +209,6 @@ impl Grid {
|
|||||||
stats.low_precision_propagation += start.elapsed();
|
stats.low_precision_propagation += start.elapsed();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// COPIED FROM BEVY
|
|
||||||
///
|
|
||||||
/// Recursively propagates the transforms for `entity` and all of its descendants.
|
/// Recursively propagates the transforms for `entity` and all of its descendants.
|
||||||
///
|
///
|
||||||
/// # Panics
|
/// # Panics
|
||||||
@ -221,18 +222,22 @@ impl Grid {
|
|||||||
/// nor any of its descendants.
|
/// nor any of its descendants.
|
||||||
/// - The caller must ensure that the hierarchy leading to `entity` is well-formed and must
|
/// - The caller must ensure that the hierarchy leading to `entity` is well-formed and must
|
||||||
/// remain as a tree or a forest. Each entity must have at most one parent.
|
/// remain as a tree or a forest. Each entity must have at most one parent.
|
||||||
|
#[expect(
|
||||||
|
unsafe_code,
|
||||||
|
reason = "This function uses `Query::get_unchecked()`, which can result in multiple mutable references if the preconditions are not met."
|
||||||
|
)]
|
||||||
unsafe fn propagate_recursive(
|
unsafe fn propagate_recursive(
|
||||||
parent: &GlobalTransform,
|
parent: &GlobalTransform,
|
||||||
transform_query: &Query<
|
transform_query: &Query<
|
||||||
(Ref<Transform>, &mut GlobalTransform, Option<&Children>),
|
(Ref<Transform>, &mut GlobalTransform, Option<&Children>),
|
||||||
(
|
(
|
||||||
With<Parent>,
|
With<ChildOf>,
|
||||||
Without<GridCell>, // ***ADDED*** Only recurse low-precision entities
|
Without<GridCell>, // ***ADDED*** Only recurse low-precision entities
|
||||||
Without<Grid>, // ***ADDED*** Only recurse low-precision entities
|
Without<Grid>, // ***ADDED*** Only recurse low-precision entities
|
||||||
),
|
),
|
||||||
>,
|
>,
|
||||||
parent_query: &Query<
|
parent_query: &Query<
|
||||||
(Entity, Ref<Parent>),
|
(Entity, Ref<ChildOf>),
|
||||||
(
|
(
|
||||||
With<Transform>,
|
With<Transform>,
|
||||||
With<GlobalTransform>,
|
With<GlobalTransform>,
|
||||||
@ -245,28 +250,34 @@ impl Grid {
|
|||||||
) {
|
) {
|
||||||
let (global_matrix, children) = {
|
let (global_matrix, children) = {
|
||||||
let Ok((transform, mut global_transform, children)) =
|
let Ok((transform, mut global_transform, children)) =
|
||||||
// SAFETY: This call cannot create aliased mutable references.
|
// SAFETY: This call cannot create aliased mutable references.
|
||||||
// - The top level iteration parallelizes on the roots of the hierarchy.
|
// - The top level iteration parallelizes on the roots of the hierarchy.
|
||||||
// - The caller ensures that each child has one and only one unique parent throughout
|
// - The caller ensures that each child has one and only one unique parent
|
||||||
// the entire hierarchy.
|
// throughout the entire hierarchy.
|
||||||
//
|
//
|
||||||
// For example, consider the following malformed hierarchy:
|
// For example, consider the following malformed hierarchy:
|
||||||
//
|
//
|
||||||
// A
|
// A
|
||||||
// / \
|
// / \
|
||||||
// B C \ / D
|
// B C
|
||||||
//
|
// \ /
|
||||||
// D has two parents, B and C. If the propagation passes through C, but the Parent
|
// D
|
||||||
// component on D points to B, the above check will panic as the origin parent does
|
//
|
||||||
// match the recorded parent.
|
// D has two parents, B and C. If the propagation passes through C, but the ChildOf
|
||||||
//
|
// component on D points to B, the above check will panic as the origin parent does
|
||||||
// Also consider the following case, where A and B are roots:
|
// match the recorded parent.
|
||||||
//
|
//
|
||||||
// A B \ / C D \ / E
|
// Also consider the following case, where A and B are roots:
|
||||||
//
|
//
|
||||||
// Even if these A and B start two separate tasks running in parallel, one of them will
|
// A B
|
||||||
// panic before attempting to mutably access E.
|
// \ /
|
||||||
(unsafe { transform_query.get_unchecked(entity) }) else {
|
// C D
|
||||||
|
// \ /
|
||||||
|
// E
|
||||||
|
//
|
||||||
|
// Even if these A and B start two separate tasks running in parallel, one of them
|
||||||
|
// will panic before attempting to mutably access E.
|
||||||
|
(unsafe { transform_query.get_unchecked(entity) }) else {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -278,11 +289,11 @@ impl Grid {
|
|||||||
};
|
};
|
||||||
|
|
||||||
let Some(children) = children else { return };
|
let Some(children) = children else { return };
|
||||||
for (child, actual_parent) in parent_query.iter_many(children) {
|
for (child, child_of) in parent_query.iter_many(children) {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
actual_parent.get(), entity,
|
child_of.parent, entity,
|
||||||
"Malformed hierarchy. This probably means that your hierarchy has been improperly maintained, or contains a cycle"
|
"Malformed hierarchy. This probably means that your hierarchy has been improperly maintained, or contains a cycle"
|
||||||
);
|
);
|
||||||
// SAFETY: The caller guarantees that `transform_query` will not be fetched for any
|
// SAFETY: The caller guarantees that `transform_query` will not be fetched for any
|
||||||
// descendants of `entity`, so it is safe to call `propagate_recursive` for each child.
|
// descendants of `entity`, so it is safe to call `propagate_recursive` for each child.
|
||||||
//
|
//
|
||||||
@ -294,7 +305,7 @@ impl Grid {
|
|||||||
transform_query,
|
transform_query,
|
||||||
parent_query,
|
parent_query,
|
||||||
child,
|
child,
|
||||||
changed || actual_parent.is_changed(),
|
changed || child_of.is_changed(),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -337,10 +348,10 @@ mod tests {
|
|||||||
let mut q = app
|
let mut q = app
|
||||||
.world_mut()
|
.world_mut()
|
||||||
.query_filtered::<&GlobalTransform, With<Test>>();
|
.query_filtered::<&GlobalTransform, With<Test>>();
|
||||||
let actual_transform = *q.single(app.world());
|
let actual_transform = *q.single(app.world()).unwrap();
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
actual_transform,
|
actual_transform,
|
||||||
GlobalTransform::from_xyz(2004.0, 2005.0, 2006.0)
|
GlobalTransform::from_xyz(2004.0, 2005.0, 2006.0)
|
||||||
)
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,16 +1,18 @@
|
|||||||
//! Components for spatial hashing.
|
//! Components for spatial hashing.
|
||||||
|
|
||||||
use std::hash::{Hash, Hasher};
|
use alloc::vec::Vec;
|
||||||
|
use core::hash::{BuildHasher, Hash, Hasher};
|
||||||
|
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
use bevy_ecs::prelude::*;
|
use bevy_ecs::{prelude::*, relationship::Relationship};
|
||||||
use bevy_hierarchy::Parent;
|
|
||||||
use bevy_math::IVec3;
|
use bevy_math::IVec3;
|
||||||
|
use bevy_platform_support::{hash::FixedHasher, time::Instant};
|
||||||
use bevy_reflect::Reflect;
|
use bevy_reflect::Reflect;
|
||||||
use bevy_utils::{AHasher, Instant, Parallel};
|
|
||||||
|
|
||||||
use super::{ChangedGridHashes, GridHashMapFilter};
|
use super::{ChangedGridHashes, GridHashMapFilter};
|
||||||
|
|
||||||
|
use crate::portable_par::PortableParallel;
|
||||||
|
|
||||||
/// A fast but lossy version of [`GridHash`]. Use this component when you don't care about false
|
/// A fast but lossy version of [`GridHash`]. Use this component when you don't care about false
|
||||||
/// positives (hash collisions). See the docs on [`GridHash::fast_eq`] for more details on fast but
|
/// positives (hash collisions). See the docs on [`GridHash::fast_eq`] for more details on fast but
|
||||||
/// lossy equality checks.
|
/// lossy equality checks.
|
||||||
@ -47,7 +49,7 @@ impl From<GridHash> for FastGridHash {
|
|||||||
/// [`GridHashMap`] resource.
|
/// [`GridHashMap`] resource.
|
||||||
///
|
///
|
||||||
/// Due to grids and multiple big spaces in a single world, this must use both the [`GridCell`] and
|
/// Due to grids and multiple big spaces in a single world, this must use both the [`GridCell`] and
|
||||||
/// the [`Parent`] of the entity to uniquely identify its position. These two values are then hashed
|
/// the [`ChildOf`] of the entity to uniquely identify its position. These two values are then hashed
|
||||||
/// and stored in this spatial hash component.
|
/// and stored in this spatial hash component.
|
||||||
#[derive(Component, Clone, Copy, Debug, Reflect)]
|
#[derive(Component, Clone, Copy, Debug, Reflect)]
|
||||||
pub struct GridHash {
|
pub struct GridHash {
|
||||||
@ -89,15 +91,15 @@ impl GridHash {
|
|||||||
/// Intentionally left private, so we can ensure the only place these are constructed/mutated is
|
/// Intentionally left private, so we can ensure the only place these are constructed/mutated is
|
||||||
/// this module. This allows us to optimize change detection using [`ChangedGridHashes`].
|
/// this module. This allows us to optimize change detection using [`ChangedGridHashes`].
|
||||||
#[inline]
|
#[inline]
|
||||||
pub(super) fn new(parent: &Parent, cell: &GridCell) -> Self {
|
pub(super) fn new(parent: &ChildOf, cell: &GridCell) -> Self {
|
||||||
Self::from_parent(parent.get(), cell)
|
Self::from_parent(parent.get(), cell)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub(super) fn from_parent(parent: Entity, cell: &GridCell) -> Self {
|
pub(super) fn from_parent(parent: Entity, cell: &GridCell) -> Self {
|
||||||
let hasher = &mut AHasher::default();
|
let mut hasher = FixedHasher.build_hasher();
|
||||||
hasher.write_u64(parent.to_bits());
|
hasher.write_u64(parent.to_bits());
|
||||||
cell.hash(hasher);
|
cell.hash(&mut hasher);
|
||||||
|
|
||||||
GridHash {
|
GridHash {
|
||||||
cell: *cell,
|
cell: *cell,
|
||||||
@ -131,7 +133,7 @@ impl GridHash {
|
|||||||
/// errors.
|
/// errors.
|
||||||
///
|
///
|
||||||
/// In other words, this should only be used for acceleration, when you want to quickly cull
|
/// In other words, this should only be used for acceleration, when you want to quickly cull
|
||||||
/// non-overlapping cells, and you will be double checking for false positives later.
|
/// non-overlapping cells, and you will be double-checking for false positives later.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn fast_eq(&self, other: &Self) -> bool {
|
pub fn fast_eq(&self, other: &Self) -> bool {
|
||||||
self.pre_hash == other.pre_hash
|
self.pre_hash == other.pre_hash
|
||||||
@ -160,13 +162,19 @@ impl GridHash {
|
|||||||
mut commands: Commands,
|
mut commands: Commands,
|
||||||
mut changed_hashes: ResMut<ChangedGridHashes<F>>,
|
mut changed_hashes: ResMut<ChangedGridHashes<F>>,
|
||||||
mut spatial_entities: Query<
|
mut spatial_entities: Query<
|
||||||
(Entity, &Parent, &GridCell, &mut GridHash, &mut FastGridHash),
|
(
|
||||||
(F, Or<(Changed<Parent>, Changed<GridCell>)>),
|
Entity,
|
||||||
|
&ChildOf,
|
||||||
|
&GridCell,
|
||||||
|
&mut GridHash,
|
||||||
|
&mut FastGridHash,
|
||||||
|
),
|
||||||
|
(F, Or<(Changed<ChildOf>, Changed<GridCell>)>),
|
||||||
>,
|
>,
|
||||||
added_entities: Query<(Entity, &Parent, &GridCell), (F, Without<GridHash>)>,
|
added_entities: Query<(Entity, &ChildOf, &GridCell), (F, Without<GridHash>)>,
|
||||||
mut stats: Option<ResMut<crate::timing::GridHashStats>>,
|
mut stats: Option<ResMut<crate::timing::GridHashStats>>,
|
||||||
mut thread_updated_hashes: Local<Parallel<Vec<Entity>>>,
|
mut thread_updated_hashes: Local<PortableParallel<Vec<Entity>>>,
|
||||||
mut thread_commands: Local<Parallel<Vec<(Entity, GridHash, FastGridHash)>>>,
|
mut thread_commands: Local<PortableParallel<Vec<(Entity, GridHash, FastGridHash)>>>,
|
||||||
) {
|
) {
|
||||||
let start = Instant::now();
|
let start = Instant::now();
|
||||||
|
|
||||||
@ -206,7 +214,7 @@ impl GridHash {
|
|||||||
self.cell
|
self.cell
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The [`Parent`] [`Grid`] of this spatial hash.
|
/// The [`ChildOf`] [`Grid`] of this spatial hash.
|
||||||
pub fn grid(&self) -> Entity {
|
pub fn grid(&self) -> Entity {
|
||||||
self.grid
|
self.grid
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,13 +1,16 @@
|
|||||||
//! The [`GridHashMap`] that contains mappings between entities and their spatial hash.
|
//! The [`GridHashMap`] that contains mappings between entities and their spatial hash.
|
||||||
|
|
||||||
use std::{collections::VecDeque, marker::PhantomData, time::Instant};
|
use alloc::collections::VecDeque;
|
||||||
|
use core::marker::PhantomData;
|
||||||
|
|
||||||
use super::GridHashMapFilter;
|
use super::GridHashMapFilter;
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
use bevy_ecs::{entity::EntityHash, prelude::*};
|
use bevy_ecs::{entity::EntityHash, prelude::*};
|
||||||
use bevy_utils::{
|
use bevy_platform_support::{
|
||||||
hashbrown::{HashMap, HashSet},
|
collections::{HashMap, HashSet},
|
||||||
PassHash,
|
hash::PassHash,
|
||||||
|
prelude::*,
|
||||||
|
time::Instant,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// An entry in a [`GridHashMap`], accessed with a [`GridHash`].
|
/// An entry in a [`GridHashMap`], accessed with a [`GridHash`].
|
||||||
@ -51,10 +54,10 @@ pub trait SpatialEntryToEntities<'a> {
|
|||||||
impl<'a, T, I> SpatialEntryToEntities<'a> for T
|
impl<'a, T, I> SpatialEntryToEntities<'a> for T
|
||||||
where
|
where
|
||||||
T: Iterator<Item = I> + 'a,
|
T: Iterator<Item = I> + 'a,
|
||||||
I: SpatialEntryToEntities<'a>,
|
I: SpatialEntryToEntities<'a> + 'a,
|
||||||
{
|
{
|
||||||
fn entities(self) -> impl Iterator<Item = Entity> + 'a {
|
fn entities(self) -> impl Iterator<Item = Entity> + 'a {
|
||||||
self.flat_map(|entry| entry.entities())
|
self.flat_map(SpatialEntryToEntities::entities)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -87,11 +90,11 @@ where
|
|||||||
spooky: PhantomData<F>,
|
spooky: PhantomData<F>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<F> std::fmt::Debug for GridHashMap<F>
|
impl<F> core::fmt::Debug for GridHashMap<F>
|
||||||
where
|
where
|
||||||
F: GridHashMapFilter,
|
F: GridHashMapFilter,
|
||||||
{
|
{
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
||||||
f.debug_struct("GridHashMap")
|
f.debug_struct("GridHashMap")
|
||||||
.field("map", &self.map)
|
.field("map", &self.map)
|
||||||
.field("reverse_map", &self.reverse_map)
|
.field("reverse_map", &self.reverse_map)
|
||||||
@ -150,9 +153,9 @@ where
|
|||||||
&'a self,
|
&'a self,
|
||||||
entry: &'a GridHashEntry,
|
entry: &'a GridHashEntry,
|
||||||
) -> impl Iterator<Item = &'a GridHashEntry> + 'a {
|
) -> impl Iterator<Item = &'a GridHashEntry> + 'a {
|
||||||
// Use `std::iter::once` to avoid returning a function-local variable.
|
// Use `core::iter::once` to avoid returning a function-local variable.
|
||||||
Iterator::chain(
|
Iterator::chain(
|
||||||
std::iter::once(entry),
|
core::iter::once(entry),
|
||||||
entry.occupied_neighbors.iter().map(|neighbor_hash| {
|
entry.occupied_neighbors.iter().map(|neighbor_hash| {
|
||||||
self.get(neighbor_hash)
|
self.get(neighbor_hash)
|
||||||
.expect("occupied_neighbors should be occupied")
|
.expect("occupied_neighbors should be occupied")
|
||||||
@ -177,8 +180,8 @@ where
|
|||||||
center: &'a GridHash,
|
center: &'a GridHash,
|
||||||
radius: u8,
|
radius: u8,
|
||||||
) -> impl Iterator<Item = &'a GridHashEntry> + 'a {
|
) -> impl Iterator<Item = &'a GridHashEntry> + 'a {
|
||||||
// Use `std::iter::once` to avoid returning a function-local variable.
|
// Use `core::iter::once` to avoid returning a function-local variable.
|
||||||
Iterator::chain(std::iter::once(*center), center.adjacent(radius))
|
Iterator::chain(core::iter::once(*center), center.adjacent(radius))
|
||||||
.filter_map(|hash| self.get(&hash))
|
.filter_map(|hash| self.get(&hash))
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -194,7 +197,7 @@ where
|
|||||||
///
|
///
|
||||||
/// Consider the case of a long thin U-shaped set of connected cells. While iterating from one
|
/// Consider the case of a long thin U-shaped set of connected cells. While iterating from one
|
||||||
/// end of the "U" to the other with this flood fill, if any of the cells near the base of the
|
/// end of the "U" to the other with this flood fill, if any of the cells near the base of the
|
||||||
/// "U" exceed the max_depth (radius), iteration will stop. Even if the "U" loops back within
|
/// "U" exceed the `max_depth` (radius), iteration will stop. Even if the "U" loops back within
|
||||||
/// the radius, those cells will never be visited.
|
/// the radius, those cells will never be visited.
|
||||||
///
|
///
|
||||||
/// Also note that the `max_depth` (radius) is a chebyshev distance, not a euclidean distance.
|
/// Also note that the `max_depth` (radius) is a chebyshev distance, not a euclidean distance.
|
||||||
@ -261,7 +264,7 @@ where
|
|||||||
spatial_map.map.just_removed.clear();
|
spatial_map.map.just_removed.clear();
|
||||||
|
|
||||||
for entity in removed.read() {
|
for entity in removed.read() {
|
||||||
spatial_map.remove(entity)
|
spatial_map.remove(entity);
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(ref mut stats) = stats {
|
if let Some(ref mut stats) = stats {
|
||||||
@ -309,7 +312,7 @@ where
|
|||||||
#[inline]
|
#[inline]
|
||||||
fn remove(&mut self, entity: Entity) {
|
fn remove(&mut self, entity: Entity) {
|
||||||
if let Some(old_hash) = self.reverse_map.remove(&entity) {
|
if let Some(old_hash) = self.reverse_map.remove(&entity) {
|
||||||
self.map.remove(entity, old_hash)
|
self.map.remove(entity, old_hash);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,10 +1,11 @@
|
|||||||
//! Spatial hashing acceleration structure. See [`GridHashPlugin`].
|
//! Spatial hashing acceleration structure. See [`GridHashPlugin`].
|
||||||
|
|
||||||
use std::marker::PhantomData;
|
use core::marker::PhantomData;
|
||||||
|
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
use bevy_app::prelude::*;
|
use bevy_app::prelude::*;
|
||||||
use bevy_ecs::{prelude::*, query::QueryFilter};
|
use bevy_ecs::{prelude::*, query::QueryFilter};
|
||||||
|
use bevy_platform_support::prelude::*;
|
||||||
|
|
||||||
pub mod component;
|
pub mod component;
|
||||||
pub mod map;
|
pub mod map;
|
||||||
@ -110,10 +111,8 @@ impl<F: GridHashMapFilter> Default for ChangedGridHashes<F> {
|
|||||||
// hash ever recomputed? Is it removed? Is the spatial map updated?
|
// hash ever recomputed? Is it removed? Is the spatial map updated?
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use std::sync::OnceLock;
|
|
||||||
|
|
||||||
use crate::{hash::map::SpatialEntryToEntities, prelude::*};
|
use crate::{hash::map::SpatialEntryToEntities, prelude::*};
|
||||||
use bevy_utils::hashbrown::HashSet;
|
use bevy_platform_support::{collections::HashSet, sync::OnceLock};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn entity_despawn() {
|
fn entity_despawn() {
|
||||||
@ -269,7 +268,7 @@ mod tests {
|
|||||||
let entities = app.world().resource::<Entities>().clone();
|
let entities = app.world().resource::<Entities>().clone();
|
||||||
let parent = app
|
let parent = app
|
||||||
.world_mut()
|
.world_mut()
|
||||||
.query::<&Parent>()
|
.query::<&ChildOf>()
|
||||||
.get(app.world(), entities.a)
|
.get(app.world(), entities.a)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
|
|||||||
@ -1,14 +1,16 @@
|
|||||||
//! Detect and update groups of nearby occupied cells.
|
//! Detect and update groups of nearby occupied cells.
|
||||||
|
|
||||||
use std::{hash::Hash, marker::PhantomData, ops::Deref};
|
use core::{hash::Hash, marker::PhantomData, ops::Deref};
|
||||||
|
|
||||||
use bevy_app::prelude::*;
|
use bevy_app::prelude::*;
|
||||||
use bevy_ecs::prelude::*;
|
use bevy_ecs::prelude::*;
|
||||||
use bevy_tasks::{ComputeTaskPool, ParallelSliceMut};
|
use bevy_platform_support::prelude::*;
|
||||||
use bevy_utils::{
|
use bevy_platform_support::{
|
||||||
hashbrown::{HashMap, HashSet},
|
collections::{HashMap, HashSet},
|
||||||
Instant, PassHash,
|
hash::PassHash,
|
||||||
|
time::Instant,
|
||||||
};
|
};
|
||||||
|
use bevy_tasks::{ComputeTaskPool, ParallelSliceMut};
|
||||||
|
|
||||||
use super::{GridCell, GridHash, GridHashMap, GridHashMapFilter, GridHashMapSystem};
|
use super::{GridCell, GridHash, GridHashMap, GridHashMapFilter, GridHashMapSystem};
|
||||||
|
|
||||||
@ -55,7 +57,7 @@ impl GridPartitionId {
|
|||||||
|
|
||||||
impl Hash for GridPartitionId {
|
impl Hash for GridPartitionId {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
|
fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
|
||||||
state.write_u64(self.0);
|
state.write_u64(self.0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -146,7 +148,7 @@ where
|
|||||||
#[inline]
|
#[inline]
|
||||||
fn push(&mut self, partition: &GridPartitionId, hash: &GridHash) {
|
fn push(&mut self, partition: &GridPartitionId, hash: &GridHash) {
|
||||||
if let Some(partition) = self.partitions.get_mut(partition) {
|
if let Some(partition) = self.partitions.get_mut(partition) {
|
||||||
partition.insert(*hash)
|
partition.insert(*hash);
|
||||||
} else {
|
} else {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -180,11 +182,7 @@ where
|
|||||||
fn merge(&mut self, partitions: &[GridPartitionId]) {
|
fn merge(&mut self, partitions: &[GridPartitionId]) {
|
||||||
let Some(largest_partition) = partitions
|
let Some(largest_partition) = partitions
|
||||||
.iter()
|
.iter()
|
||||||
.filter_map(|id| {
|
.filter_map(|id| self.resolve(id).map(GridPartition::num_cells).zip(Some(id)))
|
||||||
self.resolve(id)
|
|
||||||
.map(|partition| partition.num_cells())
|
|
||||||
.zip(Some(id))
|
|
||||||
})
|
|
||||||
.reduce(|acc, elem| if elem.0 > acc.0 { elem } else { acc })
|
.reduce(|acc, elem| if elem.0 > acc.0 { elem } else { acc })
|
||||||
.map(|(_cells, id)| id)
|
.map(|(_cells, id)| id)
|
||||||
else {
|
else {
|
||||||
@ -307,10 +305,10 @@ where
|
|||||||
// it means the partition has not been split, and we can continue to
|
// it means the partition has not been split, and we can continue to
|
||||||
// the next partition.
|
// the next partition.
|
||||||
return None;
|
return None;
|
||||||
} else {
|
|
||||||
new_partitions
|
|
||||||
.push(hash_grid.flood(&this_cell, None).map(|n| n.0).collect());
|
|
||||||
}
|
}
|
||||||
|
new_partitions
|
||||||
|
.push(hash_grid.flood(&this_cell, None).map(|n| n.0).collect());
|
||||||
|
|
||||||
counter += 1;
|
counter += 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -330,7 +328,7 @@ where
|
|||||||
{
|
{
|
||||||
// We want the original partition to retain the most cells to ensure that the smaller
|
// We want the original partition to retain the most cells to ensure that the smaller
|
||||||
// sets are the ones that are assigned a new partition ID.
|
// sets are the ones that are assigned a new partition ID.
|
||||||
new_partitions.sort_unstable_by_key(|set| set.len());
|
new_partitions.sort_unstable_by_key(HashSet::len);
|
||||||
if let Some(largest_partition) = new_partitions.pop() {
|
if let Some(largest_partition) = new_partitions.pop() {
|
||||||
partition_map.insert(*original_partition_id, largest_partition);
|
partition_map.insert(*original_partition_id, largest_partition);
|
||||||
}
|
}
|
||||||
@ -357,7 +355,7 @@ mod private {
|
|||||||
use super::{GridCell, GridHash};
|
use super::{GridCell, GridHash};
|
||||||
use crate::precision::GridPrecision;
|
use crate::precision::GridPrecision;
|
||||||
use bevy_ecs::prelude::*;
|
use bevy_ecs::prelude::*;
|
||||||
use bevy_utils::{hashbrown::HashSet, PassHash};
|
use bevy_platform_support::{collections::HashSet, hash::PassHash, prelude::*};
|
||||||
|
|
||||||
/// A group of nearby [`GridCell`](crate::GridCell)s in an island disconnected from all other
|
/// A group of nearby [`GridCell`](crate::GridCell)s in an island disconnected from all other
|
||||||
/// [`GridCell`](crate::GridCell)s.
|
/// [`GridCell`](crate::GridCell)s.
|
||||||
@ -385,7 +383,7 @@ mod private {
|
|||||||
/// Returns the total number of cells in this partition.
|
/// Returns the total number of cells in this partition.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn num_cells(&self) -> usize {
|
pub fn num_cells(&self) -> usize {
|
||||||
self.tables.iter().map(|t| t.len()).sum()
|
self.tables.iter().map(HashSet::len).sum()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The grid this partition resides in.
|
/// The grid this partition resides in.
|
||||||
@ -462,22 +460,23 @@ mod private {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub(crate) fn extend(&mut self, mut partition: GridPartition) {
|
pub(crate) fn extend(&mut self, mut other: GridPartition) {
|
||||||
for mut table in partition.tables.drain(..) {
|
assert_eq!(self.grid, other.grid);
|
||||||
if table.len() < Self::MIN_TABLE_SIZE {
|
|
||||||
|
for other_table in other.tables.drain(..) {
|
||||||
|
if other_table.len() < Self::MIN_TABLE_SIZE {
|
||||||
if let Some(i) = self.smallest_table() {
|
if let Some(i) = self.smallest_table() {
|
||||||
for hash in table.drain() {
|
self.tables[i].reserve(other_table.len());
|
||||||
self.tables[i].insert_unique_unchecked(hash);
|
self.tables[i].extend(other_table);
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
self.tables.push(table);
|
self.tables.push(other_table);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
self.tables.push(table);
|
self.tables.push(other_table);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
self.min = self.min.min(partition.min);
|
self.min = self.min.min(other.min);
|
||||||
self.max = self.max.max(partition.max);
|
self.max = self.max.max(other.max);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Removes a grid hash from the partition. Returns whether the value was present.
|
/// Removes a grid hash from the partition. Returns whether the value was present.
|
||||||
@ -512,12 +511,8 @@ mod private {
|
|||||||
/// partition.
|
/// partition.
|
||||||
#[inline]
|
#[inline]
|
||||||
fn compute_min(&mut self) {
|
fn compute_min(&mut self) {
|
||||||
if let Some(min) = self
|
if let Some(min) = self.iter().map(GridHash::cell).reduce(|acc, e| acc.min(e)) {
|
||||||
.iter()
|
self.min = min;
|
||||||
.map(|hash| hash.cell())
|
|
||||||
.reduce(|acc, e| acc.min(e))
|
|
||||||
{
|
|
||||||
self.min = min
|
|
||||||
} else {
|
} else {
|
||||||
self.min = GridCell::ONE * 1e10f64 as GridPrecision;
|
self.min = GridCell::ONE * 1e10f64 as GridPrecision;
|
||||||
}
|
}
|
||||||
@ -527,12 +522,8 @@ mod private {
|
|||||||
/// partition.
|
/// partition.
|
||||||
#[inline]
|
#[inline]
|
||||||
fn compute_max(&mut self) {
|
fn compute_max(&mut self) {
|
||||||
if let Some(max) = self
|
if let Some(max) = self.iter().map(GridHash::cell).reduce(|acc, e| acc.max(e)) {
|
||||||
.iter()
|
self.max = max;
|
||||||
.map(|hash| hash.cell())
|
|
||||||
.reduce(|acc, e| acc.max(e))
|
|
||||||
{
|
|
||||||
self.max = max
|
|
||||||
} else {
|
} else {
|
||||||
self.min = GridCell::ONE * -1e10 as GridPrecision;
|
self.min = GridCell::ONE * -1e10 as GridPrecision;
|
||||||
}
|
}
|
||||||
|
|||||||
16
src/lib.rs
16
src/lib.rs
@ -106,7 +106,7 @@
|
|||||||
//! grid, in high precision.
|
//! grid, in high precision.
|
||||||
//!
|
//!
|
||||||
//! Entities at the root of bevy's entity hierarchy are not in a grid. This allows plugins from the
|
//! Entities at the root of bevy's entity hierarchy are not in a grid. This allows plugins from the
|
||||||
//! rest of the ecosystem to operate normally, such as bevy_ui, which relies on the built-in
|
//! rest of the ecosystem to operate normally, such as `bevy_ui`, which relies on the built-in
|
||||||
//! transform propagation system. This also means that if you don't need to place entities in a
|
//! transform propagation system. This also means that if you don't need to place entities in a
|
||||||
//! high-precision grid, you don't have to, as the process is opt-in. The high-precision
|
//! high-precision grid, you don't have to, as the process is opt-in. The high-precision
|
||||||
//! hierarchical grids are explicit. Each high-precision tree must have a [`BigSpace`] at the root,
|
//! hierarchical grids are explicit. Each high-precision tree must have a [`BigSpace`] at the root,
|
||||||
@ -197,12 +197,17 @@
|
|||||||
|
|
||||||
#![allow(clippy::type_complexity)]
|
#![allow(clippy::type_complexity)]
|
||||||
#![warn(missing_docs)]
|
#![warn(missing_docs)]
|
||||||
|
#![no_std]
|
||||||
|
|
||||||
|
extern crate alloc;
|
||||||
|
|
||||||
#[allow(unused_imports)] // For docs
|
#[allow(unused_imports)] // For docs
|
||||||
use bevy_transform::prelude::*;
|
use bevy_transform::prelude::*;
|
||||||
#[allow(unused_imports)] // For docs
|
#[allow(unused_imports)] // For docs
|
||||||
use prelude::*;
|
use prelude::*;
|
||||||
|
|
||||||
|
pub(crate) mod portable_par;
|
||||||
|
|
||||||
pub mod bundles;
|
pub mod bundles;
|
||||||
pub mod commands;
|
pub mod commands;
|
||||||
pub mod floating_origins;
|
pub mod floating_origins;
|
||||||
@ -220,13 +225,11 @@ pub mod debug;
|
|||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests;
|
mod tests;
|
||||||
|
|
||||||
/// Common big_space imports.
|
/// Common `big_space` imports.
|
||||||
pub mod prelude {
|
pub mod prelude {
|
||||||
use crate::*;
|
use crate::*;
|
||||||
pub use bundles::{BigGridBundle, BigSpaceRootBundle, BigSpatialBundle};
|
pub use bundles::{BigGridBundle, BigSpaceRootBundle, BigSpatialBundle};
|
||||||
pub use commands::{BigSpaceCommands, GridCommands, SpatialEntityCommands};
|
pub use commands::{BigSpaceCommands, GridCommands, SpatialEntityCommands};
|
||||||
#[cfg(feature = "debug")]
|
|
||||||
pub use debug::FloatingOriginDebugPlugin;
|
|
||||||
pub use floating_origins::{BigSpace, FloatingOrigin};
|
pub use floating_origins::{BigSpace, FloatingOrigin};
|
||||||
pub use grid::{
|
pub use grid::{
|
||||||
cell::GridCell,
|
cell::GridCell,
|
||||||
@ -242,6 +245,11 @@ pub mod prelude {
|
|||||||
pub use plugin::{BigSpacePlugin, FloatingOriginSystem};
|
pub use plugin::{BigSpacePlugin, FloatingOriginSystem};
|
||||||
pub use precision::GridPrecision;
|
pub use precision::GridPrecision;
|
||||||
pub use world_query::{GridTransform, GridTransformOwned, GridTransformReadOnly};
|
pub use world_query::{GridTransform, GridTransformOwned, GridTransformReadOnly};
|
||||||
|
|
||||||
|
#[cfg(feature = "camera")]
|
||||||
|
pub use camera::{CameraController, CameraControllerPlugin};
|
||||||
|
#[cfg(feature = "debug")]
|
||||||
|
pub use debug::FloatingOriginDebugPlugin;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Contains the [`GridPrecision`] integer index type, which defines how much precision is available
|
/// Contains the [`GridPrecision`] integer index type, which defines how much precision is available
|
||||||
|
|||||||
159
src/plugin.rs
159
src/plugin.rs
@ -1,6 +1,7 @@
|
|||||||
//! The bevy plugin for big_space.
|
//! The bevy plugin for `big_space`.
|
||||||
|
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
|
use alloc::vec::Vec;
|
||||||
use bevy_app::prelude::*;
|
use bevy_app::prelude::*;
|
||||||
use bevy_ecs::prelude::*;
|
use bevy_ecs::prelude::*;
|
||||||
use bevy_transform::prelude::*;
|
use bevy_transform::prelude::*;
|
||||||
@ -39,9 +40,6 @@ pub enum FloatingOriginSystem {
|
|||||||
|
|
||||||
impl Plugin for BigSpacePlugin {
|
impl Plugin for BigSpacePlugin {
|
||||||
fn build(&self, app: &mut App) {
|
fn build(&self, app: &mut App) {
|
||||||
// Silence bevy's built-in error spam about GlobalTransforms in the hierarchy
|
|
||||||
app.insert_resource(bevy_hierarchy::ReportHierarchyIssue::<GlobalTransform>::new(false));
|
|
||||||
|
|
||||||
// Performance timings
|
// Performance timings
|
||||||
app.add_plugins(crate::timing::TimingStatsPlugin);
|
app.add_plugins(crate::timing::TimingStatsPlugin);
|
||||||
|
|
||||||
@ -72,6 +70,7 @@ impl Plugin for BigSpacePlugin {
|
|||||||
// Reflect
|
// Reflect
|
||||||
.register_type::<Transform>()
|
.register_type::<Transform>()
|
||||||
.register_type::<GlobalTransform>()
|
.register_type::<GlobalTransform>()
|
||||||
|
.register_type::<TransformTreeChanged>()
|
||||||
.register_type::<GridCell>()
|
.register_type::<GridCell>()
|
||||||
.register_type::<Grid>()
|
.register_type::<Grid>()
|
||||||
.register_type::<BigSpace>()
|
.register_type::<BigSpace>()
|
||||||
@ -101,18 +100,166 @@ impl Plugin for BigSpacePlugin {
|
|||||||
.add_systems(
|
.add_systems(
|
||||||
PostStartup,
|
PostStartup,
|
||||||
(
|
(
|
||||||
|
propagate_parent_transforms,
|
||||||
bevy_transform::systems::sync_simple_transforms,
|
bevy_transform::systems::sync_simple_transforms,
|
||||||
bevy_transform::systems::propagate_transforms,
|
|
||||||
)
|
)
|
||||||
.in_set(TransformSystem::TransformPropagate),
|
.in_set(TransformSystem::TransformPropagate),
|
||||||
)
|
)
|
||||||
.add_systems(
|
.add_systems(
|
||||||
PostUpdate,
|
PostUpdate,
|
||||||
(
|
(
|
||||||
|
propagate_parent_transforms,
|
||||||
bevy_transform::systems::sync_simple_transforms,
|
bevy_transform::systems::sync_simple_transforms,
|
||||||
bevy_transform::systems::propagate_transforms,
|
|
||||||
)
|
)
|
||||||
.in_set(TransformSystem::TransformPropagate),
|
.in_set(TransformSystem::TransformPropagate),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Copied from bevy. This is the simpler propagation implementation that doesn't use dirty tree
|
||||||
|
/// marking. This is needed because dirty tree marking doesn't start from the root, and will end up
|
||||||
|
/// doing the work for big space hierarchies, which it cannot affect anyway.
|
||||||
|
pub fn propagate_parent_transforms(
|
||||||
|
mut root_query: Query<
|
||||||
|
(Entity, &Children, Ref<Transform>, &mut GlobalTransform),
|
||||||
|
Without<ChildOf>,
|
||||||
|
>,
|
||||||
|
mut orphaned: RemovedComponents<ChildOf>,
|
||||||
|
transform_query: Query<
|
||||||
|
(Ref<Transform>, &mut GlobalTransform, Option<&Children>),
|
||||||
|
With<ChildOf>,
|
||||||
|
>,
|
||||||
|
child_query: Query<(Entity, Ref<ChildOf>), With<GlobalTransform>>,
|
||||||
|
mut orphaned_entities: Local<Vec<Entity>>,
|
||||||
|
) {
|
||||||
|
orphaned_entities.clear();
|
||||||
|
orphaned_entities.extend(orphaned.read());
|
||||||
|
orphaned_entities.sort_unstable();
|
||||||
|
root_query.par_iter_mut().for_each(
|
||||||
|
|(entity, children, transform, mut global_transform)| {
|
||||||
|
let changed = transform.is_changed() || global_transform.is_added() || orphaned_entities.binary_search(&entity).is_ok();
|
||||||
|
if changed {
|
||||||
|
*global_transform = GlobalTransform::from(*transform);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (child, child_of) in child_query.iter_many(children) {
|
||||||
|
assert_eq!(
|
||||||
|
child_of.parent, entity,
|
||||||
|
"Malformed hierarchy. This probably means that your hierarchy has been improperly maintained, or contains a cycle"
|
||||||
|
);
|
||||||
|
// SAFETY:
|
||||||
|
// - `child` must have consistent parentage, or the above assertion would panic.
|
||||||
|
// Since `child` is parented to a root entity, the entire hierarchy leading to it
|
||||||
|
// is consistent.
|
||||||
|
// - We may operate as if all descendants are consistent, since
|
||||||
|
// `propagate_recursive` will panic before continuing to propagate if it
|
||||||
|
// encounters an entity with inconsistent parentage.
|
||||||
|
// - Since each root entity is unique and the hierarchy is consistent and
|
||||||
|
// forest-like, other root entities' `propagate_recursive` calls will not conflict
|
||||||
|
// with this one.
|
||||||
|
// - Since this is the only place where `transform_query` gets used, there will be
|
||||||
|
// no conflicting fetches elsewhere.
|
||||||
|
#[expect(unsafe_code, reason = "`propagate_recursive()` is unsafe due to its use of `Query::get_unchecked()`.")]
|
||||||
|
unsafe {
|
||||||
|
propagate_recursive(
|
||||||
|
&global_transform,
|
||||||
|
&transform_query,
|
||||||
|
&child_query,
|
||||||
|
child,
|
||||||
|
changed || child_of.is_changed(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Recursively propagates the transforms for `entity` and all of its descendants.
|
||||||
|
///
|
||||||
|
/// # Panics
|
||||||
|
///
|
||||||
|
/// If `entity`'s descendants have a malformed hierarchy, this function will panic occur before
|
||||||
|
/// propagating the transforms of any malformed entities and their descendants.
|
||||||
|
///
|
||||||
|
/// # Safety
|
||||||
|
///
|
||||||
|
/// - While this function is running, `transform_query` must not have any fetches for `entity`,
|
||||||
|
/// nor any of its descendants.
|
||||||
|
/// - The caller must ensure that the hierarchy leading to `entity` is well-formed and must
|
||||||
|
/// remain as a tree or a forest. Each entity must have at most one parent.
|
||||||
|
#[expect(
|
||||||
|
unsafe_code,
|
||||||
|
reason = "This function uses `Query::get_unchecked()`, which can result in multiple mutable references if the preconditions are not met."
|
||||||
|
)]
|
||||||
|
unsafe fn propagate_recursive(
|
||||||
|
parent: &GlobalTransform,
|
||||||
|
transform_query: &Query<
|
||||||
|
(Ref<Transform>, &mut GlobalTransform, Option<&Children>),
|
||||||
|
With<ChildOf>,
|
||||||
|
>,
|
||||||
|
child_query: &Query<(Entity, Ref<ChildOf>), With<GlobalTransform>>,
|
||||||
|
entity: Entity,
|
||||||
|
mut changed: bool,
|
||||||
|
) {
|
||||||
|
let (global_matrix, children) = {
|
||||||
|
let Ok((transform, mut global_transform, children)) =
|
||||||
|
// SAFETY: This call cannot create aliased mutable references.
|
||||||
|
// - The top level iteration parallelizes on the roots of the hierarchy.
|
||||||
|
// - The caller ensures that each child has one and only one unique parent throughout
|
||||||
|
// the entire hierarchy.
|
||||||
|
//
|
||||||
|
// For example, consider the following malformed hierarchy:
|
||||||
|
//
|
||||||
|
// A
|
||||||
|
// / \
|
||||||
|
// B C
|
||||||
|
// \ /
|
||||||
|
// D
|
||||||
|
//
|
||||||
|
// D has two parents, B and C. If the propagation passes through C, but the ChildOf
|
||||||
|
// component on D points to B, the above check will panic as the origin parent does
|
||||||
|
// match the recorded parent.
|
||||||
|
//
|
||||||
|
// Also consider the following case, where A and B are roots:
|
||||||
|
//
|
||||||
|
// A B
|
||||||
|
// \ /
|
||||||
|
// C D
|
||||||
|
// \ /
|
||||||
|
// E
|
||||||
|
//
|
||||||
|
// Even if these A and B start two separate tasks running in parallel, one of them will
|
||||||
|
// panic before attempting to mutably access E.
|
||||||
|
(unsafe { transform_query.get_unchecked(entity) }) else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
changed |= transform.is_changed() || global_transform.is_added();
|
||||||
|
if changed {
|
||||||
|
*global_transform = parent.mul_transform(*transform);
|
||||||
|
}
|
||||||
|
(global_transform, children)
|
||||||
|
};
|
||||||
|
|
||||||
|
let Some(children) = children else { return };
|
||||||
|
for (child, child_of) in child_query.iter_many(children) {
|
||||||
|
assert_eq!(
|
||||||
|
child_of.parent, entity,
|
||||||
|
"Malformed hierarchy. This probably means that your hierarchy has been improperly maintained, or contains a cycle"
|
||||||
|
);
|
||||||
|
// SAFETY: The caller guarantees that `transform_query` will not be fetched for any
|
||||||
|
// descendants of `entity`, so it is safe to call `propagate_recursive` for each child.
|
||||||
|
//
|
||||||
|
// The above assertion ensures that each child has one and only one unique parent
|
||||||
|
// throughout the entire hierarchy.
|
||||||
|
unsafe {
|
||||||
|
propagate_recursive(
|
||||||
|
global_matrix.as_ref(),
|
||||||
|
transform_query,
|
||||||
|
child_query,
|
||||||
|
child,
|
||||||
|
changed || child_of.is_changed(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
126
src/portable_par.rs
Normal file
126
src/portable_par.rs
Normal file
@ -0,0 +1,126 @@
|
|||||||
|
//! Module for defining a `no_std` compatible `Parallel` thread local.
|
||||||
|
#![allow(dead_code)]
|
||||||
|
|
||||||
|
use alloc::vec::Vec;
|
||||||
|
use core::ops::DerefMut;
|
||||||
|
|
||||||
|
/// A no_std-compatible version of bevy's `Parallel`.
|
||||||
|
#[derive(Default)]
|
||||||
|
pub struct PortableParallel<T: Send>(
|
||||||
|
#[cfg(feature = "std")] bevy_utils::Parallel<T>,
|
||||||
|
#[cfg(not(feature = "std"))] bevy_platform_support::sync::RwLock<Option<T>>,
|
||||||
|
);
|
||||||
|
|
||||||
|
/// A scope guard of a `Parallel`, when this struct is dropped ,the value will writeback to its `Parallel`
|
||||||
|
impl<T: Send> PortableParallel<T> {
|
||||||
|
/// Gets a mutable iterator over all of the per-thread queues.
|
||||||
|
pub fn iter_mut(&mut self) -> impl Iterator<Item = impl DerefMut<Target = T> + '_> {
|
||||||
|
#[cfg(feature = "std")]
|
||||||
|
{
|
||||||
|
self.0.iter_mut()
|
||||||
|
}
|
||||||
|
#[cfg(not(feature = "std"))]
|
||||||
|
{
|
||||||
|
self.0.get_mut().unwrap().iter_mut()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Clears all of the stored thread local values.
|
||||||
|
pub fn clear(&mut self) {
|
||||||
|
#[cfg(feature = "std")]
|
||||||
|
self.0.clear();
|
||||||
|
#[cfg(not(feature = "std"))]
|
||||||
|
{
|
||||||
|
*self.0.write().unwrap() = None;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Default + Send> PortableParallel<T> {
|
||||||
|
/// Retrieves the thread-local value for the current thread and runs `f` on it.
|
||||||
|
///
|
||||||
|
/// If there is no thread-local value, it will be initialized to its default.
|
||||||
|
pub fn scope<R>(&self, f: impl FnOnce(&mut T) -> R) -> R {
|
||||||
|
#[cfg(feature = "std")]
|
||||||
|
let ret = self.0.scope(f);
|
||||||
|
#[cfg(not(feature = "std"))]
|
||||||
|
let ret = {
|
||||||
|
let mut cell = self.0.write().unwrap();
|
||||||
|
let value = cell.get_or_insert_default();
|
||||||
|
f(value)
|
||||||
|
};
|
||||||
|
ret
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Mutably borrows the thread-local value.
|
||||||
|
///
|
||||||
|
/// If there is no thread-local value, it will be initialized to it's default.
|
||||||
|
pub fn borrow_local_mut(&self) -> impl DerefMut<Target = T> + '_ {
|
||||||
|
#[cfg(feature = "std")]
|
||||||
|
let ret = self.0.borrow_local_mut();
|
||||||
|
#[cfg(not(feature = "std"))]
|
||||||
|
let ret = {
|
||||||
|
if self.0.read().unwrap().is_none() {
|
||||||
|
*self.0.write().unwrap() = Some(Default::default());
|
||||||
|
}
|
||||||
|
let opt = self.0.write().unwrap();
|
||||||
|
no_std_deref::UncheckedDerefMutWrapper(opt)
|
||||||
|
};
|
||||||
|
|
||||||
|
ret
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Needed until Parallel is portable. This assumes the value is `Some`.
|
||||||
|
#[cfg(not(feature = "std"))]
|
||||||
|
mod no_std_deref {
|
||||||
|
use bevy_platform_support::sync::RwLockWriteGuard;
|
||||||
|
use core::ops::{Deref, DerefMut};
|
||||||
|
|
||||||
|
pub struct UncheckedDerefMutWrapper<'a, T>(pub(super) RwLockWriteGuard<'a, Option<T>>);
|
||||||
|
|
||||||
|
impl<T> Deref for UncheckedDerefMutWrapper<'_, T> {
|
||||||
|
type Target = T;
|
||||||
|
fn deref(&self) -> &Self::Target {
|
||||||
|
self.0.as_ref().unwrap()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl<T> DerefMut for UncheckedDerefMutWrapper<'_, T> {
|
||||||
|
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||||
|
self.0.as_mut().unwrap()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T, I> PortableParallel<I>
|
||||||
|
where
|
||||||
|
I: IntoIterator<Item = T> + Default + Send + 'static,
|
||||||
|
{
|
||||||
|
/// Drains all enqueued items from all threads and returns an iterator over them.
|
||||||
|
///
|
||||||
|
/// Unlike [`Vec::drain`], this will piecemeal remove chunks of the data stored.
|
||||||
|
/// If iteration is terminated part way, the rest of the enqueued items in the same
|
||||||
|
/// chunk will be dropped, and the rest of the undrained elements will remain.
|
||||||
|
///
|
||||||
|
/// The ordering is not guaranteed.
|
||||||
|
pub fn drain(&mut self) -> impl Iterator<Item = T> + '_ {
|
||||||
|
#[cfg(feature = "std")]
|
||||||
|
let ret = self.0.drain();
|
||||||
|
#[cfg(not(feature = "std"))]
|
||||||
|
let ret = self.0.get_mut().unwrap().take().into_iter().flatten();
|
||||||
|
ret
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Send + 'static> PortableParallel<Vec<T>> {
|
||||||
|
/// Collect all enqueued items from all threads and appends them to the end of a
|
||||||
|
/// single Vec.
|
||||||
|
///
|
||||||
|
/// The ordering is not guaranteed.
|
||||||
|
pub fn drain_into(&mut self, out: &mut Vec<T>) {
|
||||||
|
#[cfg(feature = "std")]
|
||||||
|
self.0.drain_into(out);
|
||||||
|
#[cfg(not(feature = "std"))]
|
||||||
|
out.extend(self.drain());
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,6 +1,7 @@
|
|||||||
//! Timing statistics for transform propagation
|
//! Timing statistics for transform propagation
|
||||||
|
|
||||||
use std::{collections::VecDeque, iter::Sum, ops::Div, time::Duration};
|
use alloc::collections::VecDeque;
|
||||||
|
use core::{iter::Sum, ops::Div, time::Duration};
|
||||||
|
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
use bevy_app::prelude::*;
|
use bevy_app::prelude::*;
|
||||||
@ -12,7 +13,7 @@ use bevy_transform::TransformSystem;
|
|||||||
pub struct TimingStatsPlugin;
|
pub struct TimingStatsPlugin;
|
||||||
|
|
||||||
impl Plugin for TimingStatsPlugin {
|
impl Plugin for TimingStatsPlugin {
|
||||||
fn build(&self, app: &mut bevy_app::App) {
|
fn build(&self, app: &mut App) {
|
||||||
app.init_resource::<PropagationStats>()
|
app.init_resource::<PropagationStats>()
|
||||||
.register_type::<PropagationStats>()
|
.register_type::<PropagationStats>()
|
||||||
.init_resource::<GridHashStats>()
|
.init_resource::<GridHashStats>()
|
||||||
@ -108,7 +109,7 @@ impl PropagationStats {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> std::iter::Sum<&'a PropagationStats> for PropagationStats {
|
impl<'a> Sum<&'a PropagationStats> for PropagationStats {
|
||||||
fn sum<I: Iterator<Item = &'a PropagationStats>>(iter: I) -> Self {
|
fn sum<I: Iterator<Item = &'a PropagationStats>>(iter: I) -> Self {
|
||||||
iter.fold(PropagationStats::default(), |mut acc, e| {
|
iter.fold(PropagationStats::default(), |mut acc, e| {
|
||||||
acc.grid_recentering += e.grid_recentering;
|
acc.grid_recentering += e.grid_recentering;
|
||||||
@ -122,7 +123,7 @@ impl<'a> std::iter::Sum<&'a PropagationStats> for PropagationStats {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl std::ops::Div<u32> for PropagationStats {
|
impl Div<u32> for PropagationStats {
|
||||||
type Output = Self;
|
type Output = Self;
|
||||||
|
|
||||||
fn div(self, rhs: u32) -> Self::Output {
|
fn div(self, rhs: u32) -> Self::Output {
|
||||||
@ -178,7 +179,7 @@ impl GridHashStats {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> std::iter::Sum<&'a GridHashStats> for GridHashStats {
|
impl<'a> Sum<&'a GridHashStats> for GridHashStats {
|
||||||
fn sum<I: Iterator<Item = &'a GridHashStats>>(iter: I) -> Self {
|
fn sum<I: Iterator<Item = &'a GridHashStats>>(iter: I) -> Self {
|
||||||
iter.fold(GridHashStats::default(), |mut acc, e| {
|
iter.fold(GridHashStats::default(), |mut acc, e| {
|
||||||
acc.hash_update_duration += e.hash_update_duration;
|
acc.hash_update_duration += e.hash_update_duration;
|
||||||
|
|||||||
@ -1,9 +1,11 @@
|
|||||||
//! Tools for validating high-precision transform hierarchies
|
//! Tools for validating high-precision transform hierarchies
|
||||||
|
|
||||||
use bevy_ecs::prelude::*;
|
use bevy_ecs::prelude::*;
|
||||||
use bevy_hierarchy::prelude::*;
|
use bevy_platform_support::{
|
||||||
|
collections::{HashMap, HashSet},
|
||||||
|
prelude::*,
|
||||||
|
};
|
||||||
use bevy_transform::prelude::*;
|
use bevy_transform::prelude::*;
|
||||||
use bevy_utils::{HashMap, HashSet};
|
|
||||||
|
|
||||||
use crate::{grid::Grid, BigSpace, FloatingOrigin, GridCell};
|
use crate::{grid::Grid, BigSpace, FloatingOrigin, GridCell};
|
||||||
|
|
||||||
@ -16,7 +18,7 @@ struct ValidationStackEntry {
|
|||||||
struct ValidatorCaches {
|
struct ValidatorCaches {
|
||||||
query_state_cache: HashMap<&'static str, QueryState<(Entity, Option<&'static Children>)>>,
|
query_state_cache: HashMap<&'static str, QueryState<(Entity, Option<&'static Children>)>>,
|
||||||
validator_cache: HashMap<&'static str, Vec<Box<dyn ValidHierarchyNode>>>,
|
validator_cache: HashMap<&'static str, Vec<Box<dyn ValidHierarchyNode>>>,
|
||||||
root_query: Option<QueryState<Entity, Without<Parent>>>,
|
root_query: Option<QueryState<Entity, Without<ChildOf>>>,
|
||||||
stack: Vec<ValidationStackEntry>,
|
stack: Vec<ValidationStackEntry>,
|
||||||
/// Only report errors for an entity one time.
|
/// Only report errors for an entity one time.
|
||||||
error_entities: HashSet<Entity>,
|
error_entities: HashSet<Entity>,
|
||||||
@ -29,7 +31,7 @@ pub fn validate_hierarchy<V: 'static + ValidHierarchyNode + Default>(world: &mut
|
|||||||
|
|
||||||
let root_entities = caches
|
let root_entities = caches
|
||||||
.root_query
|
.root_query
|
||||||
.get_or_insert(world.query_filtered::<Entity, Without<Parent>>())
|
.get_or_insert(world.query_filtered::<Entity, Without<ChildOf>>())
|
||||||
.iter(world)
|
.iter(world)
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
@ -89,11 +91,15 @@ pub fn validate_hierarchy<V: 'static + ValidHierarchyNode + Default>(world: &mut
|
|||||||
});
|
});
|
||||||
|
|
||||||
let mut inspect = String::new();
|
let mut inspect = String::new();
|
||||||
world.inspect_entity(*entity).for_each(|info| {
|
world
|
||||||
inspect.push_str(" - ");
|
.inspect_entity(*entity)
|
||||||
inspect.push_str(info.name());
|
.into_iter()
|
||||||
inspect.push('\n');
|
.flatten()
|
||||||
});
|
.for_each(|info| {
|
||||||
|
inspect.push_str(" - ");
|
||||||
|
inspect.push_str(info.name());
|
||||||
|
inspect.push('\n');
|
||||||
|
});
|
||||||
|
|
||||||
tracing::error!("
|
tracing::error!("
|
||||||
-------------------------------------------
|
-------------------------------------------
|
||||||
@ -134,12 +140,13 @@ pub trait ValidHierarchyNode: sealed::CloneHierarchy + Send + Sync {
|
|||||||
fn allowed_child_nodes(&self) -> Vec<Box<dyn ValidHierarchyNode>>;
|
fn allowed_child_nodes(&self) -> Vec<Box<dyn ValidHierarchyNode>>;
|
||||||
/// A unique identifier of this type
|
/// A unique identifier of this type
|
||||||
fn name(&self) -> &'static str {
|
fn name(&self) -> &'static str {
|
||||||
std::any::type_name::<Self>()
|
core::any::type_name::<Self>()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
mod sealed {
|
mod sealed {
|
||||||
use super::ValidHierarchyNode;
|
use super::ValidHierarchyNode;
|
||||||
|
use bevy_platform_support::prelude::*;
|
||||||
|
|
||||||
pub trait CloneHierarchy {
|
pub trait CloneHierarchy {
|
||||||
fn clone_box(&self) -> Box<dyn ValidHierarchyNode>;
|
fn clone_box(&self) -> Box<dyn ValidHierarchyNode>;
|
||||||
@ -219,7 +226,7 @@ impl ValidHierarchyNode for RootFrame {
|
|||||||
.with::<GlobalTransform>()
|
.with::<GlobalTransform>()
|
||||||
.without::<GridCell>()
|
.without::<GridCell>()
|
||||||
.without::<Transform>()
|
.without::<Transform>()
|
||||||
.without::<Parent>()
|
.without::<ChildOf>()
|
||||||
.without::<FloatingOrigin>();
|
.without::<FloatingOrigin>();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -248,7 +255,7 @@ impl ValidHierarchyNode for RootSpatialLowPrecision {
|
|||||||
.without::<GridCell>()
|
.without::<GridCell>()
|
||||||
.without::<BigSpace>()
|
.without::<BigSpace>()
|
||||||
.without::<Grid>()
|
.without::<Grid>()
|
||||||
.without::<Parent>()
|
.without::<ChildOf>()
|
||||||
.without::<FloatingOrigin>();
|
.without::<FloatingOrigin>();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -274,7 +281,7 @@ impl ValidHierarchyNode for ChildFrame {
|
|||||||
.with::<GridCell>()
|
.with::<GridCell>()
|
||||||
.with::<Transform>()
|
.with::<Transform>()
|
||||||
.with::<GlobalTransform>()
|
.with::<GlobalTransform>()
|
||||||
.with::<Parent>()
|
.with::<ChildOf>()
|
||||||
.without::<BigSpace>();
|
.without::<BigSpace>();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -300,7 +307,7 @@ impl ValidHierarchyNode for ChildRootSpatialLowPrecision {
|
|||||||
query
|
query
|
||||||
.with::<Transform>()
|
.with::<Transform>()
|
||||||
.with::<GlobalTransform>()
|
.with::<GlobalTransform>()
|
||||||
.with::<Parent>()
|
.with::<ChildOf>()
|
||||||
.with::<crate::grid::propagation::LowPrecisionRoot>()
|
.with::<crate::grid::propagation::LowPrecisionRoot>()
|
||||||
.without::<GridCell>()
|
.without::<GridCell>()
|
||||||
.without::<BigSpace>()
|
.without::<BigSpace>()
|
||||||
@ -328,7 +335,7 @@ impl ValidHierarchyNode for ChildSpatialLowPrecision {
|
|||||||
query
|
query
|
||||||
.with::<Transform>()
|
.with::<Transform>()
|
||||||
.with::<GlobalTransform>()
|
.with::<GlobalTransform>()
|
||||||
.with::<Parent>()
|
.with::<ChildOf>()
|
||||||
.without::<GridCell>()
|
.without::<GridCell>()
|
||||||
.without::<BigSpace>()
|
.without::<BigSpace>()
|
||||||
.without::<Grid>()
|
.without::<Grid>()
|
||||||
@ -356,7 +363,7 @@ impl ValidHierarchyNode for ChildSpatialHighPrecision {
|
|||||||
.with::<GridCell>()
|
.with::<GridCell>()
|
||||||
.with::<Transform>()
|
.with::<Transform>()
|
||||||
.with::<GlobalTransform>()
|
.with::<GlobalTransform>()
|
||||||
.with::<Parent>()
|
.with::<ChildOf>()
|
||||||
.without::<BigSpace>()
|
.without::<BigSpace>()
|
||||||
.without::<Grid>();
|
.without::<Grid>();
|
||||||
}
|
}
|
||||||
|
|||||||
@ -68,7 +68,7 @@ pub struct GridTransformOwned {
|
|||||||
pub cell: GridCell,
|
pub cell: GridCell,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl std::ops::Sub for GridTransformOwned {
|
impl core::ops::Sub for GridTransformOwned {
|
||||||
type Output = Self;
|
type Output = Self;
|
||||||
|
|
||||||
/// Compute a new transform that maps from `source` to `self`.
|
/// Compute a new transform that maps from `source` to `self`.
|
||||||
@ -81,7 +81,7 @@ impl std::ops::Sub for GridTransformOwned {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl std::ops::Add for GridTransformOwned {
|
impl core::ops::Add for GridTransformOwned {
|
||||||
type Output = Self;
|
type Output = Self;
|
||||||
|
|
||||||
/// Compute a new transform that shifts, scales and rotates `self` by `diff`.
|
/// Compute a new transform that shifts, scales and rotates `self` by `diff`.
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user