#How to access image handles by enum?

1 messages · Page 1 of 1 (latest)

slender violet
#

I want to generate textures programmatically and then store them in a way so I can access them by enum, i.e. I would have an enum for RED, GREEN, etc. and in the creation of a texture bundle I want to reference the generated texture for RED for example, instead of generating new textures every time. I've been trying to use a resource hashmap like this:

struct LightTextures  {
    map: HashMap<LightColor, Handle<Image>>,
}

However, I don't understand how to get from Image to Handle<Image> - nor do I feel my whole approach is correct. I was going to do something like this:

    mut commands: Commands,
    asset_server: Res<AssetServer>,
    mut texture_handles: ResMut<LightTextures>,
) {
    let texture_red = create_texture(LightColor::RED);
    texture_handles.map.insert(LightColor::RED, texture_red);
}```

Then, in a commands.spawn() I would use the enum to refer to the texture. Am I missing something really basic? Is there a way to use the resource system to access such generated textures in an efficient way or do I really have to make my own resource hashmap? And if I do, can anyone help me find the correct way to insert and address the textures I generated?
crisp geyser
#

Something like

mut images: ResMut<Assets<Image>>,
// ...
let handle = images.add(create_texture(LightColor::RED));
craggy tulip
quiet spoke
#

Generally it's not easy to create and access an asset in the same scope, since it takes time and computational effort to build the asset and file it away in its appropriate collection. The first way I thought of doing this was based off of constant handle IDs. This assumes that the textures you want are the same each time, i.e. the red texture is always the same resolution, and there should only be one copy of it at a time, and the problem is just referencing it.

Essentially the approach is to create a constant u64 handle ID for each texture you want to have on hand, so a u64 for RED, an u64 for GREEN and so on. Then create a resource that implements FromWorld that holds a strong handle to each of these, so something like this:

const RED_IMAGE_HANDLE: HandleUntyped = HandleUntyped::weak_from_uuid(Image::type_uuid, 12345678912345);
const GREEN_IMAGE_HANDLE: HandleUntyped = HandleUntyped::weak_from_uuid(Image::type_uuid, 98765432154321);

struct PrefabImages {
  red: Handle<Image>,
  green: Handle<Image>,
}

impl FromWorld for PrefabImages {
  fn from_world(world: &mut World) -> PrefabImages {
    PrefabImages {
      red: world.get_resource_mut::<Assets<Image>>().set(RED_IMAGE_HANDLE.into(), /* insert whatever makes the `Image` here */),
      green: world.get_resource_mut::<Assets<Image>>().set(GREEN_IMAGE_HANDLE.into(), /* insert whatever makes the `Image` here */),
    }
  }
}

pub enum PrefabImageHandles {
  Red,
  Green,
}

impl PrefabImageHandles {
  fn get_handle(&self) -> Handle<Image> {
    match &self {
      Red => RED_IMAGE_HANDLE.into(),
      Green => GREEN_IMAGE_HANDLE.into(),
    }
  }
}

/* somewhere in a plugin or your app setup */
app.init_resource::<PrefabImages>()
/* -- */
quiet spoke
#

So this works by generating your images at program start, adding them to the image asset collections with specific constant IDs, and then holding strong handles to them so that bevy doesn't drop them, and then wherever you need to access the images, you can use the enum that just regurgitates the constant IDs as handles.

#

this is based around the concept of using an enum to hold the handles, but you're probably better off dropping the enum and just using the resource directly, so you also don't have to have preset IDs. it's less code maintenance. that would look like this:

struct PrefabImages {
  red: Handle<Image>,
  green: Handle<Image>,
}

impl FromWorld for PrefabImages {
  fn from_world(world: &mut World) -> PrefabImages {
    PrefabImages {
      red: world.get_resource_mut::<Assets<Image>>().add(/* insert whatever makes the `Image` here */),
      green: world.get_resource_mut::<Assets<Image>>().add(/* insert whatever makes the `Image` here */),
    }
  }
}

fn some_system(
  mut commands: Commands,
  prefabs: Res<PrefabImages>,
) {
  commands.spawn((
    SpatialBundle::default(),
    prefabs.red,
  ));
}


/* somewhere in a plugin or your app setup */
app
  .init_resource::<PrefabImages>()
  .add_systems(Update, some_system)
/* -- */

This is simpler because we've removed constraints.

slender violet
#

This looks exactly like what I was looking for. I shall give it a try and report back!

slender violet
#

@quiet spoke unfortunately this doesn't work, for two reasons:

  1. I am using the following code to generate the textures:
    let mut image = Image::new_fill(
        Extent3d {
            width: 16,
            height: 16,
            depth_or_array_layers: 1,
        },
        TextureDimension::D2,
        &vec![0; 16 * 16 * 4],
        TextureFormat::Rgba8UnormSrgb,
    );
    let color: [u8; 4];
    match light_color {
        LightColor::BLUE => color = [0, 0, 255, 255], // RGBA blue
        LightColor::GREEN => color = [0, 255, 0, 255], // RGBA green
        LightColor::RED => color = [255, 0, 0, 255],  // RGBA red
        LightColor::WHITE => color = [255, 255, 255, 255], // RGBA white
        LightColor::YELLOW => color = [255, 255, 0, 255], // RGBA yellow
    }
    image.data = (0..16 * 16).flat_map(|_| color).collect();
    image
}```

To get an image handle, I need to do something like this:

```image_handle = images.add(create_texture(LightColor::RED));```

However, I can't access ```mut images: ResMut<Assets<Image>>``` in the from_world code.

2) The second snippet also doesn't like me doing ```world.get_resource_mut::<Assets<Image>>().add()``` at all.
#

I tried to do something like this, but that makes rustc complain that the trait bound TypePath is not satisfied:

#[uuid="58b43f34-80b3-4886-b9a0-93a48bf3ae6f"]
struct PrefabImages {
    red: Handle<Image>,
    green: Handle<Image>,
    blue: Handle<Image>,
    yellow: Handle<Image>,
    white: Handle<Image>,
}


pub fn initialize_textures(
    mut images: ResMut<Assets<Image>>,
    mut image_handles: ResMut<Assets<PrefabImages>>,
) {
    image_handles.red = images.add(create_texture(LightColor::RED));
    image_handles.green = images.add(create_texture(LightColor::GREEN));
    image_handles.blue = images.add(create_texture(LightColor::BLUE));
    image_handles.yellow = images.add(create_texture(LightColor::YELLOW));
    image_handles.white = images.add(create_texture(LightColor::WHITE));
}```
slender violet
#

Update! This can be solved by adding the Reflect trait to PrefabImages, and now I can access it via the following:

image_handles: Res<PrefabImages>,
) {
  let red_handle = image_handles.red.clone();
}```
#

Many thanks to everyone's suggestions!