Add animation store for pre-made clips
aac.AnimationStore(folder) seeds a clip from an existing .anim asset and returns a normal ClipBuilder, so pre-made clips work anywhere generated clips do and can still be edited (looping, keyframes, toggles, blend shapes). The Unity side clones the asset into the container and applies the script's changes to the clone, leaving the original untouched. - ClipData gains optional source; looping becomes Option<bool> so an untouched store clip keeps the asset's own looping setting. - Add rust/examples/animation_store.rhai, a worked store example.
This commit is contained in:
@@ -87,6 +87,13 @@ pub struct ClipBuilder {
|
||||
pub(crate) name: String,
|
||||
}
|
||||
|
||||
/// A folder of pre-made `AnimationClip` assets, resolved on the Unity side at generation time.
|
||||
#[derive(Clone)]
|
||||
pub struct AnimationStore {
|
||||
pub(crate) aac: Aac,
|
||||
pub(crate) folder: String,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct BlendTreeBuilder {
|
||||
pub(crate) aac: Aac,
|
||||
@@ -197,6 +204,12 @@ impl ClipBuilder {
|
||||
self.aac.write(|graph| clip_mut(graph, &name).map(f))
|
||||
}
|
||||
|
||||
/// Override the clip's looping setting. A store clip with no override keeps the source asset's own setting.
|
||||
pub fn set_looping(&self, value: bool) -> Result<(), String> {
|
||||
self.data_mut(|data| data.looping = Some(value));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Append one keyframe to the curve for (path, property), creating the curve if needed.
|
||||
pub fn push_key(&self, path: &str, property: &str, key: Keyframe) -> Result<(), String> {
|
||||
let target = infer_target(property).ok_or_else(|| {
|
||||
@@ -212,6 +225,38 @@ impl ClipBuilder {
|
||||
}
|
||||
}
|
||||
|
||||
impl AnimationStore {
|
||||
/// Resolve `folder/<name>.anim` and declare it as a clip the first time it is requested. The clip
|
||||
/// name is the asset path, so the same asset seen through different names is still one clip.
|
||||
pub fn clip(&self, name: &str) -> Result<ClipBuilder, String> {
|
||||
if name.trim().is_empty() {
|
||||
return Err("animation store clip name is empty".to_string());
|
||||
}
|
||||
let mut path = format!(
|
||||
"{}/{}",
|
||||
self.folder.trim_end_matches('/'),
|
||||
name.trim_start_matches('/')
|
||||
);
|
||||
if !path.ends_with(".anim") {
|
||||
path.push_str(".anim");
|
||||
}
|
||||
self.aac.write(|graph| {
|
||||
if !graph.clips.iter().any(|clip| clip.source.as_deref() == Some(path.as_str())) {
|
||||
graph.clips.push(ClipData {
|
||||
name: path.clone(),
|
||||
looping: None,
|
||||
curves: Vec::new(),
|
||||
source: Some(path.clone()),
|
||||
});
|
||||
}
|
||||
});
|
||||
Ok(ClipBuilder {
|
||||
aac: self.aac.clone(),
|
||||
name: path,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl BlendTreeBuilder {
|
||||
pub fn data_mut<R>(&self, f: impl FnOnce(&mut BlendTreeData) -> R) -> Option<R> {
|
||||
let name = self.name.clone();
|
||||
|
||||
Reference in New Issue
Block a user