bevyengine / bevy

A refreshingly simple data-driven game engine built in Rust
https://bevyengine.org
Apache License 2.0
34.7k stars 3.39k forks source link

Animated meshes disapear if their original position is not within the field of view #4971

Open nicopap opened 2 years ago

nicopap commented 2 years ago

Bevy version

cdb62af4bf52 (main as of 2022-06-09)

What you did

I've a giraffe where the eyes are a different mesh from the rest of the body, and when the neck moves enough, it's possible that the eyes after application of animations are so far away from their initial position that the engine thinks it's outside of frustum, while they should be within view, and doesn't render them.

Video demonstrating the bug:

https://user-images.githubusercontent.com/26321040/172806192-d2bcb442-0a93-4e0d-a841-2c19edb34506.mp4

As you can observe, the blue eyes disappear if the original eye position are not within the field of view.

The rest of the mesh (skin) also disappear when the camera view doesn't include the original mesh's position.

Additional information

This is because entity mesh AABBs are not computed based on the animation. The animation shader runs on the GPUs, while AABB filtering runs on CPU before anything is sent to the GPU. There is no way for bevy to filter meshes based on the animation position.

This seems to be something barely possible if not impossible to fix within bevy. Suggestions include:

Workaround

There are two options if you hit this issue:

aevyrie commented 2 years ago

A simple starting point might be to use a conservative bounding sphere of the longest bone chain to generate an AABB: https://gamedev.stackexchange.com/questions/43986/calculate-an-aabb-for-bone-animated-model

Shatur commented 1 year ago

This also causes raycast to work incorrectly for animated meshes (because actual mesh could be in a different location). @aevyrie is it something that should be addressed in raycast implementation or could be fixed on Bevy side?

nicopap commented 1 year ago

The problem with raycasting is double:

If you content yourself with CPU-side raycasting, you'll have to accept either ridiculous performance loss or inaccurate (too broad) collision detection.

But there is a way! This is where GPU picking is useful. GPU picking constructs a buffer with individual ID per entity, you can then read that buffer for the pixel under the cursor, and you have the picked entity!

Issue is I don't think someone has yet made a GPU picking implementation in bevy.

torsteingrindvik commented 7 months ago

As a note, I had a GPU picking impl that worked with skinned meshes as well a while ago: https://github.com/bevyengine/bevy/pull/6991

it is superseded by @IceSentry 's PR here: https://github.com/bevyengine/bevy/pull/8784

I see the most recent comment there by @mtsr is to consider having GPU picking as a pre-pass. I don't know how/when/where culling happens so I'm not sure how relevant it is for this issue.

coreh commented 3 months ago

Workaround snippet, for convenience:

use bevy::{
    prelude::*,
    render::{mesh::skinning::SkinnedMesh, view::NoFrustumCulling},
};

/// System that automatically disables frustum culling for
/// all skinned meshes, as soon as they are added to the world.
fn disable_culling_for_skinned_meshes(
    mut commands: Commands,
    skinned: Query<Entity, Added<SkinnedMesh>>,
) {
    for entity in &skinned {
        commands.entity(entity).insert(NoFrustumCulling);
    }
}

Then just add it as a system:

fn main() {
    App::new()
        .add_plugins(DefaultPlugins)
        .add_systems(Update, disable_culling_for_skinned_meshes);
}
Sirmadeira commented 3 months ago

This issue doesn't occur solely with animations. It occurs with any type of entity that has a skeleton animation. In my case scenario, I have a player following camera. And if i stop looking at the mesh boom, no rendering. Or shift the orbit in y boom half rendering.