Skip to content

VisibilityRange dithering feature is broken #19783

Open
@Greatness7

Description

@Greatness7

Bevy version

The problem occurs in both 0.16 release and current main. I did not test older versions.

System/Adapter Info:

SystemInfo { os: "Windows 11 Pro", kernel: "26100", cpu: "Intel(R) Core(TM) i7-8700K CPU @ 3.70GHz", core_count: "6", memory: "15.9 GiB" }
AdapterInfo { name: "NVIDIA GeForce GTX 1080 Ti", vendor: 4318, device: 6918, device_type: DiscreteGpu, driver: "NVIDIA", driver_info: "576.02", backend: Vulkan }

What you did

Spawn more than one gltf model with dithering enabled (via inserting a VisibilityRange component).

What went wrong

Only the first model receives dithering.

Additional information

Interestingly, if you spawn a bunch of models very quickly, all that started spawning before the first one finished spawning will have working dithering, and all after that will not.

Video

Image

Code Example

I put together a reduced version of examples/3d/visibility_range.rs to demonstrate the problem.

It differs from the original example in that it will only spawn the gltf model when you press the F key.

Press F to spawn the first model, then wait until its visible and press F again to spawn a second copy. Only the first will receive dithering.

Restart the app and then press F key several times very quickly. All those models will receive dithering, but any more you spawn after they are finished will not.

visibility_range.rs
use std::f32::consts::PI;

use bevy::prelude::*;

use bevy_render::view::VisibilityRange;

const CAMERA_FOCAL_POINT: Vec3 = vec3(0.0, 0.3, 0.0);
const CAMERA_KEYBOARD_ZOOM_SPEED: f32 = 0.05;
const CAMERA_KEYBOARD_PAN_SPEED: f32 = 0.01;
const MIN_ZOOM_DISTANCE: f32 = 0.5;

static VISIBILITY_RANGE: VisibilityRange = VisibilityRange {
    start_margin: 0.0..0.0,
    end_margin: 0.5..4.0,
    use_aabb: false,
};

#[derive(Component, Clone, Copy)]
struct ModelRoot;

fn main() {
    App::new()
        .add_plugins(DefaultPlugins)
        .add_systems(Startup, setup)
        .add_systems(
            Update,
            (move_camera, spawn_new_instance, set_visibility_ranges),
        )
        .run();
}

fn setup(
    mut commands: Commands,
    mut meshes: ResMut<Assets<Mesh>>,
    mut materials: ResMut<Assets<StandardMaterial>>,
) {
    // Spawn a plane.

    commands.spawn((
        Mesh3d(meshes.add(Plane3d::default().mesh().size(50.0, 50.0))),
        MeshMaterial3d(materials.add(Color::srgb(0.1, 0.2, 0.1))),
    ));

    // Spawn a light.

    commands.spawn((
        DirectionalLight::default(),
        Transform::from_rotation(Quat::from_euler(EulerRot::ZYX, 0.0, PI * -0.15, PI * -0.15)),
    ));

    // Spawn a camera.

    commands.spawn((
        Camera3d::default(),
        Transform::from_xyz(0.7, 0.7, 1.0).looking_at(CAMERA_FOCAL_POINT, Vec3::Y),
    ));
}

fn move_camera(
    keyboard_input: Res<ButtonInput<KeyCode>>,
    cameras: Query<&mut Transform, With<Camera3d>>,
) {
    let (mut zoom_delta, mut theta_delta) = (0.0, 0.0);

    if keyboard_input.pressed(KeyCode::KeyW) {
        zoom_delta -= CAMERA_KEYBOARD_ZOOM_SPEED;
    } else if keyboard_input.pressed(KeyCode::KeyS) {
        zoom_delta += CAMERA_KEYBOARD_ZOOM_SPEED;
    }

    if keyboard_input.pressed(KeyCode::KeyA) {
        theta_delta -= CAMERA_KEYBOARD_PAN_SPEED;
    } else if keyboard_input.pressed(KeyCode::KeyD) {
        theta_delta += CAMERA_KEYBOARD_PAN_SPEED;
    }

    // Update the camera transform.
    for transform in cameras {
        let transform = transform.into_inner();

        let direction = transform.translation.normalize_or_zero();
        let magnitude = transform.translation.length();

        let new_direction = Mat3::from_rotation_y(theta_delta) * direction;
        let new_magnitude = (magnitude + zoom_delta).max(MIN_ZOOM_DISTANCE);

        transform.translation = new_direction * new_magnitude;
        transform.look_at(CAMERA_FOCAL_POINT, Vec3::Y);
    }
}

fn spawn_new_instance(
    mut commands: Commands,
    keyboard_input: Res<ButtonInput<KeyCode>>,
    asset_server: Res<AssetServer>,
    mut offset: Local<f32>,
) {
    if keyboard_input.just_pressed(KeyCode::KeyF) {
        commands.spawn((
            SceneRoot(asset_server.load(
                GltfAssetLabel::Scene(0).from_asset("models/FlightHelmet/FlightHelmet.gltf"),
            )),
            ModelRoot,
            Transform::from_translation(vec3(*offset, 0.0, 0.0)),
        ));
        *offset += 0.5;
    }
}

// We need to add the `VisibilityRange` components manually, as glTF currently
// has no way to specify visibility ranges. This system watches for new meshes,
// determines which `Scene` they're under, and adds the `VisibilityRange`
// component as appropriate.
fn set_visibility_ranges(
    mut commands: Commands,
    meshes: Query<Entity, Added<Mesh3d>>,
    roots: Query<&ModelRoot>,
    children: Query<&ChildOf>,
) {
    for mesh in meshes {
        for ancestor in children.iter_ancestors(mesh) {
            if roots.contains(ancestor) {
                info!("Adding visibility range to mesh: {mesh}");
                commands.entity(mesh).insert(VISIBILITY_RANGE.clone());
                break;
            }
        }
    }
}

Metadata

Metadata

Assignees

No one assigned

    Labels

    A-RenderingDrawing game state to the screenC-BugAn unexpected or incorrect behaviorS-Needs-InvestigationThis issue requires detective work to figure out what's going wrong

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions