Skip to content

Commit

Permalink
Only use the AABB center for mesh visibility range testing if specifi…
Browse files Browse the repository at this point in the history
…ed. (bevyengine#16468)

PR bevyengine#15164 made Bevy consider the center of the mesh to be the center of
the axis-aligned bounding box (AABB). Unfortunately, this breaks
crossfading in many cases. LODs may have different AABBs and so the
center of the AABB may differ for different LODs of the same mesh. The
crossfading, however, relies on all LODs having *precisely* the same
position.

To address this problem, this PR adds a new field, `use_aabb`, to
`VisibilityRange`, which makes the AABB center point behavior opt-in.

@BenjaminBrienen first noticed this issue when reviewing PR bevyengine#16286. That
PR contains a video showing the effects of this regression on the
`visibility_range` example. This commit fixes that example.

## Migration Guide

* The `VisibilityRange` component now has an extra field, `use_aabb`.
Generally, you can safely set it to false.
  • Loading branch information
pcwalton authored and ecoskey committed Dec 2, 2024
1 parent 9ab470b commit 0a89fa0
Show file tree
Hide file tree
Showing 2 changed files with 29 additions and 7 deletions.
32 changes: 25 additions & 7 deletions crates/bevy_render/src/view/visibility/range.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -132,6 +132,20 @@ pub struct VisibilityRange {
///
/// `end_margin.start` must be greater than or equal to `start_margin.end`.
pub end_margin: Range<f32>,

/// 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 {}
Expand Down Expand Up @@ -161,6 +175,7 @@ impl VisibilityRange {
Self {
start_margin: start..start,
end_margin: end..end,
use_aabb: false,
}
}

Expand Down Expand Up @@ -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;
}
}
Expand Down
4 changes: 4 additions & 0 deletions examples/3d/visibility_range.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,23 +26,27 @@ 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
// far zoomed out that it's culled entirely).
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.
Expand Down

0 comments on commit 0a89fa0

Please sign in to comment.