Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Merged by Bors] - Fixed the frustum-sphere collision and added tests #4035

Closed
wants to merge 5 commits into from

Conversation

hayashi-stl
Copy link
Contributor

Objective

Fixes #3744

Solution

The old code used the formula normal . center + d + radius <= 0 to determine if the sphere with center center and radius radius is outside the plane with normal normal and distance from origin d. This only works if normal is normalized, which is not necessarily the case. Instead, normal and d are both multiplied by some factor that radius isn't multiplied by. So the additional code multiplied radius by that factor.

@github-actions github-actions bot added the S-Needs-Triage This issue needs to be labelled label Feb 25, 2022
@james7132 james7132 added A-Rendering Drawing game state to the screen C-Bug An unexpected or incorrect behavior and removed S-Needs-Triage This issue needs to be labelled labels Feb 25, 2022
Copy link
Member

@james7132 james7132 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is just a review on the code quality itself, not the correctness of it.

if plane.normal_d.dot(sphere.center.extend(1.0)) + sphere.radius <= 0.0 {
// The formula `normal . center + d + radius <= 0` relies on `normal` being normalized,
// which is not necessarily the case.
let factor = (plane.normal_d.truncate().length_squared()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please split up this statement. It's hard to read in it's current state.

crates/bevy_render/src/primitives/mod.rs Outdated Show resolved Hide resolved
@superdump
Copy link
Contributor

I fixed this yesterday and was going to make a PR: superdump@34d134d

I was thinking of making a Plane constructor function that ensures the normal is normalised and the entire normal_d is scaled accordingly. Perhaps you could rework this PR to do that instead? I would rather have the normal be a unit normal and d define the plane according to that unit normal.

Copy link
Contributor

@superdump superdump left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've made a few suggestions for further changes for how I think the API should be after a bit more thought. Hopefully the suggested changes are all correct and there's not too much to fix elsewhere.

@@ -72,14 +72,26 @@ impl Sphere {
}
}

/// A plane defined by a normal and distance value along the normal
/// A plane defined by a normalized normal and distance value along the normal
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
/// A plane defined by a normalized normal and distance value along the normal
/// A plane defined by a unit normal and distance from the origin along the normal

@@ -72,14 +72,26 @@ impl Sphere {
}
}

/// A plane defined by a normal and distance value along the normal
/// A plane defined by a normalized normal and distance value along the normal
/// Any point p is in the plane if n.p = d
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
/// Any point p is in the plane if n.p = d
/// Any point p is in the plane if n.p + d = 0

/// Any point p is in the plane if n.p = d
/// For planes defining half-spaces such as for frusta, if n.p > d then p is on the positive side of the plane.
/// For planes defining half-spaces such as for frusta, if n.p > d then p is on the positive side (inside) of the plane.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
/// For planes defining half-spaces such as for frusta, if n.p > d then p is on the positive side (inside) of the plane.
/// For planes defining half-spaces such as for frusta, if n.p + d > 0 then p is on
/// the positive side (inside) of the plane.

