Skip to content

Assets<Image>'s get_mut prevents the texture of a mesh using the Image as a texture to be updated #8767

@Selene-Amanita

Description

@Selene-Amanita

Bevy version

v0.10.1

What you did

Render a camera to an image, use that image as a texture of a mesh, use Assets<Image>'s get_mut on the handle of that image

What went wrong

The texture stops updating

Minimal code to reproduce

Comment/uncomment lines

use bevy::{
    prelude::*,
    render::{
        render_resource::*,
        camera::RenderTarget
    },
};

#[derive(Component)]
struct MainCamera; // Camera that is used to render to the window, not the image

fn main() {
    App::new()
        .add_plugins(DefaultPlugins)
        .add_startup_system(setup)
        .add_system(rotate_other_camera)
        // comment and uncomment this line
        //.add_system(get_mut_image)
        .run();
}

fn get_mut_image (
    mut images: ResMut<Assets<Image>>,
    camera_query: Query<&Camera, Without<MainCamera>>
) {
    let camera = camera_query.single();
    if let RenderTarget::Image(handle) = camera.target.clone() {
        images.get_mut(&handle.clone()); // This line is the culprit, you can uncomment it too
    }
}

// Turn the texture rendering camera to make the texture update
fn rotate_other_camera(
    time: Res<Time>,
    mut camera_query: Query<&mut Transform, (With<Camera>, Without<MainCamera>)>,
) {
    let mut camera_transform = camera_query.single_mut();
    camera_transform.rotate_around(Vec3::ZERO, Quat::from_axis_angle(Vec3::Y, time.delta_seconds() * 9.))
}

// This is adapted from the Bevy example to render to a texture: https://github.com/bevyengine/bevy/blob/latest/examples/3d/render_to_texture.rs
fn setup(
    mut commands: Commands,
    mut meshes: ResMut<Assets<Mesh>>,
    mut images: ResMut<Assets<Image>>,
    mut materials: ResMut<Assets<StandardMaterial>>
) {
    commands.insert_resource(AmbientLight {
        color: Color::WHITE,
        brightness: 0.3,
    });
    
    // Main camera (displayed in window)
    commands.spawn((
        Camera3dBundle {
            transform: Transform::from_xyz(0.0, 0., 20.0).looking_at(Vec3::ZERO, Vec3::Y),
            ..default()
        },
        MainCamera
    ));

    // Second camera (renders to an image, gets rotated, and the image doesn't update if get_mut_image)
    let size = Extent3d {width: 450, height: 450, ..default()};
    let mut camera_image = Image {
        texture_descriptor: TextureDescriptor {
            label: None,
            size,
            dimension: TextureDimension::D2,
            format: TextureFormat::Bgra8UnormSrgb,
            mip_level_count: 1,
            sample_count: 1,
            usage: TextureUsages::TEXTURE_BINDING
                | TextureUsages::COPY_DST
                | TextureUsages::RENDER_ATTACHMENT,
            view_formats: &[],
        },
        ..default()
    };
    camera_image.resize(size);
    let camera_image = images.add(camera_image);
    
    commands.spawn(
        Camera3dBundle {
            camera: Camera {
                order: -1,
                target: RenderTarget::Image(camera_image),
                ..default()
            },
            transform: Transform::from_xyz(0., 0., 10.),
            ..default()
        }
    );
    
    // Object the image is rendered to
    let material_handle = materials.add(StandardMaterial {
        base_color_texture: Some(camera_image.clone()),
        reflectance: 0.02,
        unlit: false,
        ..default()
    });
    commands.spawn((
        PbrBundle {
            mesh: meshes.add(Mesh::from(shape::Cube::new(5.))),
            material: material_handle,
            transform: Transform::from_xyz(0., 6., 0.),
            ..default()
        },
    ));

    // Normal object to have something to look at
    let sphere_mesh = meshes.add(Mesh::from(shape::Cube::new(5.)));
    let debug_material = materials.add(StandardMaterial {
        base_color_texture: Some(images.add(uv_debug_texture())),
        ..default()
    });
    commands.spawn(PbrBundle {
        mesh: sphere_mesh,
        material: debug_material,
        ..default()
    });
}

// This is taken from the 3d_shapes example: https://bevyengine.org/examples/3d/3d-shapes/
pub fn uv_debug_texture() -> Image {
    const TEXTURE_SIZE: usize = 8;

    let mut palette: [u8; 32] = [
        255, 102, 159, 255, 255, 159, 102, 255, 236, 255, 102, 255, 121, 255, 102, 255, 102, 255,
        198, 255, 102, 198, 255, 255, 121, 102, 255, 255, 236, 102, 255, 255,
    ];

    let mut texture_data = [0; TEXTURE_SIZE * TEXTURE_SIZE * 4];
    for y in 0..TEXTURE_SIZE {
        let offset = TEXTURE_SIZE * y * 4;
        texture_data[offset..(offset + TEXTURE_SIZE * 4)].copy_from_slice(&palette);
        palette.rotate_right(4);
    }

    Image::new_fill(
        Extent3d {
            width: TEXTURE_SIZE as u32,
            height: TEXTURE_SIZE as u32,
            depth_or_array_layers: 1,
        },
        TextureDimension::D2,
        &texture_data,
        TextureFormat::Rgba8UnormSrgb,
    )
}

Additional information

Discord threads (potentially) related:

Potential relevant system information

  • Rust version:
  • Operating System: Linux Mint MATE 1.26, x11
  • AdapterInfo { name: "Intel(R) Xe Graphics (TGL GT2)", vendor: 32902, device: 39497, device_type: IntegratedGpu, driver: "Intel open-source Mesa driver", driver_info: "Mesa 22.2.5", backend: Vulkan }

Metadata

Metadata

Assignees

No one assigned

    Labels

    A-AssetsLoad files from disk to use for things like images, models, and soundsA-RenderingDrawing game state to the screenC-BugAn unexpected or incorrect behavior

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions