mirror of
https://github.com/bevyengine/bevy
synced 2024-11-26 14:40:19 +00:00
Merge branch 'main' into transmission
This commit is contained in:
commit
c353b41bd6
388 changed files with 9154 additions and 6501 deletions
684
CHANGELOG.md
684
CHANGELOG.md
|
@ -7,7 +7,689 @@ While we try to keep the `Unreleased` changes updated, it is often behind and do
|
|||
all merged pull requests. To see a list of all changes since the latest release, you may compare
|
||||
current changes on git with [previous release tags][git_tag_comparison].
|
||||
|
||||
[git_tag_comparison]: https://github.com/bevyengine/bevy/compare/v0.9.0...main
|
||||
[git_tag_comparison]: https://github.com/bevyengine/bevy/compare/v0.10.0...main
|
||||
|
||||
## Version 0.11.0 (2023-07-09)
|
||||
|
||||
### Rendering
|
||||
|
||||
- [Webgpu support][8336]
|
||||
- [improve shader import model][5703]
|
||||
- [Screen Space Ambient Occlusion (SSAO) MVP][7402]
|
||||
- [Temporal Antialiasing (TAA)][7291]
|
||||
- [Immediate Mode Line/Gizmo Drawing][6529]
|
||||
- [Make render graph slots optional for most cases][8109]
|
||||
- [Split opaque and transparent phases][8090]
|
||||
- [Built-in skybox][8275]
|
||||
- [Add parallax mapping to bevy PBR][5928]
|
||||
- [Add port of AMD's Robust Contrast Adaptive Sharpening][7422]
|
||||
- [Add RenderGraphApp to simplify adding render nodes][8007]
|
||||
- [Add screenshot api][7163]
|
||||
- [Add morph targets][8158]
|
||||
- [Screenshots in wasm][8455]
|
||||
- [Add ViewNode to simplify render node management][8118]
|
||||
- [Bias texture mipmaps][7614]
|
||||
- [Instanced line rendering for gizmos based on `bevy_polyline`][8427]
|
||||
- [Add `RenderTarget::TextureView`][8042]
|
||||
- [Change default tonemapping method][8685]
|
||||
- [Allow custom depth texture usage][6815]
|
||||
- [Use the prepass normal texture in main pass when possible][8231]
|
||||
- [Left-handed y-up cubemap coordinates][8122]
|
||||
- [Allow SPIR-V shaders to process when shader defs are present][7772]
|
||||
- [Remove unnecesssary values Vec from DynamicUniformBuffer and DynamicStorageBuffer][8299]
|
||||
- [Add `MAY_DISCARD` shader def, enabling early depth tests for most cases][6697]
|
||||
- [Add `Aabb` calculation for `Sprite`, `TextureAtlasSprite` and `Mesh2d`][7885]
|
||||
- [Color::Lcha constructors][8041]
|
||||
- [Fix Color::as_rgba_linear for Color::Lcha][8040]
|
||||
- [Added Globals struct to prepass shader][8070]
|
||||
- [Derive Copy and Clone for Collision][8121]
|
||||
- [Fix crash when enabling HDR on 2d cameras][8151]
|
||||
- [Dither fix][7977]
|
||||
- [Compute `vertex_count` for indexed meshes on `GpuMesh`][8460]
|
||||
- [Run update_previous_view_projections in PreUpdate schedule][9024]
|
||||
- [Added `WebP` image format support][8220]
|
||||
- [Add support for pnm textures][8601]
|
||||
- [fix invalid bone weights][8316]
|
||||
- [Fix pbr shader breaking on missing UVs][8412]
|
||||
- [Fix Plane UVs / texture flip][8878]
|
||||
- [Fix look_to resulting in NaN rotations][7817]
|
||||
- [Fix look_to variable naming][8627]
|
||||
- [Fix segfault with 2d gizmos][8223]
|
||||
- [Use RenderGraphApp in more places][8298]
|
||||
- [Fix viewport change detection][8323]
|
||||
- [Remove capacity fields from all Buffer wrapper types][8301]
|
||||
- [Sync pbr_types.wgsl StandardMaterial values][8380]
|
||||
- [Avoid spawning gizmo meshes when no gizmos are being drawn][8180]
|
||||
- [Use a consistent seed for AABB gizmo colors][9030]
|
||||
- [bevy_pbr: Do not cull meshes without Aabbs from cascades][8444]
|
||||
- [Handle vertex_uvs if they are present in default prepass fragment shader][8330]
|
||||
- [Changed (Vec2, Vec2) to Rect in Camera::logical_viewport_rect][7867]
|
||||
- [make glsl and spirv support optional][8491]
|
||||
- [fix prepass normal_mapping][8978]
|
||||
- [conversions between [u8; 4] and Color][8564]
|
||||
- [Add option to disable gizmo rendering for specific cameras][8952]
|
||||
- [Fix morph target prepass shader][9013]
|
||||
- [Fix bloom wasm support][8631]
|
||||
- [Fix black spots appearing due to NANs when SSAO is enabled][8926]
|
||||
- [fix normal prepass][8890]
|
||||
- [Refs #8975 -- Add return to RenderDevice::poll()][8977]
|
||||
- [Fix WebGL mode for Adreno GPUs][8508]
|
||||
- [Fix parallax mapping][9003]
|
||||
- [Added Vec append to BufferVec - Issue #3531][8575]
|
||||
- [Fix CAS shader with explicit FullscreenVertexOutput import][8993]
|
||||
- [Make `TextureAtlas::texture_handles` `pub` instead of `pub(crate)` (#8633)][8643]
|
||||
- [Make Material2d pipeline systems public][8642]
|
||||
- [Fix screenshots on Wayland + Nvidia][8701]
|
||||
- [Apply codebase changes in preparation for `StandardMaterial` transmission][8704]
|
||||
- [Use ViewNode for TAA][8732]
|
||||
- [Change Camera3dBundle::tonemapping to Default][8753]
|
||||
- [Remove `Component` derive for AlphaMode][8804]
|
||||
- [Make setup of Opaque3dPrepass and AlphaMask3dPrepass phase items consistent with others][8408]
|
||||
- [Rename `Plane` struct to `HalfSpace`][8744]
|
||||
- [Expand `FallbackImage` to include a `GpuImage` for each possible `TextureViewDimension`][6974]
|
||||
- [Cascaded shadow maps: Fix prepass ortho depth clamping][8877]
|
||||
- [Fix gizmos in WebGPU][8910]
|
||||
- [Fix AsBindGroup derive, texture attribute, visibility flag parsing][8868]
|
||||
- [Disable camera on window close][8802]
|
||||
- [Reflect `Component` and `Default` of `BloomSettings`][8283]
|
||||
- [Add Reflection Macros to TextureAtlasSprite][8428]
|
||||
- [Implement Reflect on NoFrustumCulling][8801]
|
||||
|
||||
### Audio
|
||||
|
||||
- [ECS-based API redesign][8424]
|
||||
- [Ability to set a Global Volume][7706]
|
||||
- [Expose `AudioSink::empty()`][8145]
|
||||
|
||||
### Diagnostics
|
||||
|
||||
- [Allow systems using Diagnostics to run in parallel][8677]
|
||||
- [add a feature for memory tracing with tracy][8272]
|
||||
- [Re-add the "frame" span for tracy comparisons][8362]
|
||||
- [log to stderr instead of stdout][8886]
|
||||
|
||||
### Scenes
|
||||
|
||||
- [bevy_scene: Add SceneFilter][6793]
|
||||
- [(De) serialize resources in scenes][6846]
|
||||
- [add position to scene errors][8065]
|
||||
- [Bugfix: Scene reload fix (nonbreaking)][7951]
|
||||
- [avoid panic with parented scenes on deleted entities][8512]
|
||||
|
||||
### Transform + Hierarchy
|
||||
|
||||
- [Fix transform propagation of orphaned entities][7264]
|
||||
|
||||
### Gizmo
|
||||
|
||||
- [Add a bounding box gizmo][8468]
|
||||
- [Added `arc_2d` function for gizmos][8448]
|
||||
- [Use AHash to get color from entity in bevy_gizmos][8960]
|
||||
- [do not crash when rendering only one gizmo][8434]
|
||||
|
||||
### Reflection
|
||||
|
||||
- [reflect: stable type path v2][7184]
|
||||
- [bevy_reflect: Better proxies][6971]
|
||||
- [bevy_reflect: FromReflect Ergonomics Implementation][6056]
|
||||
- [bevy_reflect: Allow `#[reflect(default)]` on enum variant fields][8514]
|
||||
- [Add FromReflect where Reflect is used][8776]
|
||||
- [Add get_at_mut to bevy_reflect::Map trait][8691]
|
||||
- [Reflect now requires DynamicTypePath. Remove Reflect::get_type_path()][8764]
|
||||
- [bevy_ui: Add `FromReflect` derives][8495]
|
||||
- [Add Reflect and FromReflect for AssetPath][8531]
|
||||
- [bevy_reflect: Fix trailing comma breaking derives][8014]
|
||||
- [Fix Box dyn Reflect struct with a hashmap in it panicking when clone_value is called on it][8184]
|
||||
- [bevy_reflect: Add `ReflectFromReflect` to the prelude][8496]
|
||||
- [bevy_reflect: Allow construction of MapIter outside of the bevy_reflect crate.][8723]
|
||||
- [bevy_reflect: Disambiguate type bounds in where clauses.][8761]
|
||||
- [adding reflection for Cow<'static, [T]>][7454]
|
||||
- [Do not require mut on ParsedPath::element_mut][8891]
|
||||
- [Reflect UUID][8905]
|
||||
- [Don't ignore additional entries in `UntypedReflectDeserializerVisitor`][7112]
|
||||
- [Construct Box dyn Reflect from world for ReflectComponent][7407]
|
||||
- [reflect: avoid deadlock in GenericTypeCell][8957]
|
||||
|
||||
### App
|
||||
|
||||
- [Allow tuples and single plugins in `add_plugins`, deprecate `add_plugin`][8097]
|
||||
- [Merge ScheduleRunnerSettings into ScheduleRunnerPlugin][8585]
|
||||
- [correctly setup everything in the default run_once runner][8740]
|
||||
- [Fix `Plugin::build` detection][8103]
|
||||
- [Fix not calling App::finish and App::cleanup in `ScheduleRunnerPlugin`][9054]
|
||||
- [Relaxed runner type from Fn to FnOnce][8961]
|
||||
- [Relax FnMut to FnOnce in app::edit_schedule][8982]
|
||||
|
||||
### Windowing + Reflection
|
||||
|
||||
- [Register missing types in bevy_window][7993]
|
||||
- [bevy_reflect: implement Reflect for SmolStr][8771]
|
||||
|
||||
### Hierarchy
|
||||
|
||||
- [fix panic when moving child][8346]
|
||||
- [Remove `Children` component when calling `despawn_descendants`][8476]
|
||||
- [Change `despawn_descendants` to return `&mut Self`][8928]
|
||||
|
||||
### Time
|
||||
|
||||
- [Fix timer with zero duration][8467]
|
||||
|
||||
### Assets
|
||||
|
||||
- [Delay asset hot reloading][8503]
|
||||
- [Add support for custom glTF vertex attributes.][5370]
|
||||
- [Fix panic when using debug_asset_server][8485]
|
||||
- [`unused_variables` warning when building with `filesystem_watcher` feature disabled][7938]
|
||||
- [bevy_asset: Add `LoadContext::get_handle_untyped`][8470]
|
||||
|
||||
### Windowing
|
||||
|
||||
- [Move cursor position to internal state][7988]
|
||||
- [Set cursor hittest during window creation][7966]
|
||||
- [do not set hit test unconditionally on window creation][7996]
|
||||
- [Add winit's `wayland-csd-adwaita` feature to Bevy's `wayland` feature][8722]
|
||||
- [Support to set window theme and expose system window theme changed event][8593]
|
||||
- [Touchpad magnify and rotate events][8791]
|
||||
- [Fix windows not being centered properly when system interface is scaled][8903]
|
||||
- [Expose WindowDestroyed events][9016]
|
||||
|
||||
### Animation
|
||||
|
||||
- [Register bevy_animation::PlayingAnimation][9023]
|
||||
|
||||
### UI
|
||||
|
||||
- [Ui Node Borders][7795]
|
||||
- [Add CSS Grid support to `bevy_ui`][8026]
|
||||
- [`text_system` split][7779]
|
||||
- [Replace the local text queues in the text systems with flags stored in a component][8549]
|
||||
- [`NoWrap` `Text` feature][8947]
|
||||
- [add a default font][8445]
|
||||
- [UI texture atlas support][8822]
|
||||
- [Improved UI render batching][8793]
|
||||
- [Consistent screen-space coordinates][8306]
|
||||
- [`UiImage` helper functions][8199]
|
||||
- [Perform text scaling calculations per text, not per glyph][7819]
|
||||
- [Fix size of clipped text glyphs.][8197]
|
||||
- [Apply scale factor to `ImageMeasure` sizes][8545]
|
||||
- [Fix WebGPU error in "ui_pipeline" by adding a flat interpolate attribute][8933]
|
||||
- [Rename Interaction::Clicked -> Interaction::Pressed][9027]
|
||||
- [Flatten UI `Style` properties that use `Size` + remove `Size`][8548]
|
||||
- [Split UI `Overflow` by axis][8095]
|
||||
- [Add methods for calculating the size and postion of UI nodes][7930]
|
||||
- [Skip the UV calculations for untextured UI nodes][7809]
|
||||
- [Fix text measurement algorithm][8425]
|
||||
- [Divide by UiScale when converting UI coordinates from physical to logical][8720]
|
||||
- [`MeasureFunc` improvements][8402]
|
||||
- [Expose sorting methods in `Children`][8522]
|
||||
- [Fix min and max size using size value][7948]
|
||||
- [Fix the `Text2d` text anchor's incorrect horizontal alignment][8019]
|
||||
- [Remove `Val::Undefined`][7485]
|
||||
- [`Val` viewport unit variants][8137]
|
||||
- [Remove the corresponding measure from Taffy when a `CalculatedSize` component is removed.][8294]
|
||||
- [`UiRect` axes constructor][7656]
|
||||
- [Fix the UV calculations for clipped and flipped ImageNodes][8195]
|
||||
- [Fix text systems broken when resolving merge conflicts in #8026][8422]
|
||||
- [Allow `bevy_ui` crate to compile without the `text` feature enabled][8437]
|
||||
- [Fix the double leaf node updates in `flex_node_system`][8264]
|
||||
- [also import the default handle when feature disabled][8456]
|
||||
- [`measure_text_system` text query fix][8466]
|
||||
- [Fix panic in example: text_wrap_debug.rs][8497]
|
||||
- [UI layout tree debug print][8521]
|
||||
- [Fix `Node::physical_rect` and add a `physical_size` method][8551]
|
||||
- [Perform `relative_cursor_position` calculation vectorwise in `ui_focus_system`][8795]
|
||||
- [Add `UiRect::px()` and `UiRect::percent()` utils][8866]
|
||||
- [Add missing dependencies to `bevy_text` feature][8920]
|
||||
- [Remove "bevy_text" feature attributes on imports used by non-text systems][8907]
|
||||
- [Growing UI nodes Fix][8931]
|
||||
|
||||
### ECS
|
||||
|
||||
- [Schedule-First: the new and improved add_systems][8079]
|
||||
- [Add OnTransition schedule that is ran between OnExit and OnEnter][7936]
|
||||
- [`run_if` for `SystemConfigs` via anonymous system sets][7676]
|
||||
- [Remove OnUpdate system set][8260]
|
||||
- [Rename apply_system_buffers to apply_deferred][8726]
|
||||
- [Rename Command's "write" method to "apply"][8814]
|
||||
- [Require `#[derive(Event)]` on all Events][7086]
|
||||
- [Implement WorldQuery for EntityRef][6960]
|
||||
- [Improve or-with disjoint checks][7085]
|
||||
- [Add a method to run read-only systems using `&World`][8849]
|
||||
- [Reduce branching when inserting components][8053]
|
||||
- [Make `#[system_param(ignore)]` and `#[world_query(ignore)]` unnecessary][8030]
|
||||
- [Remove `#[system_param(ignore)]` and `#[world_query(ignore)]`][8265]
|
||||
- [Extend the `WorldQuery` macro to tuple structs][8119]
|
||||
- [Make state private and only accessible through getter for State resource][8009]
|
||||
- [implement `Deref` for `State<S>`][8668]
|
||||
- [Inline more ECS functions][8083]
|
||||
- [Add a `scope` API for world schedules][8387]
|
||||
- [Simplify system piping and make it more flexible][8377]
|
||||
- [Add `any_component_removed` condition][8326]
|
||||
- [Use `UnsafeWorldCell` to increase code quality for `SystemParam`][8174]
|
||||
- [Improve safety for the multi-threaded executor using `UnsafeWorldCell`][8292]
|
||||
- [Migrate the rest of the engine to `UnsafeWorldCell`][8833]
|
||||
- [Make the `Condition` trait generic][8721]
|
||||
- [Add or_else combinator to run_conditions.rs][8714]
|
||||
- [Add iter_many_manual QueryState method][8772]
|
||||
- [Provide access to world storages via UnsafeWorldCell][8987]
|
||||
- [Added Has T WorldQuery type][8844]
|
||||
- [Add/fix `track_caller` attribute on panicking entity accessor methods][8951]
|
||||
- [Increase type safety and clarity for change detection][7905]
|
||||
- [Make `WorldQuery` meta types unnameable][7964]
|
||||
- [Add a public constructor for `Mut<T>`][7931]
|
||||
- [Remove ChangeTrackers][7902]
|
||||
- [Derive Eq, PartialEq for Tick][9020]
|
||||
- [Initialize empty schedules when calling `.in_schedule` if they do not already exist][7911]
|
||||
- [Replace multiple calls to `add_system` with `add_systems`][8001]
|
||||
- [don't panic on unknown ambiguity][7950]
|
||||
- [add Clone to common conditions][8060]
|
||||
- [Make BundleInfo's fields not pub(crate)][8068]
|
||||
- [Pass query change ticks to `QueryParIter` instead of always using change ticks from `World`.][8029]
|
||||
- [Remove redundant bounds check in `Entities::get`][8108]
|
||||
- [Add World::try_run_schedule][8028]
|
||||
- [change not implemation to custom system struct][8105]
|
||||
- [Fix name conflicts caused by the `SystemParam` and `WorldQuery` macros][8012]
|
||||
- [Check for conflicting accesses in `assert_is_system`][8154]
|
||||
- [Fix field visibility for read-only `WorldQuery` types][8163]
|
||||
- [`Or<T>` should be a new type of `PhantomData<T>`][8212]
|
||||
- [Make standard commands more ergonomic (in niche cases)][8249]
|
||||
- [Remove base set error variants of `ScheduleBuildError`][8269]
|
||||
- [Replace some unsafe system executor code with safe code][8274]
|
||||
- [Update `increment_change_tick` to return a strongly-typed `Tick`][8295]
|
||||
- [Move event traces to detailed_trace!][7732]
|
||||
- [Only trigger state transitons if `next_state != old_state`][8359]
|
||||
- [Fix panics and docs when using World schedules][8364]
|
||||
- [Improve warning for Send resources marked as non_send][8000]
|
||||
- [Reorganize system modules][8419]
|
||||
- [Fix boxed labels][8436]
|
||||
- [Simplify world schedule methods][8403]
|
||||
- [Just print out name string, not the entire Name struct][8494]
|
||||
- [Manually implement common traits for `EventId`][8529]
|
||||
- [Replace remaining uses of `&T, Changed<T>` with `Ref` in UI system queries][8567]
|
||||
- [Rename `UnsafeWorldCell::read_change_tick`][8588]
|
||||
- [Improve encapsulation for commands and add docs][8725]
|
||||
- [Fix all_tuples + added docs.][8743]
|
||||
- [Add `new` and `map` methods to `Ref`][8797]
|
||||
- [Allow unsized types as mapped value in `Ref::map`][8817]
|
||||
- [Implement `Clone` for `CombinatorSystem`][8826]
|
||||
- [Add get_ref to EntityRef][8818]
|
||||
- [Make `QueryParIter::for_each_unchecked` private][8848]
|
||||
- [Simplify the `ComponentIdFor` type][8845]
|
||||
- [Add last_changed_tick and added_tick to ComponentTicks][8803]
|
||||
- [Require read-only queries in `QueryState::par_iter`][8832]
|
||||
- [Fix any_component_removed][8939]
|
||||
- [Deprecate type aliases for `WorldQuery::Fetch`][8843]
|
||||
- [bevy_ecs: add untyped methods for inserting components and bundles][7204]
|
||||
- [Move AppTypeRegistry to bevy_ecs][8901]
|
||||
- [skip check change tick for apply_deferred systems][8760]
|
||||
- [Split the bevy_ecs reflect.rs module][8834]
|
||||
- [Make function pointers of ecs Reflect* public][8687]
|
||||
|
||||
### Rendering + Reflection + Scenes
|
||||
|
||||
- [fix: register Cascade in the TypeRegistry][8088]
|
||||
|
||||
### Tasks
|
||||
|
||||
- [Add optional single-threaded feature to bevy_ecs/bevy_tasks][6690]
|
||||
|
||||
### Math
|
||||
|
||||
- [Re-export glam_assert feature][8232]
|
||||
- [Fix CubicCurve::iter_samples iteration count][8049]
|
||||
- [Add integer equivalents for `Rect`][7984]
|
||||
- [Add `CubicCurve::segment_count` + `iter_samples` adjustment][8711]
|
||||
|
||||
### Rendering + Assets + Meta
|
||||
|
||||
- [Add depending bevy features for higher level one][7855]
|
||||
|
||||
### ECS + Scenes
|
||||
|
||||
- [Make scene handling of entity references robust][7335]
|
||||
- [Rename map_entities and map_specific_entities][7570]
|
||||
|
||||
### Util
|
||||
|
||||
- [bevy_derive: Add `#[deref]` attribute][8552]
|
||||
|
||||
### Input
|
||||
|
||||
- [Add gamepad rumble support to bevy_input][8398]
|
||||
- [Rename keys like `LAlt` to `AltLeft`][8792]
|
||||
- [Add window entity to mouse and keyboard events][8852]
|
||||
- [Add get_unclamped to Axis][8871]
|
||||
|
||||
### Upgrades
|
||||
|
||||
- [Upgrade Taffy requirement to v0.3.5][7959]
|
||||
- [Update ruzstd and basis universal][8622]
|
||||
- [Updated to wgpu 0.16.0, wgpu-hal 0.16.0 and naga 0.12.0][8446]
|
||||
- [Update sysinfo requirement from 0.28.1 to 0.29.0][8650]
|
||||
- [Update libloading requirement from 0.7 to 0.8][8649]
|
||||
- [update syn, encase, glam and hexasphere][8573]
|
||||
- [Update android_log-sys requirement from 0.2.0 to 0.3.0][7925]
|
||||
- [update bitflags to 2.3][8728]
|
||||
- [Update ruzstd requirement from 0.3.1 to 0.4.0][8755]
|
||||
- [Update notify requirement from 5.0.0 to 6.0.0][8757]
|
||||
- [Bump hashbrown to 0.14][8904]
|
||||
- [update ahash and hashbrown][8623]
|
||||
- [Bump accesskit and accesskit_winit][8655]
|
||||
|
||||
### Examples
|
||||
|
||||
- [new example showcase tool][8561]
|
||||
- [Adding a bezier curve example][8194]
|
||||
- [Add low level post process example using a custom render pass][6909]
|
||||
- [Add example to demonstrate manual generation and UV mapping of 3D mesh (generate_custom_mesh) solve #4922][8909]
|
||||
- [Add `overflow_debug` example][8198]
|
||||
- [UI text wrapping and `LineBreakOn` example][7761]
|
||||
- [Size Constraints Example][7956]
|
||||
- [UI Display and Visibility Example][7629]
|
||||
|
||||
[5370]: https://github.com/bevyengine/bevy/pull/5370
|
||||
[5703]: https://github.com/bevyengine/bevy/pull/5703
|
||||
[5928]: https://github.com/bevyengine/bevy/pull/5928
|
||||
[6529]: https://github.com/bevyengine/bevy/pull/6529
|
||||
[6697]: https://github.com/bevyengine/bevy/pull/6697
|
||||
[6815]: https://github.com/bevyengine/bevy/pull/6815
|
||||
[6846]: https://github.com/bevyengine/bevy/pull/6846
|
||||
[6909]: https://github.com/bevyengine/bevy/pull/6909
|
||||
[6960]: https://github.com/bevyengine/bevy/pull/6960
|
||||
[6971]: https://github.com/bevyengine/bevy/pull/6971
|
||||
[6974]: https://github.com/bevyengine/bevy/pull/6974
|
||||
[7085]: https://github.com/bevyengine/bevy/pull/7085
|
||||
[7086]: https://github.com/bevyengine/bevy/pull/7086
|
||||
[7112]: https://github.com/bevyengine/bevy/pull/7112
|
||||
[7163]: https://github.com/bevyengine/bevy/pull/7163
|
||||
[7184]: https://github.com/bevyengine/bevy/pull/7184
|
||||
[7204]: https://github.com/bevyengine/bevy/pull/7204
|
||||
[7264]: https://github.com/bevyengine/bevy/pull/7264
|
||||
[7291]: https://github.com/bevyengine/bevy/pull/7291
|
||||
[7335]: https://github.com/bevyengine/bevy/pull/7335
|
||||
[7402]: https://github.com/bevyengine/bevy/pull/7402
|
||||
[7407]: https://github.com/bevyengine/bevy/pull/7407
|
||||
[7422]: https://github.com/bevyengine/bevy/pull/7422
|
||||
[7454]: https://github.com/bevyengine/bevy/pull/7454
|
||||
[7485]: https://github.com/bevyengine/bevy/pull/7485
|
||||
[7570]: https://github.com/bevyengine/bevy/pull/7570
|
||||
[7614]: https://github.com/bevyengine/bevy/pull/7614
|
||||
[7629]: https://github.com/bevyengine/bevy/pull/7629
|
||||
[7656]: https://github.com/bevyengine/bevy/pull/7656
|
||||
[7676]: https://github.com/bevyengine/bevy/pull/7676
|
||||
[7706]: https://github.com/bevyengine/bevy/pull/7706
|
||||
[7732]: https://github.com/bevyengine/bevy/pull/7732
|
||||
[7761]: https://github.com/bevyengine/bevy/pull/7761
|
||||
[7772]: https://github.com/bevyengine/bevy/pull/7772
|
||||
[7779]: https://github.com/bevyengine/bevy/pull/7779
|
||||
[7795]: https://github.com/bevyengine/bevy/pull/7795
|
||||
[7809]: https://github.com/bevyengine/bevy/pull/7809
|
||||
[7817]: https://github.com/bevyengine/bevy/pull/7817
|
||||
[7819]: https://github.com/bevyengine/bevy/pull/7819
|
||||
[7855]: https://github.com/bevyengine/bevy/pull/7855
|
||||
[7867]: https://github.com/bevyengine/bevy/pull/7867
|
||||
[7885]: https://github.com/bevyengine/bevy/pull/7885
|
||||
[7902]: https://github.com/bevyengine/bevy/pull/7902
|
||||
[7905]: https://github.com/bevyengine/bevy/pull/7905
|
||||
[7911]: https://github.com/bevyengine/bevy/pull/7911
|
||||
[7925]: https://github.com/bevyengine/bevy/pull/7925
|
||||
[7930]: https://github.com/bevyengine/bevy/pull/7930
|
||||
[7931]: https://github.com/bevyengine/bevy/pull/7931
|
||||
[7936]: https://github.com/bevyengine/bevy/pull/7936
|
||||
[7938]: https://github.com/bevyengine/bevy/pull/7938
|
||||
[7948]: https://github.com/bevyengine/bevy/pull/7948
|
||||
[7950]: https://github.com/bevyengine/bevy/pull/7950
|
||||
[7951]: https://github.com/bevyengine/bevy/pull/7951
|
||||
[7956]: https://github.com/bevyengine/bevy/pull/7956
|
||||
[7959]: https://github.com/bevyengine/bevy/pull/7959
|
||||
[7964]: https://github.com/bevyengine/bevy/pull/7964
|
||||
[7966]: https://github.com/bevyengine/bevy/pull/7966
|
||||
[7977]: https://github.com/bevyengine/bevy/pull/7977
|
||||
[7984]: https://github.com/bevyengine/bevy/pull/7984
|
||||
[7988]: https://github.com/bevyengine/bevy/pull/7988
|
||||
[7993]: https://github.com/bevyengine/bevy/pull/7993
|
||||
[7996]: https://github.com/bevyengine/bevy/pull/7996
|
||||
[8000]: https://github.com/bevyengine/bevy/pull/8000
|
||||
[8001]: https://github.com/bevyengine/bevy/pull/8001
|
||||
[8007]: https://github.com/bevyengine/bevy/pull/8007
|
||||
[8009]: https://github.com/bevyengine/bevy/pull/8009
|
||||
[8012]: https://github.com/bevyengine/bevy/pull/8012
|
||||
[8014]: https://github.com/bevyengine/bevy/pull/8014
|
||||
[8019]: https://github.com/bevyengine/bevy/pull/8019
|
||||
[8026]: https://github.com/bevyengine/bevy/pull/8026
|
||||
[8028]: https://github.com/bevyengine/bevy/pull/8028
|
||||
[8029]: https://github.com/bevyengine/bevy/pull/8029
|
||||
[8030]: https://github.com/bevyengine/bevy/pull/8030
|
||||
[8040]: https://github.com/bevyengine/bevy/pull/8040
|
||||
[8041]: https://github.com/bevyengine/bevy/pull/8041
|
||||
[8042]: https://github.com/bevyengine/bevy/pull/8042
|
||||
[8049]: https://github.com/bevyengine/bevy/pull/8049
|
||||
[8053]: https://github.com/bevyengine/bevy/pull/8053
|
||||
[8060]: https://github.com/bevyengine/bevy/pull/8060
|
||||
[8065]: https://github.com/bevyengine/bevy/pull/8065
|
||||
[8068]: https://github.com/bevyengine/bevy/pull/8068
|
||||
[8070]: https://github.com/bevyengine/bevy/pull/8070
|
||||
[8079]: https://github.com/bevyengine/bevy/pull/8079
|
||||
[8083]: https://github.com/bevyengine/bevy/pull/8083
|
||||
[8088]: https://github.com/bevyengine/bevy/pull/8088
|
||||
[8090]: https://github.com/bevyengine/bevy/pull/8090
|
||||
[8095]: https://github.com/bevyengine/bevy/pull/8095
|
||||
[8097]: https://github.com/bevyengine/bevy/pull/8097
|
||||
[8103]: https://github.com/bevyengine/bevy/pull/8103
|
||||
[8105]: https://github.com/bevyengine/bevy/pull/8105
|
||||
[8108]: https://github.com/bevyengine/bevy/pull/8108
|
||||
[8109]: https://github.com/bevyengine/bevy/pull/8109
|
||||
[8118]: https://github.com/bevyengine/bevy/pull/8118
|
||||
[8119]: https://github.com/bevyengine/bevy/pull/8119
|
||||
[8121]: https://github.com/bevyengine/bevy/pull/8121
|
||||
[8122]: https://github.com/bevyengine/bevy/pull/8122
|
||||
[8137]: https://github.com/bevyengine/bevy/pull/8137
|
||||
[8145]: https://github.com/bevyengine/bevy/pull/8145
|
||||
[8151]: https://github.com/bevyengine/bevy/pull/8151
|
||||
[8154]: https://github.com/bevyengine/bevy/pull/8154
|
||||
[8158]: https://github.com/bevyengine/bevy/pull/8158
|
||||
[8163]: https://github.com/bevyengine/bevy/pull/8163
|
||||
[8174]: https://github.com/bevyengine/bevy/pull/8174
|
||||
[8180]: https://github.com/bevyengine/bevy/pull/8180
|
||||
[8184]: https://github.com/bevyengine/bevy/pull/8184
|
||||
[8194]: https://github.com/bevyengine/bevy/pull/8194
|
||||
[8195]: https://github.com/bevyengine/bevy/pull/8195
|
||||
[8197]: https://github.com/bevyengine/bevy/pull/8197
|
||||
[8198]: https://github.com/bevyengine/bevy/pull/8198
|
||||
[8199]: https://github.com/bevyengine/bevy/pull/8199
|
||||
[8212]: https://github.com/bevyengine/bevy/pull/8212
|
||||
[8220]: https://github.com/bevyengine/bevy/pull/8220
|
||||
[8223]: https://github.com/bevyengine/bevy/pull/8223
|
||||
[8231]: https://github.com/bevyengine/bevy/pull/8231
|
||||
[8232]: https://github.com/bevyengine/bevy/pull/8232
|
||||
[8249]: https://github.com/bevyengine/bevy/pull/8249
|
||||
[8260]: https://github.com/bevyengine/bevy/pull/8260
|
||||
[8264]: https://github.com/bevyengine/bevy/pull/8264
|
||||
[8265]: https://github.com/bevyengine/bevy/pull/8265
|
||||
[8269]: https://github.com/bevyengine/bevy/pull/8269
|
||||
[8272]: https://github.com/bevyengine/bevy/pull/8272
|
||||
[8274]: https://github.com/bevyengine/bevy/pull/8274
|
||||
[8275]: https://github.com/bevyengine/bevy/pull/8275
|
||||
[8283]: https://github.com/bevyengine/bevy/pull/8283
|
||||
[8292]: https://github.com/bevyengine/bevy/pull/8292
|
||||
[8294]: https://github.com/bevyengine/bevy/pull/8294
|
||||
[8295]: https://github.com/bevyengine/bevy/pull/8295
|
||||
[8298]: https://github.com/bevyengine/bevy/pull/8298
|
||||
[8299]: https://github.com/bevyengine/bevy/pull/8299
|
||||
[8301]: https://github.com/bevyengine/bevy/pull/8301
|
||||
[8306]: https://github.com/bevyengine/bevy/pull/8306
|
||||
[8316]: https://github.com/bevyengine/bevy/pull/8316
|
||||
[8323]: https://github.com/bevyengine/bevy/pull/8323
|
||||
[8326]: https://github.com/bevyengine/bevy/pull/8326
|
||||
[8330]: https://github.com/bevyengine/bevy/pull/8330
|
||||
[8336]: https://github.com/bevyengine/bevy/pull/8336
|
||||
[8346]: https://github.com/bevyengine/bevy/pull/8346
|
||||
[8359]: https://github.com/bevyengine/bevy/pull/8359
|
||||
[8362]: https://github.com/bevyengine/bevy/pull/8362
|
||||
[8364]: https://github.com/bevyengine/bevy/pull/8364
|
||||
[8377]: https://github.com/bevyengine/bevy/pull/8377
|
||||
[8380]: https://github.com/bevyengine/bevy/pull/8380
|
||||
[8387]: https://github.com/bevyengine/bevy/pull/8387
|
||||
[8398]: https://github.com/bevyengine/bevy/pull/8398
|
||||
[8402]: https://github.com/bevyengine/bevy/pull/8402
|
||||
[8403]: https://github.com/bevyengine/bevy/pull/8403
|
||||
[8408]: https://github.com/bevyengine/bevy/pull/8408
|
||||
[8412]: https://github.com/bevyengine/bevy/pull/8412
|
||||
[8419]: https://github.com/bevyengine/bevy/pull/8419
|
||||
[8422]: https://github.com/bevyengine/bevy/pull/8422
|
||||
[8425]: https://github.com/bevyengine/bevy/pull/8425
|
||||
[8427]: https://github.com/bevyengine/bevy/pull/8427
|
||||
[8428]: https://github.com/bevyengine/bevy/pull/8428
|
||||
[8434]: https://github.com/bevyengine/bevy/pull/8434
|
||||
[8436]: https://github.com/bevyengine/bevy/pull/8436
|
||||
[8437]: https://github.com/bevyengine/bevy/pull/8437
|
||||
[8444]: https://github.com/bevyengine/bevy/pull/8444
|
||||
[8445]: https://github.com/bevyengine/bevy/pull/8445
|
||||
[8446]: https://github.com/bevyengine/bevy/pull/8446
|
||||
[8448]: https://github.com/bevyengine/bevy/pull/8448
|
||||
[8455]: https://github.com/bevyengine/bevy/pull/8455
|
||||
[8456]: https://github.com/bevyengine/bevy/pull/8456
|
||||
[8460]: https://github.com/bevyengine/bevy/pull/8460
|
||||
[8466]: https://github.com/bevyengine/bevy/pull/8466
|
||||
[8467]: https://github.com/bevyengine/bevy/pull/8467
|
||||
[8468]: https://github.com/bevyengine/bevy/pull/8468
|
||||
[8470]: https://github.com/bevyengine/bevy/pull/8470
|
||||
[8476]: https://github.com/bevyengine/bevy/pull/8476
|
||||
[8485]: https://github.com/bevyengine/bevy/pull/8485
|
||||
[8491]: https://github.com/bevyengine/bevy/pull/8491
|
||||
[8494]: https://github.com/bevyengine/bevy/pull/8494
|
||||
[8495]: https://github.com/bevyengine/bevy/pull/8495
|
||||
[8496]: https://github.com/bevyengine/bevy/pull/8496
|
||||
[8497]: https://github.com/bevyengine/bevy/pull/8497
|
||||
[8503]: https://github.com/bevyengine/bevy/pull/8503
|
||||
[8512]: https://github.com/bevyengine/bevy/pull/8512
|
||||
[8514]: https://github.com/bevyengine/bevy/pull/8514
|
||||
[8521]: https://github.com/bevyengine/bevy/pull/8521
|
||||
[8522]: https://github.com/bevyengine/bevy/pull/8522
|
||||
[8529]: https://github.com/bevyengine/bevy/pull/8529
|
||||
[8531]: https://github.com/bevyengine/bevy/pull/8531
|
||||
[8545]: https://github.com/bevyengine/bevy/pull/8545
|
||||
[8548]: https://github.com/bevyengine/bevy/pull/8548
|
||||
[8549]: https://github.com/bevyengine/bevy/pull/8549
|
||||
[8551]: https://github.com/bevyengine/bevy/pull/8551
|
||||
[8552]: https://github.com/bevyengine/bevy/pull/8552
|
||||
[8561]: https://github.com/bevyengine/bevy/pull/8561
|
||||
[8564]: https://github.com/bevyengine/bevy/pull/8564
|
||||
[8567]: https://github.com/bevyengine/bevy/pull/8567
|
||||
[8573]: https://github.com/bevyengine/bevy/pull/8573
|
||||
[8575]: https://github.com/bevyengine/bevy/pull/8575
|
||||
[8585]: https://github.com/bevyengine/bevy/pull/8585
|
||||
[8588]: https://github.com/bevyengine/bevy/pull/8588
|
||||
[8593]: https://github.com/bevyengine/bevy/pull/8593
|
||||
[8601]: https://github.com/bevyengine/bevy/pull/8601
|
||||
[8622]: https://github.com/bevyengine/bevy/pull/8622
|
||||
[8623]: https://github.com/bevyengine/bevy/pull/8623
|
||||
[8627]: https://github.com/bevyengine/bevy/pull/8627
|
||||
[8631]: https://github.com/bevyengine/bevy/pull/8631
|
||||
[8642]: https://github.com/bevyengine/bevy/pull/8642
|
||||
[8643]: https://github.com/bevyengine/bevy/pull/8643
|
||||
[8649]: https://github.com/bevyengine/bevy/pull/8649
|
||||
[8650]: https://github.com/bevyengine/bevy/pull/8650
|
||||
[8668]: https://github.com/bevyengine/bevy/pull/8668
|
||||
[8677]: https://github.com/bevyengine/bevy/pull/8677
|
||||
[8685]: https://github.com/bevyengine/bevy/pull/8685
|
||||
[8687]: https://github.com/bevyengine/bevy/pull/8687
|
||||
[8691]: https://github.com/bevyengine/bevy/pull/8691
|
||||
[8701]: https://github.com/bevyengine/bevy/pull/8701
|
||||
[8704]: https://github.com/bevyengine/bevy/pull/8704
|
||||
[8711]: https://github.com/bevyengine/bevy/pull/8711
|
||||
[8714]: https://github.com/bevyengine/bevy/pull/8714
|
||||
[8721]: https://github.com/bevyengine/bevy/pull/8721
|
||||
[8722]: https://github.com/bevyengine/bevy/pull/8722
|
||||
[8723]: https://github.com/bevyengine/bevy/pull/8723
|
||||
[8725]: https://github.com/bevyengine/bevy/pull/8725
|
||||
[8726]: https://github.com/bevyengine/bevy/pull/8726
|
||||
[8728]: https://github.com/bevyengine/bevy/pull/8728
|
||||
[8732]: https://github.com/bevyengine/bevy/pull/8732
|
||||
[8740]: https://github.com/bevyengine/bevy/pull/8740
|
||||
[8743]: https://github.com/bevyengine/bevy/pull/8743
|
||||
[8744]: https://github.com/bevyengine/bevy/pull/8744
|
||||
[8753]: https://github.com/bevyengine/bevy/pull/8753
|
||||
[8755]: https://github.com/bevyengine/bevy/pull/8755
|
||||
[8757]: https://github.com/bevyengine/bevy/pull/8757
|
||||
[8760]: https://github.com/bevyengine/bevy/pull/8760
|
||||
[8761]: https://github.com/bevyengine/bevy/pull/8761
|
||||
[8764]: https://github.com/bevyengine/bevy/pull/8764
|
||||
[8771]: https://github.com/bevyengine/bevy/pull/8771
|
||||
[8772]: https://github.com/bevyengine/bevy/pull/8772
|
||||
[8776]: https://github.com/bevyengine/bevy/pull/8776
|
||||
[8791]: https://github.com/bevyengine/bevy/pull/8791
|
||||
[8792]: https://github.com/bevyengine/bevy/pull/8792
|
||||
[8793]: https://github.com/bevyengine/bevy/pull/8793
|
||||
[8795]: https://github.com/bevyengine/bevy/pull/8795
|
||||
[8797]: https://github.com/bevyengine/bevy/pull/8797
|
||||
[8801]: https://github.com/bevyengine/bevy/pull/8801
|
||||
[8802]: https://github.com/bevyengine/bevy/pull/8802
|
||||
[8803]: https://github.com/bevyengine/bevy/pull/8803
|
||||
[8804]: https://github.com/bevyengine/bevy/pull/8804
|
||||
[8814]: https://github.com/bevyengine/bevy/pull/8814
|
||||
[8817]: https://github.com/bevyengine/bevy/pull/8817
|
||||
[8818]: https://github.com/bevyengine/bevy/pull/8818
|
||||
[8822]: https://github.com/bevyengine/bevy/pull/8822
|
||||
[8826]: https://github.com/bevyengine/bevy/pull/8826
|
||||
[8832]: https://github.com/bevyengine/bevy/pull/8832
|
||||
[8833]: https://github.com/bevyengine/bevy/pull/8833
|
||||
[8834]: https://github.com/bevyengine/bevy/pull/8834
|
||||
[8843]: https://github.com/bevyengine/bevy/pull/8843
|
||||
[8844]: https://github.com/bevyengine/bevy/pull/8844
|
||||
[8845]: https://github.com/bevyengine/bevy/pull/8845
|
||||
[8848]: https://github.com/bevyengine/bevy/pull/8848
|
||||
[8849]: https://github.com/bevyengine/bevy/pull/8849
|
||||
[8852]: https://github.com/bevyengine/bevy/pull/8852
|
||||
[8866]: https://github.com/bevyengine/bevy/pull/8866
|
||||
[8868]: https://github.com/bevyengine/bevy/pull/8868
|
||||
[8871]: https://github.com/bevyengine/bevy/pull/8871
|
||||
[8877]: https://github.com/bevyengine/bevy/pull/8877
|
||||
[8878]: https://github.com/bevyengine/bevy/pull/8878
|
||||
[8886]: https://github.com/bevyengine/bevy/pull/8886
|
||||
[8890]: https://github.com/bevyengine/bevy/pull/8890
|
||||
[8891]: https://github.com/bevyengine/bevy/pull/8891
|
||||
[8901]: https://github.com/bevyengine/bevy/pull/8901
|
||||
[8903]: https://github.com/bevyengine/bevy/pull/8903
|
||||
[8904]: https://github.com/bevyengine/bevy/pull/8904
|
||||
[8905]: https://github.com/bevyengine/bevy/pull/8905
|
||||
[8907]: https://github.com/bevyengine/bevy/pull/8907
|
||||
[8909]: https://github.com/bevyengine/bevy/pull/8909
|
||||
[8910]: https://github.com/bevyengine/bevy/pull/8910
|
||||
[8920]: https://github.com/bevyengine/bevy/pull/8920
|
||||
[8928]: https://github.com/bevyengine/bevy/pull/8928
|
||||
[8933]: https://github.com/bevyengine/bevy/pull/8933
|
||||
[8939]: https://github.com/bevyengine/bevy/pull/8939
|
||||
[8947]: https://github.com/bevyengine/bevy/pull/8947
|
||||
[8951]: https://github.com/bevyengine/bevy/pull/8951
|
||||
[8960]: https://github.com/bevyengine/bevy/pull/8960
|
||||
[8957]: https://github.com/bevyengine/bevy/pull/8957
|
||||
[9054]: https://github.com/bevyengine/bevy/pull/9054
|
||||
[6690]: https://github.com/bevyengine/bevy/pull/6690
|
||||
[8424]: https://github.com/bevyengine/bevy/pull/8424
|
||||
[8655]: https://github.com/bevyengine/bevy/pull/8655
|
||||
[6793]: https://github.com/bevyengine/bevy/pull/6793
|
||||
[8720]: https://github.com/bevyengine/bevy/pull/8720
|
||||
[9024]: https://github.com/bevyengine/bevy/pull/9024
|
||||
[9027]: https://github.com/bevyengine/bevy/pull/9027
|
||||
[9016]: https://github.com/bevyengine/bevy/pull/9016
|
||||
[9023]: https://github.com/bevyengine/bevy/pull/9023
|
||||
[9020]: https://github.com/bevyengine/bevy/pull/9020
|
||||
[9030]: https://github.com/bevyengine/bevy/pull/9030
|
||||
[9013]: https://github.com/bevyengine/bevy/pull/9013
|
||||
[8926]: https://github.com/bevyengine/bevy/pull/8926
|
||||
[9003]: https://github.com/bevyengine/bevy/pull/9003
|
||||
[8993]: https://github.com/bevyengine/bevy/pull/8993
|
||||
[8508]: https://github.com/bevyengine/bevy/pull/8508
|
||||
[6056]: https://github.com/bevyengine/bevy/pull/6056
|
||||
[8987]: https://github.com/bevyengine/bevy/pull/8987
|
||||
[8952]: https://github.com/bevyengine/bevy/pull/8952
|
||||
[8961]: https://github.com/bevyengine/bevy/pull/8961
|
||||
[8978]: https://github.com/bevyengine/bevy/pull/8978
|
||||
[8982]: https://github.com/bevyengine/bevy/pull/8982
|
||||
[8977]: https://github.com/bevyengine/bevy/pull/8977
|
||||
[8931]: https://github.com/bevyengine/bevy/pull/8931
|
||||
|
||||
## Version 0.10.0 (2023-03-06)
|
||||
|
||||
|
|
13
CREDITS.md
13
CREDITS.md
|
@ -21,8 +21,15 @@
|
|||
* Ground tile from [Kenney's Tower Defense Kit](https://www.kenney.nl/assets/tower-defense-kit) (CC0 1.0 Universal)
|
||||
* Game icons from [Kenney's Game Icons](https://www.kenney.nl/assets/game-icons) (CC0 1.0 Universal)
|
||||
* Space ships from [Kenny's Simple Space Kit](https://www.kenney.nl/assets/simple-space) (CC0 1.0 Universal)
|
||||
* glTF animated fox from [glTF Sample Models](https://github.com/KhronosGroup/glTF-Sample-Models/tree/master/2.0/Fox)
|
||||
* Low poly fox [by PixelMannen](https://opengameart.org/content/fox-and-shiba) (CC0 1.0 Universal)
|
||||
* Rigging and animation [by @tomkranis on Sketchfab](https://sketchfab.com/models/371dea88d7e04a76af5763f2a36866bc) ([CC-BY 4.0](https://creativecommons.org/licenses/by/4.0/))
|
||||
* glTF animated fox from [glTF Sample Models][fox]
|
||||
* Low poly fox [by PixelMannen] (CC0 1.0 Universal)
|
||||
* Rigging and animation [by @tomkranis on Sketchfab] ([CC-BY 4.0])
|
||||
* FiraMono by The Mozilla Foundation and Telefonica S.A (SIL Open Font License, Version 1.1: assets/fonts/FiraMono-LICENSE)
|
||||
* Barycentric from [mk_bary_gltf](https://github.com/komadori/mk_bary_gltf) (MIT OR Apache-2.0)
|
||||
* `MorphStressTest.gltf`, [MorphStressTest] ([CC-BY 4.0] by Analytical Graphics, Inc, Model and textures by Ed Mackey)
|
||||
|
||||
[MorphStressTest]: https://github.com/KhronosGroup/glTF-Sample-Models/tree/master/2.0/MorphStressTest
|
||||
[fox]: https://github.com/KhronosGroup/glTF-Sample-Models/tree/master/2.0/Fox
|
||||
[by PixelMannen]: https://opengameart.org/content/fox-and-shiba
|
||||
[by @tomkranis on Sketchfab]: https://sketchfab.com/models/371dea88d7e04a76af5763f2a36866bc
|
||||
[CC-BY 4.0]: https://creativecommons.org/licenses/by/4.0/
|
||||
|
|
33
Cargo.toml
33
Cargo.toml
|
@ -1,6 +1,6 @@
|
|||
[package]
|
||||
name = "bevy"
|
||||
version = "0.11.0-dev"
|
||||
version = "0.12.0-dev"
|
||||
edition = "2021"
|
||||
categories = ["game-engines", "graphics", "gui", "rendering"]
|
||||
description = "A refreshingly simple data-driven game engine and app framework"
|
||||
|
@ -44,6 +44,7 @@ default = [
|
|||
"bevy_sprite",
|
||||
"bevy_text",
|
||||
"bevy_ui",
|
||||
"multi-threaded",
|
||||
"png",
|
||||
"hdr",
|
||||
"ktx2",
|
||||
|
@ -95,7 +96,7 @@ bevy_scene = ["bevy_internal/bevy_scene", "bevy_asset"]
|
|||
bevy_sprite = ["bevy_internal/bevy_sprite", "bevy_render", "bevy_core_pipeline"]
|
||||
|
||||
# Provides text functionality
|
||||
bevy_text = ["bevy_internal/bevy_text"]
|
||||
bevy_text = ["bevy_internal/bevy_text", "bevy_asset", "bevy_sprite"]
|
||||
|
||||
# A custom ECS-driven UI framework
|
||||
bevy_ui = ["bevy_internal/bevy_ui", "bevy_core_pipeline", "bevy_text", "bevy_sprite"]
|
||||
|
@ -199,6 +200,9 @@ filesystem_watcher = ["bevy_internal/filesystem_watcher"]
|
|||
# Enable serialization support through serde
|
||||
serialize = ["bevy_internal/serialize"]
|
||||
|
||||
# Enables multithreaded parallelism in the engine. Disabling it forces all engine tasks to run on a single thread.
|
||||
multi-threaded = ["bevy_internal/multi-threaded"]
|
||||
|
||||
# Wayland display server support
|
||||
wayland = ["bevy_internal/wayland"]
|
||||
|
||||
|
@ -248,8 +252,8 @@ pbr_transmission_textures = ["bevy_internal/pbr_transmission_textures"]
|
|||
webgl2 = ["bevy_internal/webgl"]
|
||||
|
||||
[dependencies]
|
||||
bevy_dylib = { path = "crates/bevy_dylib", version = "0.11.0-dev", default-features = false, optional = true }
|
||||
bevy_internal = { path = "crates/bevy_internal", version = "0.11.0-dev", default-features = false }
|
||||
bevy_dylib = { path = "crates/bevy_dylib", version = "0.12.0-dev", default-features = false, optional = true }
|
||||
bevy_internal = { path = "crates/bevy_internal", version = "0.12.0-dev", default-features = false }
|
||||
|
||||
[dev-dependencies]
|
||||
anyhow = "1.0.4"
|
||||
|
@ -450,6 +454,16 @@ description = "A scene showcasing the built-in 3D shapes"
|
|||
category = "3D Rendering"
|
||||
wasm = true
|
||||
|
||||
[[example]]
|
||||
name = "generate_custom_mesh"
|
||||
path = "examples/3d/generate_custom_mesh.rs"
|
||||
|
||||
[package.metadata.example.generate_custom_mesh]
|
||||
name = "Generate Custom Mesh"
|
||||
description = "Simple showcase of how to generate a custom mesh with a custom texture"
|
||||
category = "3D Rendering"
|
||||
wasm = true
|
||||
|
||||
[[example]]
|
||||
name = "anti_aliasing"
|
||||
path = "examples/3d/anti_aliasing.rs"
|
||||
|
@ -563,7 +577,6 @@ wasm = true
|
|||
[[example]]
|
||||
name = "tonemapping"
|
||||
path = "examples/3d/tonemapping.rs"
|
||||
required-features = ["ktx2", "zstd"]
|
||||
|
||||
[package.metadata.example.tonemapping]
|
||||
name = "Tonemapping"
|
||||
|
@ -759,6 +772,16 @@ description = "Plays an animation from a skinned glTF"
|
|||
category = "Animation"
|
||||
wasm = true
|
||||
|
||||
[[example]]
|
||||
name = "morph_targets"
|
||||
path = "examples/animation/morph_targets.rs"
|
||||
|
||||
[package.metadata.example.morph_targets]
|
||||
name = "Morph Targets"
|
||||
description = "Plays an animation from a glTF file with meshes with morph targets"
|
||||
category = "Animation"
|
||||
wasm = true
|
||||
|
||||
[[example]]
|
||||
name = "animated_transform"
|
||||
path = "examples/animation/animated_transform.rs"
|
||||
|
|
|
@ -87,7 +87,7 @@ Bevy can be built just fine using default configuration on stable Rust. However
|
|||
|
||||
Bevy is only possible because of the hard work put into these foundational technologies:
|
||||
|
||||
* [wgpu](https://wgpu.rs/): modern / low-level / cross-platform graphics library inspired by Vulkan
|
||||
* [wgpu](https://wgpu.rs/): modern / low-level / cross-platform graphics library based on the [WebGPU](https://gpuweb.github.io/gpuweb/) API.
|
||||
* [glam-rs](https://github.com/bitshifter/glam-rs): a simple and fast 3D math library for games and graphics
|
||||
* [winit](https://github.com/rust-windowing/winit): cross-platform window creation and management in Rust
|
||||
|
||||
|
|
1055
assets/models/animated/MorphStressTest.gltf
Normal file
1055
assets/models/animated/MorphStressTest.gltf
Normal file
File diff suppressed because one or more lines are too long
|
@ -1,6 +1,6 @@
|
|||
#import bevy_pbr::mesh_types
|
||||
// The time since startup data is in the globals binding which is part of the mesh_view_bindings import
|
||||
#import bevy_pbr::mesh_view_bindings
|
||||
#import bevy_pbr::mesh_view_bindings globals
|
||||
#import bevy_pbr::mesh_vertex_output MeshVertexOutput
|
||||
|
||||
fn oklab_to_linear_srgb(c: vec3<f32>) -> vec3<f32> {
|
||||
let L = c.x;
|
||||
|
@ -22,12 +22,8 @@ fn oklab_to_linear_srgb(c: vec3<f32>) -> vec3<f32> {
|
|||
);
|
||||
}
|
||||
|
||||
struct FragmentInput {
|
||||
#import bevy_pbr::mesh_vertex_output
|
||||
}
|
||||
|
||||
@fragment
|
||||
fn fragment(in: FragmentInput) -> @location(0) vec4<f32> {
|
||||
fn fragment(in: MeshVertexOutput) -> @location(0) vec4<f32> {
|
||||
let speed = 2.0;
|
||||
// The globals binding contains various global values like time
|
||||
// which is the time since startup in seconds
|
||||
|
|
|
@ -1,60 +1,52 @@
|
|||
#import bevy_pbr::mesh_view_bindings
|
||||
#import bevy_pbr::mesh_bindings
|
||||
|
||||
#import bevy_pbr::pbr_types
|
||||
#import bevy_pbr::utils
|
||||
#import bevy_pbr::clustered_forward
|
||||
#import bevy_pbr::lighting
|
||||
#import bevy_pbr::shadows
|
||||
#import bevy_pbr::fog
|
||||
#import bevy_pbr::pbr_functions
|
||||
#import bevy_pbr::pbr_ambient
|
||||
#import bevy_pbr::mesh_vertex_output MeshVertexOutput
|
||||
#import bevy_pbr::mesh_view_bindings view
|
||||
#import bevy_pbr::pbr_types STANDARD_MATERIAL_FLAGS_DOUBLE_SIDED_BIT
|
||||
#import bevy_core_pipeline::tonemapping tone_mapping
|
||||
#import bevy_pbr::pbr_functions as fns
|
||||
|
||||
@group(1) @binding(0)
|
||||
var my_array_texture: texture_2d_array<f32>;
|
||||
@group(1) @binding(1)
|
||||
var my_array_texture_sampler: sampler;
|
||||
|
||||
struct FragmentInput {
|
||||
@builtin(front_facing) is_front: bool,
|
||||
@builtin(position) frag_coord: vec4<f32>,
|
||||
#import bevy_pbr::mesh_vertex_output
|
||||
};
|
||||
|
||||
@fragment
|
||||
fn fragment(in: FragmentInput) -> @location(0) vec4<f32> {
|
||||
let layer = i32(in.world_position.x) & 0x3;
|
||||
fn fragment(
|
||||
@builtin(front_facing) is_front: bool,
|
||||
mesh: MeshVertexOutput,
|
||||
) -> @location(0) vec4<f32> {
|
||||
let layer = i32(mesh.world_position.x) & 0x3;
|
||||
|
||||
// Prepare a 'processed' StandardMaterial by sampling all textures to resolve
|
||||
// the material members
|
||||
var pbr_input: PbrInput = pbr_input_new();
|
||||
var pbr_input: fns::PbrInput = fns::pbr_input_new();
|
||||
|
||||
pbr_input.material.base_color = textureSample(my_array_texture, my_array_texture_sampler, in.uv, layer);
|
||||
pbr_input.material.base_color = textureSample(my_array_texture, my_array_texture_sampler, mesh.uv, layer);
|
||||
#ifdef VERTEX_COLORS
|
||||
pbr_input.material.base_color = pbr_input.material.base_color * in.color;
|
||||
pbr_input.material.base_color = pbr_input.material.base_color * mesh.color;
|
||||
#endif
|
||||
|
||||
pbr_input.frag_coord = in.frag_coord;
|
||||
pbr_input.world_position = in.world_position;
|
||||
pbr_input.world_normal = prepare_world_normal(
|
||||
in.world_normal,
|
||||
pbr_input.frag_coord = mesh.position;
|
||||
pbr_input.world_position = mesh.world_position;
|
||||
pbr_input.world_normal = fns::prepare_world_normal(
|
||||
mesh.world_normal,
|
||||
(pbr_input.material.flags & STANDARD_MATERIAL_FLAGS_DOUBLE_SIDED_BIT) != 0u,
|
||||
in.is_front,
|
||||
is_front,
|
||||
);
|
||||
|
||||
pbr_input.is_orthographic = view.projection[3].w == 1.0;
|
||||
|
||||
pbr_input.N = apply_normal_mapping(
|
||||
pbr_input.N = fns::apply_normal_mapping(
|
||||
pbr_input.material.flags,
|
||||
pbr_input.world_normal,
|
||||
mesh.world_normal,
|
||||
#ifdef VERTEX_TANGENTS
|
||||
#ifdef STANDARDMATERIAL_NORMAL_MAP
|
||||
in.world_tangent,
|
||||
mesh.world_tangent,
|
||||
#endif
|
||||
#endif
|
||||
in.uv,
|
||||
mesh.uv,
|
||||
view.mip_bias,
|
||||
);
|
||||
pbr_input.V = calculate_view(in.world_position, pbr_input.is_orthographic);
|
||||
pbr_input.V = fns::calculate_view(mesh.world_position, pbr_input.is_orthographic);
|
||||
|
||||
return tone_mapping(pbr(pbr_input));
|
||||
return tone_mapping(fns::pbr(pbr_input), view.color_grading);
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
#import bevy_pbr::mesh_view_bindings
|
||||
#import bevy_pbr::mesh_vertex_output MeshVertexOutput
|
||||
|
||||
#ifdef CUBEMAP_ARRAY
|
||||
@group(1) @binding(0)
|
||||
|
@ -13,9 +13,9 @@ var base_color_sampler: sampler;
|
|||
|
||||
@fragment
|
||||
fn fragment(
|
||||
#import bevy_pbr::mesh_vertex_output
|
||||
mesh: MeshVertexOutput,
|
||||
) -> @location(0) vec4<f32> {
|
||||
let fragment_position_view_lh = world_position.xyz * vec3<f32>(1.0, 1.0, -1.0);
|
||||
let fragment_position_view_lh = mesh.world_position.xyz * vec3<f32>(1.0, 1.0, -1.0);
|
||||
return textureSample(
|
||||
base_color_texture,
|
||||
base_color_sampler,
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
#import bevy_sprite::mesh2d_view_bindings
|
||||
#import bevy_sprite::mesh2d_bindings
|
||||
#import bevy_sprite::mesh2d_functions
|
||||
#import bevy_sprite::mesh2d_view_bindings globals
|
||||
#import bevy_sprite::mesh2d_bindings mesh
|
||||
#import bevy_sprite::mesh2d_functions mesh2d_position_local_to_clip
|
||||
|
||||
struct Vertex {
|
||||
@location(0) position: vec3<f32>,
|
||||
|
|
|
@ -10,7 +10,11 @@ layout(set = 1, binding = 0) uniform CustomMaterial {
|
|||
layout(set = 1, binding = 1) uniform texture2D CustomMaterial_texture;
|
||||
layout(set = 1, binding = 2) uniform sampler CustomMaterial_sampler;
|
||||
|
||||
// wgsl modules can be imported and used in glsl
|
||||
// FIXME - this doesn't work any more ...
|
||||
// #import bevy_pbr::pbr_functions as PbrFuncs
|
||||
|
||||
void main() {
|
||||
// o_Target = PbrFuncs::tone_mapping(Color * texture(sampler2D(CustomMaterial_texture,CustomMaterial_sampler), v_Uv));
|
||||
o_Target = Color * texture(sampler2D(CustomMaterial_texture,CustomMaterial_sampler), v_Uv);
|
||||
}
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
#import bevy_pbr::mesh_vertex_output MeshVertexOutput
|
||||
|
||||
struct CustomMaterial {
|
||||
color: vec4<f32>,
|
||||
};
|
||||
|
@ -11,7 +13,7 @@ var base_color_sampler: sampler;
|
|||
|
||||
@fragment
|
||||
fn fragment(
|
||||
#import bevy_pbr::mesh_vertex_output
|
||||
mesh: MeshVertexOutput,
|
||||
) -> @location(0) vec4<f32> {
|
||||
return material.color * textureSample(base_color_texture, base_color_sampler, uv);
|
||||
return material.color * textureSample(base_color_texture, base_color_sampler, mesh.uv);
|
||||
}
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
#import bevy_pbr::mesh_view_bindings
|
||||
#import bevy_pbr::utils
|
||||
#import bevy_pbr::mesh_view_bindings view
|
||||
#import bevy_pbr::mesh_vertex_output MeshVertexOutput
|
||||
#import bevy_pbr::utils coords_to_viewport_uv
|
||||
|
||||
@group(1) @binding(0)
|
||||
var texture: texture_2d<f32>;
|
||||
|
@ -8,10 +9,9 @@ var texture_sampler: sampler;
|
|||
|
||||
@fragment
|
||||
fn fragment(
|
||||
@builtin(position) position: vec4<f32>,
|
||||
#import bevy_pbr::mesh_vertex_output
|
||||
mesh: MeshVertexOutput,
|
||||
) -> @location(0) vec4<f32> {
|
||||
let viewport_uv = coords_to_viewport_uv(position.xy, view.viewport);
|
||||
let viewport_uv = coords_to_viewport_uv(mesh.position.xy, view.viewport);
|
||||
let color = textureSample(texture, texture_sampler, viewport_uv);
|
||||
return color;
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
#import bevy_pbr::mesh_view_bindings
|
||||
#import bevy_pbr::mesh_bindings
|
||||
#import bevy_pbr::mesh_bindings mesh
|
||||
#import bevy_pbr::mesh_functions mesh_position_local_to_clip
|
||||
|
||||
struct CustomMaterial {
|
||||
color: vec4<f32>,
|
||||
|
@ -7,9 +7,6 @@ struct CustomMaterial {
|
|||
@group(1) @binding(0)
|
||||
var<uniform> material: CustomMaterial;
|
||||
|
||||
// NOTE: Bindings must come before functions that use them!
|
||||
#import bevy_pbr::mesh_functions
|
||||
|
||||
struct Vertex {
|
||||
@location(0) position: vec3<f32>,
|
||||
@location(1) blend_color: vec4<f32>,
|
||||
|
@ -23,7 +20,10 @@ struct VertexOutput {
|
|||
@vertex
|
||||
fn vertex(vertex: Vertex) -> VertexOutput {
|
||||
var out: VertexOutput;
|
||||
out.clip_position = mesh_position_local_to_clip(mesh.model, vec4<f32>(vertex.position, 1.0));
|
||||
out.clip_position = mesh_position_local_to_clip(
|
||||
mesh.model,
|
||||
vec4<f32>(vertex.position, 1.0)
|
||||
);
|
||||
out.blend_color = vertex.blend_color;
|
||||
return out;
|
||||
}
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
#import bevy_pbr::mesh_view_bindings
|
||||
#import bevy_pbr::mesh_bindings
|
||||
#import bevy_pbr::mesh_vertex_output MeshVertexOutput
|
||||
|
||||
@group(1) @binding(0)
|
||||
var test_texture_1d: texture_1d<f32>;
|
||||
|
@ -31,9 +32,5 @@ var test_texture_3d: texture_3d<f32>;
|
|||
@group(1) @binding(11)
|
||||
var test_texture_3d_sampler: sampler;
|
||||
|
||||
struct FragmentInput {
|
||||
#import bevy_pbr::mesh_vertex_output
|
||||
};
|
||||
|
||||
@fragment
|
||||
fn fragment(in: FragmentInput) {}
|
||||
fn fragment(in: MeshVertexOutput) {}
|
||||
|
|
|
@ -1,11 +1,5 @@
|
|||
#import bevy_pbr::mesh_types
|
||||
#import bevy_pbr::mesh_view_bindings
|
||||
|
||||
@group(1) @binding(0)
|
||||
var<uniform> mesh: Mesh;
|
||||
|
||||
// NOTE: Bindings must come before functions that use them!
|
||||
#import bevy_pbr::mesh_functions
|
||||
#import bevy_pbr::mesh_functions mesh_position_local_to_clip
|
||||
#import bevy_pbr::mesh_bindings mesh
|
||||
|
||||
struct Vertex {
|
||||
@location(0) position: vec3<f32>,
|
||||
|
@ -25,7 +19,10 @@ struct VertexOutput {
|
|||
fn vertex(vertex: Vertex) -> VertexOutput {
|
||||
let position = vertex.position * vertex.i_pos_scale.w + vertex.i_pos_scale.xyz;
|
||||
var out: VertexOutput;
|
||||
out.clip_position = mesh_position_local_to_clip(mesh.model, vec4<f32>(position, 1.0));
|
||||
out.clip_position = mesh_position_local_to_clip(
|
||||
mesh.model,
|
||||
vec4<f32>(position, 1.0)
|
||||
);
|
||||
out.color = vertex.i_color;
|
||||
return out;
|
||||
}
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
#import bevy_pbr::mesh_vertex_output MeshVertexOutput
|
||||
|
||||
struct LineMaterial {
|
||||
color: vec4<f32>,
|
||||
};
|
||||
|
@ -7,7 +9,7 @@ var<uniform> material: LineMaterial;
|
|||
|
||||
@fragment
|
||||
fn fragment(
|
||||
#import bevy_pbr::mesh_vertex_output
|
||||
mesh: MeshVertexOutput,
|
||||
) -> @location(0) vec4<f32> {
|
||||
return material.color;
|
||||
}
|
||||
|
|
|
@ -20,7 +20,7 @@
|
|||
// As you can see, the triangle ends up bigger than the screen.
|
||||
//
|
||||
// You don't need to worry about this too much since bevy will compute the correct UVs for you.
|
||||
#import bevy_core_pipeline::fullscreen_vertex_shader
|
||||
#import bevy_core_pipeline::fullscreen_vertex_shader FullscreenVertexOutput
|
||||
|
||||
@group(0) @binding(0)
|
||||
var screen_texture: texture_2d<f32>;
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
#import bevy_pbr::mesh_vertex_output MeshVertexOutput
|
||||
|
||||
struct CustomMaterial {
|
||||
color: vec4<f32>,
|
||||
};
|
||||
|
@ -7,7 +9,7 @@ var<uniform> material: CustomMaterial;
|
|||
|
||||
@fragment
|
||||
fn fragment(
|
||||
#import bevy_pbr::mesh_vertex_output
|
||||
mesh: MeshVertexOutput,
|
||||
) -> @location(0) vec4<f32> {
|
||||
#ifdef IS_RED
|
||||
return vec4<f32>(1.0, 0.0, 0.0, 1.0);
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
#import bevy_pbr::mesh_types
|
||||
#import bevy_pbr::mesh_view_bindings
|
||||
#import bevy_pbr::mesh_view_bindings globals
|
||||
#import bevy_pbr::prepass_utils
|
||||
#import bevy_pbr::mesh_vertex_output MeshVertexOutput
|
||||
|
||||
struct ShowPrepassSettings {
|
||||
show_depth: u32,
|
||||
|
@ -14,27 +15,26 @@ var<uniform> settings: ShowPrepassSettings;
|
|||
|
||||
@fragment
|
||||
fn fragment(
|
||||
@builtin(position) frag_coord: vec4<f32>,
|
||||
#ifdef MULTISAMPLED
|
||||
@builtin(sample_index) sample_index: u32,
|
||||
#endif
|
||||
#import bevy_pbr::mesh_vertex_output
|
||||
mesh: MeshVertexOutput,
|
||||
) -> @location(0) vec4<f32> {
|
||||
#ifndef MULTISAMPLED
|
||||
let sample_index = 0u;
|
||||
#endif
|
||||
if settings.show_depth == 1u {
|
||||
#ifdef PREPASS_DEPTH_SUPPORTED
|
||||
let depth = prepass_depth(frag_coord, sample_index);
|
||||
let depth = bevy_pbr::prepass_utils::prepass_depth(frag_coord, sample_index);
|
||||
#else
|
||||
let depth = 0.0;
|
||||
#endif
|
||||
return vec4(depth, depth, depth, 1.0);
|
||||
} else if settings.show_normals == 1u {
|
||||
let normal = prepass_normal(frag_coord, sample_index);
|
||||
let normal = bevy_pbr::prepass_utils::prepass_normal(mesh.position, sample_index);
|
||||
return vec4(normal, 1.0);
|
||||
} else if settings.show_motion_vectors == 1u {
|
||||
let motion_vector = prepass_motion_vector(frag_coord, sample_index);
|
||||
let motion_vector = bevy_pbr::prepass_utils::prepass_motion_vector(mesh.position, sample_index);
|
||||
return vec4(motion_vector / globals.delta_time, 0.0, 1.0);
|
||||
}
|
||||
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
#import bevy_pbr::mesh_vertex_output MeshVertexOutput
|
||||
|
||||
@group(1) @binding(0)
|
||||
var textures: binding_array<texture_2d<f32>>;
|
||||
@group(1) @binding(1)
|
||||
|
@ -7,11 +9,11 @@ var nearest_sampler: sampler;
|
|||
|
||||
@fragment
|
||||
fn fragment(
|
||||
#import bevy_pbr::mesh_vertex_output
|
||||
mesh: MeshVertexOutput,
|
||||
) -> @location(0) vec4<f32> {
|
||||
// Select the texture to sample from using non-uniform uv coordinates
|
||||
let coords = clamp(vec2<u32>(uv * 4.0), vec2<u32>(0u), vec2<u32>(3u));
|
||||
let coords = clamp(vec2<u32>(mesh.uv * 4.0), vec2<u32>(0u), vec2<u32>(3u));
|
||||
let index = coords.y * 4u + coords.x;
|
||||
let inner_uv = fract(uv * 4.0);
|
||||
let inner_uv = fract(mesh.uv * 4.0);
|
||||
return textureSample(textures[index], nearest_sampler, inner_uv);
|
||||
}
|
||||
|
|
|
@ -1,17 +1,12 @@
|
|||
#import bevy_pbr::mesh_view_bindings
|
||||
#import bevy_pbr::mesh_bindings
|
||||
#import bevy_pbr::utils
|
||||
#import bevy_pbr::mesh_vertex_output MeshVertexOutput
|
||||
#import bevy_pbr::utils PI
|
||||
|
||||
#ifdef TONEMAP_IN_SHADER
|
||||
#import bevy_core_pipeline::tonemapping
|
||||
#endif
|
||||
|
||||
struct FragmentInput {
|
||||
@builtin(front_facing) is_front: bool,
|
||||
@builtin(position) frag_coord: vec4<f32>,
|
||||
#import bevy_pbr::mesh_vertex_output
|
||||
};
|
||||
|
||||
// Sweep across hues on y axis with value from 0.0 to +15EV across x axis
|
||||
// quantized into 24 steps for both axis.
|
||||
fn color_sweep(uv: vec2<f32>) -> vec3<f32> {
|
||||
|
@ -47,7 +42,9 @@ fn continuous_hue(uv: vec2<f32>) -> vec3<f32> {
|
|||
}
|
||||
|
||||
@fragment
|
||||
fn fragment(in: FragmentInput) -> @location(0) vec4<f32> {
|
||||
fn fragment(
|
||||
in: MeshVertexOutput,
|
||||
) -> @location(0) vec4<f32> {
|
||||
var uv = in.uv;
|
||||
var out = vec3(0.0);
|
||||
if uv.y > 0.5 {
|
||||
|
@ -58,7 +55,7 @@ fn fragment(in: FragmentInput) -> @location(0) vec4<f32> {
|
|||
}
|
||||
var color = vec4(out, 1.0);
|
||||
#ifdef TONEMAP_IN_SHADER
|
||||
color = tone_mapping(color);
|
||||
color = tone_mapping(color, bevy_pbr::mesh_view_bindings::view.color_grading);
|
||||
#endif
|
||||
return color;
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
[package]
|
||||
name = "bevy_a11y"
|
||||
version = "0.11.0-dev"
|
||||
version = "0.12.0-dev"
|
||||
edition = "2021"
|
||||
description = "Provides accessibility support for Bevy Engine"
|
||||
homepage = "https://bevyengine.org"
|
||||
|
@ -10,8 +10,8 @@ keywords = ["bevy", "accessibility", "a11y"]
|
|||
|
||||
[dependencies]
|
||||
# bevy
|
||||
bevy_app = { path = "../bevy_app", version = "0.11.0-dev" }
|
||||
bevy_derive = { path = "../bevy_derive", version = "0.11.0-dev" }
|
||||
bevy_ecs = { path = "../bevy_ecs", version = "0.11.0-dev" }
|
||||
bevy_app = { path = "../bevy_app", version = "0.12.0-dev" }
|
||||
bevy_derive = { path = "../bevy_derive", version = "0.12.0-dev" }
|
||||
bevy_ecs = { path = "../bevy_ecs", version = "0.12.0-dev" }
|
||||
|
||||
accesskit = "0.10"
|
||||
accesskit = "0.11"
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
[package]
|
||||
name = "bevy_animation"
|
||||
version = "0.11.0-dev"
|
||||
version = "0.12.0-dev"
|
||||
edition = "2021"
|
||||
description = "Provides animation functionality for Bevy Engine"
|
||||
homepage = "https://bevyengine.org"
|
||||
|
@ -10,13 +10,14 @@ keywords = ["bevy"]
|
|||
|
||||
[dependencies]
|
||||
# bevy
|
||||
bevy_app = { path = "../bevy_app", version = "0.11.0-dev" }
|
||||
bevy_asset = { path = "../bevy_asset", version = "0.11.0-dev" }
|
||||
bevy_core = { path = "../bevy_core", version = "0.11.0-dev" }
|
||||
bevy_math = { path = "../bevy_math", version = "0.11.0-dev" }
|
||||
bevy_reflect = { path = "../bevy_reflect", version = "0.11.0-dev", features = ["bevy"] }
|
||||
bevy_time = { path = "../bevy_time", version = "0.11.0-dev" }
|
||||
bevy_utils = { path = "../bevy_utils", version = "0.11.0-dev" }
|
||||
bevy_ecs = { path = "../bevy_ecs", version = "0.11.0-dev" }
|
||||
bevy_transform = { path = "../bevy_transform", version = "0.11.0-dev" }
|
||||
bevy_hierarchy = { path = "../bevy_hierarchy", version = "0.11.0-dev" }
|
||||
bevy_app = { path = "../bevy_app", version = "0.12.0-dev" }
|
||||
bevy_asset = { path = "../bevy_asset", version = "0.12.0-dev" }
|
||||
bevy_core = { path = "../bevy_core", version = "0.12.0-dev" }
|
||||
bevy_math = { path = "../bevy_math", version = "0.12.0-dev" }
|
||||
bevy_reflect = { path = "../bevy_reflect", version = "0.12.0-dev", features = ["bevy"] }
|
||||
bevy_render = { path = "../bevy_render", version = "0.12.0-dev" }
|
||||
bevy_time = { path = "../bevy_time", version = "0.12.0-dev" }
|
||||
bevy_utils = { path = "../bevy_utils", version = "0.12.0-dev" }
|
||||
bevy_ecs = { path = "../bevy_ecs", version = "0.12.0-dev" }
|
||||
bevy_transform = { path = "../bevy_transform", version = "0.12.0-dev" }
|
||||
bevy_hierarchy = { path = "../bevy_hierarchy", version = "0.12.0-dev" }
|
||||
|
|
|
@ -12,7 +12,8 @@ use bevy_core::Name;
|
|||
use bevy_ecs::prelude::*;
|
||||
use bevy_hierarchy::{Children, Parent};
|
||||
use bevy_math::{Quat, Vec3};
|
||||
use bevy_reflect::{FromReflect, Reflect, TypeUuid};
|
||||
use bevy_reflect::{Reflect, TypeUuid};
|
||||
use bevy_render::mesh::morph::MorphWeights;
|
||||
use bevy_time::Time;
|
||||
use bevy_transform::{prelude::Transform, TransformSystem};
|
||||
use bevy_utils::{tracing::warn, HashMap};
|
||||
|
@ -26,7 +27,7 @@ pub mod prelude {
|
|||
}
|
||||
|
||||
/// List of keyframes for one of the attribute of a [`Transform`].
|
||||
#[derive(Reflect, FromReflect, Clone, Debug)]
|
||||
#[derive(Reflect, Clone, Debug)]
|
||||
pub enum Keyframes {
|
||||
/// Keyframes for rotation.
|
||||
Rotation(Vec<Quat>),
|
||||
|
@ -34,12 +35,21 @@ pub enum Keyframes {
|
|||
Translation(Vec<Vec3>),
|
||||
/// Keyframes for scale.
|
||||
Scale(Vec<Vec3>),
|
||||
/// Keyframes for morph target weights.
|
||||
///
|
||||
/// Note that in `.0`, each contiguous `target_count` values is a single
|
||||
/// keyframe representing the weight values at given keyframe.
|
||||
///
|
||||
/// This follows the [glTF design].
|
||||
///
|
||||
/// [glTF design]: https://registry.khronos.org/glTF/specs/2.0/glTF-2.0.html#animations
|
||||
Weights(Vec<f32>),
|
||||
}
|
||||
|
||||
/// Describes how an attribute of a [`Transform`] should be animated.
|
||||
/// Describes how an attribute of a [`Transform`] or [`MorphWeights`] should be animated.
|
||||
///
|
||||
/// `keyframe_timestamps` and `keyframes` should have the same length.
|
||||
#[derive(Reflect, FromReflect, Clone, Debug)]
|
||||
#[derive(Reflect, Clone, Debug)]
|
||||
pub struct VariableCurve {
|
||||
/// Timestamp for each of the keyframes.
|
||||
pub keyframe_timestamps: Vec<f32>,
|
||||
|
@ -48,14 +58,14 @@ pub struct VariableCurve {
|
|||
}
|
||||
|
||||
/// Path to an entity, with [`Name`]s. Each entity in a path must have a name.
|
||||
#[derive(Reflect, FromReflect, Clone, Debug, Hash, PartialEq, Eq, Default)]
|
||||
#[derive(Reflect, Clone, Debug, Hash, PartialEq, Eq, Default)]
|
||||
pub struct EntityPath {
|
||||
/// Parts of the path
|
||||
pub parts: Vec<Name>,
|
||||
}
|
||||
|
||||
/// A list of [`VariableCurve`], and the [`EntityPath`] to which they apply.
|
||||
#[derive(Reflect, FromReflect, Clone, TypeUuid, Debug, Default)]
|
||||
#[derive(Reflect, Clone, TypeUuid, Debug, Default)]
|
||||
#[uuid = "d81b7179-0448-4eb0-89fe-c067222725bf"]
|
||||
pub struct AnimationClip {
|
||||
curves: Vec<Vec<VariableCurve>>,
|
||||
|
@ -106,6 +116,11 @@ impl AnimationClip {
|
|||
self.paths.insert(path, idx);
|
||||
}
|
||||
}
|
||||
|
||||
/// Whether this animation clip can run on entity with given [`Name`].
|
||||
pub fn compatible_with(&self, name: &Name) -> bool {
|
||||
self.paths.keys().all(|path| &path.parts[0] == name)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Reflect)]
|
||||
|
@ -270,7 +285,7 @@ impl AnimationPlayer {
|
|||
}
|
||||
}
|
||||
|
||||
fn find_bone(
|
||||
fn entity_from_path(
|
||||
root: Entity,
|
||||
path: &EntityPath,
|
||||
children: &Query<&Children>,
|
||||
|
@ -336,12 +351,14 @@ fn verify_no_ancestor_player(
|
|||
|
||||
/// System that will play all animations, using any entity with a [`AnimationPlayer`]
|
||||
/// and a [`Handle<AnimationClip>`] as an animation root
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub fn animation_player(
|
||||
time: Res<Time>,
|
||||
animations: Res<Assets<AnimationClip>>,
|
||||
children: Query<&Children>,
|
||||
names: Query<&Name>,
|
||||
transforms: Query<&mut Transform>,
|
||||
morphs: Query<&mut MorphWeights>,
|
||||
parents: Query<(Option<With<AnimationPlayer>>, Option<&Parent>)>,
|
||||
mut animation_players: Query<(Entity, Option<&Parent>, &mut AnimationPlayer)>,
|
||||
) {
|
||||
|
@ -356,6 +373,7 @@ pub fn animation_player(
|
|||
&animations,
|
||||
&names,
|
||||
&transforms,
|
||||
&morphs,
|
||||
maybe_parent,
|
||||
&parents,
|
||||
&children,
|
||||
|
@ -371,6 +389,7 @@ fn run_animation_player(
|
|||
animations: &Assets<AnimationClip>,
|
||||
names: &Query<&Name>,
|
||||
transforms: &Query<&mut Transform>,
|
||||
morphs: &Query<&mut MorphWeights>,
|
||||
maybe_parent: Option<&Parent>,
|
||||
parents: &Query<(Option<With<AnimationPlayer>>, Option<&Parent>)>,
|
||||
children: &Query<&Children>,
|
||||
|
@ -392,6 +411,7 @@ fn run_animation_player(
|
|||
animations,
|
||||
names,
|
||||
transforms,
|
||||
morphs,
|
||||
maybe_parent,
|
||||
parents,
|
||||
children,
|
||||
|
@ -413,6 +433,7 @@ fn run_animation_player(
|
|||
animations,
|
||||
names,
|
||||
transforms,
|
||||
morphs,
|
||||
maybe_parent,
|
||||
parents,
|
||||
children,
|
||||
|
@ -420,6 +441,28 @@ fn run_animation_player(
|
|||
}
|
||||
}
|
||||
|
||||
/// Update `weights` based on weights in `keyframes` at index `key_index`
|
||||
/// with a linear interpolation on `key_lerp`.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// When `key_index * target_count` is larger than `keyframes`
|
||||
///
|
||||
/// This happens when `keyframes` is not formatted as described in
|
||||
/// [`Keyframes::Weights`]. A possible cause is [`AnimationClip`] not being
|
||||
/// meant to be used for the [`MorphWeights`] of the entity it's being applied to.
|
||||
fn lerp_morph_weights(weights: &mut [f32], key_lerp: f32, keyframes: &[f32], key_index: usize) {
|
||||
let target_count = weights.len();
|
||||
let start = target_count * key_index;
|
||||
let end = target_count * (key_index + 1);
|
||||
|
||||
let zipped = weights.iter_mut().zip(&keyframes[start..end]);
|
||||
for (morph_weight, keyframe) in zipped {
|
||||
let minus_lerp = 1.0 - key_lerp;
|
||||
*morph_weight = (*morph_weight * minus_lerp) + (keyframe * key_lerp);
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
fn apply_animation(
|
||||
weight: f32,
|
||||
|
@ -430,6 +473,7 @@ fn apply_animation(
|
|||
animations: &Assets<AnimationClip>,
|
||||
names: &Query<&Name>,
|
||||
transforms: &Query<&mut Transform>,
|
||||
morphs: &Query<&mut MorphWeights>,
|
||||
maybe_parent: Option<&Parent>,
|
||||
parents: &Query<(Option<With<AnimationPlayer>>, Option<&Parent>)>,
|
||||
children: &Query<&Children>,
|
||||
|
@ -456,7 +500,7 @@ fn apply_animation(
|
|||
for (path, bone_id) in &animation_clip.paths {
|
||||
let cached_path = &mut animation.path_cache[*bone_id];
|
||||
let curves = animation_clip.get_curves(*bone_id).unwrap();
|
||||
let Some(target) = find_bone(root, path, children, names, cached_path) else { continue };
|
||||
let Some(target) = entity_from_path(root, path, children, names, cached_path) else { continue };
|
||||
// SAFETY: The verify_no_ancestor_player check above ensures that two animation players cannot alias
|
||||
// any of their descendant Transforms.
|
||||
//
|
||||
|
@ -470,6 +514,7 @@ fn apply_animation(
|
|||
// to run their animation. Any players in the children or descendants will log a warning
|
||||
// and do nothing.
|
||||
let Ok(mut transform) = (unsafe { transforms.get_unchecked(target) }) else { continue };
|
||||
let mut morphs = unsafe { morphs.get_unchecked(target) };
|
||||
for curve in curves {
|
||||
// Some curves have only one keyframe used to set a transform
|
||||
if curve.keyframe_timestamps.len() == 1 {
|
||||
|
@ -484,6 +529,11 @@ fn apply_animation(
|
|||
Keyframes::Scale(keyframes) => {
|
||||
transform.scale = transform.scale.lerp(keyframes[0], weight);
|
||||
}
|
||||
Keyframes::Weights(keyframes) => {
|
||||
if let Ok(morphs) = &mut morphs {
|
||||
lerp_morph_weights(morphs.weights_mut(), weight, keyframes, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
@ -529,6 +579,11 @@ fn apply_animation(
|
|||
let result = scale_start.lerp(scale_end, lerp);
|
||||
transform.scale = transform.scale.lerp(result, weight);
|
||||
}
|
||||
Keyframes::Weights(keyframes) => {
|
||||
if let Ok(morphs) = &mut morphs {
|
||||
lerp_morph_weights(morphs.weights_mut(), weight, keyframes, step_start);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -551,6 +606,7 @@ impl Plugin for AnimationPlugin {
|
|||
app.add_asset::<AnimationClip>()
|
||||
.register_asset_reflect::<AnimationClip>()
|
||||
.register_type::<AnimationPlayer>()
|
||||
.register_type::<PlayingAnimation>()
|
||||
.add_systems(
|
||||
PostUpdate,
|
||||
animation_player.before(TransformSystem::TransformPropagate),
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
[package]
|
||||
name = "bevy_app"
|
||||
version = "0.11.0-dev"
|
||||
version = "0.12.0-dev"
|
||||
edition = "2021"
|
||||
description = "Provides core App functionality for Bevy Engine"
|
||||
homepage = "https://bevyengine.org"
|
||||
|
@ -16,11 +16,11 @@ bevy_reflect = ["dep:bevy_reflect", "bevy_ecs/bevy_reflect"]
|
|||
|
||||
[dependencies]
|
||||
# bevy
|
||||
bevy_derive = { path = "../bevy_derive", version = "0.11.0-dev" }
|
||||
bevy_ecs = { path = "../bevy_ecs", version = "0.11.0-dev", default-features = false }
|
||||
bevy_reflect = { path = "../bevy_reflect", version = "0.11.0-dev", optional = true }
|
||||
bevy_utils = { path = "../bevy_utils", version = "0.11.0-dev" }
|
||||
bevy_tasks = { path = "../bevy_tasks", version = "0.11.0-dev" }
|
||||
bevy_derive = { path = "../bevy_derive", version = "0.12.0-dev" }
|
||||
bevy_ecs = { path = "../bevy_ecs", version = "0.12.0-dev", default-features = false }
|
||||
bevy_reflect = { path = "../bevy_reflect", version = "0.12.0-dev", optional = true }
|
||||
bevy_utils = { path = "../bevy_utils", version = "0.12.0-dev" }
|
||||
bevy_tasks = { path = "../bevy_tasks", version = "0.12.0-dev" }
|
||||
|
||||
# other
|
||||
serde = { version = "1.0", features = ["derive"], optional = true }
|
||||
|
|
|
@ -1,6 +1,4 @@
|
|||
use crate::{
|
||||
First, Main, MainSchedulePlugin, Plugin, PluginGroup, Startup, StateTransition, Update,
|
||||
};
|
||||
use crate::{First, Main, MainSchedulePlugin, Plugin, Plugins, StateTransition};
|
||||
pub use bevy_derive::AppLabel;
|
||||
use bevy_ecs::{
|
||||
prelude::*,
|
||||
|
@ -18,6 +16,7 @@ use std::{
|
|||
|
||||
#[cfg(feature = "trace")]
|
||||
use bevy_utils::tracing::info_span;
|
||||
|
||||
bevy_utils::define_label!(
|
||||
/// A strongly-typed class of labels used to identify an [`App`].
|
||||
AppLabel,
|
||||
|
@ -25,11 +24,6 @@ bevy_utils::define_label!(
|
|||
AppLabelId,
|
||||
);
|
||||
|
||||
/// The [`Resource`] that stores the [`App`]'s [`TypeRegistry`](bevy_reflect::TypeRegistry).
|
||||
#[cfg(feature = "bevy_reflect")]
|
||||
#[derive(Resource, Clone, bevy_derive::Deref, bevy_derive::DerefMut, Default)]
|
||||
pub struct AppTypeRegistry(pub bevy_reflect::TypeRegistryArc);
|
||||
|
||||
pub(crate) enum AppError {
|
||||
DuplicatePlugin { plugin_name: String },
|
||||
}
|
||||
|
@ -70,7 +64,7 @@ pub struct App {
|
|||
/// the application's event loop and advancing the [`Schedule`].
|
||||
/// Typically, it is not configured manually, but set by one of Bevy's built-in plugins.
|
||||
/// See `bevy::winit::WinitPlugin` and [`ScheduleRunnerPlugin`](crate::schedule_runner::ScheduleRunnerPlugin).
|
||||
pub runner: Box<dyn Fn(App) + Send>, // Send bound is required to make App Send
|
||||
pub runner: Box<dyn FnOnce(App) + Send>, // Send bound is required to make App Send
|
||||
/// The schedule that systems are added to by default.
|
||||
///
|
||||
/// The schedule that runs the main loop of schedule execution.
|
||||
|
@ -188,7 +182,7 @@ impl Default for App {
|
|||
#[cfg(feature = "bevy_reflect")]
|
||||
app.init_resource::<AppTypeRegistry>();
|
||||
|
||||
app.add_plugin(MainSchedulePlugin);
|
||||
app.add_plugins(MainSchedulePlugin);
|
||||
app.add_event::<AppExit>();
|
||||
|
||||
#[cfg(feature = "bevy_ci_testing")]
|
||||
|
@ -334,7 +328,7 @@ impl App {
|
|||
|
||||
/// Adds [`State<S>`] and [`NextState<S>`] resources, [`OnEnter`] and [`OnExit`] schedules
|
||||
/// for each state variant (if they don't already exist), an instance of [`apply_state_transition::<S>`] in
|
||||
/// [`StateTransition`] so that transitions happen before [`Update`] and
|
||||
/// [`StateTransition`] so that transitions happen before [`Update`](crate::Update) and
|
||||
/// a instance of [`run_enter_schedule::<S>`] in [`StateTransition`] with a
|
||||
/// [`run_once`](`run_once_condition`) condition to run the on enter schedule of the
|
||||
/// initial state.
|
||||
|
@ -363,30 +357,6 @@ impl App {
|
|||
self
|
||||
}
|
||||
|
||||
/// Adds a system to the default system set and schedule of the app's [`Schedules`].
|
||||
///
|
||||
/// Refer to the [system module documentation](bevy_ecs::system) to see how a system
|
||||
/// can be defined.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// # use bevy_app::prelude::*;
|
||||
/// # use bevy_ecs::prelude::*;
|
||||
/// #
|
||||
/// # fn my_system() {}
|
||||
/// # let mut app = App::new();
|
||||
/// #
|
||||
/// app.add_system(my_system);
|
||||
/// ```
|
||||
#[deprecated(
|
||||
since = "0.11.0",
|
||||
note = "Please use `add_systems` instead. If you didn't change the default base set, you should use `add_systems(Update, your_system).`"
|
||||
)]
|
||||
pub fn add_system<M>(&mut self, system: impl IntoSystemConfigs<M>) -> &mut Self {
|
||||
self.add_systems(Update, system)
|
||||
}
|
||||
|
||||
/// Adds a system to the given schedule in this app's [`Schedules`].
|
||||
///
|
||||
/// # Examples
|
||||
|
@ -422,59 +392,6 @@ impl App {
|
|||
self
|
||||
}
|
||||
|
||||
/// Adds a system to [`Startup`].
|
||||
///
|
||||
/// These systems will run exactly once, at the start of the [`App`]'s lifecycle.
|
||||
/// To add a system that runs every frame, see [`add_system`](Self::add_system).
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// # use bevy_app::prelude::*;
|
||||
/// # use bevy_ecs::prelude::*;
|
||||
/// #
|
||||
/// fn my_startup_system(_commands: Commands) {
|
||||
/// println!("My startup system");
|
||||
/// }
|
||||
///
|
||||
/// App::new()
|
||||
/// .add_systems(Startup, my_startup_system);
|
||||
/// ```
|
||||
#[deprecated(
|
||||
since = "0.11.0",
|
||||
note = "Please use `add_systems` instead. If you didn't change the default base set, you should use `add_systems(Startup, your_system).`"
|
||||
)]
|
||||
pub fn add_startup_system<M>(&mut self, system: impl IntoSystemConfigs<M>) -> &mut Self {
|
||||
self.add_systems(Startup, system)
|
||||
}
|
||||
|
||||
/// Adds a collection of systems to [`Startup`].
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// # use bevy_app::prelude::*;
|
||||
/// # use bevy_ecs::prelude::*;
|
||||
/// #
|
||||
/// # let mut app = App::new();
|
||||
/// # fn startup_system_a() {}
|
||||
/// # fn startup_system_b() {}
|
||||
/// # fn startup_system_c() {}
|
||||
/// #
|
||||
/// app.add_systems(Startup, (
|
||||
/// startup_system_a,
|
||||
/// startup_system_b,
|
||||
/// startup_system_c,
|
||||
/// ));
|
||||
/// ```
|
||||
#[deprecated(
|
||||
since = "0.11.0",
|
||||
note = "Please use `add_systems` instead. If you didn't change the default base set, you should use `add_systems(Startup, your_system).`"
|
||||
)]
|
||||
pub fn add_startup_systems<M>(&mut self, systems: impl IntoSystemConfigs<M>) -> &mut Self {
|
||||
self.add_systems(Startup, systems.into_configs())
|
||||
}
|
||||
|
||||
/// Configures a system set in the default schedule, adding the set if it does not exist.
|
||||
pub fn configure_set(
|
||||
&mut self,
|
||||
|
@ -657,52 +574,13 @@ impl App {
|
|||
/// App::new()
|
||||
/// .set_runner(my_runner);
|
||||
/// ```
|
||||
pub fn set_runner(&mut self, run_fn: impl Fn(App) + 'static + Send) -> &mut Self {
|
||||
pub fn set_runner(&mut self, run_fn: impl FnOnce(App) + 'static + Send) -> &mut Self {
|
||||
self.runner = Box::new(run_fn);
|
||||
self
|
||||
}
|
||||
|
||||
/// Adds a single [`Plugin`].
|
||||
///
|
||||
/// One of Bevy's core principles is modularity. All Bevy engine features are implemented
|
||||
/// as [`Plugin`]s. This includes internal features like the renderer.
|
||||
///
|
||||
/// Bevy also provides a few sets of default [`Plugin`]s. See [`add_plugins`](Self::add_plugins).
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// # use bevy_app::prelude::*;
|
||||
/// #
|
||||
/// # // Dummies created to avoid using `bevy_log`,
|
||||
/// # // which pulls in too many dependencies and breaks rust-analyzer
|
||||
/// # pub mod bevy_log {
|
||||
/// # use bevy_app::prelude::*;
|
||||
/// # #[derive(Default)]
|
||||
/// # pub struct LogPlugin;
|
||||
/// # impl Plugin for LogPlugin{
|
||||
/// # fn build(&self, app: &mut App) {}
|
||||
/// # }
|
||||
/// # }
|
||||
/// App::new().add_plugin(bevy_log::LogPlugin::default());
|
||||
/// ```
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// Panics if the plugin was already added to the application.
|
||||
pub fn add_plugin<T>(&mut self, plugin: T) -> &mut Self
|
||||
where
|
||||
T: Plugin,
|
||||
{
|
||||
match self.add_boxed_plugin(Box::new(plugin)) {
|
||||
Ok(app) => app,
|
||||
Err(AppError::DuplicatePlugin { plugin_name }) => panic!(
|
||||
"Error adding plugin {plugin_name}: : plugin was already added in application"
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
/// Boxed variant of [`add_plugin`](App::add_plugin) that can be used from a [`PluginGroup`]
|
||||
/// Boxed variant of [`add_plugins`](App::add_plugins) that can be used from a
|
||||
/// [`PluginGroup`](super::PluginGroup)
|
||||
pub(crate) fn add_boxed_plugin(
|
||||
&mut self,
|
||||
plugin: Box<dyn Plugin>,
|
||||
|
@ -757,7 +635,7 @@ impl App {
|
|||
/// # fn build(&self, app: &mut App) {}
|
||||
/// # }
|
||||
/// # let mut app = App::new();
|
||||
/// # app.add_plugin(ImagePlugin::default());
|
||||
/// # app.add_plugins(ImagePlugin::default());
|
||||
/// let default_sampler = app.get_added_plugins::<ImagePlugin>()[0].default_sampler;
|
||||
/// ```
|
||||
pub fn get_added_plugins<T>(&self) -> Vec<&T>
|
||||
|
@ -770,7 +648,10 @@ impl App {
|
|||
.collect()
|
||||
}
|
||||
|
||||
/// Adds a group of [`Plugin`]s.
|
||||
/// Adds one or more [`Plugin`]s.
|
||||
///
|
||||
/// One of Bevy's core principles is modularity. All Bevy engine features are implemented
|
||||
/// as [`Plugin`]s. This includes internal features like the renderer.
|
||||
///
|
||||
/// [`Plugin`]s can be grouped into a set by using a [`PluginGroup`].
|
||||
///
|
||||
|
@ -778,23 +659,35 @@ impl App {
|
|||
/// The [`PluginGroup`]s available by default are `DefaultPlugins` and `MinimalPlugins`.
|
||||
///
|
||||
/// To customize the plugins in the group (reorder, disable a plugin, add a new plugin
|
||||
/// before / after another plugin), call [`build()`](PluginGroup::build) on the group,
|
||||
/// before / after another plugin), call [`build()`](super::PluginGroup::build) on the group,
|
||||
/// which will convert it to a [`PluginGroupBuilder`](crate::PluginGroupBuilder).
|
||||
///
|
||||
/// You can also specify a group of [`Plugin`]s by using a tuple over [`Plugin`]s and
|
||||
/// [`PluginGroup`]s. See [`Plugins`] for more details.
|
||||
///
|
||||
/// ## Examples
|
||||
/// ```
|
||||
/// # use bevy_app::{prelude::*, PluginGroupBuilder, NoopPluginGroup as MinimalPlugins};
|
||||
/// #
|
||||
/// # // Dummies created to avoid using `bevy_log`,
|
||||
/// # // which pulls in too many dependencies and breaks rust-analyzer
|
||||
/// # pub struct LogPlugin;
|
||||
/// # impl Plugin for LogPlugin {
|
||||
/// # fn build(&self, app: &mut App) {}
|
||||
/// # }
|
||||
/// App::new()
|
||||
/// .add_plugins(MinimalPlugins);
|
||||
/// App::new()
|
||||
/// .add_plugins((MinimalPlugins, LogPlugin));
|
||||
/// ```
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// Panics if one of the plugin in the group was already added to the application.
|
||||
pub fn add_plugins<T: PluginGroup>(&mut self, group: T) -> &mut Self {
|
||||
let builder = group.build();
|
||||
builder.finish(self);
|
||||
/// Panics if one of the plugins was already added to the application.
|
||||
///
|
||||
/// [`PluginGroup`]:super::PluginGroup
|
||||
pub fn add_plugins<M>(&mut self, plugins: impl Plugins<M>) -> &mut Self {
|
||||
plugins.add_to_app(self);
|
||||
self
|
||||
}
|
||||
|
||||
|
@ -940,7 +833,7 @@ impl App {
|
|||
pub fn edit_schedule(
|
||||
&mut self,
|
||||
label: impl ScheduleLabel,
|
||||
mut f: impl FnMut(&mut Schedule),
|
||||
f: impl FnOnce(&mut Schedule),
|
||||
) -> &mut Self {
|
||||
let mut schedules = self.world.resource_mut::<Schedules>();
|
||||
|
||||
|
@ -1011,23 +904,23 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn can_add_two_plugins() {
|
||||
App::new().add_plugin(PluginA).add_plugin(PluginB);
|
||||
App::new().add_plugins((PluginA, PluginB));
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic]
|
||||
fn cant_add_twice_the_same_plugin() {
|
||||
App::new().add_plugin(PluginA).add_plugin(PluginA);
|
||||
App::new().add_plugins((PluginA, PluginA));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn can_add_twice_the_same_plugin_with_different_type_param() {
|
||||
App::new().add_plugin(PluginC(0)).add_plugin(PluginC(true));
|
||||
App::new().add_plugins((PluginC(0), PluginC(true)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn can_add_twice_the_same_plugin_not_unique() {
|
||||
App::new().add_plugin(PluginD).add_plugin(PluginD);
|
||||
App::new().add_plugins((PluginD, PluginD));
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -1040,10 +933,10 @@ mod tests {
|
|||
}
|
||||
impl Plugin for PluginRun {
|
||||
fn build(&self, app: &mut crate::App) {
|
||||
app.add_plugin(InnerPlugin).run();
|
||||
app.add_plugins(InnerPlugin).run();
|
||||
}
|
||||
}
|
||||
App::new().add_plugin(PluginRun);
|
||||
App::new().add_plugins(PluginRun);
|
||||
}
|
||||
|
||||
#[derive(States, PartialEq, Eq, Debug, Default, Hash, Clone)]
|
||||
|
|
|
@ -21,9 +21,6 @@ pub use schedule_runner::*;
|
|||
|
||||
#[allow(missing_docs)]
|
||||
pub mod prelude {
|
||||
#[cfg(feature = "bevy_reflect")]
|
||||
#[doc(hidden)]
|
||||
pub use crate::AppTypeRegistry;
|
||||
#[doc(hidden)]
|
||||
pub use crate::{
|
||||
app::App,
|
||||
|
|
|
@ -48,7 +48,7 @@ pub struct First;
|
|||
|
||||
/// The schedule that contains logic that must run before [`Update`]. For example, a system that reads raw keyboard
|
||||
/// input OS events into an `Events` resource. This enables systems in [`Update`] to consume the events from the `Events`
|
||||
/// resource without actually knowing about (or taking a direct scheduler dependency on) the "os-level keyboard event sytsem".
|
||||
/// resource without actually knowing about (or taking a direct scheduler dependency on) the "os-level keyboard event system".
|
||||
///
|
||||
/// [`PreUpdate`] exists to do "engine/plugin preparation work" that ensures the APIs consumed in [`Update`] are "ready".
|
||||
/// [`PreUpdate`] abstracts out "pre work implementation details".
|
||||
|
|
|
@ -65,3 +65,61 @@ impl_downcast!(Plugin);
|
|||
///
|
||||
/// See `bevy_dynamic_plugin/src/loader.rs#dynamically_load_plugin`.
|
||||
pub type CreatePlugin = unsafe fn() -> *mut dyn Plugin;
|
||||
|
||||
/// Types that represent a set of [`Plugin`]s.
|
||||
///
|
||||
/// This is implemented for all types which implement [`Plugin`],
|
||||
/// [`PluginGroup`](super::PluginGroup), and tuples over [`Plugins`].
|
||||
pub trait Plugins<Marker>: sealed::Plugins<Marker> {}
|
||||
|
||||
impl<Marker, T> Plugins<Marker> for T where T: sealed::Plugins<Marker> {}
|
||||
|
||||
mod sealed {
|
||||
|
||||
use bevy_ecs::all_tuples;
|
||||
|
||||
use crate::{App, AppError, Plugin, PluginGroup};
|
||||
|
||||
pub trait Plugins<Marker> {
|
||||
fn add_to_app(self, app: &mut App);
|
||||
}
|
||||
|
||||
pub struct PluginMarker;
|
||||
pub struct PluginGroupMarker;
|
||||
pub struct PluginsTupleMarker;
|
||||
|
||||
impl<P: Plugin> Plugins<PluginMarker> for P {
|
||||
fn add_to_app(self, app: &mut App) {
|
||||
if let Err(AppError::DuplicatePlugin { plugin_name }) =
|
||||
app.add_boxed_plugin(Box::new(self))
|
||||
{
|
||||
panic!(
|
||||
"Error adding plugin {plugin_name}: : plugin was already added in application"
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<P: PluginGroup> Plugins<PluginGroupMarker> for P {
|
||||
fn add_to_app(self, app: &mut App) {
|
||||
self.build().finish(app);
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! impl_plugins_tuples {
|
||||
($(($param: ident, $plugins: ident)),*) => {
|
||||
impl<$($param, $plugins),*> Plugins<(PluginsTupleMarker, $($param,)*)> for ($($plugins,)*)
|
||||
where
|
||||
$($plugins: Plugins<$param>),*
|
||||
{
|
||||
#[allow(non_snake_case, unused_variables)]
|
||||
fn add_to_app(self, app: &mut App) {
|
||||
let ($($plugins,)*) = self;
|
||||
$($plugins.add_to_app(app);)*
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
all_tuples!(impl_plugins_tuples, 0, 15, P, S);
|
||||
}
|
||||
|
|
|
@ -71,6 +71,13 @@ impl Plugin for ScheduleRunnerPlugin {
|
|||
fn build(&self, app: &mut App) {
|
||||
let run_mode = self.run_mode;
|
||||
app.set_runner(move |mut app: App| {
|
||||
while !app.ready() {
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
bevy_tasks::tick_global_task_pools_on_main_thread();
|
||||
}
|
||||
app.finish();
|
||||
app.cleanup();
|
||||
|
||||
let mut app_exit_event_reader = ManualEventReader::<AppExit>::default();
|
||||
match run_mode {
|
||||
RunMode::Once => {
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
[package]
|
||||
name = "bevy_asset"
|
||||
version = "0.11.0-dev"
|
||||
version = "0.12.0-dev"
|
||||
edition = "2021"
|
||||
description = "Provides asset functionality for Bevy Engine"
|
||||
homepage = "https://bevyengine.org"
|
||||
|
@ -15,13 +15,13 @@ debug_asset_server = ["filesystem_watcher"]
|
|||
|
||||
[dependencies]
|
||||
# bevy
|
||||
bevy_app = { path = "../bevy_app", version = "0.11.0-dev" }
|
||||
bevy_diagnostic = { path = "../bevy_diagnostic", version = "0.11.0-dev" }
|
||||
bevy_ecs = { path = "../bevy_ecs", version = "0.11.0-dev" }
|
||||
bevy_log = { path = "../bevy_log", version = "0.11.0-dev" }
|
||||
bevy_reflect = { path = "../bevy_reflect", version = "0.11.0-dev", features = ["bevy"] }
|
||||
bevy_tasks = { path = "../bevy_tasks", version = "0.11.0-dev" }
|
||||
bevy_utils = { path = "../bevy_utils", version = "0.11.0-dev" }
|
||||
bevy_app = { path = "../bevy_app", version = "0.12.0-dev" }
|
||||
bevy_diagnostic = { path = "../bevy_diagnostic", version = "0.12.0-dev" }
|
||||
bevy_ecs = { path = "../bevy_ecs", version = "0.12.0-dev" }
|
||||
bevy_log = { path = "../bevy_log", version = "0.12.0-dev" }
|
||||
bevy_reflect = { path = "../bevy_reflect", version = "0.12.0-dev", features = ["bevy"] }
|
||||
bevy_tasks = { path = "../bevy_tasks", version = "0.12.0-dev" }
|
||||
bevy_utils = { path = "../bevy_utils", version = "0.12.0-dev" }
|
||||
|
||||
# other
|
||||
serde = { version = "1", features = ["derive"] }
|
||||
|
@ -34,7 +34,7 @@ notify = { version = "6.0.0", optional = true }
|
|||
parking_lot = "0.12.1"
|
||||
|
||||
[target.'cfg(target_os = "android")'.dependencies]
|
||||
bevy_winit = { path = "../bevy_winit", version = "0.11.0-dev" }
|
||||
bevy_winit = { path = "../bevy_winit", version = "0.12.0-dev" }
|
||||
|
||||
[target.'cfg(target_arch = "wasm32")'.dependencies]
|
||||
wasm-bindgen = { version = "0.2" }
|
||||
|
@ -45,4 +45,4 @@ js-sys = "0.3"
|
|||
[dev-dependencies]
|
||||
futures-lite = "1.4.0"
|
||||
tempfile = "3.2.0"
|
||||
bevy_core = { path = "../bevy_core", version = "0.11.0-dev" }
|
||||
bevy_core = { path = "../bevy_core", version = "0.12.0-dev" }
|
||||
|
|
|
@ -85,7 +85,7 @@ pub struct AssetServerInternal {
|
|||
/// # use bevy_utils::Duration;
|
||||
/// # let mut app = App::new();
|
||||
/// // The asset plugin can be configured to watch for asset changes.
|
||||
/// app.add_plugin(AssetPlugin {
|
||||
/// app.add_plugins(AssetPlugin {
|
||||
/// watch_for_changes: ChangeWatcher::with_delay(Duration::from_millis(200)),
|
||||
/// ..Default::default()
|
||||
/// });
|
||||
|
|
|
@ -2,8 +2,9 @@ use crate::{
|
|||
update_asset_storage_system, Asset, AssetEvents, AssetLoader, AssetServer, Handle, HandleId,
|
||||
LoadAssets, RefChange, ReflectAsset, ReflectHandle,
|
||||
};
|
||||
use bevy_app::{App, AppTypeRegistry};
|
||||
use bevy_app::App;
|
||||
use bevy_ecs::prelude::*;
|
||||
use bevy_ecs::reflect::AppTypeRegistry;
|
||||
use bevy_reflect::{FromReflect, GetTypeRegistration, Reflect};
|
||||
use bevy_utils::HashMap;
|
||||
use crossbeam_channel::Sender;
|
||||
|
@ -405,7 +406,9 @@ impl AddAsset for App {
|
|||
}
|
||||
}
|
||||
|
||||
/// Loads an internal asset.
|
||||
/// Loads an internal asset from a project source file.
|
||||
/// the file and its path are passed to the loader function, together with any additional parameters.
|
||||
/// the resulting asset is stored in the app's asset server.
|
||||
///
|
||||
/// Internal assets (e.g. shaders) are bundled directly into the app and can't be hot reloaded
|
||||
/// using the conventional API. See [`DebugAssetServerPlugin`](crate::debug_asset_server::DebugAssetServerPlugin).
|
||||
|
@ -426,20 +429,59 @@ macro_rules! load_internal_asset {
|
|||
);
|
||||
}
|
||||
let mut assets = $app.world.resource_mut::<$crate::Assets<_>>();
|
||||
assets.set_untracked($handle, ($loader)(include_str!($path_str)));
|
||||
assets.set_untracked(
|
||||
$handle,
|
||||
($loader)(
|
||||
include_str!($path_str),
|
||||
std::path::Path::new(file!())
|
||||
.parent()
|
||||
.unwrap()
|
||||
.join($path_str)
|
||||
.to_string_lossy(),
|
||||
)
|
||||
);
|
||||
}};
|
||||
// we can't support params without variadic arguments, so internal assets with additional params can't be hot-reloaded
|
||||
($app: ident, $handle: ident, $path_str: expr, $loader: expr $(, $param:expr)+) => {{
|
||||
let mut assets = $app.world.resource_mut::<$crate::Assets<_>>();
|
||||
assets.set_untracked(
|
||||
$handle,
|
||||
($loader)(
|
||||
include_str!($path_str),
|
||||
std::path::Path::new(file!())
|
||||
.parent()
|
||||
.unwrap()
|
||||
.join($path_str)
|
||||
.to_string_lossy(),
|
||||
$($param),+
|
||||
),
|
||||
);
|
||||
}};
|
||||
}
|
||||
|
||||
/// Loads an internal asset.
|
||||
/// Loads an internal asset from a project source file.
|
||||
/// the file and its path are passed to the loader function, together with any additional parameters.
|
||||
/// the resulting asset is stored in the app's asset server.
|
||||
///
|
||||
/// Internal assets (e.g. shaders) are bundled directly into the app and can't be hot reloaded
|
||||
/// using the conventional API. See `DebugAssetServerPlugin`.
|
||||
#[cfg(not(feature = "debug_asset_server"))]
|
||||
#[macro_export]
|
||||
macro_rules! load_internal_asset {
|
||||
($app: ident, $handle: ident, $path_str: expr, $loader: expr) => {{
|
||||
($app: ident, $handle: ident, $path_str: expr, $loader: expr $(, $param:expr)*) => {{
|
||||
let mut assets = $app.world.resource_mut::<$crate::Assets<_>>();
|
||||
assets.set_untracked($handle, ($loader)(include_str!($path_str)));
|
||||
assets.set_untracked(
|
||||
$handle,
|
||||
($loader)(
|
||||
include_str!($path_str),
|
||||
std::path::Path::new(file!())
|
||||
.parent()
|
||||
.unwrap()
|
||||
.join($path_str)
|
||||
.to_string_lossy(),
|
||||
$($param),*
|
||||
),
|
||||
);
|
||||
}};
|
||||
}
|
||||
|
||||
|
@ -464,7 +506,18 @@ macro_rules! load_internal_binary_asset {
|
|||
);
|
||||
}
|
||||
let mut assets = $app.world.resource_mut::<$crate::Assets<_>>();
|
||||
assets.set_untracked($handle, ($loader)(include_bytes!($path_str).as_ref()));
|
||||
assets.set_untracked(
|
||||
$handle,
|
||||
($loader)(
|
||||
include_bytes!($path_str).as_ref(),
|
||||
std::path::Path::new(file!())
|
||||
.parent()
|
||||
.unwrap()
|
||||
.join($path_str)
|
||||
.to_string_lossy()
|
||||
.into(),
|
||||
),
|
||||
);
|
||||
}};
|
||||
}
|
||||
|
||||
|
@ -477,7 +530,18 @@ macro_rules! load_internal_binary_asset {
|
|||
macro_rules! load_internal_binary_asset {
|
||||
($app: ident, $handle: ident, $path_str: expr, $loader: expr) => {{
|
||||
let mut assets = $app.world.resource_mut::<$crate::Assets<_>>();
|
||||
assets.set_untracked($handle, ($loader)(include_bytes!($path_str).as_ref()));
|
||||
assets.set_untracked(
|
||||
$handle,
|
||||
($loader)(
|
||||
include_bytes!($path_str).as_ref(),
|
||||
std::path::Path::new(file!())
|
||||
.parent()
|
||||
.unwrap()
|
||||
.join($path_str)
|
||||
.to_string_lossy()
|
||||
.into(),
|
||||
),
|
||||
);
|
||||
}};
|
||||
}
|
||||
|
||||
|
@ -493,9 +557,11 @@ mod tests {
|
|||
#[uuid = "44115972-f31b-46e5-be5c-2b9aece6a52f"]
|
||||
struct MyAsset;
|
||||
let mut app = App::new();
|
||||
app.add_plugin(bevy_core::TaskPoolPlugin::default())
|
||||
.add_plugin(bevy_core::TypeRegistrationPlugin::default())
|
||||
.add_plugin(crate::AssetPlugin::default());
|
||||
app.add_plugins((
|
||||
bevy_core::TaskPoolPlugin::default(),
|
||||
bevy_core::TypeRegistrationPlugin,
|
||||
crate::AssetPlugin::default(),
|
||||
));
|
||||
app.add_asset::<MyAsset>();
|
||||
let mut assets_before = app.world.resource_mut::<Assets<MyAsset>>();
|
||||
let handle = assets_before.add(MyAsset);
|
||||
|
|
|
@ -71,7 +71,7 @@ impl Plugin for DebugAssetServerPlugin {
|
|||
.build()
|
||||
});
|
||||
let mut debug_asset_app = App::new();
|
||||
debug_asset_app.add_plugin(AssetPlugin {
|
||||
debug_asset_app.add_plugins(AssetPlugin {
|
||||
asset_folder: "crates".to_string(),
|
||||
watch_for_changes: ChangeWatcher::with_delay(Duration::from_millis(200)),
|
||||
});
|
||||
|
@ -114,7 +114,7 @@ pub(crate) fn sync_debug_assets<T: Asset + Clone>(
|
|||
/// If this feels a bit odd ... that's because it is. This was built to improve the UX of the
|
||||
/// `load_internal_asset` macro.
|
||||
pub fn register_handle_with_loader<A: Asset, T>(
|
||||
_loader: fn(T) -> A,
|
||||
_loader: fn(T, String) -> A,
|
||||
app: &mut DebugAssetApp,
|
||||
handle: HandleUntyped,
|
||||
file_path: &str,
|
||||
|
|
|
@ -10,28 +10,14 @@ use crate::{
|
|||
Asset, Assets,
|
||||
};
|
||||
use bevy_ecs::{component::Component, reflect::ReflectComponent};
|
||||
use bevy_reflect::{
|
||||
std_traits::ReflectDefault, FromReflect, Reflect, ReflectDeserialize, ReflectFromReflect,
|
||||
ReflectSerialize,
|
||||
};
|
||||
use bevy_reflect::{std_traits::ReflectDefault, Reflect, ReflectDeserialize, ReflectSerialize};
|
||||
use bevy_utils::Uuid;
|
||||
use crossbeam_channel::{Receiver, Sender};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
/// A unique, stable asset id.
|
||||
#[derive(
|
||||
Debug,
|
||||
Clone,
|
||||
Copy,
|
||||
Eq,
|
||||
PartialEq,
|
||||
Hash,
|
||||
Ord,
|
||||
PartialOrd,
|
||||
Serialize,
|
||||
Deserialize,
|
||||
Reflect,
|
||||
FromReflect,
|
||||
Debug, Clone, Copy, Eq, PartialEq, Hash, Ord, PartialOrd, Serialize, Deserialize, Reflect,
|
||||
)]
|
||||
#[reflect_value(Serialize, Deserialize, PartialEq, Hash)]
|
||||
pub enum HandleId {
|
||||
|
@ -54,6 +40,12 @@ impl<'a> From<AssetPath<'a>> for HandleId {
|
|||
}
|
||||
}
|
||||
|
||||
impl<'a, 'b> From<&'a AssetPath<'b>> for HandleId {
|
||||
fn from(value: &'a AssetPath<'b>) -> Self {
|
||||
HandleId::AssetPathId(AssetPathId::from(value))
|
||||
}
|
||||
}
|
||||
|
||||
impl HandleId {
|
||||
/// Creates a random id for an asset of type `T`.
|
||||
#[inline]
|
||||
|
@ -103,8 +95,8 @@ impl HandleId {
|
|||
/// handle to the unloaded asset, but it will not be able to retrieve the image data, resulting in
|
||||
/// collisions no longer being detected for that entity.
|
||||
///
|
||||
#[derive(Component, Reflect, FromReflect)]
|
||||
#[reflect(Component, Default, FromReflect)]
|
||||
#[derive(Component, Reflect)]
|
||||
#[reflect(Component, Default)]
|
||||
pub struct Handle<T>
|
||||
where
|
||||
T: Asset,
|
||||
|
@ -117,7 +109,9 @@ where
|
|||
marker: PhantomData<fn() -> T>,
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
enum HandleType {
|
||||
#[default]
|
||||
Weak,
|
||||
Strong(Sender<RefChange>),
|
||||
}
|
||||
|
@ -402,7 +396,7 @@ impl HandleUntyped {
|
|||
Handle {
|
||||
handle_type,
|
||||
id: self.id,
|
||||
marker: PhantomData::default(),
|
||||
marker: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -216,7 +216,7 @@ pub fn filesystem_watcher_system(
|
|||
// Unless we wait until we are sure the shader is finished being modified (and that there will be no more events coming),
|
||||
// we will sometimes get a crash when trying to reload a partially-modified shader.
|
||||
for (to_reload, _) in
|
||||
changed.drain_filter(|_, last_modified| last_modified.elapsed() >= watcher.delay)
|
||||
changed.extract_if(|_, last_modified| last_modified.elapsed() >= watcher.delay)
|
||||
{
|
||||
let _ = asset_server.load_untracked(to_reload.as_path().into(), true);
|
||||
}
|
||||
|
|
|
@ -34,14 +34,16 @@ pub trait AssetLoader: Send + Sync + 'static {
|
|||
/// and scripts. In Bevy, an asset is any struct that has an unique type id, as shown below:
|
||||
///
|
||||
/// ```rust
|
||||
/// use bevy_reflect::TypeUuid;
|
||||
/// use bevy_reflect::{TypePath, TypeUuid};
|
||||
/// use serde::Deserialize;
|
||||
///
|
||||
/// #[derive(Debug, Deserialize, TypeUuid)]
|
||||
/// #[derive(Debug, Deserialize, TypeUuid, TypePath)]
|
||||
/// #[uuid = "39cadc56-aa9c-4543-8640-a018b74b5052"]
|
||||
/// pub struct CustomAsset {
|
||||
/// pub value: i32,
|
||||
/// }
|
||||
/// # fn is_asset<T: bevy_asset::Asset>() {}
|
||||
/// # is_asset::<CustomAsset>();
|
||||
/// ```
|
||||
///
|
||||
/// See the `assets/custom_asset.rs` example in the repository for more details.
|
||||
|
|
|
@ -1,6 +1,4 @@
|
|||
use bevy_reflect::{
|
||||
FromReflect, Reflect, ReflectDeserialize, ReflectFromReflect, ReflectSerialize,
|
||||
};
|
||||
use bevy_reflect::{Reflect, ReflectDeserialize, ReflectSerialize};
|
||||
use bevy_utils::{AHasher, RandomState};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::{
|
||||
|
@ -10,8 +8,8 @@ use std::{
|
|||
};
|
||||
|
||||
/// Represents a path to an asset in the file system.
|
||||
#[derive(Debug, Eq, PartialEq, Hash, Clone, Serialize, Deserialize, Reflect, FromReflect)]
|
||||
#[reflect(Debug, PartialEq, Hash, Serialize, Deserialize, FromReflect)]
|
||||
#[derive(Debug, Eq, PartialEq, Hash, Clone, Serialize, Deserialize, Reflect)]
|
||||
#[reflect(Debug, PartialEq, Hash, Serialize, Deserialize)]
|
||||
pub struct AssetPath<'a> {
|
||||
path: Cow<'a, Path>,
|
||||
label: Option<Cow<'a, str>>,
|
||||
|
@ -69,38 +67,16 @@ impl<'a> AssetPath<'a> {
|
|||
|
||||
/// An unique identifier to an asset path.
|
||||
#[derive(
|
||||
Debug,
|
||||
Clone,
|
||||
Copy,
|
||||
Eq,
|
||||
PartialEq,
|
||||
Hash,
|
||||
Ord,
|
||||
PartialOrd,
|
||||
Serialize,
|
||||
Deserialize,
|
||||
Reflect,
|
||||
FromReflect,
|
||||
Debug, Clone, Copy, Eq, PartialEq, Hash, Ord, PartialOrd, Serialize, Deserialize, Reflect,
|
||||
)]
|
||||
#[reflect_value(PartialEq, Hash, Serialize, Deserialize, FromReflect)]
|
||||
#[reflect_value(PartialEq, Hash, Serialize, Deserialize)]
|
||||
pub struct AssetPathId(SourcePathId, LabelId);
|
||||
|
||||
/// An unique identifier to the source path of an asset.
|
||||
#[derive(
|
||||
Debug,
|
||||
Clone,
|
||||
Copy,
|
||||
Eq,
|
||||
PartialEq,
|
||||
Hash,
|
||||
Ord,
|
||||
PartialOrd,
|
||||
Serialize,
|
||||
Deserialize,
|
||||
Reflect,
|
||||
FromReflect,
|
||||
Debug, Clone, Copy, Eq, PartialEq, Hash, Ord, PartialOrd, Serialize, Deserialize, Reflect,
|
||||
)]
|
||||
#[reflect_value(PartialEq, Hash, Serialize, Deserialize, FromReflect)]
|
||||
#[reflect_value(PartialEq, Hash, Serialize, Deserialize)]
|
||||
pub struct SourcePathId(u64);
|
||||
|
||||
/// An unique identifier to a sub-asset label.
|
||||
|
|
|
@ -253,12 +253,13 @@ impl<A: Asset> FromType<Handle<A>> for ReflectHandle {
|
|||
mod tests {
|
||||
use std::any::TypeId;
|
||||
|
||||
use bevy_app::{App, AppTypeRegistry};
|
||||
use bevy_reflect::{FromReflect, Reflect, ReflectMut, TypeUuid};
|
||||
use bevy_app::App;
|
||||
use bevy_ecs::reflect::AppTypeRegistry;
|
||||
use bevy_reflect::{Reflect, ReflectMut, TypeUuid};
|
||||
|
||||
use crate::{AddAsset, AssetPlugin, HandleUntyped, ReflectAsset};
|
||||
|
||||
#[derive(Reflect, FromReflect, TypeUuid)]
|
||||
#[derive(Reflect, TypeUuid)]
|
||||
#[uuid = "09191350-1238-4736-9a89-46f04bda6966"]
|
||||
struct AssetType {
|
||||
field: String,
|
||||
|
@ -267,7 +268,7 @@ mod tests {
|
|||
#[test]
|
||||
fn test_reflect_asset_operations() {
|
||||
let mut app = App::new();
|
||||
app.add_plugin(AssetPlugin::default())
|
||||
app.add_plugins(AssetPlugin::default())
|
||||
.add_asset::<AssetType>()
|
||||
.register_asset_reflect::<AssetType>();
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
[package]
|
||||
name = "bevy_audio"
|
||||
version = "0.11.0-dev"
|
||||
version = "0.12.0-dev"
|
||||
edition = "2021"
|
||||
description = "Provides audio functionality for Bevy Engine"
|
||||
homepage = "https://bevyengine.org"
|
||||
|
@ -10,14 +10,14 @@ keywords = ["bevy"]
|
|||
|
||||
[dependencies]
|
||||
# bevy
|
||||
bevy_app = { path = "../bevy_app", version = "0.11.0-dev" }
|
||||
bevy_asset = { path = "../bevy_asset", version = "0.11.0-dev" }
|
||||
bevy_ecs = { path = "../bevy_ecs", version = "0.11.0-dev" }
|
||||
bevy_math = { path = "../bevy_math", version = "0.11.0-dev" }
|
||||
bevy_reflect = { path = "../bevy_reflect", version = "0.11.0-dev", features = ["bevy"] }
|
||||
bevy_transform = { path = "../bevy_transform", version = "0.11.0-dev" }
|
||||
bevy_derive = { path = "../bevy_derive", version = "0.11.0-dev" }
|
||||
bevy_utils = { path = "../bevy_utils", version = "0.11.0-dev" }
|
||||
bevy_app = { path = "../bevy_app", version = "0.12.0-dev" }
|
||||
bevy_asset = { path = "../bevy_asset", version = "0.12.0-dev" }
|
||||
bevy_ecs = { path = "../bevy_ecs", version = "0.12.0-dev" }
|
||||
bevy_math = { path = "../bevy_math", version = "0.12.0-dev" }
|
||||
bevy_reflect = { path = "../bevy_reflect", version = "0.12.0-dev", features = ["bevy"] }
|
||||
bevy_transform = { path = "../bevy_transform", version = "0.12.0-dev" }
|
||||
bevy_derive = { path = "../bevy_derive", version = "0.12.0-dev" }
|
||||
bevy_utils = { path = "../bevy_utils", version = "0.12.0-dev" }
|
||||
|
||||
# other
|
||||
anyhow = "1.0.4"
|
||||
|
|
|
@ -1,256 +1,9 @@
|
|||
use crate::{AudioSink, AudioSource, Decodable, SpatialAudioSink};
|
||||
use bevy_asset::{Asset, Handle, HandleId};
|
||||
use crate::{AudioSource, Decodable};
|
||||
use bevy_asset::{Asset, Handle};
|
||||
use bevy_derive::{Deref, DerefMut};
|
||||
use bevy_ecs::system::Resource;
|
||||
use bevy_ecs::prelude::*;
|
||||
use bevy_math::Vec3;
|
||||
use bevy_transform::prelude::Transform;
|
||||
use parking_lot::RwLock;
|
||||
use std::{collections::VecDeque, fmt};
|
||||
|
||||
/// Use this [`Resource`] to play audio.
|
||||
///
|
||||
/// ```
|
||||
/// # use bevy_ecs::system::Res;
|
||||
/// # use bevy_asset::AssetServer;
|
||||
/// # use bevy_audio::Audio;
|
||||
/// fn play_audio_system(asset_server: Res<AssetServer>, audio: Res<Audio>) {
|
||||
/// audio.play(asset_server.load("my_sound.ogg"));
|
||||
/// }
|
||||
/// ```
|
||||
#[derive(Resource)]
|
||||
pub struct Audio<Source = AudioSource>
|
||||
where
|
||||
Source: Asset + Decodable,
|
||||
{
|
||||
/// Queue for playing audio from asset handles
|
||||
pub(crate) queue: RwLock<VecDeque<AudioToPlay<Source>>>,
|
||||
}
|
||||
|
||||
impl<Source: Asset> fmt::Debug for Audio<Source>
|
||||
where
|
||||
Source: Decodable,
|
||||
{
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
f.debug_struct("Audio").field("queue", &self.queue).finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl<Source> Default for Audio<Source>
|
||||
where
|
||||
Source: Asset + Decodable,
|
||||
{
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
queue: Default::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<Source> Audio<Source>
|
||||
where
|
||||
Source: Asset + Decodable,
|
||||
{
|
||||
/// Play audio from a [`Handle`] to the audio source
|
||||
///
|
||||
/// ```
|
||||
/// # use bevy_ecs::system::Res;
|
||||
/// # use bevy_asset::AssetServer;
|
||||
/// # use bevy_audio::Audio;
|
||||
/// fn play_audio_system(asset_server: Res<AssetServer>, audio: Res<Audio>) {
|
||||
/// audio.play(asset_server.load("my_sound.ogg"));
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// Returns a weak [`Handle`] to the [`AudioSink`]. If this handle isn't changed to a
|
||||
/// strong one, the sink will be detached and the sound will continue playing. Changing it
|
||||
/// to a strong handle allows you to control the playback through the [`AudioSink`] asset.
|
||||
///
|
||||
/// ```
|
||||
/// # use bevy_ecs::system::Res;
|
||||
/// # use bevy_asset::{AssetServer, Assets};
|
||||
/// # use bevy_audio::{Audio, AudioSink};
|
||||
/// fn play_audio_system(
|
||||
/// asset_server: Res<AssetServer>,
|
||||
/// audio: Res<Audio>,
|
||||
/// audio_sinks: Res<Assets<AudioSink>>,
|
||||
/// ) {
|
||||
/// // This is a weak handle, and can't be used to control playback.
|
||||
/// let weak_handle = audio.play(asset_server.load("my_sound.ogg"));
|
||||
/// // This is now a strong handle, and can be used to control playback.
|
||||
/// let strong_handle = audio_sinks.get_handle(weak_handle);
|
||||
/// }
|
||||
/// ```
|
||||
pub fn play(&self, audio_source: Handle<Source>) -> Handle<AudioSink> {
|
||||
let id = HandleId::random::<AudioSink>();
|
||||
let config = AudioToPlay {
|
||||
settings: PlaybackSettings::ONCE,
|
||||
sink_handle: id,
|
||||
source_handle: audio_source,
|
||||
spatial: None,
|
||||
};
|
||||
self.queue.write().push_back(config);
|
||||
Handle::<AudioSink>::weak(id)
|
||||
}
|
||||
|
||||
/// Play audio from a [`Handle`] to the audio source with [`PlaybackSettings`] that
|
||||
/// allows looping or changing volume from the start.
|
||||
///
|
||||
/// ```
|
||||
/// # use bevy_ecs::system::Res;
|
||||
/// # use bevy_asset::AssetServer;
|
||||
/// # use bevy_audio::{Audio, Volume};
|
||||
/// # use bevy_audio::PlaybackSettings;
|
||||
/// fn play_audio_system(asset_server: Res<AssetServer>, audio: Res<Audio>) {
|
||||
/// audio.play_with_settings(
|
||||
/// asset_server.load("my_sound.ogg"),
|
||||
/// PlaybackSettings::LOOP.with_volume(Volume::new_relative(0.75)),
|
||||
/// );
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// See [`Self::play`] on how to control playback once it's started.
|
||||
pub fn play_with_settings(
|
||||
&self,
|
||||
audio_source: Handle<Source>,
|
||||
settings: PlaybackSettings,
|
||||
) -> Handle<AudioSink> {
|
||||
let id = HandleId::random::<AudioSink>();
|
||||
let config = AudioToPlay {
|
||||
settings,
|
||||
sink_handle: id,
|
||||
source_handle: audio_source,
|
||||
spatial: None,
|
||||
};
|
||||
self.queue.write().push_back(config);
|
||||
Handle::<AudioSink>::weak(id)
|
||||
}
|
||||
|
||||
/// Play audio from a [`Handle`] to the audio source, placing the listener at the given
|
||||
/// transform, an ear on each side separated by `gap`. The audio emitter will placed at
|
||||
/// `emitter`.
|
||||
///
|
||||
/// `bevy_audio` is not using HRTF for spatial audio, but is transforming the sound to a mono
|
||||
/// track, and then changing the level of each stereo channel according to the distance between
|
||||
/// the emitter and each ear by amplifying the difference between what the two ears hear.
|
||||
///
|
||||
/// ```
|
||||
/// # use bevy_ecs::system::Res;
|
||||
/// # use bevy_asset::AssetServer;
|
||||
/// # use bevy_audio::Audio;
|
||||
/// # use bevy_math::Vec3;
|
||||
/// # use bevy_transform::prelude::Transform;
|
||||
/// fn play_spatial_audio_system(asset_server: Res<AssetServer>, audio: Res<Audio>) {
|
||||
/// // Sound will be to the left and behind the listener
|
||||
/// audio.play_spatial(
|
||||
/// asset_server.load("my_sound.ogg"),
|
||||
/// Transform::IDENTITY,
|
||||
/// 1.0,
|
||||
/// Vec3::new(-2.0, 0.0, 1.0),
|
||||
/// );
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// Returns a weak [`Handle`] to the [`SpatialAudioSink`]. If this handle isn't changed to a
|
||||
/// strong one, the sink will be detached and the sound will continue playing. Changing it
|
||||
/// to a strong handle allows you to control the playback, or move the listener and emitter
|
||||
/// through the [`SpatialAudioSink`] asset.
|
||||
///
|
||||
/// ```
|
||||
/// # use bevy_ecs::system::Res;
|
||||
/// # use bevy_asset::{AssetServer, Assets};
|
||||
/// # use bevy_audio::{Audio, SpatialAudioSink};
|
||||
/// # use bevy_math::Vec3;
|
||||
/// # use bevy_transform::prelude::Transform;
|
||||
/// fn play_spatial_audio_system(
|
||||
/// asset_server: Res<AssetServer>,
|
||||
/// audio: Res<Audio>,
|
||||
/// spatial_audio_sinks: Res<Assets<SpatialAudioSink>>,
|
||||
/// ) {
|
||||
/// // This is a weak handle, and can't be used to control playback.
|
||||
/// let weak_handle = audio.play_spatial(
|
||||
/// asset_server.load("my_sound.ogg"),
|
||||
/// Transform::IDENTITY,
|
||||
/// 1.0,
|
||||
/// Vec3::new(-2.0, 0.0, 1.0),
|
||||
/// );
|
||||
/// // This is now a strong handle, and can be used to control playback, or move the emitter.
|
||||
/// let strong_handle = spatial_audio_sinks.get_handle(weak_handle);
|
||||
/// }
|
||||
/// ```
|
||||
pub fn play_spatial(
|
||||
&self,
|
||||
audio_source: Handle<Source>,
|
||||
listener: Transform,
|
||||
gap: f32,
|
||||
emitter: Vec3,
|
||||
) -> Handle<SpatialAudioSink> {
|
||||
let id = HandleId::random::<SpatialAudioSink>();
|
||||
let config = AudioToPlay {
|
||||
settings: PlaybackSettings::ONCE,
|
||||
sink_handle: id,
|
||||
source_handle: audio_source,
|
||||
spatial: Some(SpatialSettings {
|
||||
left_ear: (listener.translation + listener.left() * gap / 2.0).to_array(),
|
||||
right_ear: (listener.translation + listener.right() * gap / 2.0).to_array(),
|
||||
emitter: emitter.to_array(),
|
||||
}),
|
||||
};
|
||||
self.queue.write().push_back(config);
|
||||
Handle::<SpatialAudioSink>::weak(id)
|
||||
}
|
||||
|
||||
/// Play spatial audio from a [`Handle`] to the audio source with [`PlaybackSettings`] that
|
||||
/// allows looping or changing volume from the start. The listener is placed at the given
|
||||
/// transform, an ear on each side separated by `gap`. The audio emitter is placed at
|
||||
/// `emitter`.
|
||||
///
|
||||
/// `bevy_audio` is not using HRTF for spatial audio, but is transforming the sound to a mono
|
||||
/// track, and then changing the level of each stereo channel according to the distance between
|
||||
/// the emitter and each ear by amplifying the difference between what the two ears hear.
|
||||
///
|
||||
/// ```
|
||||
/// # use bevy_ecs::system::Res;
|
||||
/// # use bevy_asset::AssetServer;
|
||||
/// # use bevy_audio::{Audio, Volume};
|
||||
/// # use bevy_audio::PlaybackSettings;
|
||||
/// # use bevy_math::Vec3;
|
||||
/// # use bevy_transform::prelude::Transform;
|
||||
/// fn play_spatial_audio_system(asset_server: Res<AssetServer>, audio: Res<Audio>) {
|
||||
/// audio.play_spatial_with_settings(
|
||||
/// asset_server.load("my_sound.ogg"),
|
||||
/// PlaybackSettings::LOOP.with_volume(Volume::new_relative(0.75)),
|
||||
/// Transform::IDENTITY,
|
||||
/// 1.0,
|
||||
/// Vec3::new(-2.0, 0.0, 1.0),
|
||||
/// );
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// See [`Self::play_spatial`] on how to control playback once it's started, or how to move
|
||||
/// the listener or the emitter.
|
||||
pub fn play_spatial_with_settings(
|
||||
&self,
|
||||
audio_source: Handle<Source>,
|
||||
settings: PlaybackSettings,
|
||||
listener: Transform,
|
||||
gap: f32,
|
||||
emitter: Vec3,
|
||||
) -> Handle<SpatialAudioSink> {
|
||||
let id = HandleId::random::<SpatialAudioSink>();
|
||||
let config = AudioToPlay {
|
||||
settings,
|
||||
sink_handle: id,
|
||||
source_handle: audio_source,
|
||||
spatial: Some(SpatialSettings {
|
||||
left_ear: (listener.translation + listener.left() * gap / 2.0).to_array(),
|
||||
right_ear: (listener.translation + listener.right() * gap / 2.0).to_array(),
|
||||
emitter: emitter.to_array(),
|
||||
}),
|
||||
};
|
||||
self.queue.write().push_back(config);
|
||||
Handle::<SpatialAudioSink>::weak(id)
|
||||
}
|
||||
}
|
||||
|
||||
/// Defines the volume to play an audio source at.
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
|
@ -278,7 +31,7 @@ impl Volume {
|
|||
}
|
||||
}
|
||||
|
||||
/// A volume level equivalent to a positive only float.
|
||||
/// A volume level equivalent to a non-negative float.
|
||||
#[derive(Clone, Copy, Deref, DerefMut, Debug)]
|
||||
pub struct VolumeLevel(pub(crate) f32);
|
||||
|
||||
|
@ -300,38 +53,83 @@ impl VolumeLevel {
|
|||
}
|
||||
}
|
||||
|
||||
/// Settings to control playback from the start.
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
/// How should Bevy manage the sound playback?
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub enum PlaybackMode {
|
||||
/// Play the sound once. Do nothing when it ends.
|
||||
Once,
|
||||
/// Repeat the sound forever.
|
||||
Loop,
|
||||
/// Despawn the entity when the sound finishes playing.
|
||||
Despawn,
|
||||
/// Remove the audio components from the entity, when the sound finishes playing.
|
||||
Remove,
|
||||
}
|
||||
|
||||
/// Initial settings to be used when audio starts playing.
|
||||
/// If you would like to control the audio while it is playing, query for the
|
||||
/// [`AudioSink`][crate::AudioSink] or [`SpatialAudioSink`][crate::SpatialAudioSink]
|
||||
/// components. Changes to this component will *not* be applied to already-playing audio.
|
||||
#[derive(Component, Clone, Copy, Debug)]
|
||||
pub struct PlaybackSettings {
|
||||
/// Play in repeat
|
||||
pub repeat: bool,
|
||||
/// The desired playback behavior.
|
||||
pub mode: PlaybackMode,
|
||||
/// Volume to play at.
|
||||
pub volume: Volume,
|
||||
/// Speed to play at.
|
||||
pub speed: f32,
|
||||
/// Create the sink in paused state.
|
||||
/// Useful for "deferred playback", if you want to prepare
|
||||
/// the entity, but hear the sound later.
|
||||
pub paused: bool,
|
||||
}
|
||||
|
||||
impl Default for PlaybackSettings {
|
||||
fn default() -> Self {
|
||||
// TODO: what should the default be: ONCE/DESPAWN/REMOVE?
|
||||
Self::ONCE
|
||||
}
|
||||
}
|
||||
|
||||
impl PlaybackSettings {
|
||||
/// Will play the associate audio source once.
|
||||
/// Will play the associated audio source once.
|
||||
pub const ONCE: PlaybackSettings = PlaybackSettings {
|
||||
repeat: false,
|
||||
mode: PlaybackMode::Once,
|
||||
volume: Volume::Relative(VolumeLevel(1.0)),
|
||||
speed: 1.0,
|
||||
paused: false,
|
||||
};
|
||||
|
||||
/// Will play the associate audio source in a loop.
|
||||
/// Will play the associated audio source in a loop.
|
||||
pub const LOOP: PlaybackSettings = PlaybackSettings {
|
||||
repeat: true,
|
||||
mode: PlaybackMode::Loop,
|
||||
volume: Volume::Relative(VolumeLevel(1.0)),
|
||||
speed: 1.0,
|
||||
paused: false,
|
||||
};
|
||||
|
||||
/// Will play the associated audio source once and despawn the entity afterwards.
|
||||
pub const DESPAWN: PlaybackSettings = PlaybackSettings {
|
||||
mode: PlaybackMode::Despawn,
|
||||
volume: Volume::Relative(VolumeLevel(1.0)),
|
||||
speed: 1.0,
|
||||
paused: false,
|
||||
};
|
||||
|
||||
/// Will play the associated audio source once and remove the audio components afterwards.
|
||||
pub const REMOVE: PlaybackSettings = PlaybackSettings {
|
||||
mode: PlaybackMode::Remove,
|
||||
volume: Volume::Relative(VolumeLevel(1.0)),
|
||||
speed: 1.0,
|
||||
paused: false,
|
||||
};
|
||||
|
||||
/// Helper to start in a paused state.
|
||||
pub const fn paused(mut self) -> Self {
|
||||
self.paused = true;
|
||||
self
|
||||
}
|
||||
|
||||
/// Helper to set the volume from start of playback.
|
||||
pub const fn with_volume(mut self, volume: Volume) -> Self {
|
||||
self.volume = volume;
|
||||
|
@ -345,40 +143,35 @@ impl PlaybackSettings {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub(crate) struct SpatialSettings {
|
||||
/// Settings for playing spatial audio.
|
||||
///
|
||||
/// Note: Bevy does not currently support HRTF or any other high-quality 3D sound rendering
|
||||
/// features. Spatial audio is implemented via simple left-right stereo panning.
|
||||
#[derive(Component, Clone, Debug)]
|
||||
pub struct SpatialSettings {
|
||||
pub(crate) left_ear: [f32; 3],
|
||||
pub(crate) right_ear: [f32; 3],
|
||||
pub(crate) emitter: [f32; 3],
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub(crate) struct AudioToPlay<Source>
|
||||
where
|
||||
Source: Asset + Decodable,
|
||||
{
|
||||
pub(crate) sink_handle: HandleId,
|
||||
pub(crate) source_handle: Handle<Source>,
|
||||
pub(crate) settings: PlaybackSettings,
|
||||
pub(crate) spatial: Option<SpatialSettings>,
|
||||
}
|
||||
|
||||
impl<Source> fmt::Debug for AudioToPlay<Source>
|
||||
where
|
||||
Source: Asset + Decodable,
|
||||
{
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.debug_struct("AudioToPlay")
|
||||
.field("sink_handle", &self.sink_handle)
|
||||
.field("source_handle", &self.source_handle)
|
||||
.field("settings", &self.settings)
|
||||
.finish()
|
||||
impl SpatialSettings {
|
||||
/// Configure spatial audio coming from the `emitter` position and heard by a `listener`.
|
||||
///
|
||||
/// The `listener` transform provides the position and rotation where the sound is to be
|
||||
/// heard from. `gap` is the distance between the left and right "ears" of the listener.
|
||||
/// `emitter` is the position where the sound comes from.
|
||||
pub fn new(listener: Transform, gap: f32, emitter: Vec3) -> Self {
|
||||
SpatialSettings {
|
||||
left_ear: (listener.translation + listener.left() * gap / 2.0).to_array(),
|
||||
right_ear: (listener.translation + listener.right() * gap / 2.0).to_array(),
|
||||
emitter: emitter.to_array(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Use this [`Resource`] to control the global volume of all audio with a [`Volume::Relative`] volume.
|
||||
///
|
||||
/// Keep in mind that changing this value will not affect already playing audio.
|
||||
/// Note: changing this value will not affect already playing audio.
|
||||
#[derive(Resource, Default, Clone, Copy)]
|
||||
pub struct GlobalVolume {
|
||||
/// The global volume of all audio.
|
||||
|
@ -393,3 +186,67 @@ impl GlobalVolume {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Bundle for playing a standard bevy audio asset
|
||||
pub type AudioBundle = AudioSourceBundle<AudioSource>;
|
||||
|
||||
/// Bundle for playing a standard bevy audio asset with a 3D position
|
||||
pub type SpatialAudioBundle = SpatialAudioSourceBundle<AudioSource>;
|
||||
|
||||
/// Bundle for playing a sound.
|
||||
///
|
||||
/// Insert this bundle onto an entity to trigger a sound source to begin playing.
|
||||
///
|
||||
/// If the handle refers to an unavailable asset (such as if it has not finished loading yet),
|
||||
/// the audio will not begin playing immediately. The audio will play when the asset is ready.
|
||||
///
|
||||
/// When Bevy begins the audio playback, an [`AudioSink`][crate::AudioSink] component will be
|
||||
/// added to the entity. You can use that component to control the audio settings during playback.
|
||||
#[derive(Bundle)]
|
||||
pub struct AudioSourceBundle<Source = AudioSource>
|
||||
where
|
||||
Source: Asset + Decodable,
|
||||
{
|
||||
/// Asset containing the audio data to play.
|
||||
pub source: Handle<Source>,
|
||||
/// Initial settings that the audio starts playing with.
|
||||
/// If you would like to control the audio while it is playing,
|
||||
/// query for the [`AudioSink`][crate::AudioSink] component.
|
||||
/// Changes to this component will *not* be applied to already-playing audio.
|
||||
pub settings: PlaybackSettings,
|
||||
}
|
||||
|
||||
impl<T: Decodable + Asset> Default for AudioSourceBundle<T> {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
source: Default::default(),
|
||||
settings: Default::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Bundle for playing a sound with a 3D position.
|
||||
///
|
||||
/// Insert this bundle onto an entity to trigger a sound source to begin playing.
|
||||
///
|
||||
/// If the handle refers to an unavailable asset (such as if it has not finished loading yet),
|
||||
/// the audio will not begin playing immediately. The audio will play when the asset is ready.
|
||||
///
|
||||
/// When Bevy begins the audio playback, a [`SpatialAudioSink`][crate::SpatialAudioSink]
|
||||
/// component will be added to the entity. You can use that component to control the audio
|
||||
/// settings during playback.
|
||||
#[derive(Bundle)]
|
||||
pub struct SpatialAudioSourceBundle<Source = AudioSource>
|
||||
where
|
||||
Source: Asset + Decodable,
|
||||
{
|
||||
/// Asset containing the audio data to play.
|
||||
pub source: Handle<Source>,
|
||||
/// Initial settings that the audio starts playing with.
|
||||
/// If you would like to control the audio while it is playing,
|
||||
/// query for the [`SpatialAudioSink`][crate::SpatialAudioSink] component.
|
||||
/// Changes to this component will *not* be applied to already-playing audio.
|
||||
pub settings: PlaybackSettings,
|
||||
/// Spatial audio configuration. Specifies the positions of the source and listener.
|
||||
pub spatial: SpatialSettings,
|
||||
}
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
use crate::{
|
||||
Audio, AudioSource, Decodable, GlobalVolume, SpatialAudioSink, SpatialSettings, Volume,
|
||||
AudioSourceBundle, Decodable, GlobalVolume, PlaybackMode, PlaybackSettings, SpatialAudioSink,
|
||||
SpatialAudioSourceBundle, SpatialSettings, Volume,
|
||||
};
|
||||
use bevy_asset::{Asset, Assets};
|
||||
use bevy_ecs::system::{Res, ResMut, Resource};
|
||||
use bevy_asset::{Asset, Assets, Handle};
|
||||
use bevy_ecs::prelude::*;
|
||||
use bevy_utils::tracing::warn;
|
||||
use rodio::{OutputStream, OutputStreamHandle, Sink, Source, SpatialSink};
|
||||
use std::marker::PhantomData;
|
||||
|
||||
use crate::AudioSink;
|
||||
|
||||
|
@ -22,157 +22,227 @@ use crate::AudioSink;
|
|||
/// since the memory cost will be the same.
|
||||
/// However, repeatedly inserting this resource into the app will **leak more memory**.
|
||||
#[derive(Resource)]
|
||||
pub struct AudioOutput<Source = AudioSource>
|
||||
where
|
||||
Source: Decodable,
|
||||
{
|
||||
pub(crate) struct AudioOutput {
|
||||
stream_handle: Option<OutputStreamHandle>,
|
||||
phantom: PhantomData<Source>,
|
||||
}
|
||||
|
||||
impl<Source> Default for AudioOutput<Source>
|
||||
where
|
||||
Source: Decodable,
|
||||
{
|
||||
impl Default for AudioOutput {
|
||||
fn default() -> Self {
|
||||
if let Ok((stream, stream_handle)) = OutputStream::try_default() {
|
||||
// We leak `OutputStream` to prevent the audio from stopping.
|
||||
std::mem::forget(stream);
|
||||
Self {
|
||||
stream_handle: Some(stream_handle),
|
||||
phantom: PhantomData,
|
||||
}
|
||||
} else {
|
||||
warn!("No audio device found.");
|
||||
Self {
|
||||
stream_handle: None,
|
||||
phantom: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<Source> AudioOutput<Source>
|
||||
where
|
||||
Source: Asset + Decodable,
|
||||
/// Marker for internal use, to despawn entities when playback finishes.
|
||||
#[derive(Component)]
|
||||
pub struct PlaybackDespawnMarker;
|
||||
|
||||
/// Marker for internal use, to remove audio components when playback finishes.
|
||||
#[derive(Component)]
|
||||
pub struct PlaybackRemoveMarker;
|
||||
|
||||
/// Plays "queued" audio through the [`AudioOutput`] resource.
|
||||
///
|
||||
/// "Queued" audio is any audio entity (with the components from
|
||||
/// [`AudioBundle`][crate::AudioBundle] or [`SpatialAudioBundle`][crate::SpatialAudioBundle])
|
||||
/// that does not have an [`AudioSink`]/[`SpatialAudioSink`] component.
|
||||
///
|
||||
/// This system detects such entities, checks if their source asset
|
||||
/// data is available, and creates/inserts the sink.
|
||||
pub(crate) fn play_queued_audio_system<Source: Asset + Decodable>(
|
||||
audio_output: Res<AudioOutput>,
|
||||
audio_sources: Res<Assets<Source>>,
|
||||
global_volume: Res<GlobalVolume>,
|
||||
query_nonplaying: Query<
|
||||
(
|
||||
Entity,
|
||||
&Handle<Source>,
|
||||
&PlaybackSettings,
|
||||
Option<&SpatialSettings>,
|
||||
),
|
||||
(Without<AudioSink>, Without<SpatialAudioSink>),
|
||||
>,
|
||||
mut commands: Commands,
|
||||
) where
|
||||
f32: rodio::cpal::FromSample<Source::DecoderItem>,
|
||||
{
|
||||
fn play_source(&self, audio_source: &Source, repeat: bool) -> Option<Sink> {
|
||||
self.stream_handle
|
||||
.as_ref()
|
||||
.and_then(|stream_handle| match Sink::try_new(stream_handle) {
|
||||
Ok(sink) => {
|
||||
if repeat {
|
||||
sink.append(audio_source.decoder().repeat_infinite());
|
||||
} else {
|
||||
sink.append(audio_source.decoder());
|
||||
}
|
||||
Some(sink)
|
||||
}
|
||||
Err(err) => {
|
||||
warn!("Error playing sound: {err:?}");
|
||||
None
|
||||
}
|
||||
})
|
||||
}
|
||||
let Some(stream_handle) = audio_output.stream_handle.as_ref() else {
|
||||
// audio output unavailable; cannot play sound
|
||||
return;
|
||||
};
|
||||
|
||||
fn play_spatial_source(
|
||||
&self,
|
||||
audio_source: &Source,
|
||||
repeat: bool,
|
||||
spatial: SpatialSettings,
|
||||
) -> Option<SpatialSink> {
|
||||
self.stream_handle.as_ref().and_then(|stream_handle| {
|
||||
match SpatialSink::try_new(
|
||||
stream_handle,
|
||||
spatial.emitter,
|
||||
spatial.left_ear,
|
||||
spatial.right_ear,
|
||||
) {
|
||||
Ok(sink) => {
|
||||
if repeat {
|
||||
sink.append(audio_source.decoder().repeat_infinite());
|
||||
} else {
|
||||
sink.append(audio_source.decoder());
|
||||
}
|
||||
Some(sink)
|
||||
}
|
||||
Err(err) => {
|
||||
warn!("Error playing spatial sound: {err:?}");
|
||||
None
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fn try_play_queued(
|
||||
&self,
|
||||
audio_sources: &Assets<Source>,
|
||||
audio: &mut Audio<Source>,
|
||||
sinks: &mut Assets<AudioSink>,
|
||||
spatial_sinks: &mut Assets<SpatialAudioSink>,
|
||||
global_volume: &GlobalVolume,
|
||||
) {
|
||||
let mut queue = audio.queue.write();
|
||||
let len = queue.len();
|
||||
let mut i = 0;
|
||||
while i < len {
|
||||
let config = queue.pop_front().unwrap();
|
||||
if let Some(audio_source) = audio_sources.get(&config.source_handle) {
|
||||
if let Some(spatial) = config.spatial {
|
||||
if let Some(sink) =
|
||||
self.play_spatial_source(audio_source, config.settings.repeat, spatial)
|
||||
{
|
||||
sink.set_speed(config.settings.speed);
|
||||
match config.settings.volume {
|
||||
for (entity, source_handle, settings, spatial) in &query_nonplaying {
|
||||
if let Some(audio_source) = audio_sources.get(source_handle) {
|
||||
// audio data is available (has loaded), begin playback and insert sink component
|
||||
if let Some(spatial) = spatial {
|
||||
match SpatialSink::try_new(
|
||||
stream_handle,
|
||||
spatial.emitter,
|
||||
spatial.left_ear,
|
||||
spatial.right_ear,
|
||||
) {
|
||||
Ok(sink) => {
|
||||
sink.set_speed(settings.speed);
|
||||
match settings.volume {
|
||||
Volume::Relative(vol) => {
|
||||
sink.set_volume(vol.0 * global_volume.volume.0);
|
||||
}
|
||||
Volume::Absolute(vol) => sink.set_volume(vol.0),
|
||||
}
|
||||
|
||||
// don't keep the strong handle. there is no way to return it to the user here as it is async
|
||||
let _ = spatial_sinks
|
||||
.set(config.sink_handle, SpatialAudioSink { sink: Some(sink) });
|
||||
if settings.paused {
|
||||
sink.pause();
|
||||
}
|
||||
match settings.mode {
|
||||
PlaybackMode::Loop => {
|
||||
sink.append(audio_source.decoder().repeat_infinite());
|
||||
commands
|
||||
.entity(entity)
|
||||
.insert(SpatialAudioSink { sink: Some(sink) });
|
||||
}
|
||||
PlaybackMode::Once => {
|
||||
sink.append(audio_source.decoder());
|
||||
commands
|
||||
.entity(entity)
|
||||
.insert(SpatialAudioSink { sink: Some(sink) });
|
||||
}
|
||||
PlaybackMode::Despawn => {
|
||||
sink.append(audio_source.decoder());
|
||||
commands
|
||||
.entity(entity)
|
||||
// PERF: insert as bundle to reduce archetype moves
|
||||
.insert((
|
||||
SpatialAudioSink { sink: Some(sink) },
|
||||
PlaybackDespawnMarker,
|
||||
));
|
||||
}
|
||||
PlaybackMode::Remove => {
|
||||
sink.append(audio_source.decoder());
|
||||
commands
|
||||
.entity(entity)
|
||||
// PERF: insert as bundle to reduce archetype moves
|
||||
.insert((
|
||||
SpatialAudioSink { sink: Some(sink) },
|
||||
PlaybackRemoveMarker,
|
||||
));
|
||||
}
|
||||
};
|
||||
}
|
||||
} else if let Some(sink) = self.play_source(audio_source, config.settings.repeat) {
|
||||
sink.set_speed(config.settings.speed);
|
||||
|
||||
match config.settings.volume {
|
||||
Volume::Relative(vol) => sink.set_volume(vol.0 * global_volume.volume.0),
|
||||
Volume::Absolute(vol) => sink.set_volume(vol.0),
|
||||
Err(err) => {
|
||||
warn!("Error playing spatial sound: {err:?}");
|
||||
}
|
||||
|
||||
// don't keep the strong handle. there is no way to return it to the user here as it is async
|
||||
let _ = sinks.set(config.sink_handle, AudioSink { sink: Some(sink) });
|
||||
}
|
||||
} else {
|
||||
// audio source hasn't loaded yet. add it back to the queue
|
||||
queue.push_back(config);
|
||||
match Sink::try_new(stream_handle) {
|
||||
Ok(sink) => {
|
||||
sink.set_speed(settings.speed);
|
||||
match settings.volume {
|
||||
Volume::Relative(vol) => {
|
||||
sink.set_volume(vol.0 * global_volume.volume.0);
|
||||
}
|
||||
Volume::Absolute(vol) => sink.set_volume(vol.0),
|
||||
}
|
||||
if settings.paused {
|
||||
sink.pause();
|
||||
}
|
||||
match settings.mode {
|
||||
PlaybackMode::Loop => {
|
||||
sink.append(audio_source.decoder().repeat_infinite());
|
||||
commands
|
||||
.entity(entity)
|
||||
.insert(AudioSink { sink: Some(sink) });
|
||||
}
|
||||
PlaybackMode::Once => {
|
||||
sink.append(audio_source.decoder());
|
||||
commands
|
||||
.entity(entity)
|
||||
.insert(AudioSink { sink: Some(sink) });
|
||||
}
|
||||
PlaybackMode::Despawn => {
|
||||
sink.append(audio_source.decoder());
|
||||
commands
|
||||
.entity(entity)
|
||||
// PERF: insert as bundle to reduce archetype moves
|
||||
.insert((
|
||||
AudioSink { sink: Some(sink) },
|
||||
PlaybackDespawnMarker,
|
||||
));
|
||||
}
|
||||
PlaybackMode::Remove => {
|
||||
sink.append(audio_source.decoder());
|
||||
commands
|
||||
.entity(entity)
|
||||
// PERF: insert as bundle to reduce archetype moves
|
||||
.insert((AudioSink { sink: Some(sink) }, PlaybackRemoveMarker));
|
||||
}
|
||||
};
|
||||
}
|
||||
Err(err) => {
|
||||
warn!("Error playing sound: {err:?}");
|
||||
}
|
||||
}
|
||||
}
|
||||
i += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Plays audio currently queued in the [`Audio`] resource through the [`AudioOutput`] resource
|
||||
pub fn play_queued_audio_system<Source: Asset + Decodable>(
|
||||
audio_output: Res<AudioOutput<Source>>,
|
||||
audio_sources: Option<Res<Assets<Source>>>,
|
||||
global_volume: Res<GlobalVolume>,
|
||||
mut audio: ResMut<Audio<Source>>,
|
||||
mut sinks: ResMut<Assets<AudioSink>>,
|
||||
mut spatial_sinks: ResMut<Assets<SpatialAudioSink>>,
|
||||
) where
|
||||
f32: rodio::cpal::FromSample<Source::DecoderItem>,
|
||||
{
|
||||
if let Some(audio_sources) = audio_sources {
|
||||
audio_output.try_play_queued(
|
||||
&*audio_sources,
|
||||
&mut *audio,
|
||||
&mut sinks,
|
||||
&mut spatial_sinks,
|
||||
&global_volume,
|
||||
);
|
||||
};
|
||||
pub(crate) fn cleanup_finished_audio<T: Decodable + Asset>(
|
||||
mut commands: Commands,
|
||||
query_nonspatial_despawn: Query<
|
||||
(Entity, &AudioSink),
|
||||
(With<PlaybackDespawnMarker>, With<Handle<T>>),
|
||||
>,
|
||||
query_spatial_despawn: Query<
|
||||
(Entity, &SpatialAudioSink),
|
||||
(With<PlaybackDespawnMarker>, With<Handle<T>>),
|
||||
>,
|
||||
query_nonspatial_remove: Query<
|
||||
(Entity, &AudioSink),
|
||||
(With<PlaybackRemoveMarker>, With<Handle<T>>),
|
||||
>,
|
||||
query_spatial_remove: Query<
|
||||
(Entity, &SpatialAudioSink),
|
||||
(With<PlaybackRemoveMarker>, With<Handle<T>>),
|
||||
>,
|
||||
) {
|
||||
for (entity, sink) in &query_nonspatial_despawn {
|
||||
if sink.sink.as_ref().unwrap().empty() {
|
||||
commands.entity(entity).despawn();
|
||||
}
|
||||
}
|
||||
for (entity, sink) in &query_spatial_despawn {
|
||||
if sink.sink.as_ref().unwrap().empty() {
|
||||
commands.entity(entity).despawn();
|
||||
}
|
||||
}
|
||||
for (entity, sink) in &query_nonspatial_remove {
|
||||
if sink.sink.as_ref().unwrap().empty() {
|
||||
commands
|
||||
.entity(entity)
|
||||
.remove::<(AudioSourceBundle<T>, AudioSink, PlaybackRemoveMarker)>();
|
||||
}
|
||||
}
|
||||
for (entity, sink) in &query_spatial_remove {
|
||||
if sink.sink.as_ref().unwrap().empty() {
|
||||
commands.entity(entity).remove::<(
|
||||
SpatialAudioSourceBundle<T>,
|
||||
SpatialAudioSink,
|
||||
PlaybackRemoveMarker,
|
||||
)>();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Run Condition to only play audio if the audio output is available
|
||||
pub(crate) fn audio_output_available(audio_output: Res<AudioOutput>) -> bool {
|
||||
audio_output.stream_handle.is_some()
|
||||
}
|
||||
|
|
|
@ -1,21 +1,22 @@
|
|||
//! Audio support for the game engine Bevy
|
||||
//!
|
||||
//! ```no_run
|
||||
//! # use bevy_ecs::{system::Res, event::EventWriter};
|
||||
//! # use bevy_audio::{Audio, AudioPlugin};
|
||||
//! # use bevy_ecs::prelude::*;
|
||||
//! # use bevy_audio::{AudioBundle, AudioPlugin, PlaybackSettings};
|
||||
//! # use bevy_asset::{AssetPlugin, AssetServer};
|
||||
//! # use bevy_app::{App, AppExit, NoopPluginGroup as MinimalPlugins, Startup};
|
||||
//! fn main() {
|
||||
//! App::new()
|
||||
//! .add_plugins(MinimalPlugins)
|
||||
//! .add_plugin(AssetPlugin::default())
|
||||
//! .add_plugin(AudioPlugin::default())
|
||||
//! .add_plugins((MinimalPlugins, AssetPlugin::default(), AudioPlugin::default()))
|
||||
//! .add_systems(Startup, play_background_audio)
|
||||
//! .run();
|
||||
//! }
|
||||
//!
|
||||
//! fn play_background_audio(asset_server: Res<AssetServer>, audio: Res<Audio>) {
|
||||
//! audio.play(asset_server.load("background_audio.ogg"));
|
||||
//! fn play_background_audio(asset_server: Res<AssetServer>, mut commands: Commands) {
|
||||
//! commands.spawn(AudioBundle {
|
||||
//! source: asset_server.load("background_audio.ogg"),
|
||||
//! settings: PlaybackSettings::LOOP,
|
||||
//! });
|
||||
//! }
|
||||
//! ```
|
||||
|
||||
|
@ -32,13 +33,13 @@ mod sinks;
|
|||
pub mod prelude {
|
||||
#[doc(hidden)]
|
||||
pub use crate::{
|
||||
Audio, AudioOutput, AudioSink, AudioSinkPlayback, AudioSource, Decodable, GlobalVolume,
|
||||
PlaybackSettings, SpatialAudioSink,
|
||||
AudioBundle, AudioSink, AudioSinkPlayback, AudioSource, AudioSourceBundle, Decodable,
|
||||
GlobalVolume, PlaybackSettings, SpatialAudioBundle, SpatialAudioSink,
|
||||
SpatialAudioSourceBundle, SpatialSettings,
|
||||
};
|
||||
}
|
||||
|
||||
pub use audio::*;
|
||||
pub use audio_output::*;
|
||||
pub use audio_source::*;
|
||||
|
||||
pub use rodio::cpal::Sample as CpalSample;
|
||||
|
@ -48,28 +49,34 @@ pub use sinks::*;
|
|||
|
||||
use bevy_app::prelude::*;
|
||||
use bevy_asset::{AddAsset, Asset};
|
||||
use bevy_ecs::prelude::*;
|
||||
|
||||
use audio_output::*;
|
||||
|
||||
/// Set for the audio playback systems, so they can share a run condition
|
||||
#[derive(SystemSet, Debug, Default, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
struct AudioPlaySet;
|
||||
|
||||
/// Adds support for audio playback to a Bevy Application
|
||||
///
|
||||
/// Use the [`Audio`] resource to play audio.
|
||||
/// Insert an [`AudioBundle`] or [`SpatialAudioBundle`] onto your entities to play audio.
|
||||
#[derive(Default)]
|
||||
pub struct AudioPlugin {
|
||||
/// The global volume for all audio sources with a [`Volume::Relative`] volume.
|
||||
/// The global volume for all audio entities with a [`Volume::Relative`] volume.
|
||||
pub global_volume: GlobalVolume,
|
||||
}
|
||||
|
||||
impl Plugin for AudioPlugin {
|
||||
fn build(&self, app: &mut App) {
|
||||
app.init_resource::<AudioOutput<AudioSource>>()
|
||||
.add_asset::<AudioSource>()
|
||||
.add_asset::<AudioSink>()
|
||||
.add_asset::<SpatialAudioSink>()
|
||||
.init_resource::<Audio<AudioSource>>()
|
||||
.insert_resource(self.global_volume)
|
||||
.add_systems(PostUpdate, play_queued_audio_system::<AudioSource>);
|
||||
app.insert_resource(self.global_volume)
|
||||
.configure_set(PostUpdate, AudioPlaySet.run_if(audio_output_available))
|
||||
.init_resource::<AudioOutput>();
|
||||
|
||||
#[cfg(any(feature = "mp3", feature = "flac", feature = "wav", feature = "vorbis"))]
|
||||
app.init_asset_loader::<AudioLoader>();
|
||||
{
|
||||
app.add_audio_source::<AudioSource>();
|
||||
app.init_asset_loader::<AudioLoader>();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -79,9 +86,11 @@ impl AddAudioSource for App {
|
|||
T: Decodable + Asset,
|
||||
f32: rodio::cpal::FromSample<T::DecoderItem>,
|
||||
{
|
||||
self.add_asset::<T>()
|
||||
.init_resource::<Audio<T>>()
|
||||
.init_resource::<AudioOutput<T>>()
|
||||
.add_systems(PostUpdate, play_queued_audio_system::<T>)
|
||||
self.add_asset::<T>().add_systems(
|
||||
PostUpdate,
|
||||
play_queued_audio_system::<T>.in_set(AudioPlaySet),
|
||||
);
|
||||
self.add_systems(PostUpdate, cleanup_finished_audio::<T>.in_set(AudioPlaySet));
|
||||
self
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
use bevy_ecs::component::Component;
|
||||
use bevy_math::Vec3;
|
||||
use bevy_reflect::{TypePath, TypeUuid};
|
||||
use bevy_transform::prelude::Transform;
|
||||
use rodio::{Sink, SpatialSink};
|
||||
|
||||
|
@ -65,30 +65,13 @@ pub trait AudioSinkPlayback {
|
|||
fn empty(&self) -> bool;
|
||||
}
|
||||
|
||||
/// Asset controlling the playback of a sound
|
||||
/// Used to control audio during playback.
|
||||
///
|
||||
/// ```
|
||||
/// # use bevy_ecs::system::{Local, Res};
|
||||
/// # use bevy_asset::{Assets, Handle};
|
||||
/// # use bevy_audio::{AudioSink, AudioSinkPlayback};
|
||||
/// // Execution of this system should be controlled by a state or input,
|
||||
/// // otherwise it would just toggle between play and pause every frame.
|
||||
/// fn pause(
|
||||
/// audio_sinks: Res<Assets<AudioSink>>,
|
||||
/// music_controller: Local<Handle<AudioSink>>,
|
||||
/// ) {
|
||||
/// if let Some(sink) = audio_sinks.get(&*music_controller) {
|
||||
/// if sink.is_paused() {
|
||||
/// sink.play()
|
||||
/// } else {
|
||||
/// sink.pause()
|
||||
/// }
|
||||
/// }
|
||||
/// }
|
||||
/// ```
|
||||
/// Bevy inserts this component onto your entities when it begins playing an audio source.
|
||||
/// Use [`AudioBundle`][crate::AudioBundle] to trigger that to happen.
|
||||
///
|
||||
#[derive(TypePath, TypeUuid)]
|
||||
#[uuid = "8BEE570C-57C2-4FC0-8CFB-983A22F7D981"]
|
||||
/// You can use this component to modify the playback settings while the audio is playing.
|
||||
#[derive(Component)]
|
||||
pub struct AudioSink {
|
||||
// This field is an Option in order to allow us to have a safe drop that will detach the sink.
|
||||
// It will never be None during its life
|
||||
|
@ -139,27 +122,13 @@ impl AudioSinkPlayback for AudioSink {
|
|||
}
|
||||
}
|
||||
|
||||
/// Asset controlling the playback of a sound, or the locations of its listener and emitter.
|
||||
/// Used to control spatial audio during playback.
|
||||
///
|
||||
/// ```
|
||||
/// # use bevy_ecs::system::{Local, Res};
|
||||
/// # use bevy_asset::{Assets, Handle};
|
||||
/// # use bevy_audio::SpatialAudioSink;
|
||||
/// # use bevy_math::Vec3;
|
||||
/// // Execution of this system should be controlled by a state or input,
|
||||
/// // otherwise it would just trigger every frame.
|
||||
/// fn pause(
|
||||
/// spatial_audio_sinks: Res<Assets<SpatialAudioSink>>,
|
||||
/// audio_controller: Local<Handle<SpatialAudioSink>>,
|
||||
/// ) {
|
||||
/// if let Some(spatial_sink) = spatial_audio_sinks.get(&*audio_controller) {
|
||||
/// spatial_sink.set_emitter_position(Vec3::new(1.0, 0.5, 1.0));
|
||||
/// }
|
||||
/// }
|
||||
/// ```
|
||||
/// Bevy inserts this component onto your entities when it begins playing an audio source.
|
||||
/// Use [`SpatialAudioBundle`][crate::SpatialAudioBundle] to trigger that to happen.
|
||||
///
|
||||
#[derive(TypePath, TypeUuid)]
|
||||
#[uuid = "F3CA4C47-595E-453B-96A7-31C3DDF2A177"]
|
||||
/// You can use this component to modify the playback settings while the audio is playing.
|
||||
#[derive(Component)]
|
||||
pub struct SpatialAudioSink {
|
||||
// This field is an Option in order to allow us to have a safe drop that will detach the sink.
|
||||
// It will never be None during its life
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
[package]
|
||||
name = "bevy_core"
|
||||
version = "0.11.0-dev"
|
||||
version = "0.12.0-dev"
|
||||
edition = "2021"
|
||||
description = "Provides core functionality for Bevy Engine"
|
||||
homepage = "https://bevyengine.org"
|
||||
|
@ -11,12 +11,12 @@ keywords = ["bevy"]
|
|||
|
||||
[dependencies]
|
||||
# bevy
|
||||
bevy_app = { path = "../bevy_app", version = "0.11.0-dev", features = ["bevy_reflect"] }
|
||||
bevy_ecs = { path = "../bevy_ecs", version = "0.11.0-dev", features = ["bevy_reflect"] }
|
||||
bevy_math = { path = "../bevy_math", version = "0.11.0-dev" }
|
||||
bevy_reflect = { path = "../bevy_reflect", version = "0.11.0-dev", features = ["bevy"] }
|
||||
bevy_tasks = { path = "../bevy_tasks", version = "0.11.0-dev" }
|
||||
bevy_utils = { path = "../bevy_utils", version = "0.11.0-dev" }
|
||||
bevy_app = { path = "../bevy_app", version = "0.12.0-dev", features = ["bevy_reflect"] }
|
||||
bevy_ecs = { path = "../bevy_ecs", version = "0.12.0-dev", features = ["bevy_reflect"] }
|
||||
bevy_math = { path = "../bevy_math", version = "0.12.0-dev" }
|
||||
bevy_reflect = { path = "../bevy_reflect", version = "0.12.0-dev", features = ["bevy"] }
|
||||
bevy_tasks = { path = "../bevy_tasks", version = "0.12.0-dev" }
|
||||
bevy_utils = { path = "../bevy_utils", version = "0.12.0-dev" }
|
||||
|
||||
# other
|
||||
bytemuck = "1.5"
|
||||
|
|
|
@ -165,8 +165,7 @@ mod tests {
|
|||
#[test]
|
||||
fn runs_spawn_local_tasks() {
|
||||
let mut app = App::new();
|
||||
app.add_plugin(TaskPoolPlugin::default());
|
||||
app.add_plugin(TypeRegistrationPlugin::default());
|
||||
app.add_plugins((TaskPoolPlugin::default(), TypeRegistrationPlugin));
|
||||
|
||||
let (async_tx, async_rx) = crossbeam_channel::unbounded();
|
||||
AsyncComputeTaskPool::get()
|
||||
|
@ -199,9 +198,11 @@ mod tests {
|
|||
#[test]
|
||||
fn frame_counter_update() {
|
||||
let mut app = App::new();
|
||||
app.add_plugin(TaskPoolPlugin::default());
|
||||
app.add_plugin(TypeRegistrationPlugin::default());
|
||||
app.add_plugin(FrameCountPlugin::default());
|
||||
app.add_plugins((
|
||||
TaskPoolPlugin::default(),
|
||||
TypeRegistrationPlugin,
|
||||
FrameCountPlugin,
|
||||
));
|
||||
app.update();
|
||||
|
||||
let frame_count = app.world.resource::<FrameCount>();
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
use bevy_ecs::{
|
||||
component::Component, entity::Entity, query::WorldQuery, reflect::ReflectComponent,
|
||||
};
|
||||
use bevy_reflect::{std_traits::ReflectDefault, FromReflect};
|
||||
use bevy_reflect::{Reflect, ReflectFromReflect};
|
||||
use bevy_reflect::std_traits::ReflectDefault;
|
||||
use bevy_reflect::Reflect;
|
||||
use bevy_utils::AHasher;
|
||||
use std::{
|
||||
borrow::Cow,
|
||||
|
@ -17,8 +17,8 @@ use std::{
|
|||
/// [`Name`] should not be treated as a globally unique identifier for entities,
|
||||
/// as multiple entities can have the same name. [`bevy_ecs::entity::Entity`] should be
|
||||
/// used instead as the default unique identifier.
|
||||
#[derive(Reflect, FromReflect, Component, Clone)]
|
||||
#[reflect(Component, Default, Debug, FromReflect)]
|
||||
#[derive(Reflect, Component, Clone)]
|
||||
#[reflect(Component, Default, Debug)]
|
||||
pub struct Name {
|
||||
hash: u64, // TODO: Shouldn't be serialized
|
||||
name: Cow<'static, str>,
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
[package]
|
||||
name = "bevy_core_pipeline"
|
||||
version = "0.11.0-dev"
|
||||
version = "0.12.0-dev"
|
||||
edition = "2021"
|
||||
authors = [
|
||||
"Bevy Contributors <bevyengine@gmail.com>",
|
||||
|
@ -19,16 +19,16 @@ tonemapping_luts = []
|
|||
|
||||
[dependencies]
|
||||
# bevy
|
||||
bevy_app = { path = "../bevy_app", version = "0.11.0-dev" }
|
||||
bevy_asset = { path = "../bevy_asset", version = "0.11.0-dev" }
|
||||
bevy_core = { path = "../bevy_core", version = "0.11.0-dev" }
|
||||
bevy_derive = { path = "../bevy_derive", version = "0.11.0-dev" }
|
||||
bevy_ecs = { path = "../bevy_ecs", version = "0.11.0-dev" }
|
||||
bevy_reflect = { path = "../bevy_reflect", version = "0.11.0-dev" }
|
||||
bevy_render = { path = "../bevy_render", version = "0.11.0-dev" }
|
||||
bevy_transform = { path = "../bevy_transform", version = "0.11.0-dev" }
|
||||
bevy_math = { path = "../bevy_math", version = "0.11.0-dev" }
|
||||
bevy_utils = { path = "../bevy_utils", version = "0.11.0-dev" }
|
||||
bevy_app = { path = "../bevy_app", version = "0.12.0-dev" }
|
||||
bevy_asset = { path = "../bevy_asset", version = "0.12.0-dev" }
|
||||
bevy_core = { path = "../bevy_core", version = "0.12.0-dev" }
|
||||
bevy_derive = { path = "../bevy_derive", version = "0.12.0-dev" }
|
||||
bevy_ecs = { path = "../bevy_ecs", version = "0.12.0-dev" }
|
||||
bevy_reflect = { path = "../bevy_reflect", version = "0.12.0-dev" }
|
||||
bevy_render = { path = "../bevy_render", version = "0.12.0-dev" }
|
||||
bevy_transform = { path = "../bevy_transform", version = "0.12.0-dev" }
|
||||
bevy_math = { path = "../bevy_math", version = "0.12.0-dev" }
|
||||
bevy_utils = { path = "../bevy_utils", version = "0.12.0-dev" }
|
||||
|
||||
serde = { version = "1", features = ["derive"] }
|
||||
bitflags = "2.3"
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
#import bevy_core_pipeline::fullscreen_vertex_shader
|
||||
#import bevy_core_pipeline::fullscreen_vertex_shader FullscreenVertexOutput
|
||||
|
||||
@group(0) @binding(0)
|
||||
var in_texture: texture_2d<f32>;
|
||||
|
|
|
@ -52,8 +52,10 @@ impl Plugin for BloomPlugin {
|
|||
app.register_type::<BloomSettings>();
|
||||
app.register_type::<BloomPrefilterSettings>();
|
||||
app.register_type::<BloomCompositeMode>();
|
||||
app.add_plugin(ExtractComponentPlugin::<BloomSettings>::default());
|
||||
app.add_plugin(UniformComponentPlugin::<BloomUniforms>::default());
|
||||
app.add_plugins((
|
||||
ExtractComponentPlugin::<BloomSettings>::default(),
|
||||
UniformComponentPlugin::<BloomUniforms>::default(),
|
||||
));
|
||||
|
||||
let render_app = match app.get_sub_app_mut(RenderApp) {
|
||||
Ok(render_app) => render_app,
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
use super::downsampling_pipeline::BloomUniforms;
|
||||
use bevy_ecs::{prelude::Component, query::QueryItem, reflect::ReflectComponent};
|
||||
use bevy_math::{UVec4, Vec4};
|
||||
use bevy_reflect::{std_traits::ReflectDefault, FromReflect, Reflect, ReflectFromReflect};
|
||||
use bevy_math::{URect, UVec4, Vec4};
|
||||
use bevy_reflect::{std_traits::ReflectDefault, Reflect};
|
||||
use bevy_render::{extract_component::ExtractComponent, prelude::Camera};
|
||||
|
||||
/// Applies a bloom effect to an HDR-enabled 2d or 3d camera.
|
||||
|
@ -119,7 +119,7 @@ impl BloomSettings {
|
|||
composite_mode: BloomCompositeMode::EnergyConserving,
|
||||
};
|
||||
|
||||
/// A preset that's similiar to how older games did bloom.
|
||||
/// A preset that's similar to how older games did bloom.
|
||||
pub const OLD_SCHOOL: Self = Self {
|
||||
intensity: 0.05,
|
||||
low_frequency_boost: 0.7,
|
||||
|
@ -159,9 +159,8 @@ impl Default for BloomSettings {
|
|||
/// # Considerations
|
||||
/// * Changing these settings creates a physically inaccurate image
|
||||
/// * Changing these settings makes it easy to make the final result look worse
|
||||
/// * Non-default prefilter settings should be used in conjuction with [`BloomCompositeMode::Additive`]
|
||||
#[derive(Default, Clone, Reflect, FromReflect)]
|
||||
#[reflect(FromReflect)]
|
||||
/// * Non-default prefilter settings should be used in conjunction with [`BloomCompositeMode::Additive`]
|
||||
#[derive(Default, Clone, Reflect)]
|
||||
pub struct BloomPrefilterSettings {
|
||||
/// Baseline of the quadratic threshold curve (default: 0.0).
|
||||
///
|
||||
|
@ -197,7 +196,7 @@ impl ExtractComponent for BloomSettings {
|
|||
camera.is_active,
|
||||
camera.hdr,
|
||||
) {
|
||||
(Some((origin, _)), Some(size), Some(target_size), true, true) => {
|
||||
(Some(URect { min: origin, .. }), Some(size), Some(target_size), true, true) => {
|
||||
let threshold = settings.prefilter_settings.threshold;
|
||||
let threshold_softness = settings.prefilter_settings.threshold_softness;
|
||||
let knee = threshold * threshold_softness.clamp(0.0, 1.0);
|
||||
|
|
|
@ -1,13 +1,11 @@
|
|||
use bevy_derive::{Deref, DerefMut};
|
||||
use bevy_ecs::prelude::*;
|
||||
use bevy_reflect::{
|
||||
FromReflect, Reflect, ReflectDeserialize, ReflectFromReflect, ReflectSerialize,
|
||||
};
|
||||
use bevy_reflect::{Reflect, ReflectDeserialize, ReflectSerialize};
|
||||
use bevy_render::{color::Color, extract_resource::ExtractResource};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Reflect, FromReflect, Serialize, Deserialize, Clone, Debug, Default)]
|
||||
#[reflect(Serialize, Deserialize, FromReflect)]
|
||||
#[derive(Reflect, Serialize, Deserialize, Clone, Debug, Default)]
|
||||
#[reflect(Serialize, Deserialize)]
|
||||
pub enum ClearColorConfig {
|
||||
#[default]
|
||||
Default,
|
||||
|
@ -19,8 +17,8 @@ pub enum ClearColorConfig {
|
|||
///
|
||||
/// This color appears as the "background" color for simple apps,
|
||||
/// when there are portions of the screen with nothing rendered.
|
||||
#[derive(Resource, Clone, Debug, Deref, DerefMut, ExtractResource, Reflect, FromReflect)]
|
||||
#[reflect(Resource, FromReflect)]
|
||||
#[derive(Resource, Clone, Debug, Deref, DerefMut, ExtractResource, Reflect)]
|
||||
#[reflect(Resource)]
|
||||
pub struct ClearColor(pub Color);
|
||||
|
||||
impl Default for ClearColor {
|
||||
|
|
|
@ -108,8 +108,10 @@ impl Plugin for CASPlugin {
|
|||
);
|
||||
|
||||
app.register_type::<ContrastAdaptiveSharpeningSettings>();
|
||||
app.add_plugin(ExtractComponentPlugin::<ContrastAdaptiveSharpeningSettings>::default());
|
||||
app.add_plugin(UniformComponentPlugin::<CASUniform>::default());
|
||||
app.add_plugins((
|
||||
ExtractComponentPlugin::<ContrastAdaptiveSharpeningSettings>::default(),
|
||||
UniformComponentPlugin::<CASUniform>::default(),
|
||||
));
|
||||
|
||||
let render_app = match app.get_sub_app_mut(RenderApp) {
|
||||
Ok(render_app) => render_app,
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
// THE SOFTWARE.
|
||||
|
||||
#import bevy_core_pipeline::fullscreen_vertex_shader
|
||||
#import bevy_core_pipeline::fullscreen_vertex_shader FullscreenVertexOutput
|
||||
|
||||
struct CASUniforms {
|
||||
sharpness: f32,
|
||||
|
|
|
@ -3,7 +3,7 @@ use crate::{
|
|||
tonemapping::{DebandDither, Tonemapping},
|
||||
};
|
||||
use bevy_ecs::prelude::*;
|
||||
use bevy_reflect::{FromReflect, Reflect, ReflectFromReflect};
|
||||
use bevy_reflect::Reflect;
|
||||
use bevy_render::{
|
||||
camera::{Camera, CameraProjection, CameraRenderGraph, OrthographicProjection},
|
||||
extract_component::ExtractComponent,
|
||||
|
@ -12,9 +12,9 @@ use bevy_render::{
|
|||
};
|
||||
use bevy_transform::prelude::{GlobalTransform, Transform};
|
||||
|
||||
#[derive(Component, Default, Reflect, FromReflect, Clone, ExtractComponent)]
|
||||
#[derive(Component, Default, Reflect, Clone, ExtractComponent)]
|
||||
#[extract_component_filter(With<Camera>)]
|
||||
#[reflect(Component, FromReflect)]
|
||||
#[reflect(Component)]
|
||||
pub struct Camera2d {
|
||||
pub clear_color: ClearColorConfig,
|
||||
}
|
||||
|
|
|
@ -45,7 +45,7 @@ pub struct Core2dPlugin;
|
|||
impl Plugin for Core2dPlugin {
|
||||
fn build(&self, app: &mut App) {
|
||||
app.register_type::<Camera2d>()
|
||||
.add_plugin(ExtractComponentPlugin::<Camera2d>::default());
|
||||
.add_plugins(ExtractComponentPlugin::<Camera2d>::default());
|
||||
|
||||
let render_app = match app.get_sub_app_mut(RenderApp) {
|
||||
Ok(render_app) => render_app,
|
||||
|
|
|
@ -3,9 +3,7 @@ use crate::{
|
|||
tonemapping::{DebandDither, Tonemapping},
|
||||
};
|
||||
use bevy_ecs::prelude::*;
|
||||
use bevy_reflect::{
|
||||
FromReflect, Reflect, ReflectDeserialize, ReflectFromReflect, ReflectSerialize,
|
||||
};
|
||||
use bevy_reflect::{Reflect, ReflectDeserialize, ReflectSerialize};
|
||||
use bevy_render::{
|
||||
camera::{Camera, CameraRenderGraph, Projection},
|
||||
extract_component::ExtractComponent,
|
||||
|
@ -17,9 +15,9 @@ use bevy_transform::prelude::{GlobalTransform, Transform};
|
|||
use serde::{Deserialize, Serialize};
|
||||
|
||||
/// Configuration for the "main 3d render graph".
|
||||
#[derive(Component, Reflect, FromReflect, Clone, ExtractComponent)]
|
||||
#[derive(Component, Reflect, Clone, ExtractComponent)]
|
||||
#[extract_component_filter(With<Camera>)]
|
||||
#[reflect(Component, FromReflect)]
|
||||
#[reflect(Component)]
|
||||
pub struct Camera3d {
|
||||
/// The clear color operation to perform for the main 3d pass.
|
||||
///
|
||||
|
@ -56,8 +54,7 @@ impl Default for Camera3d {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Reflect, FromReflect)]
|
||||
#[reflect(FromReflect)]
|
||||
#[derive(Clone, Copy, Reflect)]
|
||||
pub struct Camera3dDepthTextureUsage(u32);
|
||||
|
||||
impl From<TextureUsages> for Camera3dDepthTextureUsage {
|
||||
|
@ -72,8 +69,8 @@ impl From<Camera3dDepthTextureUsage> for TextureUsages {
|
|||
}
|
||||
|
||||
/// The depth clear operation to perform for the main 3d pass.
|
||||
#[derive(Reflect, FromReflect, Serialize, Deserialize, Clone, Debug)]
|
||||
#[reflect(Serialize, Deserialize, FromReflect)]
|
||||
#[derive(Reflect, Serialize, Deserialize, Clone, Debug)]
|
||||
#[reflect(Serialize, Deserialize)]
|
||||
pub enum Camera3dDepthLoadOp {
|
||||
/// Clear with a specified value.
|
||||
/// Note that 0.0 is the far plane due to bevy's use of reverse-z projections.
|
||||
|
|
|
@ -72,8 +72,7 @@ impl Plugin for Core3dPlugin {
|
|||
fn build(&self, app: &mut App) {
|
||||
app.register_type::<Camera3d>()
|
||||
.register_type::<Camera3dDepthLoadOp>()
|
||||
.add_plugin(SkyboxPlugin)
|
||||
.add_plugin(ExtractComponentPlugin::<Camera3d>::default());
|
||||
.add_plugins((SkyboxPlugin, ExtractComponentPlugin::<Camera3d>::default()));
|
||||
|
||||
let render_app = match app.get_sub_app_mut(RenderApp) {
|
||||
Ok(render_app) => render_app,
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
//
|
||||
// Tweaks by mrDIMAS - https://github.com/FyroxEngine/Fyrox/blob/master/src/renderer/shaders/fxaa_fs.glsl
|
||||
|
||||
#import bevy_core_pipeline::fullscreen_vertex_shader
|
||||
#import bevy_core_pipeline::fullscreen_vertex_shader FullscreenVertexOutput
|
||||
|
||||
@group(0) @binding(0)
|
||||
var screenTexture: texture_2d<f32>;
|
||||
|
|
|
@ -7,9 +7,7 @@ use bevy_app::prelude::*;
|
|||
use bevy_asset::{load_internal_asset, HandleUntyped};
|
||||
use bevy_derive::Deref;
|
||||
use bevy_ecs::prelude::*;
|
||||
use bevy_reflect::{
|
||||
std_traits::ReflectDefault, FromReflect, Reflect, ReflectFromReflect, TypeUuid,
|
||||
};
|
||||
use bevy_reflect::{std_traits::ReflectDefault, Reflect, TypeUuid};
|
||||
use bevy_render::{
|
||||
extract_component::{ExtractComponent, ExtractComponentPlugin},
|
||||
prelude::Camera,
|
||||
|
@ -26,8 +24,8 @@ mod node;
|
|||
|
||||
pub use node::FxaaNode;
|
||||
|
||||
#[derive(Reflect, FromReflect, Eq, PartialEq, Hash, Clone, Copy)]
|
||||
#[reflect(FromReflect, PartialEq, Hash)]
|
||||
#[derive(Reflect, Eq, PartialEq, Hash, Clone, Copy)]
|
||||
#[reflect(PartialEq, Hash)]
|
||||
pub enum Sensitivity {
|
||||
Low,
|
||||
Medium,
|
||||
|
@ -48,8 +46,8 @@ impl Sensitivity {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Reflect, FromReflect, Component, Clone, ExtractComponent)]
|
||||
#[reflect(Component, FromReflect, Default)]
|
||||
#[derive(Reflect, Component, Clone, ExtractComponent)]
|
||||
#[reflect(Component, Default)]
|
||||
#[extract_component_filter(With<Camera>)]
|
||||
pub struct Fxaa {
|
||||
/// Enable render passes for FXAA.
|
||||
|
@ -87,7 +85,7 @@ impl Plugin for FxaaPlugin {
|
|||
load_internal_asset!(app, FXAA_SHADER_HANDLE, "fxaa.wgsl", Shader::from_wgsl);
|
||||
|
||||
app.register_type::<Fxaa>();
|
||||
app.add_plugin(ExtractComponentPlugin::<Fxaa>::default());
|
||||
app.add_plugins(ExtractComponentPlugin::<Fxaa>::default());
|
||||
|
||||
let render_app = match app.get_sub_app_mut(RenderApp) {
|
||||
Ok(render_app) => render_app,
|
||||
|
|
|
@ -68,15 +68,17 @@ impl Plugin for CorePipelinePlugin {
|
|||
.register_type::<DepthPrepass>()
|
||||
.register_type::<NormalPrepass>()
|
||||
.init_resource::<ClearColor>()
|
||||
.add_plugin(ExtractResourcePlugin::<ClearColor>::default())
|
||||
.add_plugin(Core2dPlugin)
|
||||
.add_plugin(Core3dPlugin)
|
||||
.add_plugin(BlitPlugin)
|
||||
.add_plugin(MsaaWritebackPlugin)
|
||||
.add_plugin(TonemappingPlugin)
|
||||
.add_plugin(UpscalingPlugin)
|
||||
.add_plugin(BloomPlugin)
|
||||
.add_plugin(FxaaPlugin)
|
||||
.add_plugin(CASPlugin);
|
||||
.add_plugins((
|
||||
ExtractResourcePlugin::<ClearColor>::default(),
|
||||
Core2dPlugin,
|
||||
Core3dPlugin,
|
||||
BlitPlugin,
|
||||
MsaaWritebackPlugin,
|
||||
TonemappingPlugin,
|
||||
UpscalingPlugin,
|
||||
BloomPlugin,
|
||||
FxaaPlugin,
|
||||
CASPlugin,
|
||||
));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -30,7 +30,7 @@ pub mod node;
|
|||
use std::cmp::Reverse;
|
||||
|
||||
use bevy_ecs::prelude::*;
|
||||
use bevy_reflect::{FromReflect, Reflect, ReflectFromReflect};
|
||||
use bevy_reflect::Reflect;
|
||||
use bevy_render::{
|
||||
render_phase::{CachedRenderPipelinePhaseItem, DrawFunctionId, PhaseItem},
|
||||
render_resource::{CachedRenderPipelineId, Extent3d, TextureFormat},
|
||||
|
@ -43,19 +43,16 @@ pub const NORMAL_PREPASS_FORMAT: TextureFormat = TextureFormat::Rgb10a2Unorm;
|
|||
pub const MOTION_VECTOR_PREPASS_FORMAT: TextureFormat = TextureFormat::Rg16Float;
|
||||
|
||||
/// If added to a [`crate::prelude::Camera3d`] then depth values will be copied to a separate texture available to the main pass.
|
||||
#[derive(Component, Default, Reflect, FromReflect)]
|
||||
#[reflect(FromReflect)]
|
||||
#[derive(Component, Default, Reflect)]
|
||||
pub struct DepthPrepass;
|
||||
|
||||
/// If added to a [`crate::prelude::Camera3d`] then vertex world normals will be copied to a separate texture available to the main pass.
|
||||
/// Normals will have normal map textures already applied.
|
||||
#[derive(Component, Default, Reflect, FromReflect)]
|
||||
#[reflect(FromReflect)]
|
||||
#[derive(Component, Default, Reflect)]
|
||||
pub struct NormalPrepass;
|
||||
|
||||
/// If added to a [`crate::prelude::Camera3d`] then screen space motion vectors will be copied to a separate texture available to the main pass.
|
||||
#[derive(Component, Default, Reflect, FromReflect)]
|
||||
#[reflect(FromReflect)]
|
||||
#[derive(Component, Default, Reflect)]
|
||||
pub struct MotionVectorPrepass;
|
||||
|
||||
/// Textures that are written to by the prepass.
|
||||
|
|
|
@ -34,7 +34,7 @@ impl Plugin for SkyboxPlugin {
|
|||
fn build(&self, app: &mut App) {
|
||||
load_internal_asset!(app, SKYBOX_SHADER_HANDLE, "skybox.wgsl", Shader::from_wgsl);
|
||||
|
||||
app.add_plugin(ExtractComponentPlugin::<Skybox>::default());
|
||||
app.add_plugins(ExtractComponentPlugin::<Skybox>::default());
|
||||
|
||||
let render_app = match app.get_sub_app_mut(RenderApp) {
|
||||
Ok(render_app) => render_app,
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
#import bevy_render::view
|
||||
#import bevy_render::view View
|
||||
|
||||
@group(0) @binding(0)
|
||||
var skybox: texture_cube<f32>;
|
||||
|
|
|
@ -10,14 +10,14 @@ use bevy_core::FrameCount;
|
|||
use bevy_ecs::{
|
||||
prelude::{Bundle, Component, Entity},
|
||||
query::{QueryItem, With},
|
||||
schedule::IntoSystemConfigs,
|
||||
schedule::{apply_deferred, IntoSystemConfigs},
|
||||
system::{Commands, Query, Res, ResMut, Resource},
|
||||
world::{FromWorld, World},
|
||||
};
|
||||
use bevy_math::vec2;
|
||||
use bevy_reflect::{Reflect, TypeUuid};
|
||||
use bevy_render::{
|
||||
camera::{ExtractedCamera, TemporalJitter},
|
||||
camera::{ExtractedCamera, MipBias, TemporalJitter},
|
||||
prelude::{Camera, Projection},
|
||||
render_graph::{NodeRunError, RenderGraphApp, RenderGraphContext, ViewNode, ViewNodeRunner},
|
||||
render_resource::{
|
||||
|
@ -65,7 +65,8 @@ impl Plugin for TemporalAntiAliasPlugin {
|
|||
.add_systems(
|
||||
Render,
|
||||
(
|
||||
prepare_taa_jitter
|
||||
(prepare_taa_jitter_and_mip_bias, apply_deferred)
|
||||
.chain()
|
||||
.before(prepare_view_uniforms)
|
||||
.in_set(RenderSet::Prepare),
|
||||
prepare_taa_history_textures.in_set(RenderSet::Prepare),
|
||||
|
@ -131,7 +132,8 @@ pub struct TemporalAntiAliasBundle {
|
|||
///
|
||||
/// Cannot be used with [`bevy_render::camera::OrthographicProjection`].
|
||||
///
|
||||
/// Currently does not support skinned meshes. There will probably be ghosting artifacts if used with them.
|
||||
/// Currently does not support skinned meshes and morph targets.
|
||||
/// There will probably be ghosting artifacts if used with them.
|
||||
/// Does not work well with alpha-blended meshes as it requires depth writing to determine motion.
|
||||
///
|
||||
/// It is very important that correct motion vectors are written for everything on screen.
|
||||
|
@ -139,12 +141,14 @@ pub struct TemporalAntiAliasBundle {
|
|||
/// are added using a third party library, the library must either:
|
||||
/// 1. Write particle motion vectors to the motion vectors prepass texture
|
||||
/// 2. Render particles after TAA
|
||||
///
|
||||
/// If no [`MipBias`] component is attached to the camera, TAA will add a MipBias(-1.0) component.
|
||||
#[derive(Component, Reflect, Clone)]
|
||||
pub struct TemporalAntiAliasSettings {
|
||||
/// Set to true to delete the saved temporal history (past frames).
|
||||
///
|
||||
/// Useful for preventing ghosting when the history is no longer
|
||||
/// representive of the current frame, such as in sudden camera cuts.
|
||||
/// representative of the current frame, such as in sudden camera cuts.
|
||||
///
|
||||
/// After setting this to true, it will automatically be toggled
|
||||
/// back to false after one frame.
|
||||
|
@ -435,9 +439,13 @@ fn extract_taa_settings(mut commands: Commands, mut main_world: ResMut<MainWorld
|
|||
}
|
||||
}
|
||||
|
||||
fn prepare_taa_jitter(
|
||||
fn prepare_taa_jitter_and_mip_bias(
|
||||
frame_count: Res<FrameCount>,
|
||||
mut query: Query<&mut TemporalJitter, With<TemporalAntiAliasSettings>>,
|
||||
mut query: Query<
|
||||
(Entity, &mut TemporalJitter, Option<&MipBias>),
|
||||
With<TemporalAntiAliasSettings>,
|
||||
>,
|
||||
mut commands: Commands,
|
||||
) {
|
||||
// Halton sequence (2, 3) - 0.5, skipping i = 0
|
||||
let halton_sequence = [
|
||||
|
@ -453,8 +461,12 @@ fn prepare_taa_jitter(
|
|||
|
||||
let offset = halton_sequence[frame_count.0 as usize % halton_sequence.len()];
|
||||
|
||||
for mut jitter in &mut query {
|
||||
for (entity, mut jitter, mip_bias) in &mut query {
|
||||
jitter.offset = offset;
|
||||
|
||||
if mip_bias.is_none() {
|
||||
commands.entity(entity).insert(MipBias(-1.0));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -2,7 +2,7 @@ use crate::fullscreen_vertex_shader::fullscreen_shader_vertex_state;
|
|||
use bevy_app::prelude::*;
|
||||
use bevy_asset::{load_internal_asset, Assets, Handle, HandleUntyped};
|
||||
use bevy_ecs::prelude::*;
|
||||
use bevy_reflect::{FromReflect, Reflect, ReflectFromReflect, TypeUuid};
|
||||
use bevy_reflect::{Reflect, TypeUuid};
|
||||
use bevy_render::camera::Camera;
|
||||
use bevy_render::extract_component::{ExtractComponent, ExtractComponentPlugin};
|
||||
use bevy_render::extract_resource::{ExtractResource, ExtractResourcePlugin};
|
||||
|
@ -82,13 +82,15 @@ impl Plugin for TonemappingPlugin {
|
|||
app.insert_resource(tonemapping_luts);
|
||||
}
|
||||
|
||||
app.add_plugin(ExtractResourcePlugin::<TonemappingLuts>::default());
|
||||
app.add_plugins(ExtractResourcePlugin::<TonemappingLuts>::default());
|
||||
|
||||
app.register_type::<Tonemapping>();
|
||||
app.register_type::<DebandDither>();
|
||||
|
||||
app.add_plugin(ExtractComponentPlugin::<Tonemapping>::default());
|
||||
app.add_plugin(ExtractComponentPlugin::<DebandDither>::default());
|
||||
app.add_plugins((
|
||||
ExtractComponentPlugin::<Tonemapping>::default(),
|
||||
ExtractComponentPlugin::<DebandDither>::default(),
|
||||
));
|
||||
|
||||
if let Ok(render_app) = app.get_sub_app_mut(RenderApp) {
|
||||
render_app
|
||||
|
@ -114,20 +116,10 @@ pub struct TonemappingPipeline {
|
|||
|
||||
/// Optionally enables a tonemapping shader that attempts to map linear input stimulus into a perceptually uniform image for a given [`Camera`] entity.
|
||||
#[derive(
|
||||
Component,
|
||||
Debug,
|
||||
Hash,
|
||||
Clone,
|
||||
Copy,
|
||||
Reflect,
|
||||
Default,
|
||||
ExtractComponent,
|
||||
PartialEq,
|
||||
Eq,
|
||||
FromReflect,
|
||||
Component, Debug, Hash, Clone, Copy, Reflect, Default, ExtractComponent, PartialEq, Eq,
|
||||
)]
|
||||
#[extract_component_filter(With<Camera>)]
|
||||
#[reflect(Component, FromReflect)]
|
||||
#[reflect(Component)]
|
||||
pub enum Tonemapping {
|
||||
/// Bypass tonemapping.
|
||||
None,
|
||||
|
@ -301,20 +293,10 @@ pub fn queue_view_tonemapping_pipelines(
|
|||
}
|
||||
/// Enables a debanding shader that applies dithering to mitigate color banding in the final image for a given [`Camera`] entity.
|
||||
#[derive(
|
||||
Component,
|
||||
Debug,
|
||||
Hash,
|
||||
Clone,
|
||||
Copy,
|
||||
Reflect,
|
||||
Default,
|
||||
ExtractComponent,
|
||||
PartialEq,
|
||||
Eq,
|
||||
FromReflect,
|
||||
Component, Debug, Hash, Clone, Copy, Reflect, Default, ExtractComponent, PartialEq, Eq,
|
||||
)]
|
||||
#[extract_component_filter(With<Camera>)]
|
||||
#[reflect(Component, FromReflect)]
|
||||
#[reflect(Component)]
|
||||
pub enum DebandDither {
|
||||
#[default]
|
||||
Disabled,
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
#import bevy_core_pipeline::fullscreen_vertex_shader
|
||||
#define TONEMAPPING_PASS
|
||||
|
||||
#import bevy_render::view
|
||||
#import bevy_core_pipeline::fullscreen_vertex_shader FullscreenVertexOutput
|
||||
#import bevy_render::view View
|
||||
#import bevy_core_pipeline::tonemapping tone_mapping, powsafe, screen_space_dither
|
||||
|
||||
@group(0) @binding(0)
|
||||
var<uniform> view: View;
|
||||
|
@ -20,11 +22,11 @@ var dt_lut_sampler: sampler;
|
|||
fn fragment(in: FullscreenVertexOutput) -> @location(0) vec4<f32> {
|
||||
let hdr_color = textureSample(hdr_texture, hdr_sampler, in.uv);
|
||||
|
||||
var output_rgb = tone_mapping(hdr_color).rgb;
|
||||
var output_rgb = tone_mapping(hdr_color, view.color_grading).rgb;
|
||||
|
||||
#ifdef DEBAND_DITHER
|
||||
output_rgb = powsafe(output_rgb.rgb, 1.0 / 2.2);
|
||||
output_rgb = output_rgb + screen_space_dither(in.position.xy);
|
||||
output_rgb = output_rgb + bevy_core_pipeline::tonemapping::screen_space_dither(in.position.xy);
|
||||
// This conversion back to linear space is required because our output texture format is
|
||||
// SRGB; the GPU will assume our output is linear and will apply an SRGB conversion.
|
||||
output_rgb = powsafe(output_rgb.rgb, 2.2);
|
||||
|
|
|
@ -1,5 +1,19 @@
|
|||
#define_import_path bevy_core_pipeline::tonemapping
|
||||
|
||||
#import bevy_render::view View, ColorGrading
|
||||
|
||||
// hack !! not sure what to do with this
|
||||
#ifdef TONEMAPPING_PASS
|
||||
@group(0) @binding(3)
|
||||
var dt_lut_texture: texture_3d<f32>;
|
||||
@group(0) @binding(4)
|
||||
var dt_lut_sampler: sampler;
|
||||
#else
|
||||
@group(0) @binding(15)
|
||||
var dt_lut_texture: texture_3d<f32>;
|
||||
@group(0) @binding(16)
|
||||
var dt_lut_sampler: sampler;
|
||||
#endif
|
||||
|
||||
fn sample_current_lut(p: vec3<f32>) -> vec3<f32> {
|
||||
// Don't include code that will try to sample from LUTs if tonemap method doesn't require it
|
||||
|
@ -48,7 +62,7 @@ fn tonemap_curve(v: f32) -> f32 {
|
|||
#endif
|
||||
}
|
||||
|
||||
fn tonemap_curve3(v: vec3<f32>) -> vec3<f32> {
|
||||
fn tonemap_curve3_(v: vec3<f32>) -> vec3<f32> {
|
||||
return vec3(tonemap_curve(v.r), tonemap_curve(v.g), tonemap_curve(v.b));
|
||||
}
|
||||
|
||||
|
@ -65,7 +79,7 @@ fn somewhat_boring_display_transform(col: vec3<f32>) -> vec3<f32> {
|
|||
let tm_luma = tonemap_curve(ycbcr.x);
|
||||
let tm0 = boring_color.rgb * max(0.0, tm_luma / max(1e-5, tonemapping_luminance(boring_color.rgb)));
|
||||
let final_mult = 0.97;
|
||||
let tm1 = tonemap_curve3(desat_col);
|
||||
let tm1 = tonemap_curve3_(desat_col);
|
||||
|
||||
boring_color = mix(tm0, tm1, bt * bt);
|
||||
|
||||
|
@ -167,7 +181,7 @@ fn saturation(color: vec3<f32>, saturationAmount: f32) -> vec3<f32> {
|
|||
Similar to OCIO lg2 AllocationTransform.
|
||||
ref[0]
|
||||
*/
|
||||
fn convertOpenDomainToNormalizedLog2(color: vec3<f32>, minimum_ev: f32, maximum_ev: f32) -> vec3<f32> {
|
||||
fn convertOpenDomainToNormalizedLog2_(color: vec3<f32>, minimum_ev: f32, maximum_ev: f32) -> vec3<f32> {
|
||||
let in_midgray = 0.18;
|
||||
|
||||
// remove negative before log transform
|
||||
|
@ -210,7 +224,7 @@ fn applyAgXLog(Image: vec3<f32>) -> vec3<f32> {
|
|||
let b = dot(prepared_image, vec3(0.04237565, 0.0784336, 0.87914297));
|
||||
prepared_image = vec3(r, g, b);
|
||||
|
||||
prepared_image = convertOpenDomainToNormalizedLog2(prepared_image, -10.0, 6.5);
|
||||
prepared_image = convertOpenDomainToNormalizedLog2_(prepared_image, -10.0, 6.5);
|
||||
|
||||
prepared_image = clamp(prepared_image, vec3(0.0), vec3(1.0));
|
||||
return prepared_image;
|
||||
|
@ -226,7 +240,7 @@ fn applyLUT3D(Image: vec3<f32>, block_size: f32) -> vec3<f32> {
|
|||
|
||||
fn sample_blender_filmic_lut(stimulus: vec3<f32>) -> vec3<f32> {
|
||||
let block_size = 64.0;
|
||||
let normalized = saturate(convertOpenDomainToNormalizedLog2(stimulus, -11.0, 12.0));
|
||||
let normalized = saturate(convertOpenDomainToNormalizedLog2_(stimulus, -11.0, 12.0));
|
||||
return applyLUT3D(normalized, block_size);
|
||||
}
|
||||
|
||||
|
@ -270,7 +284,7 @@ fn screen_space_dither(frag_coord: vec2<f32>) -> vec3<f32> {
|
|||
return (dither - 0.5) / 255.0;
|
||||
}
|
||||
|
||||
fn tone_mapping(in: vec4<f32>) -> vec4<f32> {
|
||||
fn tone_mapping(in: vec4<f32>, color_grading: ColorGrading) -> vec4<f32> {
|
||||
var color = max(in.rgb, vec3(0.0));
|
||||
|
||||
// Possible future grading:
|
||||
|
@ -282,9 +296,9 @@ fn tone_mapping(in: vec4<f32>) -> vec4<f32> {
|
|||
// color += color * luma.xxx * 1.0;
|
||||
|
||||
// Linear pre tonemapping grading
|
||||
color = saturation(color, view.color_grading.pre_saturation);
|
||||
color = powsafe(color, view.color_grading.gamma);
|
||||
color = color * powsafe(vec3(2.0), view.color_grading.exposure);
|
||||
color = saturation(color, color_grading.pre_saturation);
|
||||
color = powsafe(color, color_grading.gamma);
|
||||
color = color * powsafe(vec3(2.0), color_grading.exposure);
|
||||
color = max(color, vec3(0.0));
|
||||
|
||||
// tone_mapping
|
||||
|
@ -308,7 +322,7 @@ fn tone_mapping(in: vec4<f32>) -> vec4<f32> {
|
|||
#endif
|
||||
|
||||
// Perceptual post tonemapping grading
|
||||
color = saturation(color, view.color_grading.post_saturation);
|
||||
color = saturation(color, color_grading.post_saturation);
|
||||
|
||||
return vec4(color, in.a);
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
[package]
|
||||
name = "bevy_derive"
|
||||
version = "0.11.0-dev"
|
||||
version = "0.12.0-dev"
|
||||
edition = "2021"
|
||||
description = "Provides derive implementations for Bevy Engine"
|
||||
homepage = "https://bevyengine.org"
|
||||
|
@ -12,7 +12,7 @@ keywords = ["bevy"]
|
|||
proc-macro = true
|
||||
|
||||
[dependencies]
|
||||
bevy_macro_utils = { path = "../bevy_macro_utils", version = "0.11.0-dev" }
|
||||
bevy_macro_utils = { path = "../bevy_macro_utils", version = "0.12.0-dev" }
|
||||
|
||||
quote = "1.0"
|
||||
syn = { version = "2.0", features = ["full"] }
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
[package]
|
||||
name = "bevy_diagnostic"
|
||||
version = "0.11.0-dev"
|
||||
version = "0.12.0-dev"
|
||||
edition = "2021"
|
||||
description = "Provides diagnostic functionality for Bevy Engine"
|
||||
homepage = "https://bevyengine.org"
|
||||
|
@ -14,12 +14,12 @@ dynamic_linking = []
|
|||
|
||||
[dependencies]
|
||||
# bevy
|
||||
bevy_app = { path = "../bevy_app", version = "0.11.0-dev" }
|
||||
bevy_core = { path = "../bevy_core", version = "0.11.0-dev" }
|
||||
bevy_ecs = { path = "../bevy_ecs", version = "0.11.0-dev" }
|
||||
bevy_log = { path = "../bevy_log", version = "0.11.0-dev" }
|
||||
bevy_time = { path = "../bevy_time", version = "0.11.0-dev" }
|
||||
bevy_utils = { path = "../bevy_utils", version = "0.11.0-dev" }
|
||||
bevy_app = { path = "../bevy_app", version = "0.12.0-dev" }
|
||||
bevy_core = { path = "../bevy_core", version = "0.12.0-dev" }
|
||||
bevy_ecs = { path = "../bevy_ecs", version = "0.12.0-dev" }
|
||||
bevy_log = { path = "../bevy_log", version = "0.12.0-dev" }
|
||||
bevy_time = { path = "../bevy_time", version = "0.12.0-dev" }
|
||||
bevy_utils = { path = "../bevy_utils", version = "0.12.0-dev" }
|
||||
|
||||
# MacOS
|
||||
[target.'cfg(all(target_os="macos"))'.dependencies]
|
||||
|
|
|
@ -299,7 +299,7 @@ impl RegisterDiagnostic for App {
|
|||
///
|
||||
/// App::new()
|
||||
/// .register_diagnostic(Diagnostic::new(UNIQUE_DIAG_ID, "example", 10))
|
||||
/// .add_plugin(DiagnosticsPlugin)
|
||||
/// .add_plugins(DiagnosticsPlugin)
|
||||
/// .run();
|
||||
/// ```
|
||||
fn register_diagnostic(&mut self, diagnostic: Diagnostic) -> &mut Self {
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
[package]
|
||||
name = "bevy_dylib"
|
||||
version = "0.11.0-dev"
|
||||
version = "0.12.0-dev"
|
||||
edition = "2021"
|
||||
description = "Force the Bevy Engine to be dynamically linked for faster linking"
|
||||
homepage = "https://bevyengine.org"
|
||||
|
@ -12,4 +12,4 @@ keywords = ["bevy"]
|
|||
crate-type = ["dylib"]
|
||||
|
||||
[dependencies]
|
||||
bevy_internal = { path = "../bevy_internal", version = "0.11.0-dev", default-features = false }
|
||||
bevy_internal = { path = "../bevy_internal", version = "0.12.0-dev", default-features = false }
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
[package]
|
||||
name = "bevy_dynamic_plugin"
|
||||
version = "0.11.0-dev"
|
||||
version = "0.12.0-dev"
|
||||
edition = "2021"
|
||||
description = "Provides dynamic plugin loading capabilities for non-wasm platforms"
|
||||
homepage = "https://bevyengine.org"
|
||||
|
@ -10,7 +10,7 @@ keywords = ["bevy"]
|
|||
|
||||
[dependencies]
|
||||
# bevy
|
||||
bevy_app = { path = "../bevy_app", version = "0.11.0-dev" }
|
||||
bevy_app = { path = "../bevy_app", version = "0.12.0-dev" }
|
||||
|
||||
# other
|
||||
libloading = { version = "0.8" }
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
[package]
|
||||
name = "bevy_ecs"
|
||||
version = "0.11.0-dev"
|
||||
version = "0.12.0-dev"
|
||||
edition = "2021"
|
||||
description = "Bevy Engine's entity component system"
|
||||
homepage = "https://bevyengine.org"
|
||||
|
@ -11,14 +11,15 @@ categories = ["game-engines", "data-structures"]
|
|||
|
||||
[features]
|
||||
trace = []
|
||||
default = ["bevy_reflect"]
|
||||
multi-threaded = ["bevy_tasks/multi-threaded"]
|
||||
default = ["bevy_reflect", "multi-threaded"]
|
||||
|
||||
[dependencies]
|
||||
bevy_ptr = { path = "../bevy_ptr", version = "0.11.0-dev" }
|
||||
bevy_reflect = { path = "../bevy_reflect", version = "0.11.0-dev", optional = true }
|
||||
bevy_tasks = { path = "../bevy_tasks", version = "0.11.0-dev" }
|
||||
bevy_utils = { path = "../bevy_utils", version = "0.11.0-dev" }
|
||||
bevy_ecs_macros = { path = "macros", version = "0.11.0-dev" }
|
||||
bevy_ptr = { path = "../bevy_ptr", version = "0.12.0-dev" }
|
||||
bevy_reflect = { path = "../bevy_reflect", version = "0.12.0-dev", optional = true }
|
||||
bevy_tasks = { path = "../bevy_tasks", version = "0.12.0-dev" }
|
||||
bevy_utils = { path = "../bevy_utils", version = "0.12.0-dev" }
|
||||
bevy_ecs_macros = { path = "macros", version = "0.12.0-dev" }
|
||||
|
||||
async-channel = "1.4"
|
||||
event-listener = "2.5"
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
[package]
|
||||
name = "bevy_ecs_macros"
|
||||
version = "0.11.0-dev"
|
||||
version = "0.12.0-dev"
|
||||
description = "Bevy ECS Macros"
|
||||
edition = "2021"
|
||||
license = "MIT OR Apache-2.0"
|
||||
|
@ -9,7 +9,7 @@ license = "MIT OR Apache-2.0"
|
|||
proc-macro = true
|
||||
|
||||
[dependencies]
|
||||
bevy_macro_utils = { path = "../../bevy_macro_utils", version = "0.11.0-dev" }
|
||||
bevy_macro_utils = { path = "../../bevy_macro_utils", version = "0.12.0-dev" }
|
||||
|
||||
syn = "2.0"
|
||||
quote = "1.0"
|
||||
|
|
|
@ -101,7 +101,7 @@ fn parse_component_attr(ast: &DeriveInput) -> Result<Attrs> {
|
|||
};
|
||||
Ok(())
|
||||
} else {
|
||||
Err(nested.error("Unsuported attribute"))
|
||||
Err(nested.error("Unsupported attribute"))
|
||||
}
|
||||
})?;
|
||||
}
|
||||
|
|
|
@ -599,7 +599,7 @@ impl SparseSetIndex for ArchetypeComponentId {
|
|||
/// For more information, see the *[module level documentation]*.
|
||||
///
|
||||
/// [`World`]: crate::world::World
|
||||
/// [*module level documentation]: crate::archetype
|
||||
/// [module level documentation]: crate::archetype
|
||||
pub struct Archetypes {
|
||||
pub(crate) archetypes: Vec<Archetype>,
|
||||
pub(crate) archetype_component_count: usize,
|
||||
|
|
|
@ -645,7 +645,7 @@ impl Components {
|
|||
|
||||
/// A value that tracks when a system ran relative to other systems.
|
||||
/// This is used to power change detection.
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
|
||||
pub struct Tick {
|
||||
tick: u32,
|
||||
}
|
||||
|
|
|
@ -905,7 +905,7 @@ mod tests {
|
|||
|
||||
assert!(entities.reserve_generations(entity.index, GENERATIONS));
|
||||
|
||||
// The very next entitiy allocated should be a further generation on the same index
|
||||
// The very next entity allocated should be a further generation on the same index
|
||||
let next_entity = entities.alloc();
|
||||
assert_eq!(next_entity.index(), entity.index());
|
||||
assert!(next_entity.generation > entity.generation + GENERATIONS);
|
||||
|
|
|
@ -1038,10 +1038,7 @@ mod tests {
|
|||
events.send_default();
|
||||
|
||||
let mut reader = events.get_reader();
|
||||
assert_eq!(
|
||||
get_events(&events, &mut reader),
|
||||
vec![EmptyTestEvent::default()]
|
||||
);
|
||||
assert_eq!(get_events(&events, &mut reader), vec![EmptyTestEvent]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
|
@ -29,7 +29,7 @@ pub use bevy_ptr as ptr;
|
|||
pub mod prelude {
|
||||
#[doc(hidden)]
|
||||
#[cfg(feature = "bevy_reflect")]
|
||||
pub use crate::reflect::{ReflectComponent, ReflectResource};
|
||||
pub use crate::reflect::{AppTypeRegistry, ReflectComponent, ReflectResource};
|
||||
#[doc(hidden)]
|
||||
pub use crate::{
|
||||
bundle::Bundle,
|
||||
|
@ -50,7 +50,7 @@ pub mod prelude {
|
|||
Commands, Deferred, In, IntoSystem, Local, NonSend, NonSendMut, ParallelCommands,
|
||||
ParamSet, Query, ReadOnlySystem, Res, ResMut, Resource, System, SystemParamFunction,
|
||||
},
|
||||
world::{FromWorld, World},
|
||||
world::{EntityRef, FromWorld, World},
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -70,7 +70,7 @@ mod tests {
|
|||
entity::Entity,
|
||||
query::{Added, Changed, FilteredAccess, ReadOnlyWorldQuery, With, Without},
|
||||
system::Resource,
|
||||
world::{Mut, World},
|
||||
world::{EntityRef, Mut, World},
|
||||
};
|
||||
use bevy_tasks::{ComputeTaskPool, TaskPool};
|
||||
use std::{
|
||||
|
@ -1323,6 +1323,13 @@ mod tests {
|
|||
world.query::<(&A, &mut A)>();
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic]
|
||||
fn entity_ref_and_mut_query_panic() {
|
||||
let mut world = World::new();
|
||||
world.query::<(EntityRef, &mut A)>();
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic]
|
||||
fn mut_and_ref_query_panic() {
|
||||
|
@ -1330,6 +1337,13 @@ mod tests {
|
|||
world.query::<(&mut A, &A)>();
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic]
|
||||
fn mut_and_entity_ref_query_panic() {
|
||||
let mut world = World::new();
|
||||
world.query::<(&mut A, EntityRef)>();
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic]
|
||||
fn mut_and_mut_query_panic() {
|
||||
|
|
|
@ -121,6 +121,11 @@ impl<T: SparseSetIndex> Access<T> {
|
|||
self.writes.contains(index.sparse_set_index())
|
||||
}
|
||||
|
||||
/// Returns `true` if this accesses anything mutably.
|
||||
pub fn has_any_write(&self) -> bool {
|
||||
!self.writes.is_clear()
|
||||
}
|
||||
|
||||
/// Sets this as having access to all indexed elements (i.e. `&World`).
|
||||
pub fn read_all(&mut self) {
|
||||
self.reads_all = true;
|
||||
|
|
|
@ -5,7 +5,7 @@ use crate::{
|
|||
entity::Entity,
|
||||
query::{Access, DebugCheckedUnwrap, FilteredAccess},
|
||||
storage::{ComponentSparseSet, Table, TableRow},
|
||||
world::{unsafe_world_cell::UnsafeWorldCell, Mut, Ref, World},
|
||||
world::{unsafe_world_cell::UnsafeWorldCell, EntityRef, Mut, Ref, World},
|
||||
};
|
||||
pub use bevy_ecs_macros::WorldQuery;
|
||||
use bevy_ptr::{ThinSlicePtr, UnsafeCellDeref};
|
||||
|
@ -460,13 +460,6 @@ pub type QueryItem<'w, Q> = <Q as WorldQuery>::Item<'w>;
|
|||
/// The read-only variant of the item type returned when a [`WorldQuery`] is iterated over immutably
|
||||
pub type ROQueryItem<'w, Q> = QueryItem<'w, <Q as WorldQuery>::ReadOnly>;
|
||||
|
||||
/// The `Fetch` of a [`WorldQuery`], which is used to store state for each archetype/table.
|
||||
#[deprecated = "use <Q as WorldQuery>::Fetch<'w> instead"]
|
||||
pub type QueryFetch<'w, Q> = <Q as WorldQuery>::Fetch<'w>;
|
||||
/// The read-only `Fetch` of a [`WorldQuery`], which is used to store state for each archetype/table.
|
||||
#[deprecated = "use <<Q as WorldQuery>::ReadOnly as WorldQuery>::Fetch<'w> instead"]
|
||||
pub type ROQueryFetch<'w, Q> = <<Q as WorldQuery>::ReadOnly as WorldQuery>::Fetch<'w>;
|
||||
|
||||
/// SAFETY: no component or archetype access
|
||||
unsafe impl WorldQuery for Entity {
|
||||
type Fetch<'w> = ();
|
||||
|
@ -536,6 +529,89 @@ unsafe impl WorldQuery for Entity {
|
|||
/// SAFETY: access is read only
|
||||
unsafe impl ReadOnlyWorldQuery for Entity {}
|
||||
|
||||
/// SAFETY: `Self` is the same as `Self::ReadOnly`
|
||||
unsafe impl<'a> WorldQuery for EntityRef<'a> {
|
||||
type Fetch<'w> = &'w World;
|
||||
type Item<'w> = EntityRef<'w>;
|
||||
type ReadOnly = Self;
|
||||
type State = ();
|
||||
|
||||
fn shrink<'wlong: 'wshort, 'wshort>(item: Self::Item<'wlong>) -> Self::Item<'wshort> {
|
||||
item
|
||||
}
|
||||
|
||||
const IS_DENSE: bool = true;
|
||||
|
||||
const IS_ARCHETYPAL: bool = true;
|
||||
|
||||
unsafe fn init_fetch<'w>(
|
||||
world: UnsafeWorldCell<'w>,
|
||||
_state: &Self::State,
|
||||
_last_run: Tick,
|
||||
_this_run: Tick,
|
||||
) -> Self::Fetch<'w> {
|
||||
// SAFE: EntityRef has permission to access the whole world immutably thanks to update_component_access and update_archetype_component_access
|
||||
world.world()
|
||||
}
|
||||
|
||||
unsafe fn clone_fetch<'w>(world: &Self::Fetch<'w>) -> Self::Fetch<'w> {
|
||||
world
|
||||
}
|
||||
|
||||
#[inline]
|
||||
unsafe fn set_archetype<'w>(
|
||||
_fetch: &mut Self::Fetch<'w>,
|
||||
_state: &Self::State,
|
||||
_archetype: &'w Archetype,
|
||||
_table: &Table,
|
||||
) {
|
||||
}
|
||||
|
||||
#[inline]
|
||||
unsafe fn set_table<'w>(_fetch: &mut Self::Fetch<'w>, _state: &Self::State, _table: &'w Table) {
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
unsafe fn fetch<'w>(
|
||||
world: &mut Self::Fetch<'w>,
|
||||
entity: Entity,
|
||||
_table_row: TableRow,
|
||||
) -> Self::Item<'w> {
|
||||
// SAFETY: `fetch` must be called with an entity that exists in the world
|
||||
unsafe { world.get_entity(entity).debug_checked_unwrap() }
|
||||
}
|
||||
|
||||
fn update_component_access(_state: &Self::State, access: &mut FilteredAccess<ComponentId>) {
|
||||
assert!(
|
||||
!access.access().has_any_write(),
|
||||
"EntityRef conflicts with a previous access in this query. Shared access cannot coincide with exclusive access.",
|
||||
);
|
||||
access.read_all();
|
||||
}
|
||||
|
||||
fn update_archetype_component_access(
|
||||
_state: &Self::State,
|
||||
archetype: &Archetype,
|
||||
access: &mut Access<ArchetypeComponentId>,
|
||||
) {
|
||||
for component_id in archetype.components() {
|
||||
access.add_read(archetype.get_archetype_component_id(component_id).unwrap());
|
||||
}
|
||||
}
|
||||
|
||||
fn init_state(_world: &mut World) {}
|
||||
|
||||
fn matches_component_set(
|
||||
_state: &Self::State,
|
||||
_set_contains_id: &impl Fn(ComponentId) -> bool,
|
||||
) -> bool {
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
/// SAFETY: access is read only
|
||||
unsafe impl<'a> ReadOnlyWorldQuery for EntityRef<'a> {}
|
||||
|
||||
#[doc(hidden)]
|
||||
pub struct ReadFetch<'w, T> {
|
||||
// T::Storage = TableStorage
|
||||
|
@ -579,7 +655,6 @@ unsafe impl<T: Component> WorldQuery for &T {
|
|||
// which we are allowed to access since we registered it in `update_archetype_component_access`.
|
||||
// Note that we do not actually access any components in this function, we just get a shared
|
||||
// reference to the sparse set, which is used to access the components in `Self::fetch`.
|
||||
.unsafe_world()
|
||||
.storages()
|
||||
.sparse_sets
|
||||
.get(component_id)
|
||||
|
@ -727,7 +802,6 @@ unsafe impl<'__w, T: Component> WorldQuery for Ref<'__w, T> {
|
|||
sparse_set: (T::Storage::STORAGE_TYPE == StorageType::SparseSet).then(|| {
|
||||
world
|
||||
// SAFETY: See &T::init_fetch.
|
||||
.unsafe_world()
|
||||
.storages()
|
||||
.sparse_sets
|
||||
.get(component_id)
|
||||
|
@ -891,7 +965,6 @@ unsafe impl<'__w, T: Component> WorldQuery for &'__w mut T {
|
|||
sparse_set: (T::Storage::STORAGE_TYPE == StorageType::SparseSet).then(|| {
|
||||
world
|
||||
// SAFETY: See &T::init_fetch.
|
||||
.unsafe_world()
|
||||
.storages()
|
||||
.sparse_sets
|
||||
.get(component_id)
|
||||
|
|
|
@ -34,7 +34,7 @@ impl<'w, 's, Q: WorldQuery, F: ReadOnlyWorldQuery> QueryIter<'w, 's, Q, F> {
|
|||
QueryIter {
|
||||
query_state,
|
||||
// SAFETY: We only access table data that has been registered in `query_state`.
|
||||
tables: &world.unsafe_world().storages().tables,
|
||||
tables: &world.storages().tables,
|
||||
archetypes: world.archetypes(),
|
||||
cursor: QueryIterationCursor::init(world, query_state, last_run, this_run),
|
||||
}
|
||||
|
@ -107,7 +107,7 @@ where
|
|||
archetypes: world.archetypes(),
|
||||
// SAFETY: We only access table data that has been registered in `query_state`.
|
||||
// This means `world` has permission to access the data we use.
|
||||
tables: &world.unsafe_world().storages.tables,
|
||||
tables: &world.storages().tables,
|
||||
fetch,
|
||||
filter,
|
||||
entity_iter: entity_list.into_iter(),
|
||||
|
@ -316,7 +316,7 @@ impl<'w, 's, Q: WorldQuery, F: ReadOnlyWorldQuery, const K: usize>
|
|||
QueryCombinationIter {
|
||||
query_state,
|
||||
// SAFETY: We only access table data that has been registered in `query_state`.
|
||||
tables: &world.unsafe_world().storages().tables,
|
||||
tables: &world.storages().tables,
|
||||
archetypes: world.archetypes(),
|
||||
cursors: array.assume_init(),
|
||||
}
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
use crate::{component::Tick, world::unsafe_world_cell::UnsafeWorldCell};
|
||||
use bevy_tasks::ComputeTaskPool;
|
||||
use std::ops::Range;
|
||||
|
||||
use super::{QueryItem, QueryState, ROQueryItem, ReadOnlyWorldQuery, WorldQuery};
|
||||
|
@ -34,6 +33,8 @@ pub struct BatchingStrategy {
|
|||
/// increase the scheduling overhead for the iteration.
|
||||
///
|
||||
/// Defaults to 1.
|
||||
///
|
||||
/// [`ComputeTaskPool`]: bevy_tasks::ComputeTaskPool
|
||||
pub batches_per_thread: usize,
|
||||
}
|
||||
|
||||
|
@ -148,23 +149,36 @@ impl<'w, 's, Q: WorldQuery, F: ReadOnlyWorldQuery> QueryParIter<'w, 's, Q, F> {
|
|||
/// [`ComputeTaskPool`]: bevy_tasks::ComputeTaskPool
|
||||
#[inline]
|
||||
unsafe fn for_each_unchecked<FN: Fn(QueryItem<'w, Q>) + Send + Sync + Clone>(&self, func: FN) {
|
||||
let thread_count = ComputeTaskPool::get().thread_num();
|
||||
if thread_count <= 1 {
|
||||
#[cfg(any(target = "wasm32", not(feature = "multi-threaded")))]
|
||||
{
|
||||
self.state
|
||||
.for_each_unchecked_manual(self.world, func, self.last_run, self.this_run);
|
||||
} else {
|
||||
// Need a batch size of at least 1.
|
||||
let batch_size = self.get_batch_size(thread_count).max(1);
|
||||
self.state.par_for_each_unchecked_manual(
|
||||
self.world,
|
||||
batch_size,
|
||||
func,
|
||||
self.last_run,
|
||||
self.this_run,
|
||||
);
|
||||
}
|
||||
#[cfg(all(not(target = "wasm32"), feature = "multi-threaded"))]
|
||||
{
|
||||
let thread_count = bevy_tasks::ComputeTaskPool::get().thread_num();
|
||||
if thread_count <= 1 {
|
||||
self.state.for_each_unchecked_manual(
|
||||
self.world,
|
||||
func,
|
||||
self.last_run,
|
||||
self.this_run,
|
||||
);
|
||||
} else {
|
||||
// Need a batch size of at least 1.
|
||||
let batch_size = self.get_batch_size(thread_count).max(1);
|
||||
self.state.par_for_each_unchecked_manual(
|
||||
self.world,
|
||||
batch_size,
|
||||
func,
|
||||
self.last_run,
|
||||
self.this_run,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(all(not(target = "wasm32"), feature = "multi-threaded"))]
|
||||
fn get_batch_size(&self, thread_count: usize) -> usize {
|
||||
if self.batching_strategy.batch_size_limits.is_empty() {
|
||||
return self.batching_strategy.batch_size_limits.start;
|
||||
|
|
|
@ -10,7 +10,6 @@ use crate::{
|
|||
storage::{TableId, TableRow},
|
||||
world::{unsafe_world_cell::UnsafeWorldCell, World, WorldId},
|
||||
};
|
||||
use bevy_tasks::ComputeTaskPool;
|
||||
#[cfg(feature = "trace")]
|
||||
use bevy_utils::tracing::Instrument;
|
||||
use fixedbitset::FixedBitSet;
|
||||
|
@ -462,7 +461,6 @@ impl<Q: WorldQuery, F: ReadOnlyWorldQuery> QueryState<Q, F> {
|
|||
let mut filter = F::init_fetch(world, &self.filter_state, last_run, this_run);
|
||||
|
||||
let table = world
|
||||
.unsafe_world()
|
||||
.storages()
|
||||
.tables
|
||||
.get(location.table_id)
|
||||
|
@ -973,7 +971,7 @@ impl<Q: WorldQuery, F: ReadOnlyWorldQuery> QueryState<Q, F> {
|
|||
let mut fetch = Q::init_fetch(world, &self.fetch_state, last_run, this_run);
|
||||
let mut filter = F::init_fetch(world, &self.filter_state, last_run, this_run);
|
||||
|
||||
let tables = &world.unsafe_world().storages().tables;
|
||||
let tables = &world.storages().tables;
|
||||
if Q::IS_DENSE && F::IS_DENSE {
|
||||
for table_id in &self.matched_table_ids {
|
||||
let table = tables.get(*table_id).debug_checked_unwrap();
|
||||
|
@ -1032,6 +1030,9 @@ impl<Q: WorldQuery, F: ReadOnlyWorldQuery> QueryState<Q, F> {
|
|||
/// have unique access to the components they query.
|
||||
/// This does not validate that `world.id()` matches `self.world_id`. Calling this on a `world`
|
||||
/// with a mismatched [`WorldId`] is unsound.
|
||||
///
|
||||
/// [`ComputeTaskPool`]: bevy_tasks::ComputeTaskPool
|
||||
#[cfg(all(not(target = "wasm32"), feature = "multi-threaded"))]
|
||||
pub(crate) unsafe fn par_for_each_unchecked_manual<
|
||||
'w,
|
||||
FN: Fn(Q::Item<'w>) + Send + Sync + Clone,
|
||||
|
@ -1045,10 +1046,10 @@ impl<Q: WorldQuery, F: ReadOnlyWorldQuery> QueryState<Q, F> {
|
|||
) {
|
||||
// NOTE: If you are changing query iteration code, remember to update the following places, where relevant:
|
||||
// QueryIter, QueryIterationCursor, QueryManyIter, QueryCombinationIter, QueryState::for_each_unchecked_manual, QueryState::par_for_each_unchecked_manual
|
||||
ComputeTaskPool::get().scope(|scope| {
|
||||
bevy_tasks::ComputeTaskPool::get().scope(|scope| {
|
||||
if Q::IS_DENSE && F::IS_DENSE {
|
||||
// SAFETY: We only access table data that has been registered in `self.archetype_component_access`.
|
||||
let tables = &world.unsafe_world().storages().tables;
|
||||
let tables = &world.storages().tables;
|
||||
for table_id in &self.matched_table_ids {
|
||||
let table = &tables[*table_id];
|
||||
if table.is_empty() {
|
||||
|
@ -1064,7 +1065,7 @@ impl<Q: WorldQuery, F: ReadOnlyWorldQuery> QueryState<Q, F> {
|
|||
Q::init_fetch(world, &self.fetch_state, last_run, this_run);
|
||||
let mut filter =
|
||||
F::init_fetch(world, &self.filter_state, last_run, this_run);
|
||||
let tables = &world.unsafe_world().storages().tables;
|
||||
let tables = &world.storages().tables;
|
||||
let table = tables.get(*table_id).debug_checked_unwrap();
|
||||
let entities = table.entities();
|
||||
Q::set_table(&mut fetch, &self.fetch_state, table);
|
||||
|
@ -1108,7 +1109,7 @@ impl<Q: WorldQuery, F: ReadOnlyWorldQuery> QueryState<Q, F> {
|
|||
Q::init_fetch(world, &self.fetch_state, last_run, this_run);
|
||||
let mut filter =
|
||||
F::init_fetch(world, &self.filter_state, last_run, this_run);
|
||||
let tables = &world.unsafe_world().storages().tables;
|
||||
let tables = &world.storages().tables;
|
||||
let archetype =
|
||||
world.archetypes().get(*archetype_id).debug_checked_unwrap();
|
||||
let table = tables.get(archetype.table_id()).debug_checked_unwrap();
|
||||
|
|
|
@ -6,7 +6,7 @@ use crate::{
|
|||
use bevy_reflect::FromType;
|
||||
|
||||
/// For a specific type of component, this maps any fields with values of type [`Entity`] to a new world.
|
||||
/// Since a given `Entity` ID is only valid for the world it came frome, when performing deserialization
|
||||
/// Since a given `Entity` ID is only valid for the world it came from, when performing deserialization
|
||||
/// any stored IDs need to be re-allocated in the destination world.
|
||||
///
|
||||
/// See [`MapEntities`] for more information.
|
||||
|
|
|
@ -1,9 +1,10 @@
|
|||
//! Types that enable reflection support.
|
||||
|
||||
use crate::entity::Entity;
|
||||
use bevy_reflect::{
|
||||
impl_from_reflect_value, impl_reflect_value, ReflectDeserialize, ReflectSerialize,
|
||||
};
|
||||
use std::ops::{Deref, DerefMut};
|
||||
|
||||
use crate as bevy_ecs;
|
||||
use crate::{entity::Entity, system::Resource};
|
||||
use bevy_reflect::{impl_reflect_value, ReflectDeserialize, ReflectSerialize, TypeRegistryArc};
|
||||
|
||||
mod component;
|
||||
mod map_entities;
|
||||
|
@ -13,5 +14,25 @@ pub use component::{ReflectComponent, ReflectComponentFns};
|
|||
pub use map_entities::ReflectMapEntities;
|
||||
pub use resource::{ReflectResource, ReflectResourceFns};
|
||||
|
||||
/// A [`Resource`] storing [`TypeRegistry`](bevy_reflect::TypeRegistry) for
|
||||
/// type registrations relevant to a whole app.
|
||||
#[derive(Resource, Clone, Default)]
|
||||
pub struct AppTypeRegistry(pub TypeRegistryArc);
|
||||
|
||||
impl Deref for AppTypeRegistry {
|
||||
type Target = TypeRegistryArc;
|
||||
|
||||
#[inline]
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl DerefMut for AppTypeRegistry {
|
||||
#[inline]
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
&mut self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl_reflect_value!((in bevy_ecs) Entity(Hash, PartialEq, Serialize, Deserialize));
|
||||
impl_from_reflect_value!(Entity);
|
||||
|
|
|
@ -246,7 +246,7 @@ pub mod common_conditions {
|
|||
/// # let mut app = Schedule::new();
|
||||
/// # let mut world = World::new();
|
||||
/// app.add_systems(
|
||||
/// // `resource_exsists` will only return true if the given resource exsists in the world
|
||||
/// // `resource_exists` will only return true if the given resource exists in the world
|
||||
/// my_system.run_if(resource_exists::<Counter>()),
|
||||
/// );
|
||||
///
|
||||
|
@ -324,7 +324,7 @@ pub mod common_conditions {
|
|||
/// # let mut world = World::new();
|
||||
/// app.add_systems(
|
||||
/// // `resource_exists_and_equals` will only return true
|
||||
/// // if the given resource exsists and equals the given value
|
||||
/// // if the given resource exists and equals the given value
|
||||
/// my_system.run_if(resource_exists_and_equals(Counter(0))),
|
||||
/// );
|
||||
///
|
||||
|
@ -422,7 +422,7 @@ pub mod common_conditions {
|
|||
/// my_system.run_if(
|
||||
/// resource_changed::<Counter>()
|
||||
/// // By default detecting changes will also trigger if the resource was
|
||||
/// // just added, this won't work with my example so I will addd a second
|
||||
/// // just added, this won't work with my example so I will add a second
|
||||
/// // condition to make sure the resource wasn't just added
|
||||
/// .and_then(not(resource_added::<Counter>()))
|
||||
/// ),
|
||||
|
@ -471,11 +471,11 @@ pub mod common_conditions {
|
|||
/// # let mut world = World::new();
|
||||
/// app.add_systems(
|
||||
/// // `resource_exists_and_changed` will only return true if the
|
||||
/// // given resource exsists and was just changed (or added)
|
||||
/// // given resource exists and was just changed (or added)
|
||||
/// my_system.run_if(
|
||||
/// resource_exists_and_changed::<Counter>()
|
||||
/// // By default detecting changes will also trigger if the resource was
|
||||
/// // just added, this won't work with my example so I will addd a second
|
||||
/// // just added, this won't work with my example so I will add a second
|
||||
/// // condition to make sure the resource wasn't just added
|
||||
/// .and_then(not(resource_added::<Counter>()))
|
||||
/// ),
|
||||
|
@ -537,7 +537,7 @@ pub mod common_conditions {
|
|||
/// my_system.run_if(
|
||||
/// resource_changed_or_removed::<Counter>()
|
||||
/// // By default detecting changes will also trigger if the resource was
|
||||
/// // just added, this won't work with my example so I will addd a second
|
||||
/// // just added, this won't work with my example so I will add a second
|
||||
/// // condition to make sure the resource wasn't just added
|
||||
/// .and_then(not(resource_added::<Counter>()))
|
||||
/// ),
|
||||
|
@ -665,7 +665,7 @@ pub mod common_conditions {
|
|||
///
|
||||
/// app.add_systems(
|
||||
/// // `state_exists` will only return true if the
|
||||
/// // given state exsists
|
||||
/// // given state exists
|
||||
/// my_system.run_if(state_exists::<GameState>()),
|
||||
/// );
|
||||
///
|
||||
|
@ -764,7 +764,7 @@ pub mod common_conditions {
|
|||
///
|
||||
/// app.add_systems((
|
||||
/// // `state_exists_and_equals` will only return true if the
|
||||
/// // given state exsists and equals the given value
|
||||
/// // given state exists and equals the given value
|
||||
/// play_system.run_if(state_exists_and_equals(GameState::Playing)),
|
||||
/// pause_system.run_if(state_exists_and_equals(GameState::Paused)),
|
||||
/// ));
|
||||
|
@ -948,7 +948,7 @@ pub mod common_conditions {
|
|||
// Simply checking `is_empty` would not be enough.
|
||||
// PERF: note that `count` is efficient (not actually looping/iterating),
|
||||
// due to Bevy having a specialized implementation for events.
|
||||
move |mut removals: RemovedComponents<T>| !removals.iter().count() != 0
|
||||
move |mut removals: RemovedComponents<T>| removals.iter().count() != 0
|
||||
}
|
||||
|
||||
/// Generates a [`Condition`](super::Condition) that inverses the result of passed one.
|
||||
|
|
|
@ -5,7 +5,6 @@ use crate::{
|
|||
condition::{BoxedCondition, Condition},
|
||||
graph_utils::{Ambiguity, Dependency, DependencyKind, GraphInfo},
|
||||
set::{BoxedSystemSet, IntoSystemSet, SystemSet},
|
||||
ScheduleLabel,
|
||||
},
|
||||
system::{BoxedSystem, IntoSystem, System},
|
||||
};
|
||||
|
@ -294,35 +293,6 @@ where
|
|||
fn chain(self) -> SystemConfigs {
|
||||
self.into_configs().chain()
|
||||
}
|
||||
|
||||
/// This used to add the system to `CoreSchedule::Startup`.
|
||||
/// This was a shorthand for `self.in_schedule(CoreSchedule::Startup)`.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// Always panics. Please migrate to the new `App::add_systems` with the `Startup` schedule:
|
||||
/// Ex: `app.add_system(foo.on_startup())` -> `app.add_systems(Startup, foo)`
|
||||
#[deprecated(
|
||||
since = "0.11.0",
|
||||
note = "`app.add_system(foo.on_startup())` has been deprecated in favor of `app.add_systems(Startup, foo)`. Please migrate to that API."
|
||||
)]
|
||||
fn on_startup(self) -> SystemConfigs {
|
||||
panic!("`app.add_system(foo.on_startup())` has been deprecated in favor of `app.add_systems(Startup, foo)`. Please migrate to that API.");
|
||||
}
|
||||
|
||||
/// This used to add the system to the provided `schedule`.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// Always panics. Please migrate to the new `App::add_systems`:
|
||||
/// Ex: `app.add_system(foo.in_schedule(SomeSchedule))` -> `app.add_systems(SomeSchedule, foo)`
|
||||
#[deprecated(
|
||||
since = "0.11.0",
|
||||
note = "`app.add_system(foo.in_schedule(SomeSchedule))` has been deprecated in favor of `app.add_systems(SomeSchedule, foo)`. Please migrate to that API."
|
||||
)]
|
||||
fn in_schedule(self, _schedule: impl ScheduleLabel) -> SystemConfigs {
|
||||
panic!("`app.add_system(foo.in_schedule(SomeSchedule))` has been deprecated in favor of `app.add_systems(SomeSchedule, foo)`. Please migrate to that API.");
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoSystemConfigs<()> for SystemConfigs {
|
||||
|
@ -471,35 +441,6 @@ pub trait IntoSystemSetConfig: Sized {
|
|||
fn ambiguous_with_all(self) -> SystemSetConfig {
|
||||
self.into_config().ambiguous_with_all()
|
||||
}
|
||||
|
||||
/// This used to configure the set in the `CoreSchedule::Startup` schedule.
|
||||
/// This was a shorthand for `self.in_schedule(CoreSchedule::Startup)`.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// Always panics. Please migrate to the new `App::configure_set` with the `Startup` schedule:
|
||||
/// Ex: `app.configure_set(MySet.on_startup())` -> `app.configure_set(Startup, MySet)`
|
||||
#[deprecated(
|
||||
since = "0.11.0",
|
||||
note = "`app.configure_set(MySet.on_startup())` has been deprecated in favor of `app.configure_set(Startup, MySet)`. Please migrate to that API."
|
||||
)]
|
||||
fn on_startup(self) -> SystemSetConfigs {
|
||||
panic!("`app.configure_set(MySet.on_startup())` has been deprecated in favor of `app.configure_set(Startup, MySet)`. Please migrate to that API.");
|
||||
}
|
||||
|
||||
/// This used to configure the set in the provided `schedule`.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// Always panics. Please migrate to the new `App::configure_set`:
|
||||
/// Ex: `app.configure_set(MySet.in_schedule(SomeSchedule))` -> `app.configure_set(SomeSchedule, MySet)`
|
||||
#[deprecated(
|
||||
since = "0.11.0",
|
||||
note = "`app.configure_set(MySet.in_schedule(SomeSchedule))` has been deprecated in favor of `app.configure_set(SomeSchedule, MySet)`. Please migrate to that API."
|
||||
)]
|
||||
fn in_schedule(self, _schedule: impl ScheduleLabel) -> SystemSetConfigs {
|
||||
panic!("`app.configure_set(MySet.in_schedule(SomeSchedule))` has been deprecated in favor of `app.configure_set(SomeSchedule, MySet)`. Please migrate to that API.");
|
||||
}
|
||||
}
|
||||
|
||||
impl<S: SystemSet> IntoSystemSetConfig for S {
|
||||
|
@ -611,35 +552,6 @@ where
|
|||
fn chain(self) -> SystemSetConfigs {
|
||||
self.into_configs().chain()
|
||||
}
|
||||
|
||||
/// This used to configure the sets in the `CoreSchedule::Startup` schedule.
|
||||
/// This was a shorthand for `self.in_schedule(CoreSchedule::Startup)`.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// Always panics. Please migrate to the new `App::configure_sets` with the `Startup` schedule:
|
||||
/// Ex: `app.configure_sets((A, B).on_startup())` -> `app.configure_sets(Startup, (A, B))`
|
||||
#[deprecated(
|
||||
since = "0.11.0",
|
||||
note = "`app.configure_sets((A, B).on_startup())` has been deprecated in favor of `app.configure_sets(Startup, (A, B))`. Please migrate to that API."
|
||||
)]
|
||||
fn on_startup(self) -> SystemSetConfigs {
|
||||
panic!("`app.configure_sets((A, B).on_startup())` has been deprecated in favor of `app.configure_sets(Startup, (A, B))`. Please migrate to that API.");
|
||||
}
|
||||
|
||||
/// This used to configure the sets in the provided `schedule`.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// Always panics. Please migrate to the new `App::configure_set`:
|
||||
/// Ex: `app.configure_sets((A, B).in_schedule(SomeSchedule))` -> `app.configure_sets(SomeSchedule, (A, B))`
|
||||
#[deprecated(
|
||||
since = "0.11.0",
|
||||
note = "`app.configure_sets((A, B).in_schedule(SomeSchedule))` has been deprecated in favor of `app.configure_sets(SomeSchedule, (A, B))`. Please migrate to that API."
|
||||
)]
|
||||
fn in_schedule(self, _schedule: impl ScheduleLabel) -> SystemSetConfigs {
|
||||
panic!("`app.configure_sets((A, B).in_schedule(SomeSchedule))` has been deprecated in favor of `app.configure_sets(SomeSchedule, (A, B))`. Please migrate to that API.");
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoSystemSetConfigs for SystemSetConfigs {
|
||||
|
|
|
@ -33,13 +33,13 @@ pub enum ExecutorKind {
|
|||
///
|
||||
/// Useful if you're dealing with a single-threaded environment, saving your threads for
|
||||
/// other things, or just trying minimize overhead.
|
||||
#[cfg_attr(target_arch = "wasm32", default)]
|
||||
#[cfg_attr(any(target_arch = "wasm32", not(feature = "multi-threaded")), default)]
|
||||
SingleThreaded,
|
||||
/// Like [`SingleThreaded`](ExecutorKind::SingleThreaded) but calls [`apply_deferred`](crate::system::System::apply_deferred)
|
||||
/// immediately after running each system.
|
||||
Simple,
|
||||
/// Runs the schedule using a thread pool. Non-conflicting systems can run in parallel.
|
||||
#[cfg_attr(not(target_arch = "wasm32"), default)]
|
||||
#[cfg_attr(all(not(target_arch = "wasm32"), feature = "multi-threaded"), default)]
|
||||
MultiThreaded,
|
||||
}
|
||||
|
||||
|
|
|
@ -476,7 +476,7 @@ impl MultiThreadedExecutor {
|
|||
/// - `world` must have permission to access the world data
|
||||
/// used by the specified system.
|
||||
/// - `update_archetype_component_access` must have been called with `world`
|
||||
/// on the system assocaited with `system_index`.
|
||||
/// on the system associated with `system_index`.
|
||||
unsafe fn spawn_system_task<'scope>(
|
||||
&mut self,
|
||||
scope: &Scope<'_, 'scope, ()>,
|
||||
|
|
|
@ -76,7 +76,6 @@ pub(crate) struct GraphInfo {
|
|||
pub(crate) sets: Vec<BoxedSystemSet>,
|
||||
pub(crate) dependencies: Vec<Dependency>,
|
||||
pub(crate) ambiguous_with: Ambiguity,
|
||||
pub(crate) base_set: Option<BoxedSystemSet>,
|
||||
}
|
||||
|
||||
/// Converts 2D row-major pair of indices into a 1D array index.
|
||||
|
|
|
@ -239,7 +239,7 @@ mod tests {
|
|||
partially_ordered == [8, 9, 10] || partially_ordered == [10, 8, 9],
|
||||
"partially_ordered must be [8, 9, 10] or [10, 8, 9]"
|
||||
);
|
||||
assert!(order.len() == 11, "must have exacty 11 order entries");
|
||||
assert!(order.len() == 11, "must have exactly 11 order entries");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -174,13 +174,6 @@ impl Schedule {
|
|||
}
|
||||
}
|
||||
|
||||
/// Add a system to the schedule.
|
||||
#[deprecated(since = "0.11.0", note = "please use `add_systems` instead")]
|
||||
pub fn add_system<M>(&mut self, system: impl IntoSystemConfigs<M>) -> &mut Self {
|
||||
self.graph.add_systems_inner(system.into_configs(), false);
|
||||
self
|
||||
}
|
||||
|
||||
/// Add a collection of systems to the schedule.
|
||||
pub fn add_systems<M>(&mut self, systems: impl IntoSystemConfigs<M>) -> &mut Self {
|
||||
self.graph.add_systems_inner(systems.into_configs(), false);
|
||||
|
@ -747,10 +740,6 @@ impl ScheduleGraph {
|
|||
self.check_set(id, &**set)?;
|
||||
}
|
||||
|
||||
if let Some(base_set) = &graph_info.base_set {
|
||||
self.check_set(id, &**base_set)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
|
|
@ -94,7 +94,7 @@ impl TableRow {
|
|||
}
|
||||
}
|
||||
|
||||
/// A type-erased contiguous container for data of a homogenous type.
|
||||
/// A type-erased contiguous container for data of a homogeneous type.
|
||||
///
|
||||
/// Conceptually, a [`Column`] is very similar to a type-erased `Vec<T>`.
|
||||
/// It also stores the change detection ticks for its components, kept in two separate
|
||||
|
|
|
@ -307,11 +307,19 @@ impl<'w, 's> Commands<'w, 's> {
|
|||
#[inline]
|
||||
#[track_caller]
|
||||
pub fn entity<'a>(&'a mut self, entity: Entity) -> EntityCommands<'w, 's, 'a> {
|
||||
self.get_entity(entity).unwrap_or_else(|| {
|
||||
#[inline(never)]
|
||||
#[cold]
|
||||
#[track_caller]
|
||||
fn panic_no_entity(entity: Entity) -> ! {
|
||||
panic!(
|
||||
"Attempting to create an EntityCommands for entity {entity:?}, which doesn't exist.",
|
||||
)
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
match self.get_entity(entity) {
|
||||
Some(entity) => entity,
|
||||
None => panic_no_entity(entity),
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the [`EntityCommands`] for the requested [`Entity`], if it exists.
|
||||
|
|
|
@ -125,71 +125,6 @@ pub use system_param::*;
|
|||
|
||||
use crate::world::World;
|
||||
|
||||
/// Ensure that a given function is a [system](System).
|
||||
///
|
||||
/// This should be used when writing doc examples,
|
||||
/// to confirm that systems used in an example are
|
||||
/// valid systems.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// The following example will panic when run since the
|
||||
/// system's parameters mutably access the same component
|
||||
/// multiple times.
|
||||
///
|
||||
/// ```should_panic
|
||||
/// # use bevy_ecs::{prelude::*, system::assert_is_system};
|
||||
/// #
|
||||
/// # #[derive(Component)]
|
||||
/// # struct Transform;
|
||||
/// #
|
||||
/// fn my_system(query1: Query<&mut Transform>, query2: Query<&mut Transform>) {
|
||||
/// // ...
|
||||
/// }
|
||||
///
|
||||
/// assert_is_system(my_system);
|
||||
/// ```
|
||||
pub fn assert_is_system<In: 'static, Out: 'static, Marker>(
|
||||
system: impl IntoSystem<In, Out, Marker>,
|
||||
) {
|
||||
let mut system = IntoSystem::into_system(system);
|
||||
|
||||
// Initialize the system, which will panic if the system has access conflicts.
|
||||
let mut world = World::new();
|
||||
system.initialize(&mut world);
|
||||
}
|
||||
|
||||
/// Ensure that a given function is a [read-only system](ReadOnlySystem).
|
||||
///
|
||||
/// This should be used when writing doc examples,
|
||||
/// to confirm that systems used in an example are
|
||||
/// valid systems.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// The following example will fail to compile
|
||||
/// since the system accesses a component mutably.
|
||||
///
|
||||
/// ```compile_fail
|
||||
/// # use bevy_ecs::{prelude::*, system::assert_is_read_only_system};
|
||||
/// #
|
||||
/// # #[derive(Component)]
|
||||
/// # struct Transform;
|
||||
/// #
|
||||
/// fn my_system(query: Query<&mut Transform>) {
|
||||
/// // ...
|
||||
/// }
|
||||
///
|
||||
/// assert_is_read_only_system(my_system);
|
||||
/// ```
|
||||
pub fn assert_is_read_only_system<In: 'static, Out: 'static, Marker, S>(system: S)
|
||||
where
|
||||
S: IntoSystem<In, Out, Marker>,
|
||||
S::System: ReadOnlySystem,
|
||||
{
|
||||
assert_is_system(system);
|
||||
}
|
||||
|
||||
/// Conversion trait to turn something into a [`System`].
|
||||
///
|
||||
/// Use this to get a system from a function. Also note that every system implements this trait as
|
||||
|
@ -477,6 +412,83 @@ pub mod adapter {
|
|||
pub fn ignore<T>(In(_): In<T>) {}
|
||||
}
|
||||
|
||||
/// Ensure that a given function is a [system](System).
|
||||
///
|
||||
/// This should be used when writing doc examples,
|
||||
/// to confirm that systems used in an example are
|
||||
/// valid systems.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// The following example will panic when run since the
|
||||
/// system's parameters mutably access the same component
|
||||
/// multiple times.
|
||||
///
|
||||
/// ```should_panic
|
||||
/// # use bevy_ecs::{prelude::*, system::assert_is_system};
|
||||
/// #
|
||||
/// # #[derive(Component)]
|
||||
/// # struct Transform;
|
||||
/// #
|
||||
/// fn my_system(query1: Query<&mut Transform>, query2: Query<&mut Transform>) {
|
||||
/// // ...
|
||||
/// }
|
||||
///
|
||||
/// assert_is_system(my_system);
|
||||
/// ```
|
||||
pub fn assert_is_system<In: 'static, Out: 'static, Marker>(
|
||||
system: impl IntoSystem<In, Out, Marker>,
|
||||
) {
|
||||
let mut system = IntoSystem::into_system(system);
|
||||
|
||||
// Initialize the system, which will panic if the system has access conflicts.
|
||||
let mut world = World::new();
|
||||
system.initialize(&mut world);
|
||||
}
|
||||
|
||||
/// Ensure that a given function is a [read-only system](ReadOnlySystem).
|
||||
///
|
||||
/// This should be used when writing doc examples,
|
||||
/// to confirm that systems used in an example are
|
||||
/// valid systems.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// The following example will fail to compile
|
||||
/// since the system accesses a component mutably.
|
||||
///
|
||||
/// ```compile_fail
|
||||
/// # use bevy_ecs::{prelude::*, system::assert_is_read_only_system};
|
||||
/// #
|
||||
/// # #[derive(Component)]
|
||||
/// # struct Transform;
|
||||
/// #
|
||||
/// fn my_system(query: Query<&mut Transform>) {
|
||||
/// // ...
|
||||
/// }
|
||||
///
|
||||
/// assert_is_read_only_system(my_system);
|
||||
/// ```
|
||||
pub fn assert_is_read_only_system<In: 'static, Out: 'static, Marker, S>(system: S)
|
||||
where
|
||||
S: IntoSystem<In, Out, Marker>,
|
||||
S::System: ReadOnlySystem,
|
||||
{
|
||||
assert_is_system(system);
|
||||
}
|
||||
|
||||
/// Ensures that the provided system doesn't with itself.
|
||||
///
|
||||
/// This function will panic if the provided system conflict with itself.
|
||||
///
|
||||
/// Note: this will run the system on an empty world.
|
||||
pub fn assert_system_does_not_conflict<Out, Params, S: IntoSystem<(), Out, Params>>(sys: S) {
|
||||
let mut world = World::new();
|
||||
let mut system = IntoSystem::into_system(sys);
|
||||
system.initialize(&mut world);
|
||||
system.run((), &mut world);
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::any::TypeId;
|
||||
|
@ -1739,6 +1751,13 @@ mod tests {
|
|||
query.iter();
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic]
|
||||
fn assert_system_does_not_conflict() {
|
||||
fn system(_query: Query<(&mut W<u32>, &mut W<u32>)>) {}
|
||||
super::assert_system_does_not_conflict(system);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic]
|
||||
fn panic_inside_system() {
|
||||
|
|
|
@ -183,6 +183,52 @@ use std::{any::TypeId, borrow::Borrow, fmt::Debug};
|
|||
///
|
||||
/// An alternative to this idiom is to wrap the conflicting queries into a [`ParamSet`](super::ParamSet).
|
||||
///
|
||||
/// ## Whole Entity Access
|
||||
///
|
||||
/// [`EntityRef`]s can be fetched from a query. This will give read-only access to any component on the entity,
|
||||
/// and can be use to dynamically fetch any component without baking it into the query type. Due to this global
|
||||
/// access to the entity, this will block any other system from parallelizing with it. As such these queries
|
||||
/// should be sparingly used.
|
||||
///
|
||||
/// ```
|
||||
/// # use bevy_ecs::prelude::*;
|
||||
/// # #[derive(Component)]
|
||||
/// # struct ComponentA;
|
||||
/// # fn system(
|
||||
/// query: Query<(EntityRef, &ComponentA)>
|
||||
/// # ) {}
|
||||
/// # bevy_ecs::system::assert_is_system(system);
|
||||
/// ```
|
||||
///
|
||||
/// As `EntityRef` can read any component on an entity, a query using it will conflict with *any* mutable
|
||||
/// access. It is strongly advised to couple `EntityRef` queries with the use of either `With`/`Without`
|
||||
/// filters or `ParamSets`. This also limits the scope of the query, which will improve iteration performance
|
||||
/// and also allows it to parallelize with other non-conflicting systems.
|
||||
///
|
||||
/// ```should_panic
|
||||
/// # use bevy_ecs::prelude::*;
|
||||
/// # #[derive(Component)]
|
||||
/// # struct ComponentA;
|
||||
/// # fn system(
|
||||
/// // This will panic!
|
||||
/// query: Query<(EntityRef, &mut ComponentA)>
|
||||
/// # ) {}
|
||||
/// # bevy_ecs::system::assert_system_does_not_conflict(system);
|
||||
/// ```
|
||||
/// ```
|
||||
/// # use bevy_ecs::prelude::*;
|
||||
/// # #[derive(Component)]
|
||||
/// # struct ComponentA;
|
||||
/// # #[derive(Component)]
|
||||
/// # struct ComponentB;
|
||||
/// # fn system(
|
||||
/// // This will not panic.
|
||||
/// query_a: Query<EntityRef, With<ComponentA>>,
|
||||
/// query_b: Query<&mut ComponentB, Without<ComponentA>>,
|
||||
/// # ) {}
|
||||
/// # bevy_ecs::system::assert_system_does_not_conflict(system);
|
||||
/// ```
|
||||
///
|
||||
/// # Accessing query items
|
||||
///
|
||||
/// The following table summarizes the behavior of the safe methods that can be used to get query items.
|
||||
|
@ -248,6 +294,7 @@ use std::{any::TypeId, borrow::Borrow, fmt::Debug};
|
|||
/// [`Changed`]: crate::query::Changed
|
||||
/// [components]: crate::component::Component
|
||||
/// [entity identifiers]: crate::entity::Entity
|
||||
/// [`EntityRef`]: crate::world::EntityRef
|
||||
/// [`for_each`]: Self::for_each
|
||||
/// [`for_each_mut`]: Self::for_each_mut
|
||||
/// [`get`]: Self::get
|
||||
|
@ -379,7 +426,7 @@ impl<'w, 's, Q: WorldQuery, F: ReadOnlyWorldQuery> Query<'w, 's, Q, F> {
|
|||
pub fn iter(&self) -> QueryIter<'_, 's, Q::ReadOnly, F::ReadOnly> {
|
||||
// SAFETY:
|
||||
// - `self.world` has permission to access the required components.
|
||||
// - The query is read-only, so it can be aliased even if it was originaly mutable.
|
||||
// - The query is read-only, so it can be aliased even if it was originally mutable.
|
||||
unsafe {
|
||||
self.state
|
||||
.as_readonly()
|
||||
|
@ -445,7 +492,7 @@ impl<'w, 's, Q: WorldQuery, F: ReadOnlyWorldQuery> Query<'w, 's, Q, F> {
|
|||
) -> QueryCombinationIter<'_, 's, Q::ReadOnly, F::ReadOnly, K> {
|
||||
// SAFETY:
|
||||
// - `self.world` has permission to access the required components.
|
||||
// - The query is read-only, so it can be aliased even if it was originaly mutable.
|
||||
// - The query is read-only, so it can be aliased even if it was originally mutable.
|
||||
unsafe {
|
||||
self.state.as_readonly().iter_combinations_unchecked_manual(
|
||||
self.world,
|
||||
|
@ -531,7 +578,7 @@ impl<'w, 's, Q: WorldQuery, F: ReadOnlyWorldQuery> Query<'w, 's, Q, F> {
|
|||
{
|
||||
// SAFETY:
|
||||
// - `self.world` has permission to access the required components.
|
||||
// - The query is read-only, so it can be aliased even if it was originaly mutable.
|
||||
// - The query is read-only, so it can be aliased even if it was originally mutable.
|
||||
unsafe {
|
||||
self.state.as_readonly().iter_many_unchecked_manual(
|
||||
entities,
|
||||
|
@ -687,7 +734,7 @@ impl<'w, 's, Q: WorldQuery, F: ReadOnlyWorldQuery> Query<'w, 's, Q, F> {
|
|||
pub fn for_each<'this>(&'this self, f: impl FnMut(ROQueryItem<'this, Q>)) {
|
||||
// SAFETY:
|
||||
// - `self.world` has permission to access the required components.
|
||||
// - The query is read-only, so it can be aliased even if it was originaly mutable.
|
||||
// - The query is read-only, so it can be aliased even if it was originally mutable.
|
||||
unsafe {
|
||||
self.state.as_readonly().for_each_unchecked_manual(
|
||||
self.world,
|
||||
|
|
|
@ -627,7 +627,7 @@ unsafe impl SystemParam for &'_ World {
|
|||
world: UnsafeWorldCell<'w>,
|
||||
_change_tick: Tick,
|
||||
) -> Self::Item<'w, 's> {
|
||||
// SAFETY: Read-only access to the entire world was registerd in `init_state`.
|
||||
// SAFETY: Read-only access to the entire world was registered in `init_state`.
|
||||
world.world()
|
||||
}
|
||||
}
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue