NiklasEi / bevy_asset_loader

Bevy plugin helping with asset loading and organization
Apache License 2.0
449 stars 53 forks source link

Add Serialize derive to standard dynamic assets #178

Closed NiklasEi closed 5 months ago

NiklasEi commented 5 months ago

Resolves #177

feelingsonice commented 5 months ago

IIRC in my testing I had to remove the #[serde(untagged)] label to get this to work. Something that had to do with RON not working as intended with untagged (link). But I might be wrong if you've tested

NiklasEi commented 5 months ago

I did not test this, but having to remove untagged might be an issue. Do you have a code-available project using Serialization with bevy_asset_loader for testing?

feelingsonice commented 5 months ago

@NiklasEi yep here you go:

use bevy_asset_loader::standard_dynamic_asset::StandardDynamicAssetCollection;

fn main() {
    let x: StandardDynamicAssetCollection = ron::from_str(
        r#"({
        "texture_atlas": TextureAtlas (
            path: "images/female_adventurer_sheet.png",
            sampler: Nearest,
            tile_size_x: 96.,
            tile_size_y: 99.,
            columns: 8,
            rows: 1,
        ),
    })
    "#,
    )
    .unwrap();

    println!(
        "{}",
        ron::ser::to_string_pretty(&x, ron::ser::PrettyConfig::default()).unwrap(),
    );
}

This prints:

({
    "texture_atlas": TextureAtlas(
        path: "images/female_adventurer_sheet.png",
        sampler: Some(()),
        tile_size_x: 96.0,
        tile_size_y: 99.0,
        columns: 8,
        rows: 1,
        padding_x: None,
        padding_y: None,
        offset_x: None,
        offset_y: None,
    ),
})

Namely, sampler: Some(()),

feelingsonice commented 5 months ago

Oh I remember writing a custom serialize function for it because of the Option, but then I still had to remove the untagged

NiklasEi commented 5 months ago

Ah, that makes sense since the deserializing also has some custom logic to deserialize no value for an optional property to None and a value to Some(value). This is great for writing and reading the standard dynamic files. Do you still have the custom serialize function? https://github.com/NiklasEi/bevy_asset_loader/blob/5ac0d5e954c5f432a40acf63b52b894dddb7e916/bevy_asset_loader/src/standard_dynamic_asset.rs#L93-L100

feelingsonice commented 5 months ago

Yea found it:

#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub enum StandardDynamicAsset {
    File {
        path: String,
    },
    Folder {
        path: String,
    },
    Files {
        paths: Vec<String>,
    },
    Image {
        path: String,
        #[serde(
            with = "optional_type",
            skip_serializing_if = "Option::is_none",
            default
        )]
        sampler: Option<ImageSamplerType>,
    },
    StandardMaterial {
        path: String,
    },
    TextureAtlas {
        path: String,
        #[serde(
            with = "optional_type",
            skip_serializing_if = "Option::is_none",
            default
        )]
        sampler: Option<ImageSamplerType>,
        tile_size_x: f32,
        tile_size_y: f32,
        columns: usize,
        rows: usize,
        #[serde(
            with = "optional_type",
            skip_serializing_if = "Option::is_none",
            default
        )]
        padding_x: Option<f32>,
        #[serde(
            with = "optional_type",
            skip_serializing_if = "Option::is_none",
            default
        )]
        padding_y: Option<f32>,
        #[serde(
            with = "optional_type",
            skip_serializing_if = "Option::is_none",
            default
        )]
        offset_x: Option<f32>,
        #[serde(
            with = "optional_type",
            skip_serializing_if = "Option::is_none",
            default
        )]
        offset_y: Option<f32>,
    },
}

#[derive(Debug, Clone, Serialize, Deserialize)]
// #[serde(untagged)]
pub enum ImageSamplerType {
    Nearest,
    Linear,
}

mod optional_type {
    use serde::{Deserialize, Deserializer, Serialize, Serializer};

    pub(super) fn deserialize<'de, D, G>(deserializer: D) -> Result<Option<G>, D::Error>
    where
        D: Deserializer<'de>,
        G: Deserialize<'de>,
    {
        let opt: G = G::deserialize(deserializer)?;
        Ok(Some(opt))
    }

    pub(super) fn serialize<S, T>(value: &Option<T>, serializer: S) -> Result<S::Ok, S::Error>
    where
        T: Serialize + std::fmt::Debug,
        S: Serializer,
    {
        match value.as_ref() {
            Some(value) => value.serialize(serializer),
            None => serializer.serialize_none(),
        }
    }
}

I don't remember what happened if untagged wasn't removed.