Visibilty Inheritance, universal ComputedVisibility and RenderLayers support (#5310)
# Objective Fixes #4907. Fixes #838. Fixes #5089. Supersedes #5146. Supersedes #2087. Supersedes #865. Supersedes #5114 Visibility is currently entirely local. Set a parent entity to be invisible, and the children are still visible. This makes it hard for users to hide entire hierarchies of entities. Additionally, the semantics of `Visibility` vs `ComputedVisibility` are inconsistent across entity types. 3D meshes use `ComputedVisibility` as the "definitive" visibility component, with `Visibility` being just one data source. Sprites just use `Visibility`, which means they can't feed off of `ComputedVisibility` data, such as culling information, RenderLayers, and (added in this pr) visibility inheritance information. ## Solution Splits `ComputedVisibilty::is_visible` into `ComputedVisibilty::is_visible_in_view` and `ComputedVisibilty::is_visible_in_hierarchy`. For each visible entity, `is_visible_in_hierarchy` is computed by propagating visibility down the hierarchy. The `ComputedVisibility::is_visible()` function combines these two booleans for the canonical "is this entity visible" function. Additionally, all entities that have `Visibility` now also have `ComputedVisibility`. Sprites, Lights, and UI entities now use `ComputedVisibility` when appropriate. This means that in addition to visibility inheritance, everything using Visibility now also supports RenderLayers. Notably, Sprites (and other 2d objects) now support `RenderLayers` and work properly across multiple views. Also note that this does increase the amount of work done per sprite. Bevymark with 100,000 sprites on `main` runs in `0.017612` seconds and this runs in `0.01902`. That is certainly a gap, but I believe the api consistency and extra functionality this buys us is worth it. See [this thread](https://github.com/bevyengine/bevy/pull/5146#issuecomment-1182783452) for more info. Note that #5146 in combination with #5114 _are_ a viable alternative to this PR and _would_ perform better, but that comes at the cost of api inconsistencies and doing visibility calculations in the "wrong" place. The current visibility system does have potential for performance improvements. I would prefer to evolve that one system as a whole rather than doing custom hacks / different behaviors for each feature slice. Here is a "split screen" example where the left camera uses RenderLayers to filter out the blue sprite.  Note that this builds directly on #5146 and that @james7132 deserves the credit for the baseline visibility inheritance work. This pr moves the inherited visibility field into `ComputedVisibility`, then does the additional work of porting everything to `ComputedVisibility`. See my [comments here](https://github.com/bevyengine/bevy/pull/5146#issuecomment-1182783452) for rationale. ## Follow up work * Now that lights use ComputedVisibility, VisibleEntities now includes "visible lights" in the entity list. Functionally not a problem as we use queries to filter the list down in the desired context. But we should consider splitting this out into a separate`VisibleLights` collection for both clarity and performance reasons. And _maybe_ even consider scoping `VisibleEntities` down to `VisibleMeshes`?. * Investigate alternative sprite rendering impls (in combination with visibility system tweaks) that avoid re-generating a per-view fixedbitset of visible entities every frame, then checking each ExtractedEntity. This is where most of the performance overhead lives. Ex: we could generate ExtractedEntities per-view using the VisibleEntities list, avoiding the need for the bitset. * Should ComputedVisibility use bitflags under the hood? This would cut down on the size of the component, potentially speed up the `is_visible()` function, and allow us to cheaply expand ComputedVisibility with more data (ex: split out local visibility and parent visibility, add more culling classes, etc). --- ## Changelog * ComputedVisibility now takes hierarchy visibility into account. * 2D, UI and Light entities now use the ComputedVisibility component. ## Migration Guide If you were previously reading `Visibility::is_visible` as the "actual visibility" for sprites or lights, use `ComputedVisibilty::is_visible()` instead: ```rust // before (0.7) fn system(query: Query<&Visibility>) { for visibility in query.iter() { if visibility.is_visible { log!("found visible entity"); } } } // after (0.8) fn system(query: Query<&ComputedVisibility>) { for visibility in query.iter() { if visibility.is_visible() { log!("found visible entity"); } } } ``` Co-authored-by: Carter Anderson <mcanders1@gmail.com>
This commit is contained in:
parent
c8aa047cca
commit
40d4992401
@ -73,6 +73,8 @@ pub struct PointLightBundle {
|
|||||||
pub global_transform: GlobalTransform,
|
pub global_transform: GlobalTransform,
|
||||||
/// Enables or disables the light
|
/// Enables or disables the light
|
||||||
pub visibility: Visibility,
|
pub visibility: Visibility,
|
||||||
|
/// Algorithmically-computed indication of whether an entity is visible and should be extracted for rendering
|
||||||
|
pub computed_visibility: ComputedVisibility,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A component bundle for spot light entities
|
/// A component bundle for spot light entities
|
||||||
@ -85,6 +87,8 @@ pub struct SpotLightBundle {
|
|||||||
pub global_transform: GlobalTransform,
|
pub global_transform: GlobalTransform,
|
||||||
/// Enables or disables the light
|
/// Enables or disables the light
|
||||||
pub visibility: Visibility,
|
pub visibility: Visibility,
|
||||||
|
/// Algorithmically-computed indication of whether an entity is visible and should be extracted for rendering
|
||||||
|
pub computed_visibility: ComputedVisibility,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A component bundle for [`DirectionalLight`] entities.
|
/// A component bundle for [`DirectionalLight`] entities.
|
||||||
@ -97,4 +101,6 @@ pub struct DirectionalLightBundle {
|
|||||||
pub global_transform: GlobalTransform,
|
pub global_transform: GlobalTransform,
|
||||||
/// Enables or disables the light
|
/// Enables or disables the light
|
||||||
pub visibility: Visibility,
|
pub visibility: Visibility,
|
||||||
|
/// Algorithmically-computed indication of whether an entity is visible and should be extracted for rendering
|
||||||
|
pub computed_visibility: ComputedVisibility,
|
||||||
}
|
}
|
||||||
|
@ -150,6 +150,7 @@ impl Plugin for PbrPlugin {
|
|||||||
assign_lights_to_clusters
|
assign_lights_to_clusters
|
||||||
.label(SimulationLightSystems::AssignLightsToClusters)
|
.label(SimulationLightSystems::AssignLightsToClusters)
|
||||||
.after(TransformSystem::TransformPropagate)
|
.after(TransformSystem::TransformPropagate)
|
||||||
|
.after(VisibilitySystems::CheckVisibility)
|
||||||
.after(CameraUpdateSystem)
|
.after(CameraUpdateSystem)
|
||||||
.after(ModifiesWindows),
|
.after(ModifiesWindows),
|
||||||
)
|
)
|
||||||
@ -157,6 +158,8 @@ impl Plugin for PbrPlugin {
|
|||||||
CoreStage::PostUpdate,
|
CoreStage::PostUpdate,
|
||||||
update_directional_light_frusta
|
update_directional_light_frusta
|
||||||
.label(SimulationLightSystems::UpdateLightFrusta)
|
.label(SimulationLightSystems::UpdateLightFrusta)
|
||||||
|
// This must run after CheckVisibility because it relies on ComputedVisibility::is_visible()
|
||||||
|
.after(VisibilitySystems::CheckVisibility)
|
||||||
.after(TransformSystem::TransformPropagate),
|
.after(TransformSystem::TransformPropagate),
|
||||||
)
|
)
|
||||||
.add_system_to_stage(
|
.add_system_to_stage(
|
||||||
|
@ -10,7 +10,7 @@ use bevy_render::{
|
|||||||
primitives::{Aabb, CubemapFrusta, Frustum, Plane, Sphere},
|
primitives::{Aabb, CubemapFrusta, Frustum, Plane, Sphere},
|
||||||
render_resource::BufferBindingType,
|
render_resource::BufferBindingType,
|
||||||
renderer::RenderDevice,
|
renderer::RenderDevice,
|
||||||
view::{ComputedVisibility, RenderLayers, Visibility, VisibleEntities},
|
view::{ComputedVisibility, RenderLayers, VisibleEntities},
|
||||||
};
|
};
|
||||||
use bevy_transform::components::GlobalTransform;
|
use bevy_transform::components::GlobalTransform;
|
||||||
use bevy_utils::tracing::warn;
|
use bevy_utils::tracing::warn;
|
||||||
@ -793,8 +793,8 @@ pub(crate) fn assign_lights_to_clusters(
|
|||||||
&mut Clusters,
|
&mut Clusters,
|
||||||
Option<&mut VisiblePointLights>,
|
Option<&mut VisiblePointLights>,
|
||||||
)>,
|
)>,
|
||||||
point_lights_query: Query<(Entity, &GlobalTransform, &PointLight, &Visibility)>,
|
point_lights_query: Query<(Entity, &GlobalTransform, &PointLight, &ComputedVisibility)>,
|
||||||
spot_lights_query: Query<(Entity, &GlobalTransform, &SpotLight, &Visibility)>,
|
spot_lights_query: Query<(Entity, &GlobalTransform, &SpotLight, &ComputedVisibility)>,
|
||||||
mut lights: Local<Vec<PointLightAssignmentData>>,
|
mut lights: Local<Vec<PointLightAssignmentData>>,
|
||||||
mut cluster_aabb_spheres: Local<Vec<Option<Sphere>>>,
|
mut cluster_aabb_spheres: Local<Vec<Option<Sphere>>>,
|
||||||
mut max_point_lights_warning_emitted: Local<bool>,
|
mut max_point_lights_warning_emitted: Local<bool>,
|
||||||
@ -811,7 +811,7 @@ pub(crate) fn assign_lights_to_clusters(
|
|||||||
lights.extend(
|
lights.extend(
|
||||||
point_lights_query
|
point_lights_query
|
||||||
.iter()
|
.iter()
|
||||||
.filter(|(.., visibility)| visibility.is_visible)
|
.filter(|(.., visibility)| visibility.is_visible())
|
||||||
.map(
|
.map(
|
||||||
|(entity, transform, point_light, _visibility)| PointLightAssignmentData {
|
|(entity, transform, point_light, _visibility)| PointLightAssignmentData {
|
||||||
entity,
|
entity,
|
||||||
@ -826,7 +826,7 @@ pub(crate) fn assign_lights_to_clusters(
|
|||||||
lights.extend(
|
lights.extend(
|
||||||
spot_lights_query
|
spot_lights_query
|
||||||
.iter()
|
.iter()
|
||||||
.filter(|(.., visibility)| visibility.is_visible)
|
.filter(|(.., visibility)| visibility.is_visible())
|
||||||
.map(
|
.map(
|
||||||
|(entity, transform, spot_light, _visibility)| PointLightAssignmentData {
|
|(entity, transform, spot_light, _visibility)| PointLightAssignmentData {
|
||||||
entity,
|
entity,
|
||||||
@ -1415,7 +1415,7 @@ pub fn update_directional_light_frusta(
|
|||||||
&GlobalTransform,
|
&GlobalTransform,
|
||||||
&DirectionalLight,
|
&DirectionalLight,
|
||||||
&mut Frustum,
|
&mut Frustum,
|
||||||
&Visibility,
|
&ComputedVisibility,
|
||||||
),
|
),
|
||||||
Or<(Changed<GlobalTransform>, Changed<DirectionalLight>)>,
|
Or<(Changed<GlobalTransform>, Changed<DirectionalLight>)>,
|
||||||
>,
|
>,
|
||||||
@ -1424,7 +1424,7 @@ pub fn update_directional_light_frusta(
|
|||||||
// The frustum is used for culling meshes to the light for shadow mapping
|
// The frustum is used for culling meshes to the light for shadow mapping
|
||||||
// so if shadow mapping is disabled for this light, then the frustum is
|
// so if shadow mapping is disabled for this light, then the frustum is
|
||||||
// not needed.
|
// not needed.
|
||||||
if !directional_light.shadows_enabled || !visibility.is_visible {
|
if !directional_light.shadows_enabled || !visibility.is_visible() {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1541,45 +1541,43 @@ pub fn check_light_mesh_visibility(
|
|||||||
&Frustum,
|
&Frustum,
|
||||||
&mut VisibleEntities,
|
&mut VisibleEntities,
|
||||||
Option<&RenderLayers>,
|
Option<&RenderLayers>,
|
||||||
&Visibility,
|
&ComputedVisibility,
|
||||||
),
|
),
|
||||||
Without<SpotLight>,
|
Without<SpotLight>,
|
||||||
>,
|
>,
|
||||||
mut visible_entity_query: Query<
|
mut visible_entity_query: Query<
|
||||||
(
|
(
|
||||||
Entity,
|
Entity,
|
||||||
&Visibility,
|
|
||||||
&mut ComputedVisibility,
|
&mut ComputedVisibility,
|
||||||
Option<&RenderLayers>,
|
Option<&RenderLayers>,
|
||||||
Option<&Aabb>,
|
Option<&Aabb>,
|
||||||
Option<&GlobalTransform>,
|
Option<&GlobalTransform>,
|
||||||
),
|
),
|
||||||
Without<NotShadowCaster>,
|
(Without<NotShadowCaster>, Without<DirectionalLight>),
|
||||||
>,
|
>,
|
||||||
) {
|
) {
|
||||||
// Directonal lights
|
// Directional lights
|
||||||
for (directional_light, frustum, mut visible_entities, maybe_view_mask, visibility) in
|
for (
|
||||||
&mut directional_lights
|
directional_light,
|
||||||
|
frustum,
|
||||||
|
mut visible_entities,
|
||||||
|
maybe_view_mask,
|
||||||
|
light_computed_visibility,
|
||||||
|
) in &mut directional_lights
|
||||||
{
|
{
|
||||||
visible_entities.entities.clear();
|
visible_entities.entities.clear();
|
||||||
|
|
||||||
// NOTE: If shadow mapping is disabled for the light then it must have no visible entities
|
// NOTE: If shadow mapping is disabled for the light then it must have no visible entities
|
||||||
if !directional_light.shadows_enabled || !visibility.is_visible {
|
if !directional_light.shadows_enabled || !light_computed_visibility.is_visible() {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
let view_mask = maybe_view_mask.copied().unwrap_or_default();
|
let view_mask = maybe_view_mask.copied().unwrap_or_default();
|
||||||
|
|
||||||
for (
|
for (entity, mut computed_visibility, maybe_entity_mask, maybe_aabb, maybe_transform) in
|
||||||
entity,
|
&mut visible_entity_query
|
||||||
visibility,
|
|
||||||
mut computed_visibility,
|
|
||||||
maybe_entity_mask,
|
|
||||||
maybe_aabb,
|
|
||||||
maybe_transform,
|
|
||||||
) in &mut visible_entity_query
|
|
||||||
{
|
{
|
||||||
if !visibility.is_visible {
|
if !computed_visibility.is_visible_in_hierarchy() {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1595,7 +1593,7 @@ pub fn check_light_mesh_visibility(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
computed_visibility.is_visible = true;
|
computed_visibility.set_visible_in_view();
|
||||||
visible_entities.entities.push(entity);
|
visible_entities.entities.push(entity);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1631,14 +1629,13 @@ pub fn check_light_mesh_visibility(
|
|||||||
|
|
||||||
for (
|
for (
|
||||||
entity,
|
entity,
|
||||||
visibility,
|
|
||||||
mut computed_visibility,
|
mut computed_visibility,
|
||||||
maybe_entity_mask,
|
maybe_entity_mask,
|
||||||
maybe_aabb,
|
maybe_aabb,
|
||||||
maybe_transform,
|
maybe_transform,
|
||||||
) in &mut visible_entity_query
|
) in &mut visible_entity_query
|
||||||
{
|
{
|
||||||
if !visibility.is_visible {
|
if !computed_visibility.is_visible_in_hierarchy() {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1660,12 +1657,12 @@ pub fn check_light_mesh_visibility(
|
|||||||
.zip(cubemap_visible_entities.iter_mut())
|
.zip(cubemap_visible_entities.iter_mut())
|
||||||
{
|
{
|
||||||
if frustum.intersects_obb(aabb, &model_to_world, true) {
|
if frustum.intersects_obb(aabb, &model_to_world, true) {
|
||||||
computed_visibility.is_visible = true;
|
computed_visibility.set_visible_in_view();
|
||||||
visible_entities.entities.push(entity);
|
visible_entities.entities.push(entity);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
computed_visibility.is_visible = true;
|
computed_visibility.set_visible_in_view();
|
||||||
for visible_entities in cubemap_visible_entities.iter_mut() {
|
for visible_entities in cubemap_visible_entities.iter_mut() {
|
||||||
visible_entities.entities.push(entity);
|
visible_entities.entities.push(entity);
|
||||||
}
|
}
|
||||||
@ -1695,14 +1692,13 @@ pub fn check_light_mesh_visibility(
|
|||||||
|
|
||||||
for (
|
for (
|
||||||
entity,
|
entity,
|
||||||
visibility,
|
|
||||||
mut computed_visibility,
|
mut computed_visibility,
|
||||||
maybe_entity_mask,
|
maybe_entity_mask,
|
||||||
maybe_aabb,
|
maybe_aabb,
|
||||||
maybe_transform,
|
maybe_transform,
|
||||||
) in visible_entity_query.iter_mut()
|
) in visible_entity_query.iter_mut()
|
||||||
{
|
{
|
||||||
if !visibility.is_visible {
|
if !computed_visibility.is_visible_in_hierarchy() {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1720,11 +1716,11 @@ pub fn check_light_mesh_visibility(
|
|||||||
}
|
}
|
||||||
|
|
||||||
if frustum.intersects_obb(aabb, &model_to_world, true) {
|
if frustum.intersects_obb(aabb, &model_to_world, true) {
|
||||||
computed_visibility.is_visible = true;
|
computed_visibility.set_visible_in_view();
|
||||||
visible_entities.entities.push(entity);
|
visible_entities.entities.push(entity);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
computed_visibility.is_visible = true;
|
computed_visibility.set_visible_in_view();
|
||||||
visible_entities.entities.push(entity);
|
visible_entities.entities.push(entity);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -26,7 +26,8 @@ use bevy_render::{
|
|||||||
renderer::{RenderContext, RenderDevice, RenderQueue},
|
renderer::{RenderContext, RenderDevice, RenderQueue},
|
||||||
texture::*,
|
texture::*,
|
||||||
view::{
|
view::{
|
||||||
ExtractedView, ViewUniform, ViewUniformOffset, ViewUniforms, Visibility, VisibleEntities,
|
ComputedVisibility, ExtractedView, ViewUniform, ViewUniformOffset, ViewUniforms,
|
||||||
|
VisibleEntities,
|
||||||
},
|
},
|
||||||
Extract,
|
Extract,
|
||||||
};
|
};
|
||||||
@ -411,8 +412,22 @@ pub fn extract_lights(
|
|||||||
point_light_shadow_map: Extract<Res<PointLightShadowMap>>,
|
point_light_shadow_map: Extract<Res<PointLightShadowMap>>,
|
||||||
directional_light_shadow_map: Extract<Res<DirectionalLightShadowMap>>,
|
directional_light_shadow_map: Extract<Res<DirectionalLightShadowMap>>,
|
||||||
global_point_lights: Extract<Res<GlobalVisiblePointLights>>,
|
global_point_lights: Extract<Res<GlobalVisiblePointLights>>,
|
||||||
point_lights: Extract<Query<(&PointLight, &CubemapVisibleEntities, &GlobalTransform)>>,
|
point_lights: Extract<
|
||||||
spot_lights: Extract<Query<(&SpotLight, &VisibleEntities, &GlobalTransform)>>,
|
Query<(
|
||||||
|
&PointLight,
|
||||||
|
&CubemapVisibleEntities,
|
||||||
|
&GlobalTransform,
|
||||||
|
&ComputedVisibility,
|
||||||
|
)>,
|
||||||
|
>,
|
||||||
|
spot_lights: Extract<
|
||||||
|
Query<(
|
||||||
|
&SpotLight,
|
||||||
|
&VisibleEntities,
|
||||||
|
&GlobalTransform,
|
||||||
|
&ComputedVisibility,
|
||||||
|
)>,
|
||||||
|
>,
|
||||||
directional_lights: Extract<
|
directional_lights: Extract<
|
||||||
Query<
|
Query<
|
||||||
(
|
(
|
||||||
@ -420,7 +435,7 @@ pub fn extract_lights(
|
|||||||
&DirectionalLight,
|
&DirectionalLight,
|
||||||
&VisibleEntities,
|
&VisibleEntities,
|
||||||
&GlobalTransform,
|
&GlobalTransform,
|
||||||
&Visibility,
|
&ComputedVisibility,
|
||||||
),
|
),
|
||||||
Without<SpotLight>,
|
Without<SpotLight>,
|
||||||
>,
|
>,
|
||||||
@ -447,7 +462,12 @@ pub fn extract_lights(
|
|||||||
|
|
||||||
let mut point_lights_values = Vec::with_capacity(*previous_point_lights_len);
|
let mut point_lights_values = Vec::with_capacity(*previous_point_lights_len);
|
||||||
for entity in global_point_lights.iter().copied() {
|
for entity in global_point_lights.iter().copied() {
|
||||||
if let Ok((point_light, cubemap_visible_entities, transform)) = point_lights.get(entity) {
|
if let Ok((point_light, cubemap_visible_entities, transform, visibility)) =
|
||||||
|
point_lights.get(entity)
|
||||||
|
{
|
||||||
|
if !visibility.is_visible() {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
// TODO: This is very much not ideal. We should be able to re-use the vector memory.
|
// TODO: This is very much not ideal. We should be able to re-use the vector memory.
|
||||||
// However, since exclusive access to the main world in extract is ill-advised, we just clone here.
|
// However, since exclusive access to the main world in extract is ill-advised, we just clone here.
|
||||||
let render_cubemap_visible_entities = cubemap_visible_entities.clone();
|
let render_cubemap_visible_entities = cubemap_visible_entities.clone();
|
||||||
@ -481,7 +501,10 @@ pub fn extract_lights(
|
|||||||
|
|
||||||
let mut spot_lights_values = Vec::with_capacity(*previous_spot_lights_len);
|
let mut spot_lights_values = Vec::with_capacity(*previous_spot_lights_len);
|
||||||
for entity in global_point_lights.iter().copied() {
|
for entity in global_point_lights.iter().copied() {
|
||||||
if let Ok((spot_light, visible_entities, transform)) = spot_lights.get(entity) {
|
if let Ok((spot_light, visible_entities, transform, visibility)) = spot_lights.get(entity) {
|
||||||
|
if !visibility.is_visible() {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
// TODO: This is very much not ideal. We should be able to re-use the vector memory.
|
// TODO: This is very much not ideal. We should be able to re-use the vector memory.
|
||||||
// However, since exclusive access to the main world in extract is ill-advised, we just clone here.
|
// However, since exclusive access to the main world in extract is ill-advised, we just clone here.
|
||||||
let render_visible_entities = visible_entities.clone();
|
let render_visible_entities = visible_entities.clone();
|
||||||
@ -522,7 +545,7 @@ pub fn extract_lights(
|
|||||||
for (entity, directional_light, visible_entities, transform, visibility) in
|
for (entity, directional_light, visible_entities, transform, visibility) in
|
||||||
directional_lights.iter()
|
directional_lights.iter()
|
||||||
{
|
{
|
||||||
if !visibility.is_visible {
|
if !visibility.is_visible() {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -139,7 +139,7 @@ pub fn extract_meshes(
|
|||||||
) {
|
) {
|
||||||
let mut caster_commands = Vec::with_capacity(*prev_caster_commands_len);
|
let mut caster_commands = Vec::with_capacity(*prev_caster_commands_len);
|
||||||
let mut not_caster_commands = Vec::with_capacity(*prev_not_caster_commands_len);
|
let mut not_caster_commands = Vec::with_capacity(*prev_not_caster_commands_len);
|
||||||
let visible_meshes = meshes_query.iter().filter(|(_, vis, ..)| vis.is_visible);
|
let visible_meshes = meshes_query.iter().filter(|(_, vis, ..)| vis.is_visible());
|
||||||
|
|
||||||
for (entity, _, transform, handle, not_receiver, not_caster) in visible_meshes {
|
for (entity, _, transform, handle, not_receiver, not_caster) in visible_meshes {
|
||||||
let transform = transform.compute_matrix();
|
let transform = transform.compute_matrix();
|
||||||
@ -224,7 +224,7 @@ pub fn extract_skinned_meshes(
|
|||||||
let mut last_start = 0;
|
let mut last_start = 0;
|
||||||
|
|
||||||
for (entity, computed_visibility, skin) in query.iter() {
|
for (entity, computed_visibility, skin) in query.iter() {
|
||||||
if !computed_visibility.is_visible {
|
if !computed_visibility.is_visible() {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
// PERF: This can be expensive, can we move this to prepare?
|
// PERF: This can be expensive, can we move this to prepare?
|
||||||
|
@ -34,6 +34,7 @@ bevy_core = { path = "../bevy_core", version = "0.8.0-dev" }
|
|||||||
bevy_derive = { path = "../bevy_derive", version = "0.8.0-dev" }
|
bevy_derive = { path = "../bevy_derive", version = "0.8.0-dev" }
|
||||||
bevy_ecs = { path = "../bevy_ecs", version = "0.8.0-dev" }
|
bevy_ecs = { path = "../bevy_ecs", version = "0.8.0-dev" }
|
||||||
bevy_encase_derive = { path = "../bevy_encase_derive", version = "0.8.0-dev" }
|
bevy_encase_derive = { path = "../bevy_encase_derive", version = "0.8.0-dev" }
|
||||||
|
bevy_hierarchy = { path = "../bevy_hierarchy", version = "0.8.0-dev" }
|
||||||
bevy_log = { path = "../bevy_log", version = "0.8.0-dev" }
|
bevy_log = { path = "../bevy_log", version = "0.8.0-dev" }
|
||||||
bevy_math = { path = "../bevy_math", version = "0.8.0-dev" }
|
bevy_math = { path = "../bevy_math", version = "0.8.0-dev" }
|
||||||
bevy_mikktspace = { path = "../bevy_mikktspace", version = "0.8.0-dev" }
|
bevy_mikktspace = { path = "../bevy_mikktspace", version = "0.8.0-dev" }
|
||||||
|
@ -200,7 +200,7 @@ fn extract_visible_components<C: ExtractComponent>(
|
|||||||
) {
|
) {
|
||||||
let mut values = Vec::with_capacity(*previous_len);
|
let mut values = Vec::with_capacity(*previous_len);
|
||||||
for (entity, computed_visibility, query_item) in query.iter_mut() {
|
for (entity, computed_visibility, query_item) in query.iter_mut() {
|
||||||
if computed_visibility.is_visible {
|
if computed_visibility.is_visible() {
|
||||||
values.push((entity, (C::extract_component(query_item),)));
|
values.push((entity, (C::extract_component(query_item),)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -6,6 +6,7 @@ pub use render_layers::*;
|
|||||||
use bevy_app::{CoreStage, Plugin};
|
use bevy_app::{CoreStage, Plugin};
|
||||||
use bevy_asset::{Assets, Handle};
|
use bevy_asset::{Assets, Handle};
|
||||||
use bevy_ecs::prelude::*;
|
use bevy_ecs::prelude::*;
|
||||||
|
use bevy_hierarchy::{Children, Parent};
|
||||||
use bevy_reflect::std_traits::ReflectDefault;
|
use bevy_reflect::std_traits::ReflectDefault;
|
||||||
use bevy_reflect::Reflect;
|
use bevy_reflect::Reflect;
|
||||||
use bevy_transform::components::GlobalTransform;
|
use bevy_transform::components::GlobalTransform;
|
||||||
@ -19,10 +20,16 @@ use crate::{
|
|||||||
primitives::{Aabb, Frustum, Sphere},
|
primitives::{Aabb, Frustum, Sphere},
|
||||||
};
|
};
|
||||||
|
|
||||||
/// User indication of whether an entity is visible
|
/// User indication of whether an entity is visible. Propagates down the entity hierarchy.
|
||||||
|
|
||||||
|
/// If an entity is hidden in this way, all [`Children`] (and all of their children and so on) will also be hidden.
|
||||||
|
/// This is done by setting the values of their [`ComputedVisibility`] component.
|
||||||
#[derive(Component, Clone, Reflect, Debug)]
|
#[derive(Component, Clone, Reflect, Debug)]
|
||||||
#[reflect(Component, Default)]
|
#[reflect(Component, Default)]
|
||||||
pub struct Visibility {
|
pub struct Visibility {
|
||||||
|
/// Indicates whether this entity is visible. Hidden values will propagate down the entity hierarchy.
|
||||||
|
/// If this entity is hidden, all of its descendants will be hidden as well. See [`Children`] and [`Parent`] for
|
||||||
|
/// hierarchy info.
|
||||||
pub is_visible: bool,
|
pub is_visible: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -33,15 +40,51 @@ impl Default for Visibility {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Algorithmically-computed indication of whether an entity is visible and should be extracted for rendering
|
/// Algorithmically-computed indication of whether an entity is visible and should be extracted for rendering
|
||||||
#[derive(Component, Clone, Reflect, Debug)]
|
#[derive(Component, Clone, Reflect, Debug, Eq, PartialEq, Default)]
|
||||||
#[reflect(Component)]
|
#[reflect(Component)]
|
||||||
pub struct ComputedVisibility {
|
pub struct ComputedVisibility {
|
||||||
pub is_visible: bool,
|
is_visible_in_hierarchy: bool,
|
||||||
|
is_visible_in_view: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for ComputedVisibility {
|
impl ComputedVisibility {
|
||||||
fn default() -> Self {
|
/// Whether this entity is visible to something this frame. This is true if and only if [`Self::is_visible_in_hierarchy`] and [`Self::is_visible_in_view`]
|
||||||
Self { is_visible: true }
|
/// are true. This is the canonical method to call to determine if an entity should be drawn.
|
||||||
|
/// This value is updated in [`CoreStage::PostUpdate`] during the [`VisibilitySystems::CheckVisibility`] system label. Reading it from the
|
||||||
|
/// [`CoreStage::Update`] stage will yield the value from the previous frame.
|
||||||
|
#[inline]
|
||||||
|
pub fn is_visible(&self) -> bool {
|
||||||
|
self.is_visible_in_hierarchy && self.is_visible_in_view
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Whether this entity is visible in the entity hierarchy, which is determined by the [`Visibility`] component.
|
||||||
|
/// This takes into account "visibility inheritance". If any of this entity's ancestors (see [`Parent`]) are hidden, this entity
|
||||||
|
/// will be hidden as well. This value is updated in the [`CoreStage::PostUpdate`] stage in the
|
||||||
|
/// [`VisibilitySystems::VisibilityPropagate`] system label.
|
||||||
|
#[inline]
|
||||||
|
pub fn is_visible_in_hierarchy(&self) -> bool {
|
||||||
|
self.is_visible_in_hierarchy
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Whether this entity is visible in _any_ view (Cameras, Lights, etc). Each entity type (and view type) should choose how to set this
|
||||||
|
/// value. For cameras and drawn entities, this will take into account [`RenderLayers`].
|
||||||
|
///
|
||||||
|
/// This value is reset to `false` every frame in [`VisibilitySystems::VisibilityPropagate`] during [`CoreStage::PostUpdate`].
|
||||||
|
/// Each entity type then chooses how to set this field in the [`CoreStage::PostUpdate`] stage in the
|
||||||
|
/// [`VisibilitySystems::CheckVisibility`] system label. Meshes might use frustum culling to decide if they are visible in a view.
|
||||||
|
/// Other entities might just set this to `true` every frame.
|
||||||
|
#[inline]
|
||||||
|
pub fn is_visible_in_view(&self) -> bool {
|
||||||
|
self.is_visible_in_view
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sets `is_visible_in_view` to `true`. This is not reversible for a given frame, as it encodes whether or not this is visible in
|
||||||
|
/// _any_ view. This will be automatically reset to `false` every frame in [`VisibilitySystems::VisibilityPropagate`] and then set
|
||||||
|
/// to the proper value in [`VisibilitySystems::CheckVisibility`]. This should _only_ be set in systems with the [`VisibilitySystems::CheckVisibility`]
|
||||||
|
/// label. Don't call this unless you are defining a custom visibility system. For normal user-defined entity visibility, see [`Visibility`].
|
||||||
|
#[inline]
|
||||||
|
pub fn set_visible_in_view(&mut self) {
|
||||||
|
self.is_visible_in_view = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -88,6 +131,7 @@ pub enum VisibilitySystems {
|
|||||||
UpdateOrthographicFrusta,
|
UpdateOrthographicFrusta,
|
||||||
UpdatePerspectiveFrusta,
|
UpdatePerspectiveFrusta,
|
||||||
UpdateProjectionFrusta,
|
UpdateProjectionFrusta,
|
||||||
|
VisibilityPropagate,
|
||||||
/// Label for the [`check_visibility()`] system updating each frame the [`ComputedVisibility`]
|
/// Label for the [`check_visibility()`] system updating each frame the [`ComputedVisibility`]
|
||||||
/// of each entity and the [`VisibleEntities`] of each view.
|
/// of each entity and the [`VisibleEntities`] of each view.
|
||||||
CheckVisibility,
|
CheckVisibility,
|
||||||
@ -121,6 +165,10 @@ impl Plugin for VisibilityPlugin {
|
|||||||
.label(UpdateProjectionFrusta)
|
.label(UpdateProjectionFrusta)
|
||||||
.after(TransformSystem::TransformPropagate),
|
.after(TransformSystem::TransformPropagate),
|
||||||
)
|
)
|
||||||
|
.add_system_to_stage(
|
||||||
|
CoreStage::PostUpdate,
|
||||||
|
visibility_propagate_system.label(VisibilityPropagate),
|
||||||
|
)
|
||||||
.add_system_to_stage(
|
.add_system_to_stage(
|
||||||
CoreStage::PostUpdate,
|
CoreStage::PostUpdate,
|
||||||
check_visibility
|
check_visibility
|
||||||
@ -129,6 +177,7 @@ impl Plugin for VisibilityPlugin {
|
|||||||
.after(UpdateOrthographicFrusta)
|
.after(UpdateOrthographicFrusta)
|
||||||
.after(UpdatePerspectiveFrusta)
|
.after(UpdatePerspectiveFrusta)
|
||||||
.after(UpdateProjectionFrusta)
|
.after(UpdateProjectionFrusta)
|
||||||
|
.after(VisibilityPropagate)
|
||||||
.after(TransformSystem::TransformPropagate),
|
.after(TransformSystem::TransformPropagate),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -163,6 +212,68 @@ pub fn update_frusta<T: Component + CameraProjection + Send + Sync + 'static>(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn visibility_propagate_system(
|
||||||
|
mut root_query: Query<
|
||||||
|
(
|
||||||
|
Option<&Children>,
|
||||||
|
&Visibility,
|
||||||
|
&mut ComputedVisibility,
|
||||||
|
Entity,
|
||||||
|
),
|
||||||
|
Without<Parent>,
|
||||||
|
>,
|
||||||
|
mut visibility_query: Query<(&Visibility, &mut ComputedVisibility, &Parent)>,
|
||||||
|
children_query: Query<&Children, (With<Parent>, With<Visibility>, With<ComputedVisibility>)>,
|
||||||
|
) {
|
||||||
|
for (children, visibility, mut computed_visibility, entity) in root_query.iter_mut() {
|
||||||
|
computed_visibility.is_visible_in_hierarchy = visibility.is_visible;
|
||||||
|
// reset "view" visibility here ... if this entity should be drawn a future system should set this to true
|
||||||
|
computed_visibility.is_visible_in_view = false;
|
||||||
|
if let Some(children) = children {
|
||||||
|
for child in children.iter() {
|
||||||
|
let _ = propagate_recursive(
|
||||||
|
computed_visibility.is_visible_in_hierarchy,
|
||||||
|
&mut visibility_query,
|
||||||
|
&children_query,
|
||||||
|
*child,
|
||||||
|
entity,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn propagate_recursive(
|
||||||
|
parent_visible: bool,
|
||||||
|
visibility_query: &mut Query<(&Visibility, &mut ComputedVisibility, &Parent)>,
|
||||||
|
children_query: &Query<&Children, (With<Parent>, With<Visibility>, With<ComputedVisibility>)>,
|
||||||
|
entity: Entity,
|
||||||
|
expected_parent: Entity,
|
||||||
|
// BLOCKED: https://github.com/rust-lang/rust/issues/31436
|
||||||
|
// We use a result here to use the `?` operator. Ideally we'd use a try block instead
|
||||||
|
) -> Result<(), ()> {
|
||||||
|
let is_visible = {
|
||||||
|
let (visibility, mut computed_visibility, child_parent) =
|
||||||
|
visibility_query.get_mut(entity).map_err(drop)?;
|
||||||
|
assert_eq!(
|
||||||
|
child_parent.get(), expected_parent,
|
||||||
|
"Malformed hierarchy. This probably means that your hierarchy has been improperly maintained, or contains a cycle"
|
||||||
|
);
|
||||||
|
computed_visibility.is_visible_in_hierarchy = visibility.is_visible && parent_visible;
|
||||||
|
// reset "view" visibility here ... if this entity should be drawn a future system should set this to true
|
||||||
|
computed_visibility.is_visible_in_view = false;
|
||||||
|
computed_visibility.is_visible_in_hierarchy
|
||||||
|
};
|
||||||
|
|
||||||
|
for child in children_query.get(entity).map_err(drop)?.iter() {
|
||||||
|
let _ = propagate_recursive(is_visible, visibility_query, children_query, *child, entity);
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
// the batch size used for check_visibility, chosen because this number tends to perform well
|
||||||
|
const VISIBLE_ENTITIES_QUERY_BATCH_SIZE: usize = 1024;
|
||||||
|
|
||||||
/// System updating the visibility of entities each frame.
|
/// System updating the visibility of entities each frame.
|
||||||
///
|
///
|
||||||
/// The system is labelled with [`VisibilitySystems::CheckVisibility`]. Each frame, it updates the
|
/// The system is labelled with [`VisibilitySystems::CheckVisibility`]. Each frame, it updates the
|
||||||
@ -171,39 +282,35 @@ pub fn update_frusta<T: Component + CameraProjection + Send + Sync + 'static>(
|
|||||||
pub fn check_visibility(
|
pub fn check_visibility(
|
||||||
mut thread_queues: Local<ThreadLocal<Cell<Vec<Entity>>>>,
|
mut thread_queues: Local<ThreadLocal<Cell<Vec<Entity>>>>,
|
||||||
mut view_query: Query<(&mut VisibleEntities, &Frustum, Option<&RenderLayers>), With<Camera>>,
|
mut view_query: Query<(&mut VisibleEntities, &Frustum, Option<&RenderLayers>), With<Camera>>,
|
||||||
mut visible_entity_query: ParamSet<(
|
mut visible_aabb_query: Query<(
|
||||||
Query<&mut ComputedVisibility>,
|
|
||||||
Query<(
|
|
||||||
Entity,
|
Entity,
|
||||||
&Visibility,
|
|
||||||
&mut ComputedVisibility,
|
&mut ComputedVisibility,
|
||||||
Option<&RenderLayers>,
|
Option<&RenderLayers>,
|
||||||
Option<&Aabb>,
|
&Aabb,
|
||||||
|
&GlobalTransform,
|
||||||
Option<&NoFrustumCulling>,
|
Option<&NoFrustumCulling>,
|
||||||
Option<&GlobalTransform>,
|
|
||||||
)>,
|
|
||||||
)>,
|
)>,
|
||||||
|
mut visible_no_aabb_query: Query<
|
||||||
|
(Entity, &mut ComputedVisibility, Option<&RenderLayers>),
|
||||||
|
Without<Aabb>,
|
||||||
|
>,
|
||||||
) {
|
) {
|
||||||
// Reset the computed visibility to false
|
|
||||||
for mut computed_visibility in visible_entity_query.p0().iter_mut() {
|
|
||||||
computed_visibility.is_visible = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (mut visible_entities, frustum, maybe_view_mask) in &mut view_query {
|
for (mut visible_entities, frustum, maybe_view_mask) in &mut view_query {
|
||||||
let view_mask = maybe_view_mask.copied().unwrap_or_default();
|
let view_mask = maybe_view_mask.copied().unwrap_or_default();
|
||||||
visible_entities.entities.clear();
|
visible_entities.entities.clear();
|
||||||
visible_entity_query.p1().par_for_each_mut(
|
visible_aabb_query.par_for_each_mut(
|
||||||
1024,
|
VISIBLE_ENTITIES_QUERY_BATCH_SIZE,
|
||||||
|(
|
|(
|
||||||
entity,
|
entity,
|
||||||
visibility,
|
|
||||||
mut computed_visibility,
|
mut computed_visibility,
|
||||||
maybe_entity_mask,
|
maybe_entity_mask,
|
||||||
maybe_aabb,
|
model_aabb,
|
||||||
|
transform,
|
||||||
maybe_no_frustum_culling,
|
maybe_no_frustum_culling,
|
||||||
maybe_transform,
|
|
||||||
)| {
|
)| {
|
||||||
if !visibility.is_visible {
|
// skip computing visibility for entities that are configured to be hidden. is_visible_in_view has already been set to false
|
||||||
|
// in visibility_propagate_system
|
||||||
|
if !computed_visibility.is_visible_in_hierarchy() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -213,9 +320,7 @@ pub fn check_visibility(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// If we have an aabb and transform, do frustum culling
|
// If we have an aabb and transform, do frustum culling
|
||||||
if let (Some(model_aabb), None, Some(transform)) =
|
if maybe_no_frustum_culling.is_none() {
|
||||||
(maybe_aabb, maybe_no_frustum_culling, maybe_transform)
|
|
||||||
{
|
|
||||||
let model = transform.compute_matrix();
|
let model = transform.compute_matrix();
|
||||||
let model_sphere = Sphere {
|
let model_sphere = Sphere {
|
||||||
center: model.transform_point3a(model_aabb.center),
|
center: model.transform_point3a(model_aabb.center),
|
||||||
@ -231,7 +336,29 @@ pub fn check_visibility(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
computed_visibility.is_visible = true;
|
computed_visibility.is_visible_in_view = true;
|
||||||
|
let cell = thread_queues.get_or_default();
|
||||||
|
let mut queue = cell.take();
|
||||||
|
queue.push(entity);
|
||||||
|
cell.set(queue);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
visible_no_aabb_query.par_for_each_mut(
|
||||||
|
VISIBLE_ENTITIES_QUERY_BATCH_SIZE,
|
||||||
|
|(entity, mut computed_visibility, maybe_entity_mask)| {
|
||||||
|
// skip computing visibility for entities that are configured to be hidden. is_visible_in_view has already been set to false
|
||||||
|
// in visibility_propagate_system
|
||||||
|
if !computed_visibility.is_visible_in_hierarchy() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let entity_mask = maybe_entity_mask.copied().unwrap_or_default();
|
||||||
|
if !view_mask.intersects(&entity_mask) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
computed_visibility.is_visible_in_view = true;
|
||||||
let cell = thread_queues.get_or_default();
|
let cell = thread_queues.get_or_default();
|
||||||
let mut queue = cell.take();
|
let mut queue = cell.take();
|
||||||
queue.push(entity);
|
queue.push(entity);
|
||||||
@ -244,3 +371,151 @@ pub fn check_visibility(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod test {
|
||||||
|
use bevy_app::prelude::*;
|
||||||
|
use bevy_ecs::prelude::*;
|
||||||
|
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
use bevy_hierarchy::BuildWorldChildren;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn visibility_propagation() {
|
||||||
|
let mut app = App::new();
|
||||||
|
app.add_system(visibility_propagate_system);
|
||||||
|
|
||||||
|
let root1 = app
|
||||||
|
.world
|
||||||
|
.spawn()
|
||||||
|
.insert_bundle((
|
||||||
|
Visibility { is_visible: false },
|
||||||
|
ComputedVisibility::default(),
|
||||||
|
))
|
||||||
|
.id();
|
||||||
|
let root1_child1 = app
|
||||||
|
.world
|
||||||
|
.spawn()
|
||||||
|
.insert_bundle((Visibility::default(), ComputedVisibility::default()))
|
||||||
|
.id();
|
||||||
|
let root1_child2 = app
|
||||||
|
.world
|
||||||
|
.spawn()
|
||||||
|
.insert_bundle((
|
||||||
|
Visibility { is_visible: false },
|
||||||
|
ComputedVisibility::default(),
|
||||||
|
))
|
||||||
|
.id();
|
||||||
|
let root1_child1_grandchild1 = app
|
||||||
|
.world
|
||||||
|
.spawn()
|
||||||
|
.insert_bundle((Visibility::default(), ComputedVisibility::default()))
|
||||||
|
.id();
|
||||||
|
let root1_child2_grandchild1 = app
|
||||||
|
.world
|
||||||
|
.spawn()
|
||||||
|
.insert_bundle((Visibility::default(), ComputedVisibility::default()))
|
||||||
|
.id();
|
||||||
|
|
||||||
|
app.world
|
||||||
|
.entity_mut(root1)
|
||||||
|
.push_children(&[root1_child1, root1_child2]);
|
||||||
|
app.world
|
||||||
|
.entity_mut(root1_child1)
|
||||||
|
.push_children(&[root1_child1_grandchild1]);
|
||||||
|
app.world
|
||||||
|
.entity_mut(root1_child2)
|
||||||
|
.push_children(&[root1_child2_grandchild1]);
|
||||||
|
|
||||||
|
let root2 = app
|
||||||
|
.world
|
||||||
|
.spawn()
|
||||||
|
.insert_bundle((Visibility::default(), ComputedVisibility::default()))
|
||||||
|
.id();
|
||||||
|
let root2_child1 = app
|
||||||
|
.world
|
||||||
|
.spawn()
|
||||||
|
.insert_bundle((Visibility::default(), ComputedVisibility::default()))
|
||||||
|
.id();
|
||||||
|
let root2_child2 = app
|
||||||
|
.world
|
||||||
|
.spawn()
|
||||||
|
.insert_bundle((
|
||||||
|
Visibility { is_visible: false },
|
||||||
|
ComputedVisibility::default(),
|
||||||
|
))
|
||||||
|
.id();
|
||||||
|
let root2_child1_grandchild1 = app
|
||||||
|
.world
|
||||||
|
.spawn()
|
||||||
|
.insert_bundle((Visibility::default(), ComputedVisibility::default()))
|
||||||
|
.id();
|
||||||
|
let root2_child2_grandchild1 = app
|
||||||
|
.world
|
||||||
|
.spawn()
|
||||||
|
.insert_bundle((Visibility::default(), ComputedVisibility::default()))
|
||||||
|
.id();
|
||||||
|
|
||||||
|
app.world
|
||||||
|
.entity_mut(root2)
|
||||||
|
.push_children(&[root2_child1, root2_child2]);
|
||||||
|
app.world
|
||||||
|
.entity_mut(root2_child1)
|
||||||
|
.push_children(&[root2_child1_grandchild1]);
|
||||||
|
app.world
|
||||||
|
.entity_mut(root2_child2)
|
||||||
|
.push_children(&[root2_child2_grandchild1]);
|
||||||
|
|
||||||
|
app.update();
|
||||||
|
|
||||||
|
let is_visible = |e: Entity| {
|
||||||
|
app.world
|
||||||
|
.entity(e)
|
||||||
|
.get::<ComputedVisibility>()
|
||||||
|
.unwrap()
|
||||||
|
.is_visible_in_hierarchy
|
||||||
|
};
|
||||||
|
assert!(
|
||||||
|
!is_visible(root1),
|
||||||
|
"invisibility propagates down tree from root"
|
||||||
|
);
|
||||||
|
assert!(
|
||||||
|
!is_visible(root1_child1),
|
||||||
|
"invisibility propagates down tree from root"
|
||||||
|
);
|
||||||
|
assert!(
|
||||||
|
!is_visible(root1_child2),
|
||||||
|
"invisibility propagates down tree from root"
|
||||||
|
);
|
||||||
|
assert!(
|
||||||
|
!is_visible(root1_child1_grandchild1),
|
||||||
|
"invisibility propagates down tree from root"
|
||||||
|
);
|
||||||
|
assert!(
|
||||||
|
!is_visible(root1_child2_grandchild1),
|
||||||
|
"invisibility propagates down tree from root"
|
||||||
|
);
|
||||||
|
|
||||||
|
assert!(
|
||||||
|
is_visible(root2),
|
||||||
|
"visibility propagates down tree from root"
|
||||||
|
);
|
||||||
|
assert!(
|
||||||
|
is_visible(root2_child1),
|
||||||
|
"visibility propagates down tree from root"
|
||||||
|
);
|
||||||
|
assert!(
|
||||||
|
!is_visible(root2_child2),
|
||||||
|
"visibility propagates down tree from root, but local invisibility is preserved"
|
||||||
|
);
|
||||||
|
assert!(
|
||||||
|
is_visible(root2_child1_grandchild1),
|
||||||
|
"visibility propagates down tree from root"
|
||||||
|
);
|
||||||
|
assert!(
|
||||||
|
!is_visible(root2_child2_grandchild1),
|
||||||
|
"child's invisibility propagates down to grandchild"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -25,6 +25,7 @@ bevy_utils = { path = "../bevy_utils", version = "0.8.0-dev" }
|
|||||||
|
|
||||||
# other
|
# other
|
||||||
bytemuck = { version = "1.5", features = ["derive"] }
|
bytemuck = { version = "1.5", features = ["derive"] }
|
||||||
|
fixedbitset = "0.4"
|
||||||
guillotiere = "0.6.0"
|
guillotiere = "0.6.0"
|
||||||
thiserror = "1.0"
|
thiserror = "1.0"
|
||||||
rectangle-pack = "0.4"
|
rectangle-pack = "0.4"
|
||||||
|
@ -6,7 +6,7 @@ use bevy_asset::Handle;
|
|||||||
use bevy_ecs::bundle::Bundle;
|
use bevy_ecs::bundle::Bundle;
|
||||||
use bevy_render::{
|
use bevy_render::{
|
||||||
texture::{Image, DEFAULT_IMAGE_HANDLE},
|
texture::{Image, DEFAULT_IMAGE_HANDLE},
|
||||||
view::Visibility,
|
view::{ComputedVisibility, Visibility},
|
||||||
};
|
};
|
||||||
use bevy_transform::components::{GlobalTransform, Transform};
|
use bevy_transform::components::{GlobalTransform, Transform};
|
||||||
|
|
||||||
@ -18,6 +18,8 @@ pub struct SpriteBundle {
|
|||||||
pub texture: Handle<Image>,
|
pub texture: Handle<Image>,
|
||||||
/// User indication of whether an entity is visible
|
/// User indication of whether an entity is visible
|
||||||
pub visibility: Visibility,
|
pub visibility: Visibility,
|
||||||
|
/// Algorithmically-computed indication of whether an entity is visible and should be extracted for rendering
|
||||||
|
pub computed_visibility: ComputedVisibility,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for SpriteBundle {
|
impl Default for SpriteBundle {
|
||||||
@ -28,6 +30,7 @@ impl Default for SpriteBundle {
|
|||||||
global_transform: Default::default(),
|
global_transform: Default::default(),
|
||||||
texture: DEFAULT_IMAGE_HANDLE.typed(),
|
texture: DEFAULT_IMAGE_HANDLE.typed(),
|
||||||
visibility: Default::default(),
|
visibility: Default::default(),
|
||||||
|
computed_visibility: Default::default(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -44,4 +47,6 @@ pub struct SpriteSheetBundle {
|
|||||||
pub global_transform: GlobalTransform,
|
pub global_transform: GlobalTransform,
|
||||||
/// User indication of whether an entity is visible
|
/// User indication of whether an entity is visible
|
||||||
pub visibility: Visibility,
|
pub visibility: Visibility,
|
||||||
|
/// Algorithmically-computed indication of whether an entity is visible and should be extracted for rendering
|
||||||
|
pub computed_visibility: ComputedVisibility,
|
||||||
}
|
}
|
||||||
|
@ -128,7 +128,7 @@ pub fn extract_mesh2d(
|
|||||||
) {
|
) {
|
||||||
let mut values = Vec::with_capacity(*previous_len);
|
let mut values = Vec::with_capacity(*previous_len);
|
||||||
for (entity, computed_visibility, transform, handle) in query.iter() {
|
for (entity, computed_visibility, transform, handle) in query.iter() {
|
||||||
if !computed_visibility.is_visible {
|
if !computed_visibility.is_visible() {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
let transform = transform.compute_matrix();
|
let transform = transform.compute_matrix();
|
||||||
|
@ -22,7 +22,9 @@ use bevy_render::{
|
|||||||
render_resource::*,
|
render_resource::*,
|
||||||
renderer::{RenderDevice, RenderQueue},
|
renderer::{RenderDevice, RenderQueue},
|
||||||
texture::{BevyDefault, Image},
|
texture::{BevyDefault, Image},
|
||||||
view::{Msaa, ViewUniform, ViewUniformOffset, ViewUniforms, Visibility},
|
view::{
|
||||||
|
ComputedVisibility, Msaa, ViewUniform, ViewUniformOffset, ViewUniforms, VisibleEntities,
|
||||||
|
},
|
||||||
Extract,
|
Extract,
|
||||||
};
|
};
|
||||||
use bevy_transform::components::GlobalTransform;
|
use bevy_transform::components::GlobalTransform;
|
||||||
@ -30,6 +32,7 @@ use bevy_utils::FloatOrd;
|
|||||||
use bevy_utils::HashMap;
|
use bevy_utils::HashMap;
|
||||||
use bytemuck::{Pod, Zeroable};
|
use bytemuck::{Pod, Zeroable};
|
||||||
use copyless::VecHelper;
|
use copyless::VecHelper;
|
||||||
|
use fixedbitset::FixedBitSet;
|
||||||
|
|
||||||
pub struct SpritePipeline {
|
pub struct SpritePipeline {
|
||||||
view_layout: BindGroupLayout,
|
view_layout: BindGroupLayout,
|
||||||
@ -172,6 +175,7 @@ impl SpecializedRenderPipeline for SpritePipeline {
|
|||||||
|
|
||||||
#[derive(Component, Clone, Copy)]
|
#[derive(Component, Clone, Copy)]
|
||||||
pub struct ExtractedSprite {
|
pub struct ExtractedSprite {
|
||||||
|
pub entity: Entity,
|
||||||
pub transform: GlobalTransform,
|
pub transform: GlobalTransform,
|
||||||
pub color: Color,
|
pub color: Color,
|
||||||
/// Select an area of the texture
|
/// Select an area of the texture
|
||||||
@ -222,10 +226,19 @@ pub fn extract_sprite_events(
|
|||||||
pub fn extract_sprites(
|
pub fn extract_sprites(
|
||||||
mut extracted_sprites: ResMut<ExtractedSprites>,
|
mut extracted_sprites: ResMut<ExtractedSprites>,
|
||||||
texture_atlases: Extract<Res<Assets<TextureAtlas>>>,
|
texture_atlases: Extract<Res<Assets<TextureAtlas>>>,
|
||||||
sprite_query: Extract<Query<(&Visibility, &Sprite, &GlobalTransform, &Handle<Image>)>>,
|
sprite_query: Extract<
|
||||||
|
Query<(
|
||||||
|
Entity,
|
||||||
|
&ComputedVisibility,
|
||||||
|
&Sprite,
|
||||||
|
&GlobalTransform,
|
||||||
|
&Handle<Image>,
|
||||||
|
)>,
|
||||||
|
>,
|
||||||
atlas_query: Extract<
|
atlas_query: Extract<
|
||||||
Query<(
|
Query<(
|
||||||
&Visibility,
|
Entity,
|
||||||
|
&ComputedVisibility,
|
||||||
&TextureAtlasSprite,
|
&TextureAtlasSprite,
|
||||||
&GlobalTransform,
|
&GlobalTransform,
|
||||||
&Handle<TextureAtlas>,
|
&Handle<TextureAtlas>,
|
||||||
@ -233,12 +246,13 @@ pub fn extract_sprites(
|
|||||||
>,
|
>,
|
||||||
) {
|
) {
|
||||||
extracted_sprites.sprites.clear();
|
extracted_sprites.sprites.clear();
|
||||||
for (visibility, sprite, transform, handle) in sprite_query.iter() {
|
for (entity, visibility, sprite, transform, handle) in sprite_query.iter() {
|
||||||
if !visibility.is_visible {
|
if !visibility.is_visible() {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
// PERF: we don't check in this function that the `Image` asset is ready, since it should be in most cases and hashing the handle is expensive
|
// PERF: we don't check in this function that the `Image` asset is ready, since it should be in most cases and hashing the handle is expensive
|
||||||
extracted_sprites.sprites.alloc().init(ExtractedSprite {
|
extracted_sprites.sprites.alloc().init(ExtractedSprite {
|
||||||
|
entity,
|
||||||
color: sprite.color,
|
color: sprite.color,
|
||||||
transform: *transform,
|
transform: *transform,
|
||||||
// Use the full texture
|
// Use the full texture
|
||||||
@ -251,13 +265,14 @@ pub fn extract_sprites(
|
|||||||
anchor: sprite.anchor.as_vec(),
|
anchor: sprite.anchor.as_vec(),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
for (visibility, atlas_sprite, transform, texture_atlas_handle) in atlas_query.iter() {
|
for (entity, visibility, atlas_sprite, transform, texture_atlas_handle) in atlas_query.iter() {
|
||||||
if !visibility.is_visible {
|
if !visibility.is_visible() {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if let Some(texture_atlas) = texture_atlases.get(texture_atlas_handle) {
|
if let Some(texture_atlas) = texture_atlases.get(texture_atlas_handle) {
|
||||||
let rect = Some(texture_atlas.textures[atlas_sprite.index as usize]);
|
let rect = Some(texture_atlas.textures[atlas_sprite.index as usize]);
|
||||||
extracted_sprites.sprites.alloc().init(ExtractedSprite {
|
extracted_sprites.sprites.alloc().init(ExtractedSprite {
|
||||||
|
entity,
|
||||||
color: atlas_sprite.color,
|
color: atlas_sprite.color,
|
||||||
transform: *transform,
|
transform: *transform,
|
||||||
// Select the area in the texture atlas
|
// Select the area in the texture atlas
|
||||||
@ -334,6 +349,7 @@ pub struct ImageBindGroups {
|
|||||||
#[allow(clippy::too_many_arguments)]
|
#[allow(clippy::too_many_arguments)]
|
||||||
pub fn queue_sprites(
|
pub fn queue_sprites(
|
||||||
mut commands: Commands,
|
mut commands: Commands,
|
||||||
|
mut view_entities: Local<FixedBitSet>,
|
||||||
draw_functions: Res<DrawFunctions<Transparent2d>>,
|
draw_functions: Res<DrawFunctions<Transparent2d>>,
|
||||||
render_device: Res<RenderDevice>,
|
render_device: Res<RenderDevice>,
|
||||||
render_queue: Res<RenderQueue>,
|
render_queue: Res<RenderQueue>,
|
||||||
@ -346,7 +362,7 @@ pub fn queue_sprites(
|
|||||||
gpu_images: Res<RenderAssets<Image>>,
|
gpu_images: Res<RenderAssets<Image>>,
|
||||||
msaa: Res<Msaa>,
|
msaa: Res<Msaa>,
|
||||||
mut extracted_sprites: ResMut<ExtractedSprites>,
|
mut extracted_sprites: ResMut<ExtractedSprites>,
|
||||||
mut views: Query<&mut RenderPhase<Transparent2d>>,
|
mut views: Query<(&VisibleEntities, &mut RenderPhase<Transparent2d>)>,
|
||||||
events: Res<SpriteAssetEvents>,
|
events: Res<SpriteAssetEvents>,
|
||||||
) {
|
) {
|
||||||
// If an image has changed, the GpuImage has (probably) changed
|
// If an image has changed, the GpuImage has (probably) changed
|
||||||
@ -388,14 +404,9 @@ pub fn queue_sprites(
|
|||||||
let mut index = 0;
|
let mut index = 0;
|
||||||
let mut colored_index = 0;
|
let mut colored_index = 0;
|
||||||
|
|
||||||
// FIXME: VisibleEntities is ignored
|
|
||||||
for mut transparent_phase in &mut views {
|
|
||||||
let extracted_sprites = &mut extracted_sprites.sprites;
|
let extracted_sprites = &mut extracted_sprites.sprites;
|
||||||
let image_bind_groups = &mut *image_bind_groups;
|
|
||||||
|
|
||||||
transparent_phase.items.reserve(extracted_sprites.len());
|
|
||||||
|
|
||||||
// Sort sprites by z for correct transparency and then by handle to improve batching
|
// Sort sprites by z for correct transparency and then by handle to improve batching
|
||||||
|
// NOTE: This can be done independent of views by reasonably assuming that all 2D views look along the negative-z axis in world space
|
||||||
extracted_sprites.sort_unstable_by(|a, b| {
|
extracted_sprites.sort_unstable_by(|a, b| {
|
||||||
match a
|
match a
|
||||||
.transform
|
.transform
|
||||||
@ -407,6 +418,12 @@ pub fn queue_sprites(
|
|||||||
Some(other) => other,
|
Some(other) => other,
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
let image_bind_groups = &mut *image_bind_groups;
|
||||||
|
|
||||||
|
for (visible_entities, mut transparent_phase) in &mut views {
|
||||||
|
view_entities.clear();
|
||||||
|
view_entities.extend(visible_entities.entities.iter().map(|e| e.id() as usize));
|
||||||
|
transparent_phase.items.reserve(extracted_sprites.len());
|
||||||
|
|
||||||
// Impossible starting values that will be replaced on the first iteration
|
// Impossible starting values that will be replaced on the first iteration
|
||||||
let mut current_batch = SpriteBatch {
|
let mut current_batch = SpriteBatch {
|
||||||
@ -420,7 +437,10 @@ pub fn queue_sprites(
|
|||||||
// Compatible items share the same entity.
|
// Compatible items share the same entity.
|
||||||
// Batches are merged later (in `batch_phase_system()`), so that they can be interrupted
|
// Batches are merged later (in `batch_phase_system()`), so that they can be interrupted
|
||||||
// by any other phase item (and they can interrupt other items from batching).
|
// by any other phase item (and they can interrupt other items from batching).
|
||||||
for extracted_sprite in extracted_sprites {
|
for extracted_sprite in extracted_sprites.iter() {
|
||||||
|
if !view_entities.contains(extracted_sprite.entity.id() as usize) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
let new_batch = SpriteBatch {
|
let new_batch = SpriteBatch {
|
||||||
image_handle_id: extracted_sprite.image_handle_id,
|
image_handle_id: extracted_sprite.image_handle_id,
|
||||||
colored: extracted_sprite.color != Color::WHITE,
|
colored: extracted_sprite.color != Color::WHITE,
|
||||||
|
@ -10,7 +10,11 @@ use bevy_ecs::{
|
|||||||
};
|
};
|
||||||
use bevy_math::{Vec2, Vec3};
|
use bevy_math::{Vec2, Vec3};
|
||||||
use bevy_reflect::Reflect;
|
use bevy_reflect::Reflect;
|
||||||
use bevy_render::{texture::Image, view::Visibility, Extract};
|
use bevy_render::{
|
||||||
|
texture::Image,
|
||||||
|
view::{ComputedVisibility, Visibility},
|
||||||
|
Extract,
|
||||||
|
};
|
||||||
use bevy_sprite::{Anchor, ExtractedSprite, ExtractedSprites, TextureAtlas};
|
use bevy_sprite::{Anchor, ExtractedSprite, ExtractedSprites, TextureAtlas};
|
||||||
use bevy_transform::prelude::{GlobalTransform, Transform};
|
use bevy_transform::prelude::{GlobalTransform, Transform};
|
||||||
use bevy_utils::HashSet;
|
use bevy_utils::HashSet;
|
||||||
@ -58,6 +62,7 @@ pub struct Text2dBundle {
|
|||||||
pub text_2d_size: Text2dSize,
|
pub text_2d_size: Text2dSize,
|
||||||
pub text_2d_bounds: Text2dBounds,
|
pub text_2d_bounds: Text2dBounds,
|
||||||
pub visibility: Visibility,
|
pub visibility: Visibility,
|
||||||
|
pub computed_visibility: ComputedVisibility,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn extract_text2d_sprite(
|
pub fn extract_text2d_sprite(
|
||||||
@ -65,11 +70,20 @@ pub fn extract_text2d_sprite(
|
|||||||
texture_atlases: Extract<Res<Assets<TextureAtlas>>>,
|
texture_atlases: Extract<Res<Assets<TextureAtlas>>>,
|
||||||
text_pipeline: Extract<Res<DefaultTextPipeline>>,
|
text_pipeline: Extract<Res<DefaultTextPipeline>>,
|
||||||
windows: Extract<Res<Windows>>,
|
windows: Extract<Res<Windows>>,
|
||||||
text2d_query: Extract<Query<(Entity, &Visibility, &Text, &GlobalTransform, &Text2dSize)>>,
|
text2d_query: Extract<
|
||||||
|
Query<(
|
||||||
|
Entity,
|
||||||
|
&ComputedVisibility,
|
||||||
|
&Text,
|
||||||
|
&GlobalTransform,
|
||||||
|
&Text2dSize,
|
||||||
|
)>,
|
||||||
|
>,
|
||||||
) {
|
) {
|
||||||
let scale_factor = windows.scale_factor(WindowId::primary()) as f32;
|
let scale_factor = windows.scale_factor(WindowId::primary()) as f32;
|
||||||
for (entity, visibility, text, transform, calculated_size) in text2d_query.iter() {
|
|
||||||
if !visibility.is_visible {
|
for (entity, computed_visibility, text, transform, calculated_size) in text2d_query.iter() {
|
||||||
|
if !computed_visibility.is_visible() {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
let (width, height) = (calculated_size.size.x, calculated_size.size.y);
|
let (width, height) = (calculated_size.size.x, calculated_size.size.y);
|
||||||
@ -108,6 +122,7 @@ pub fn extract_text2d_sprite(
|
|||||||
let transform = text_transform.mul_transform(glyph_transform);
|
let transform = text_transform.mul_transform(glyph_transform);
|
||||||
|
|
||||||
extracted_sprites.sprites.push(ExtractedSprite {
|
extracted_sprites.sprites.push(ExtractedSprite {
|
||||||
|
entity,
|
||||||
transform,
|
transform,
|
||||||
color,
|
color,
|
||||||
rect,
|
rect,
|
||||||
|
@ -9,7 +9,10 @@ use bevy_ecs::{
|
|||||||
prelude::{Component, With},
|
prelude::{Component, With},
|
||||||
query::QueryItem,
|
query::QueryItem,
|
||||||
};
|
};
|
||||||
use bevy_render::{camera::Camera, extract_component::ExtractComponent, view::Visibility};
|
use bevy_render::{
|
||||||
|
camera::Camera, extract_component::ExtractComponent, prelude::ComputedVisibility,
|
||||||
|
view::Visibility,
|
||||||
|
};
|
||||||
use bevy_text::Text;
|
use bevy_text::Text;
|
||||||
use bevy_transform::prelude::{GlobalTransform, Transform};
|
use bevy_transform::prelude::{GlobalTransform, Transform};
|
||||||
|
|
||||||
@ -32,6 +35,8 @@ pub struct NodeBundle {
|
|||||||
pub global_transform: GlobalTransform,
|
pub global_transform: GlobalTransform,
|
||||||
/// Describes the visibility properties of the node
|
/// Describes the visibility properties of the node
|
||||||
pub visibility: Visibility,
|
pub visibility: Visibility,
|
||||||
|
/// Algorithmically-computed indication of whether an entity is visible and should be extracted for rendering
|
||||||
|
pub computed_visibility: ComputedVisibility,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A UI node that is an image
|
/// A UI node that is an image
|
||||||
@ -57,6 +62,8 @@ pub struct ImageBundle {
|
|||||||
pub global_transform: GlobalTransform,
|
pub global_transform: GlobalTransform,
|
||||||
/// Describes the visibility properties of the node
|
/// Describes the visibility properties of the node
|
||||||
pub visibility: Visibility,
|
pub visibility: Visibility,
|
||||||
|
/// Algorithmically-computed indication of whether an entity is visible and should be extracted for rendering
|
||||||
|
pub computed_visibility: ComputedVisibility,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A UI node that is text
|
/// A UI node that is text
|
||||||
@ -78,6 +85,8 @@ pub struct TextBundle {
|
|||||||
pub global_transform: GlobalTransform,
|
pub global_transform: GlobalTransform,
|
||||||
/// Describes the visibility properties of the node
|
/// Describes the visibility properties of the node
|
||||||
pub visibility: Visibility,
|
pub visibility: Visibility,
|
||||||
|
/// Algorithmically-computed indication of whether an entity is visible and should be extracted for rendering
|
||||||
|
pub computed_visibility: ComputedVisibility,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for TextBundle {
|
impl Default for TextBundle {
|
||||||
@ -91,6 +100,7 @@ impl Default for TextBundle {
|
|||||||
transform: Default::default(),
|
transform: Default::default(),
|
||||||
global_transform: Default::default(),
|
global_transform: Default::default(),
|
||||||
visibility: Default::default(),
|
visibility: Default::default(),
|
||||||
|
computed_visibility: Default::default(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -118,6 +128,8 @@ pub struct ButtonBundle {
|
|||||||
pub global_transform: GlobalTransform,
|
pub global_transform: GlobalTransform,
|
||||||
/// Describes the visibility properties of the node
|
/// Describes the visibility properties of the node
|
||||||
pub visibility: Visibility,
|
pub visibility: Visibility,
|
||||||
|
/// Algorithmically-computed indication of whether an entity is visible and should be extracted for rendering
|
||||||
|
pub computed_visibility: ComputedVisibility,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for ButtonBundle {
|
impl Default for ButtonBundle {
|
||||||
@ -133,6 +145,7 @@ impl Default for ButtonBundle {
|
|||||||
transform: Default::default(),
|
transform: Default::default(),
|
||||||
global_transform: Default::default(),
|
global_transform: Default::default(),
|
||||||
visibility: Default::default(),
|
visibility: Default::default(),
|
||||||
|
computed_visibility: Default::default(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -20,7 +20,7 @@ use bevy_render::{
|
|||||||
render_resource::*,
|
render_resource::*,
|
||||||
renderer::{RenderDevice, RenderQueue},
|
renderer::{RenderDevice, RenderQueue},
|
||||||
texture::Image,
|
texture::Image,
|
||||||
view::{ExtractedView, ViewUniforms, Visibility},
|
view::{ComputedVisibility, ExtractedView, ViewUniforms},
|
||||||
Extract, RenderApp, RenderStage,
|
Extract, RenderApp, RenderStage,
|
||||||
};
|
};
|
||||||
use bevy_sprite::{Rect, SpriteAssetEvents, TextureAtlas};
|
use bevy_sprite::{Rect, SpriteAssetEvents, TextureAtlas};
|
||||||
@ -182,14 +182,14 @@ pub fn extract_uinodes(
|
|||||||
&GlobalTransform,
|
&GlobalTransform,
|
||||||
&UiColor,
|
&UiColor,
|
||||||
&UiImage,
|
&UiImage,
|
||||||
&Visibility,
|
&ComputedVisibility,
|
||||||
Option<&CalculatedClip>,
|
Option<&CalculatedClip>,
|
||||||
)>,
|
)>,
|
||||||
>,
|
>,
|
||||||
) {
|
) {
|
||||||
extracted_uinodes.uinodes.clear();
|
extracted_uinodes.uinodes.clear();
|
||||||
for (uinode, transform, color, image, visibility, clip) in uinode_query.iter() {
|
for (uinode, transform, color, image, visibility, clip) in uinode_query.iter() {
|
||||||
if !visibility.is_visible {
|
if !visibility.is_visible() {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
let image = image.0.clone_weak();
|
let image = image.0.clone_weak();
|
||||||
@ -277,14 +277,14 @@ pub fn extract_text_uinodes(
|
|||||||
&Node,
|
&Node,
|
||||||
&GlobalTransform,
|
&GlobalTransform,
|
||||||
&Text,
|
&Text,
|
||||||
&Visibility,
|
&ComputedVisibility,
|
||||||
Option<&CalculatedClip>,
|
Option<&CalculatedClip>,
|
||||||
)>,
|
)>,
|
||||||
>,
|
>,
|
||||||
) {
|
) {
|
||||||
let scale_factor = windows.scale_factor(WindowId::primary()) as f32;
|
let scale_factor = windows.scale_factor(WindowId::primary()) as f32;
|
||||||
for (entity, uinode, transform, text, visibility, clip) in uinode_query.iter() {
|
for (entity, uinode, transform, text, visibility, clip) in uinode_query.iter() {
|
||||||
if !visibility.is_visible {
|
if !visibility.is_visible() {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
// Skip if size is set to zero (e.g. when a parent is set to `Display::None`)
|
// Skip if size is set to zero (e.g. when a parent is set to `Display::None`)
|
||||||
|
@ -292,7 +292,7 @@ pub fn extract_colored_mesh2d(
|
|||||||
) {
|
) {
|
||||||
let mut values = Vec::with_capacity(*previous_len);
|
let mut values = Vec::with_capacity(*previous_len);
|
||||||
for (entity, computed_visibility) in query.iter() {
|
for (entity, computed_visibility) in query.iter() {
|
||||||
if !computed_visibility.is_visible {
|
if !computed_visibility.is_visible() {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
values.push((entity, (ColoredMesh2d,)));
|
values.push((entity, (ColoredMesh2d,)));
|
||||||
|
@ -173,7 +173,7 @@ fn print_mesh_count(
|
|||||||
info!(
|
info!(
|
||||||
"Meshes: {} - Visible Meshes {}",
|
"Meshes: {} - Visible Meshes {}",
|
||||||
sprites.iter().len(),
|
sprites.iter().len(),
|
||||||
sprites.iter().filter(|(_, cv)| cv.is_visible).count(),
|
sprites.iter().filter(|(_, cv)| cv.is_visible()).count(),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user