/// Any point p is in the plane if n.p = d
/// For planes defining half-spaces such as for frusta, if n.p > d then p is on the positive side of the plane.
/// For planes defining half-spaces such as for frusta, if n.p > d then p is on the positive side (inside) of the plane.
#[derive(Clone, Copy, Debug, Default)]
pub struct Plane {
pub normal_d: Vec4,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
pub normal_d: Vec4,
normal_d: Vec4,

And then update other code that uses Plane to use Plane::new for construction.

Comment on lines 84 to 87
/// Constructs a `Plane` from a 4D vector whose first 3 components
/// are the normal and whose last component is d.
/// Ensures that the normal is normalized and d is scaled accordingly
/// so it represents the signed distance from the origin.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
/// Constructs a `Plane` from a 4D vector whose first 3 components
/// are the normal and whose last component is d.
/// Ensures that the normal is normalized and d is scaled accordingly
/// so it represents the signed distance from the origin.
/// Constructs a `Plane` from a 4D vector whose first 3 components
/// are the normal and whose last component is the distance along the normal
/// from the origin.
/// This constructor ensures that the normal is normalized and the distance is
/// scaled accordingly so it represents the signed distance from the origin.

/// are the normal and whose last component is d.
/// Ensures that the normal is normalized and d is scaled accordingly
/// so it represents the signed distance from the origin.
fn new(normal_d: Vec4) -> Self {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
fn new(normal_d: Vec4) -> Self {
pub fn new(normal_d: Vec4) -> Self {

Self {
normal_d: normal_d * normal_d.xyz().length_recip(),
}
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
}
}
/// `Plane` unit normal
#[inline]
pub fn normal(&self) -> Vec3 {
self.normal_d.xyz()
}
/// Signed distance from the origin along the unit normal such that n.p + d = 0 for point p in
/// the `Plane`
#[inline]
pub fn d(&self) -> f32 {
self.normal_d.w
}
/// `Plane` unit normal and signed distance from the origin such that n.p + d = 0 for point p
/// in the `Plane`
#[inline]
pub fn normal_d(&self) -> Vec4 {
self.normal_d
}

And then use these in the Frustum intersects sphere/obb functions below.

@superdump
Copy link
Contributor

Thanks for writing the tests by the way. With the suggestions, I would likely approve this and try to prioritise it being merged.

@superdump superdump requested a review from mockersf March 3, 2022 22:46
/// from the origin.
/// This constructor ensures that the normal is normalized and the distance is
/// scaled accordingly so it represents the signed distance from the origin.
pub fn new(normal_d: Vec4) -> Self {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should this one be inline too?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I suppose it could be. I haven’t tested it. I’m just using Cart’s of using online for accessors. But I don’t think we tend to use them for constructors.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it seems the constructor is used a few time during from_view_projection

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've made the suggestion. I'm leaving it as approved as it's too minor to block on imo.

/// from the origin.
/// This constructor ensures that the normal is normalized and the distance is
/// scaled accordingly so it represents the signed distance from the origin.
pub fn new(normal_d: Vec4) -> Self {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
pub fn new(normal_d: Vec4) -> Self {
#[inline]
pub fn new(normal_d: Vec4) -> Self {

@superdump superdump added the S-Ready-For-Final-Review This PR has been approved by the community. It's ready for a maintainer to consider merging it label Mar 7, 2022
@mockersf
Copy link
Member

mockersf commented Mar 8, 2022

bors r+

bors bot pushed a commit that referenced this pull request Mar 8, 2022
# Objective

Fixes #3744 

## Solution

The old code used the formula `normal . center + d + radius <= 0` to determine if the sphere with center `center` and radius `radius` is outside the plane with normal `normal` and distance from origin `d`. This only works if `normal` is normalized, which is not necessarily the case. Instead, `normal` and `d` are both multiplied by some factor that `radius` isn't multiplied by. So the additional code multiplied `radius` by that factor.
@bors bors bot changed the title Fixed the frustum-sphere collision and added tests [Merged by Bors] - Fixed the frustum-sphere collision and added tests Mar 8, 2022
@bors bors bot closed this Mar 8, 2022
@hayashi-stl hayashi-stl deleted the frustum_fix branch March 11, 2022 01:49
rparrett pushed a commit to rparrett/bevy that referenced this pull request Mar 15, 2022
# Objective

Fixes bevyengine#3744 

## Solution

The old code used the formula `normal . center + d + radius <= 0` to determine if the sphere with center `center` and radius `radius` is outside the plane with normal `normal` and distance from origin `d`. This only works if `normal` is normalized, which is not necessarily the case. Instead, `normal` and `d` are both multiplied by some factor that `radius` isn't multiplied by. So the additional code multiplied `radius` by that factor.
rparrett added a commit to rparrett/bevy that referenced this pull request Mar 21, 2022
…e#4035)"

This reverts commit 206c423.

This commit was a breaking change for bevy_inspector_egui and was
not actually consequential for typey birb
aevyrie pushed a commit to aevyrie/bevy that referenced this pull request Jun 7, 2022
# Objective

Fixes bevyengine#3744 

## Solution

The old code used the formula `normal . center + d + radius <= 0` to determine if the sphere with center `center` and radius `radius` is outside the plane with normal `normal` and distance from origin `d`. This only works if `normal` is normalized, which is not necessarily the case. Instead, `normal` and `d` are both multiplied by some factor that `radius` isn't multiplied by. So the additional code multiplied `radius` by that factor.
ItsDoot pushed a commit to ItsDoot/bevy that referenced this pull request Feb 1, 2023
# Objective

Fixes bevyengine#3744 

## Solution

The old code used the formula `normal . center + d + radius <= 0` to determine if the sphere with center `center` and radius `radius` is outside the plane with normal `normal` and distance from origin `d`. This only works if `normal` is normalized, which is not necessarily the case. Instead, `normal` and `d` are both multiplied by some factor that `radius` isn't multiplied by. So the additional code multiplied `radius` by that factor.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-Rendering Drawing game state to the screen C-Bug An unexpected or incorrect behavior S-Ready-For-Final-Review This PR has been approved by the community. It's ready for a maintainer to consider merging it
Projects
Archived in project
Status: Done
Development

Successfully merging this pull request may close these issues.

Error in frustum math.
4 participants