#material #bevy #load #toml #type-erased #prelude #materialize

bevy_materialize

Load, store, and apply type-erased materials in Bevy

1 unstable release

new 0.1.0 Feb 3, 2025

#479 in Game dev

Download history 127/week @ 2025-02-02

127 downloads per month

MIT/Apache

1.5MB
825 lines

bevy_materialize

Crate for loading and applying type-erased materials in Bevy.

Built-in supported formats are json, and toml, but you can easily add more.

Usage Example (TOML)

First, add the MaterializePlugin to your App.

use bevy::prelude::*;
use bevy_materialize::prelude::*;

fn main_example() {
    App::new()
        // ...
        .add_plugins(MaterializePlugin::new(TomlMaterialDeserializer))
        // ...
        .run();
}

The API for adding to an entity is quite similar to MeshMaterial3d<...>, just with GenericMaterial3d storing a Handle<GenericMaterial> instead, which you can load from a file.

use bevy::prelude::*;
use bevy_materialize::prelude::*;

fn setup(
    mut commands: Commands,
    asset_server: Res<AssetServer>,
) {
    commands.spawn((
        Mesh3d(asset_server.add(Cuboid::from_length(1.).into())),
        GenericMaterial3d(asset_server.load("materials/example.material")),
    ));
}

assets/materials/example.material

# The type name of the material. Can either be the full path (e.g. bevy_pbr::pbr_material::StandardMaterial),
# or, if only one registered material has the name, just the name itself.
# If this field is not specified, defaults to StandardMaterial
type = "StandardMaterial"

[material]
# Asset paths are relative to the material's path.
base_color_texture = "example.png"
emissive = [0.1, 0.2, 0.5, 1.0]
alpha_mode = { Mask = 0.5 }

# Optional custom properties, these can be whatever you want.
[properties]
# This one is built-in, and sets the entity's Visibility when the material is applied.
visibility = "Hidden"
collision = true
sounds = "wood"

For simplicity, you can also load a GenericMaterial directly from an image file, which puts a StandardMaterial internally. You can change the default StandardMaterial that it uses via

use bevy::prelude::*;
use bevy_materialize::{prelude::*, load::SimpleGenericMaterialLoaderSettings};

MaterializePlugin::new(TomlMaterialDeserializer).with_simple_loader_settings(Some(SimpleGenericMaterialLoaderSettings {
    material: StandardMaterial {
        // Now it's super shiny!
        perceptual_roughness: 0.1,
        ..default()
    },
    ..default()
}));

// This would disable the image loading functionality entirely.
MaterializePlugin::new(TomlMaterialDeserializer).with_simple_loader_settings(None);

For retrieving properties from a material, the easiest way is with a GenericMaterialView, which you can get via the GenericMaterials system param.

It's not as easy as getting it from the GenericMaterial because properties need additional references to parse, such as the asset server and type registry.

use bevy::prelude::*;
use bevy_materialize::prelude::*;
use bevy_materialize::GenericMaterialError;

fn retrieve_properties_example(
    materials: GenericMaterials,
) {
    // You can also do materials.get(<asset id>) to get a view.
    for view in materials.iter() {
        // The type returned is based on the generic of the property. For example, VISIBILITY is a MaterialProperty<Visibility>.
        let _: Result<Visibility, GenericMaterialError> = view.get_property(GenericMaterial::VISIBILITY);
    
        // Like get_property(), but if anything goes wrong, returns the default value instead an error.
        let _: Visibility = view.property(GenericMaterial::VISIBILITY);
    }
}

For creating your own properties, you should make an extension trait for GenericMaterial.

use bevy_materialize::prelude::*;

pub trait MyMaterialPropertiesExt {
    const MY_PROPERTY: MaterialProperty<f32> = MaterialProperty::new("my_property", || 5.);
}
impl MyMaterialPropertiesExt for GenericMaterial {}

When creating your own custom materials, all you have to do is register them in your app like so.

App::new()
    // ...
    .register_generic_material::<YourMaterial>()

This will also register the type if it hasn't been registered already.

You can also register a shorthand if your material's name is very long (like if it's an ExtendedMaterial<...>).

App::new()
    // ...
    .register_generic_material_shorthand::<YourMaterialWithALongName>("YourMaterial")

This will allow you to put the shorthand in your file's type field instead of the type name.

Supported Bevy Versions

Bevy bevy_materialize
0.15 0.1

Dependencies

~42–74MB
~1.5M SLoC