shity basic one shot

This commit is contained in:
2026-09-18 14:01:10 +01:00
parent 5851077659
commit 42a1234e7f
81 changed files with 3484 additions and 76 deletions
+263
View File
@@ -0,0 +1,263 @@
use serde::{Deserialize, Serialize};
/// The complete description of one animator controller, produced entirely by a Rhai script.
#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)]
pub struct ControllerGraph {
pub system_name: String,
pub asset_key: String,
pub parameters: Vec<Parameter>,
pub clips: Vec<ClipData>,
pub blend_trees: Vec<BlendTreeData>,
pub controller: Controller,
}
#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)]
pub struct Controller {
pub layers: Vec<Layer>,
}
#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)]
pub struct Layer {
/// The layer suffix as written in the script. Unity's layer name is derived from it by the C# side.
pub name: String,
pub state_machine: StateMachine,
}
/// Grid position in Unity's animator graph. Serialized as an object so C# can bind it directly.
#[derive(Debug, Clone, Copy, Default, PartialEq, Serialize, Deserialize)]
pub struct GridPos {
pub x: i32,
pub y: i32,
}
impl GridPos {
pub fn new(x: i32, y: i32) -> Self {
Self { x, y }
}
}
#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)]
pub struct StateMachine {
/// `None` for a layer's root machine, `Some` for a sub-state machine.
pub name: Option<String>,
pub position: GridPos,
pub states: Vec<State>,
pub sub_machines: Vec<StateMachine>,
pub any_state_transitions: Vec<Transition>,
}
#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)]
pub struct State {
pub name: String,
pub position: GridPos,
pub motion: Option<MotionRef>,
pub transitions: Vec<Transition>,
}
/// References to entries in `ControllerGraph::clips` / `ControllerGraph::blend_trees`.
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(tag = "type", rename_all = "snake_case")]
pub enum MotionRef {
Clip { name: String },
BlendTree { name: String },
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct Transition {
pub to: String,
pub conditions: Vec<Condition>,
pub has_exit_time: bool,
pub exit_time: f32,
pub duration: f32,
pub ordered_interruption: bool,
pub source_interruption: bool,
pub can_transition_to_self: bool,
}
impl Transition {
/// Mirrors `AacDefaultsProvider.ConfigureTransition`, so unspecified fields match library defaults.
pub fn new(to: impl Into<String>) -> Self {
Self {
to: to.into(),
conditions: Vec::new(),
has_exit_time: false,
exit_time: 0.0,
duration: 0.0,
ordered_interruption: true,
source_interruption: false,
can_transition_to_self: false,
}
}
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct Condition {
pub parameter: String,
pub mode: CondMode,
pub threshold: f32,
}
#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum CondMode {
Greater,
Less,
Equals,
NotEqual,
If,
IfNot,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct ClipData {
pub name: String,
pub looping: bool,
pub curves: Vec<Curve>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct Curve {
pub path: String,
pub target: TargetType,
pub property: String,
pub keys: Vec<Keyframe>,
}
/// The Unity component a curve binds to. Closed set: an unknown property is an error, not a guess.
#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum TargetType {
GameObject,
SkinnedMeshRenderer,
}
#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
pub struct Keyframe {
pub time: f32,
pub value: f32,
pub in_tangent: f32,
pub out_tangent: f32,
}
impl Keyframe {
pub fn linear(time: f32, value: f32) -> Self {
Self {
time,
value,
in_tangent: 0.0,
out_tangent: 0.0,
}
}
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct BlendTreeData {
pub name: String,
pub blend_type: BlendType,
pub param_x: String,
pub param_y: Option<String>,
pub children: Vec<BlendChild>,
pub use_automatic_thresholds: bool,
}
/// `rename_all = "snake_case"` would turn `SimpleDirectional2D` into `simple_directional2_d`,
/// so every variant is renamed explicitly.
#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
pub enum BlendType {
#[serde(rename = "simple_1d")]
Simple1D,
#[serde(rename = "simple_directional_2d")]
SimpleDirectional2D,
#[serde(rename = "freeform_directional_2d")]
FreeformDirectional2D,
#[serde(rename = "freeform_cartesian_2d")]
FreeformCartesian2D,
#[serde(rename = "direct")]
Direct,
}
impl BlendType {
pub fn is_2d(self) -> bool {
matches!(
self,
BlendType::SimpleDirectional2D
| BlendType::FreeformDirectional2D
| BlendType::FreeformCartesian2D
)
}
/// Blend trees that place children by threshold rather than by a direct blend parameter.
pub fn uses_thresholds(self) -> bool {
self != BlendType::Direct
}
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct BlendChild {
pub motion: MotionRef,
pub threshold: f32,
pub threshold_y: Option<f32>,
/// Only meaningful on `Direct` blend trees.
pub direct_param: Option<String>,
}
impl BlendChild {
pub fn motion(motion: MotionRef) -> Self {
Self {
motion,
threshold: 0.0,
threshold_y: None,
direct_param: None,
}
}
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(tag = "type", rename_all = "snake_case")]
pub enum Parameter {
Float { name: String, default: f32 },
Int { name: String, default: i32 },
Bool { name: String, default: bool },
}
impl Parameter {
pub fn name(&self) -> &str {
match self {
Parameter::Float { name, .. } | Parameter::Int { name, .. } | Parameter::Bool { name, .. } => name,
}
}
}
/// Property-name -> component-type inference. Deliberately strict; an unmapped property is an error.
pub fn infer_target(property: &str) -> Option<TargetType> {
if property.starts_with("blendShape.") {
return Some(TargetType::SkinnedMeshRenderer);
}
match property {
"m_IsActive" => Some(TargetType::GameObject),
_ => None,
}
}
impl ClipData {
/// Find-or-create the curve for one (path, target, property) binding.
pub fn curve_mut(&mut self, path: &str, target: TargetType, property: &str) -> &mut Curve {
let index = match self
.curves
.iter()
.position(|c| c.path == path && c.target == target && c.property == property)
{
Some(index) => index,
None => {
self.curves.push(Curve {
path: path.to_string(),
target,
property: property.to_string(),
keys: Vec::new(),
});
self.curves.len() - 1
}
};
&mut self.curves[index]
}
}