Merge branch 'main' into transmission

This commit is contained in:
Marco Buono 2023-07-17 23:17:56 -03:00
commit c353b41bd6
388 changed files with 9154 additions and 6501 deletions

View file

@ -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)

View file

@ -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/

View file

@ -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"

View file

@ -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

File diff suppressed because one or more lines are too long

View file

@ -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

View file

@ -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);
}

View file

@ -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,

View file

@ -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>,

View file

@ -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);
}

View file

@ -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);
}

View file

@ -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;
}

View file

@ -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;
}

View file

@ -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) {}

View file

@ -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;
}

View file

@ -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;
}

View file

@ -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>;

View file

@ -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);

View file

@ -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);
}

View file

@ -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);
}

View file

@ -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;
}

View file

@ -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"

View file

@ -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" }

View file

@ -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),

View file

@ -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 }

View file

@ -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)]

View file

@ -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,

View file

@ -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".

View file

@ -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);
}

View file

@ -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 => {

View file

@ -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" }

View file

@ -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()
/// });

View file

@ -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);

View file

@ -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,

View file

@ -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,
}
}
}

View file

@ -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);
}

View file

@ -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.

View file

@ -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.

View file

@ -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>();

View file

@ -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"

View file

@ -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,
}

View file

@ -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()
}

View file

@ -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
}
}

View file

@ -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

View file

@ -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"

View file

@ -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>();

View file

@ -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>,

View file

@ -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"

View file

@ -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>;

View file

@ -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,

View file

@ -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);

View file

@ -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 {

View file

@ -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,

View file

@ -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,

View file

@ -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,
}

View file

@ -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,

View file

@ -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.

View file

@ -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,

View file

@ -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>;

View file

@ -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,

View file

@ -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,
));
}
}

View file

@ -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.

View file

@ -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,

View file

@ -1,4 +1,4 @@
#import bevy_render::view
#import bevy_render::view View
@group(0) @binding(0)
var skybox: texture_cube<f32>;

View file

@ -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));
}
}
}

View file

@ -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,

View file

@ -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);

View file

@ -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);
}

View file

@ -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"] }

View file

@ -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]

View file

@ -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 {

View file

@ -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 }

View file

@ -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" }

View file

@ -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"

View file

@ -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"

View file

@ -101,7 +101,7 @@ fn parse_component_attr(ast: &DeriveInput) -> Result<Attrs> {
};
Ok(())
} else {
Err(nested.error("Unsuported attribute"))
Err(nested.error("Unsupported attribute"))
}
})?;
}

View file

@ -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,

View file

@ -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,
}

View file

@ -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);

View file

@ -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]

View file

@ -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() {

View file

@ -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;

View file

@ -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)

View file

@ -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(),
}

View file

@ -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;

View file

@ -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();

View file

@ -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.

View file

@ -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);

View file

@ -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.

View file

@ -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 {

View file

@ -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,
}

View file

@ -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, ()>,

View file

@ -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.

View file

@ -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");
}
}

View file

@ -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(())
}

View file

@ -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

View file

@ -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.

View file

@ -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() {

View file

@ -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,

View file

@ -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