use serde::{Deserialize, Serialize};
use crate::{
data::runtime::{
constraint::{ConstraintIdx, ConstraintSet, IkConstraintData, TransformConstraintData},
skeleton::Skeleton,
},
error::Error,
};
#[allow(non_snake_case)]
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct IkConstraintDataStore {
pub name: String,
#[serde(default)]
pub order: u32,
#[serde(default)]
pub bones: Vec<String>,
pub target: String,
#[serde(default)]
pub mix: Option<f32>,
#[serde(default)]
pub bendPositive: Option<bool>,
#[serde(default)]
pub compress: Option<bool>,
#[serde(default)]
pub stretch: Option<bool>,
}
#[allow(non_snake_case)]
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct TransformConstraintDataStore {
pub name: String,
#[serde(default)]
pub order: u32,
#[serde(default)]
pub bones: Vec<String>,
pub target: String,
#[serde(default)]
pub mixRotate: Option<f32>,
#[serde(default)]
pub mixX: Option<f32>,
#[serde(default)]
pub mixY: Option<f32>,
#[serde(default)]
pub mixScaleX: Option<f32>,
#[serde(default)]
pub mixScaleY: Option<f32>,
#[serde(default)]
pub mixShearY: Option<f32>,
#[serde(default)]
pub rotation: Option<f32>,
#[serde(default)]
pub x: Option<f32>,
#[serde(default)]
pub y: Option<f32>,
#[serde(default)]
pub scaleX: Option<f32>,
#[serde(default)]
pub scaleY: Option<f32>,
#[serde(default)]
pub shearY: Option<f32>,
#[serde(default)]
pub local: Option<bool>,
#[serde(default)]
pub relative: Option<bool>,
}
impl IkConstraintDataStore {
pub fn to_rt(self, skeleton: &Skeleton) -> Result<IkConstraintData, Error> {
let mut bones = Vec::with_capacity(self.bones.len());
for b in &self.bones {
bones.push(skeleton.get_bone_index(b).ok_or_else(|| {
Error::InvalidData(format!("IK constraint references unknown bone: {b}"))
})?);
}
let target = skeleton.get_bone_index(&self.target).ok_or_else(|| {
Error::InvalidData(format!(
"IK constraint '{}' references unknown target bone: {}",
self.name, self.target
))
})?;
Ok(IkConstraintData {
bones,
target,
mix: self.mix.unwrap_or(1.0),
bend_positive: self.bendPositive.unwrap_or(true),
compress: self.compress.unwrap_or(false),
stretch: self.stretch.unwrap_or(false),
order: self.order,
})
}
}
impl TransformConstraintDataStore {
pub fn to_rt(self, skeleton: &Skeleton) -> Result<TransformConstraintData, Error> {
let mut bones = Vec::with_capacity(self.bones.len());
for b in &self.bones {
bones.push(skeleton.get_bone_index(b).ok_or_else(|| {
Error::InvalidData(format!("transform constraint references unknown bone: {b}"))
})?);
}
let target = skeleton.get_bone_index(&self.target).ok_or_else(|| {
Error::InvalidData(format!(
"transform constraint '{}' references unknown target bone: {}",
self.name, self.target
))
})?;
Ok(TransformConstraintData {
bones,
target,
mix_rotate: self.mixRotate.unwrap_or(1.0),
mix_x: self.mixX.unwrap_or(1.0),
mix_y: self.mixY.or(self.mixX).unwrap_or(1.0),
mix_scale_x: self.mixScaleX.unwrap_or(1.0),
mix_scale_y: self.mixScaleY.or(self.mixScaleX).unwrap_or(1.0),
mix_shear_y: self.mixShearY.unwrap_or(1.0),
offset_rotation: self.rotation.unwrap_or(0.0),
offset_x: self.x.unwrap_or(0.0),
offset_y: self.y.unwrap_or(0.0),
offset_scale_x: self.scaleX.unwrap_or(0.0),
offset_scale_y: self.scaleY.unwrap_or(0.0),
offset_shear_y: self.shearY.unwrap_or(0.0),
order: self.order,
})
}
}
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct ConstraintDataStore {
#[serde(default)]
pub ik: Vec<IkConstraintDataStore>,
#[serde(default)]
pub transform: Vec<TransformConstraintDataStore>,
}
impl ConstraintDataStore {
pub fn to_rt(self, skeleton: &Skeleton) -> Result<ConstraintSet, Error> {
let mut ik = Vec::with_capacity(self.ik.len());
for c in self.ik {
ik.push(c.to_rt(skeleton)?);
}
let mut transform = Vec::with_capacity(self.transform.len());
for c in self.transform {
transform.push(c.to_rt(skeleton)?);
}
let mut order: Vec<(u32, ConstraintIdx)> = Vec::with_capacity(ik.len() + transform.len());
for (i, c) in ik.iter().enumerate() {
order.push((c.order, ConstraintIdx::Ik(i as u32)));
}
for (i, c) in transform.iter().enumerate() {
order.push((c.order, ConstraintIdx::Transform(i as u32)));
}
order.sort_by_key(|(o, _)| *o);
Ok(ConstraintSet {
ik,
transform,
order: order.into_iter().map(|(_, i)| i).collect(),
})
}
}
#[cfg(test)]
mod tests {
use super::*;
use serde_json::json;
#[test]
fn parse_ik_constraint_json() {
let v = json!({
"name": "Arm_L_Grab_IK",
"order": 5,
"bones": ["Upperarm_L_Grab", "Arm_L_2_Grab"],
"target": "Arm_L_Grab_IK",
"mix": 0,
"bendPositive": false
});
let store: IkConstraintDataStore = serde_json::from_value(v).unwrap();
assert_eq!(store.name, "Arm_L_Grab_IK");
assert_eq!(store.bones.len(), 2);
assert_eq!(store.mix, Some(0.0));
assert_eq!(store.bendPositive, Some(false));
assert_eq!(store.order, 5);
}
#[test]
fn parse_transform_constraint_json() {
let v = json!({
"name": "Foot_L_T",
"order": 8,
"bones": ["Foot_L"],
"target": "Leg_L_IK",
"mixScaleX": 0,
"mixShearY": 0,
"rotation": -87.5
});
let store: TransformConstraintDataStore = serde_json::from_value(v).unwrap();
assert_eq!(store.mixScaleX, Some(0.0));
assert_eq!(store.mixRotate, None);
assert_eq!(store.rotation, Some(-87.5));
}
}