Skip to content

Commit

Permalink
Merge pull request #185 from NiklasEi/fix_serialization_of_standard_d…
Browse files Browse the repository at this point in the history
…ynamic_assets

Fix serialization of StandardDynamicAsset
  • Loading branch information
NiklasEi authored Jan 25, 2024
2 parents 5ac0d5e + c49733c commit c6b06f8
Show file tree
Hide file tree
Showing 2 changed files with 76 additions and 19 deletions.
1 change: 1 addition & 0 deletions bevy_asset_loader/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ anyhow = "1"
iyes_progress = { version = "0.10" }
bevy_common_assets = { version = "0.9.0", features = ["ron"] }
serde = { version = "1" }
ron = "0.8.1"
trybuild = { version = "1.0" }

[package.metadata.docs.rs]
Expand Down
94 changes: 75 additions & 19 deletions bevy_asset_loader/src/standard_dynamic_asset.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,10 @@ use bevy::sprite::TextureAtlas;

#[cfg(any(feature = "3d", feature = "2d"))]
use bevy::render::texture::{Image, ImageSampler, ImageSamplerDescriptor};
#[cfg(any(feature = "3d", feature = "2d"))]
use serde::Deserializer;

/// These asset variants can be loaded from configuration files. They will then replace
/// a dynamic asset based on their keys.
#[derive(Debug, Clone, Deserialize, Serialize)]
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq)]
#[serde(deny_unknown_fields)]
pub enum StandardDynamicAsset {
/// A dynamic asset directly loaded from a single file
Expand Down Expand Up @@ -49,7 +47,7 @@ pub enum StandardDynamicAsset {
/// Image file path
path: String,
/// Sampler
#[serde(deserialize_with = "deserialize_some", default)]
#[serde(with = "optional", skip_serializing_if = "Option::is_none", default)]
sampler: Option<ImageSamplerType>,
},
/// A dynamic standard material asset directly loaded from an image file
Expand All @@ -64,7 +62,7 @@ pub enum StandardDynamicAsset {
/// Asset file path
path: String,
/// Sampler
#[serde(deserialize_with = "deserialize_some", default)]
#[serde(with = "optional", skip_serializing_if = "Option::is_none", default)]
sampler: Option<ImageSamplerType>,
/// The image width in pixels
tile_size_x: f32,
Expand All @@ -75,34 +73,48 @@ pub enum StandardDynamicAsset {
/// Rows on the sprite sheet
rows: usize,
/// Padding between columns in pixels
#[serde(deserialize_with = "deserialize_some", default)]
#[serde(with = "optional", skip_serializing_if = "Option::is_none", default)]
padding_x: Option<f32>,
/// Padding between rows in pixels
#[serde(deserialize_with = "deserialize_some", default)]
#[serde(with = "optional", skip_serializing_if = "Option::is_none", default)]
padding_y: Option<f32>,
/// Number of pixels offset of the first tile
#[serde(deserialize_with = "deserialize_some", default)]
#[serde(with = "optional", skip_serializing_if = "Option::is_none", default)]
offset_x: Option<f32>,
/// Number of pixels offset of the first tile
#[serde(deserialize_with = "deserialize_some", default)]
#[serde(with = "optional", skip_serializing_if = "Option::is_none", default)]
offset_y: Option<f32>,
},
}

#[cfg(any(feature = "3d", feature = "2d"))]
fn deserialize_some<'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))
mod optional {
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(),
}
}
}

/// Define the image sampler to configure for an image asset
#[cfg(any(feature = "3d", feature = "2d"))]
#[derive(Debug, Clone, Deserialize, Serialize)]
#[serde(untagged)]
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq)]
pub enum ImageSamplerType {
/// See [`ImageSampler::nearest`]
Nearest,
Expand Down Expand Up @@ -292,7 +304,7 @@ impl<K: Into<String> + Sync + Send + 'static> Command for RegisterStandardDynami
///
/// These assets are loaded at the beginning of a loading state
/// and combined in [`DynamicAssets`].
#[derive(Deserialize, Serialize, Asset, TypePath)]
#[derive(Deserialize, Serialize, Asset, TypePath, PartialEq, Debug)]
pub struct StandardDynamicAssetCollection(pub HashMap<String, StandardDynamicAsset>);

impl DynamicAssetCollection for StandardDynamicAssetCollection {
Expand All @@ -302,3 +314,47 @@ impl DynamicAssetCollection for StandardDynamicAssetCollection {
}
}
}

#[cfg(test)]
#[cfg(feature = "2d")]
mod tests {
use crate::prelude::StandardDynamicAssetCollection;

#[test]
fn serialize_and_deserialize_atlas() {
let dynamic_asset_file = r#"({
"texture_atlas": TextureAtlas(
path: "images/female_adventurer_sheet.png",
sampler: Nearest,
tile_size_x: 96.0,
tile_size_y: 99.0,
columns: 8,
rows: 1,
padding_x: 42.42,
),
})"#;
serialize_and_deserialize(dynamic_asset_file);
}

#[test]
fn serialize_and_deserialize_image() {
let dynamic_asset_file = r#"({
"image": Image(
path: "images/player.png",
sampler: Linear,
),
})"#;
serialize_and_deserialize(dynamic_asset_file);
}

fn serialize_and_deserialize(dynamic_asset_file: &'static str) {
let before: StandardDynamicAssetCollection = ron::from_str(dynamic_asset_file).unwrap();

let serialized_dynamic_asset_file = ron::ser::to_string_pretty(
&before,
ron::ser::PrettyConfig::default().new_line("\n".to_string()),
)
.unwrap();
assert_eq!(dynamic_asset_file, &serialized_dynamic_asset_file);
}
}

0 comments on commit c6b06f8

Please sign in to comment.