diff --git a/assets/example_mapping.json b/assets/example_mapping.json new file mode 100644 index 0000000..73878a7 --- /dev/null +++ b/assets/example_mapping.json @@ -0,0 +1,54 @@ +[ + ["eyeBlinkLeft", "EyeBlinkLeft"], + ["eyeLookDownLeft", "EyeLookDownLeft"], + ["eyeLookInLeft", "EyeLookInLeft"], + ["eyeLookOutLeft", "EyeLookOutLeft"], + ["eyeLookUpLeft", "EyeLookUpLeft"], + ["eyeSquintLeft", "EyeSquintLeft"], + ["eyeWideLeft", "EyeWideLeft"], + ["eyeBlinkRight", "EyeBlinkRight"], + ["eyeLookDownRight", "EyeLookDownRight"], + ["eyeLookInRight", "EyeLookInRight"], + ["eyeLookOutRight", "EyeLookOutRight"], + ["eyeLookUpRight", "EyeLookUpRight"], + ["eyeSquintRight", "EyeSquintRight"], + ["eyeWideRight", "EyeWideRight"], + ["jawForward", "JawForward"], + ["jawLeft", "JawLeft"], + ["jawRight", "JawRight"], + ["jawOpen", "JawOpen"], + ["mouthClose", "MouthClose"], + ["mouthFunnel", "MouthFunnel"], + ["mouthPucker", "MouthPucker"], + ["mouthLeft", "MouthLeft"], + ["mouthRight", "MouthRight"], + ["mouthSmileLeft", "MouthSmileLeft"], + ["mouthSmileRight", "MouthSmileRight"], + ["mouthFrownLeft", "MouthFrownLeft"], + ["mouthFrownRight", "MouthFrownRight"], + ["mouthDimpleLeft", "MouthDimpleLeft"], + ["mouthDimpleRight", "MouthDimpleRight"], + ["mouthStretchLeft", "MouthStretchLeft"], + ["mouthStretchRight", "MouthStretchRight"], + ["mouthRollLower", "MouthRollLower"], + ["mouthRollUpper", "MouthRollUpper"], + ["mouthShrugLower", "MouthShrugLower"], + ["mouthShrugUpper", "MouthShrugUpper"], + ["mouthPressLeft", "MouthPressLeft"], + ["mouthPressRight", "MouthPressRight"], + ["mouthLowerDownLeft", "MouthLowerDownLeft"], + ["mouthLowerDownRight", "MouthLowerDownRight"], + ["mouthUpperUpLeft", "MouthUpperUpLeft"], + ["mouthUpperUpRight", "MouthUpperUpRight"], + ["browDownLeft", "BrowDownLeft"], + ["browDownRight", "BrowDownRight"], + ["browInnerUp", "BrowInnerUp"], + ["browOuterUpLeft", "BrowOuterUpLeft"], + ["browOuterUpRight", "BrowOuterUpRight"], + ["cheekPuff", "CheekPuff"], + ["cheekSquintLeft", "CheekSquintLeft"], + ["cheekSquintRight", "CheekSquintRight"], + ["noseSneerLeft", "NoseSneerLeft"], + ["noseSneerRight", "NoseSneerRight"], + ["tongueOut", "TongueOut"] +] \ No newline at end of file diff --git a/assets/fake_test.morph_anim.json b/assets/fake_test.morph_anim.json index 46b0f81..c018d09 100644 --- a/assets/fake_test.morph_anim.json +++ b/assets/fake_test.morph_anim.json @@ -1,5 +1,6 @@ { "csv": "./fake_test.csv", "root_name": "pascal_Head", - "fps": 4 + "fps": 4, + "mapping": "example_mapping.json" } \ No newline at end of file diff --git a/assets/recorded_test.morph_anim.json b/assets/recorded_test.morph_anim.json new file mode 100644 index 0000000..e7f8551 --- /dev/null +++ b/assets/recorded_test.morph_anim.json @@ -0,0 +1,6 @@ +{ + "csv": "./20240205_shapekey_BevyImplementationFaceCap_5/shapekey_BevyImplementationFaceCap_5_ScottiPhone_cal.morph_anim.csv", + "root_name": "pascal_Head", + "fps": 60, + "mapping": "example_mapping.json" +} \ No newline at end of file diff --git a/examples/simple_example.rs b/examples/simple_example.rs index fe7ee22..3006397 100644 --- a/examples/simple_example.rs +++ b/examples/simple_example.rs @@ -57,7 +57,7 @@ fn setup_animation( mut players: Query, Without)>, ) { for entity in &mut players { - let handle = asset_server.load("fake_test.morph_anim.json"); + let handle = asset_server.load("recorded_test.morph_anim.json"); let mut player = AnimationPlayer::default(); player.play(handle.clone()).repeat(); diff --git a/src/lib.rs b/src/lib.rs index 45b8904..397c91d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -278,8 +278,12 @@ struct ClipInfo { pub csv: PathBuf, pub root_name: String, pub fps: Option, + pub mapping: Option, } +#[derive(Serialize, Deserialize, Debug, Clone)] +struct CSVAnimationMapping(Vec<(String, String)>); + #[derive(Default)] pub struct CSVAnimationLoader; @@ -291,6 +295,8 @@ pub enum CSVAnimationLoaderError { ClipInfoParseError(serde_json::Error), CSVReadBytesError(ReadAssetBytesError), CSVStringError(FromUtf8Error), + MappingReadBytesError(ReadAssetBytesError), + MappingParseError(serde_json::Error), } impl std::fmt::Display for CSVAnimationLoaderError { @@ -341,7 +347,15 @@ impl AssetLoader for CSVAnimationLoader { csv_animation.frames.len() ); - Ok(csv_animation.generate_clip(&clip_info.root_name, Option::<&[(&str, &str)]>::None)) + let mapping = if let Some(mapping) = clip_info.mapping { + let mapping = load_context.read_asset_bytes(mapping).await.map_err(CSVAnimationLoaderError::MappingReadBytesError)?; + let mapping: CSVAnimationMapping = serde_json::from_slice(&mapping).map_err(CSVAnimationLoaderError::MappingParseError)?; + Some(mapping.0) + } else { + None + }; + + Ok(csv_animation.generate_clip(&clip_info.root_name, mapping.as_deref())) }) }