diff --git a/crates/bevy_render/src/view/visibility/range.rs b/crates/bevy_render/src/view/visibility/range.rs index 66ff895421..9ea69db7f9 100644 --- a/crates/bevy_render/src/view/visibility/range.rs +++ b/crates/bevy_render/src/view/visibility/range.rs @@ -112,7 +112,7 @@ impl Plugin for VisibilityRangePlugin { /// that the `end_margin` of a higher LOD is always identical to the /// `start_margin` of the next lower LOD; this is important for the crossfade /// effect to function properly. -#[derive(Component, Clone, PartialEq, Reflect)] +#[derive(Component, Clone, PartialEq, Default, Reflect)] #[reflect(Component, PartialEq, Hash)] pub struct VisibilityRange { /// The range of distances, in world units, between which this entity will @@ -132,6 +132,20 @@ pub struct VisibilityRange { /// /// `end_margin.start` must be greater than or equal to `start_margin.end`. pub end_margin: Range, + + /// If set to true, Bevy will use the center of the axis-aligned bounding + /// box ([`Aabb`]) as the position of the mesh for the purposes of + /// visibility range computation. + /// + /// Otherwise, if this field is set to false, Bevy will use the origin of + /// the mesh as the mesh's position. + /// + /// Usually you will want to leave this set to false, because different LODs + /// may have different AABBs, and smooth crossfades between LOD levels + /// require that all LODs of a mesh be at *precisely* the same position. If + /// you aren't using crossfading, however, and your meshes aren't centered + /// around their origins, then this flag may be useful. + pub use_aabb: bool, } impl Eq for VisibilityRange {} @@ -161,6 +175,7 @@ impl VisibilityRange { Self { start_margin: start..start, end_margin: end..end, + use_aabb: false, } } @@ -391,14 +406,17 @@ pub fn check_visibility_ranges( for (entity, entity_transform, maybe_model_aabb, visibility_range) in entity_query.iter_mut() { let mut visibility = 0; for (view_index, &(_, view_position)) in views.iter().enumerate() { - let model_pos = if let Some(model_aabb) = maybe_model_aabb { - let world_from_local = entity_transform.affine(); - world_from_local.transform_point3a(model_aabb.center) - } else { - entity_transform.translation_vec3a() + // If instructed to use the AABB and the model has one, use its + // center as the model position. Otherwise, use the model's + // translation. + let model_position = match (visibility_range.use_aabb, maybe_model_aabb) { + (true, Some(model_aabb)) => entity_transform + .affine() + .transform_point3a(model_aabb.center), + _ => entity_transform.translation_vec3a(), }; - if visibility_range.is_visible_at_all((view_position - model_pos).length()) { + if visibility_range.is_visible_at_all((view_position - model_position).length()) { visibility |= 1 << view_index; } } diff --git a/examples/3d/visibility_range.rs b/examples/3d/visibility_range.rs index 64c368655f..6fdbdf13a9 100644 --- a/examples/3d/visibility_range.rs +++ b/examples/3d/visibility_range.rs @@ -26,10 +26,12 @@ const MIN_ZOOM_DISTANCE: f32 = 0.5; static NORMAL_VISIBILITY_RANGE_HIGH_POLY: VisibilityRange = VisibilityRange { start_margin: 0.0..0.0, end_margin: 3.0..4.0, + use_aabb: false, }; static NORMAL_VISIBILITY_RANGE_LOW_POLY: VisibilityRange = VisibilityRange { start_margin: 3.0..4.0, end_margin: 8.0..9.0, + use_aabb: false, }; // A visibility model that we use to always show a model (until the camera is so @@ -37,12 +39,14 @@ static NORMAL_VISIBILITY_RANGE_LOW_POLY: VisibilityRange = VisibilityRange { static SINGLE_MODEL_VISIBILITY_RANGE: VisibilityRange = VisibilityRange { start_margin: 0.0..0.0, end_margin: 8.0..9.0, + use_aabb: false, }; // A visibility range that we use to completely hide a model. static INVISIBLE_VISIBILITY_RANGE: VisibilityRange = VisibilityRange { start_margin: 0.0..0.0, end_margin: 0.0..0.0, + use_aabb: false, }; // Allows us to identify the main model.