use blender;
use crate::enum_set::*;
use std::mem;
use hashbrown::HashMap;
use crate::utils::{self, LibraryId, ObjectId};
use na::{Vec3, Vec2, Unit, clamp, Pnt3, vec4, vec3, UnitQuat, ToPnt, AsVec};
use angle::{Rad, Angle};
use pennereq::*;
use color::Rgba;
use std::f32;
use itertools::Itertools;
use std::borrow::Cow;
const PRECISSION: f32 = 10000.;
#[cfg_attr(feature = "serialize", derive(Serialize, Deserialize))]
#[derive(Clone,Debug,Copy,PartialEq,Eq)]
#[repr(u8)]
#[allow(dead_code)]
pub enum Interpolation {
Constant = 0,
Linear = 1,
Bezier = 2,
Back = 3,
Bounce = 4,
Circ = 5,
Cubic = 6,
Elastic = 7,
Expo = 8,
Quad = 9,
Quart = 10,
Quint = 11,
Sine = 12,
}
#[cfg_attr(feature = "serialize", derive(Serialize, Deserialize))]
#[derive(Clone,Debug,Copy,PartialEq,Eq)]
#[repr(u8)]
#[allow(dead_code)]
pub enum Ease{
Auto = 0,
In = 1,
Out = 2,
InOut = 3,
}
#[cfg_attr(feature = "serialize", derive(Serialize, Deserialize))]
#[derive(Clone,Debug,Copy,PartialEq,Eq)]
#[repr(u8)]
pub enum KeyframeType{
KeyFrame = 0,
Extreme = 1,
Breakdown = 2,
Jitter = 3,
}
#[cfg_attr(feature = "serialize", derive(Serialize, Deserialize))]
#[derive(Clone,Debug,Copy,PartialEq)]
#[repr(C)]
pub struct BezTriple {
vec: [Vec3; 3],
alfa: f32,
weight: f32,
radius: f32,
ipo: Interpolation,
h1: u8,
h2: u8,
f1: u8,
f2: u8,
f3: u8,
keyframetype: KeyframeType,
easing: Ease,
back: f32,
amplitude: f32,
period: f32,
pad: [u8; 4],
}
impl BezTriple{
pub fn from(&self) -> &Vec3{
&self.vec[1]
}
pub fn cp1(&self) -> &Vec3{
&self.vec[0]
}
pub fn cp2(&self) -> &Vec3{
&self.vec[2]
}
}
#[cfg_attr(feature = "serialize", derive(Serialize, Deserialize))]
#[derive(Clone,Debug,Copy,PartialEq)]
#[repr(C)]
pub struct FPoint{
vec: Vec2,
flag: i32,
pad: i32,
}
bitflags!{
#[cfg_attr(feature = "serialize", derive(Serialize, Deserialize))]
pub struct Flags: u16{
const VISIBLE = 1;
const SELECTED = 2;
const ACTIVE = 4;
const PROTECTED = 8;
const MUTED = 16;
const AUTO_HANDLES = 32;
const MOD_OFF = 64;
const DISABLED = 128;
const INT_VALUES = 256;
const DISCRETE_VALUES = 512;
const TAGGED = 1024;
}
}
#[cfg_attr(feature = "serialize", derive(Serialize, Deserialize))]
#[derive(Clone,Debug,Copy,PartialEq,Eq)]
#[repr(u16)]
pub enum Extend{
Constant = 0,
Linear = 1,
}
#[cfg_attr(feature = "serialize", derive(Serialize, Deserialize))]
#[derive(Clone,Debug,Copy,PartialEq,Eq)]
#[repr(u16)]
pub enum CyclingMode{
None = 0,
Cyclic = 1,
CyclicOffset = 2,
Mirror = 3
}
#[cfg_attr(feature = "serialize", derive(Serialize, Deserialize))]
#[derive(Clone,Debug,Copy,PartialEq,Eq)]
#[repr(C)]
pub struct ModifierCycle{
before_mode: CyclingMode,
after_mode: CyclingMode,
before_cycles: u16,
after_cycles: u16,
}
impl ModifierCycle{
fn _offset(&self, in_frame: isize, out_frame: isize, frame: f32, curve: &FCurve) -> Option<f32>{
let first = curve.bezt_at(in_frame as f32).unwrap();
let last = curve.bezt_at(out_frame as f32).unwrap();
let diff = last.vec[1].y - first.vec[1].y;
let duration = last.vec[1].x - first.vec[1].x;
let num_loops = (frame / duration) as u32;
match self.after_mode{
CyclingMode::CyclicOffset => {
Some(diff * num_loops as f32)
}
_ => None
}
}
fn cycle(&self, frame: f32, in_frame: Option<isize>, out_frame: Option<isize>, curve: &FCurve) -> (f32, Option<f32>){
let inbezt = in_frame.and_then(|in_frame| curve
.bezt_at(in_frame as f32).ok().map(|bezt| (bezt.from().x, bezt.from().y))
.or_else(||{
if curve.min_frame.map(|min| min < in_frame).unwrap_or(false) {
Some((in_frame as f32, curve.value_at(in_frame as f32, in_frame, out_frame.unwrap())))
}else{
curve.bezt.first().map(|first| (first.from().x, first.from().y))
}
}));
let outbezt = out_frame.and_then(|out_frame| curve
.bezt_at(out_frame as f32).ok().map(|bezt| (bezt.from().x, bezt.from().y))
.or_else(||{
if curve.max_frame.map(|max| max > out_frame).unwrap_or(false){
Some((out_frame as f32, curve.value_at(out_frame as f32, in_frame.unwrap(), out_frame)))
}else{
curve.bezt.last().map(|last| (last.from().x, last.from().y))
}
}));
if let (Some(inbezt), Some(outbezt)) = (inbezt, outbezt){
let (in_frame, in_value) = inbezt;
let (out_frame, out_value) = outbezt;
let side;
let mode;
let cycles;
let edge;
if frame < in_frame {
if self.before_mode != CyclingMode::None {
side = -1.;
mode = self.before_mode;
cycles = self.before_cycles;
edge = in_frame;
}else{
return (frame, None);
}
}else if frame > out_frame {
if self.after_mode != CyclingMode::None {
side = 1.;
mode = self.after_mode;
cycles = self.after_cycles;
edge = out_frame;
}else{
return (frame, None);
}
}else{
return (frame, None);
}
let cycle_duration = out_frame - in_frame;
if cycle_duration == 0. { return (frame, None) }
let cycle_offset = out_value - in_value;
let cycle = side * (frame - edge) / cycle_duration;
let cycled_t = (frame - edge) % cycle_duration;
if cycles != 0 && cycle > cycles as f32 {
return (frame, None);
}
let offset;
if mode == CyclingMode::CyclicOffset{
if side < 0. {
offset = Some(((frame - edge) / cycle_duration).floor() * cycle_offset)
}else{
offset = Some(((frame - edge) / cycle_duration).ceil() * cycle_offset)
}
}else{
offset = None
}
let mut time;
let needs_mirror = mode == CyclingMode::Mirror && (cycle + 1.) as i32 % 2 != 0;
if cycled_t == 0. {
if needs_mirror {
time = if side == 1. { in_frame } else { out_frame }
}else{
time = if side == 1. { out_frame } else { in_frame };
}
}else if needs_mirror {
if side < 0. {
time = in_frame - cycled_t
}else{
time = out_frame - cycled_t
}
}else{
time = in_frame + cycled_t
}
if time < in_frame {
time += cycle_duration
}
(time, offset)
}else{
(frame, None)
}
}
}
#[cfg_attr(feature = "serialize", derive(Serialize, Deserialize))]
#[derive(Clone,Debug,Copy,PartialEq,Eq)]
#[repr(u16)]
pub enum ModifierType{
Null,
Generator,
FnGenerator,
Envelope,
Cycles,
Noise,
Filter,
Python,
Limits,
Stepped,
}
#[cfg_attr(feature = "serialize", derive(Serialize, Deserialize))]
#[derive(Clone,Debug,Copy,PartialEq,Eq)]
pub enum ModifierData{
Cycles(ModifierCycle),
Generator,
}
bitflags!{
#[cfg_attr(feature = "serialize", derive(Serialize, Deserialize))]
struct ModifierFlags: u16{
const DISABLED = 1 << 0;
const EXPANDED = 1 << 1;
const ACTIVE = 1 << 2;
const MUTED = 1 << 3;
const RANGE_RESTRICT = 1 << 4;
const USE_INFLUENCE = 1 << 5;
}
}
#[cfg_attr(feature = "serialize", derive(Serialize, Deserialize))]
#[derive(Clone,Debug)]
struct Modifier{
data: ModifierData,
flag: ModifierFlags,
influence: f32,
start_frame: f32,
end_frame: f32,
blend_in: f32,
blend_out: f32,
}
#[cfg_attr(feature = "serialize", derive(Serialize, Deserialize))]
#[derive(Clone,Debug,PartialEq,Eq,Hash)]
pub enum Component{
Location,
RotationQuat,
RotationEuler,
RotationAxisAngle,
Scale,
Value,
HideView,
HideRender,
Hide,
Other(String),
}
bitflags! {
#[cfg_attr(feature = "serialize", derive(Serialize, Deserialize))]
pub struct DriverTargetFlags: u16{
const STRUCT_REF = 1;
const ID_OB_ONLY = 2;
const LOCAL_SPACE = 4;
const LOCAL_CONSTS = 8;
const INVALID = 16;
}
}
#[derive(Clone, Debug)]
pub struct DriverTransformation {
pub loc: Pnt3,
pub rot: Vec3,
pub scale: Vec3,
}
#[repr(u16)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[cfg_attr(feature = "serialize", derive(Serialize, Deserialize))]
pub enum TransformChannel {
LocX = 0,
LocY,
LocZ,
RotX,
RotY,
RotZ,
ScaleX,
ScaleY,
ScaleZ,
MaxTransformChannel,
}
#[cfg_attr(feature = "serialize", derive(Serialize, Deserialize))]
#[derive(Clone,Debug)]
pub struct DriverTarget {
object: Option<String>,
rna_path: Option<String>,
pchan_name: String,
transchan: TransformChannel,
rotation_mode: Option<u8>,
flag: DriverTargetFlags,
idtype: i32,
}
pub fn parse_driver_target(blend: &blender::Object) -> DriverTarget {
if blend.structure().name() != "DriverTarget" {
panic!("Trying to parse DriverTarget from a {}", blend.structure().name())
}
let object = blend.get_object("id").ok().map(|obj| obj.name().unwrap().to_owned());
let rna_path = blend.get_str("rna_path").ok().map(|s| s.to_owned());
let pchan_name = blend.get_str("pchan_name").unwrap().to_owned();
let transchan = *blend.get("transChan").unwrap();
let rotation_mode = blend.get("rotation_mode").ok().copied();
let flag = *blend.get("flag").unwrap();
let idtype = *blend.get("idtype").unwrap();
DriverTarget {
object,
rna_path,
pchan_name,
transchan,
rotation_mode,
flag,
idtype,
}
}
#[repr(u8)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[cfg_attr(feature = "serialize", derive(Serialize, Deserialize))]
pub enum DriverVarType{
SingleProp = 0,
RotDiff,
LocDiff,
TransformChannel,
}
bitflags!{
#[cfg_attr(feature = "serialize", derive(Serialize, Deserialize))]
pub struct DriverVarFlag: u16 {
const ERROR = 1;
const INVALID_NAME = 2;
const INVALID_START_NUM = 4;
const INVALID_START_CHAR = 8;
const INVALID_HAS_SPACE = 16;
const INVALID_HAS_DOT = 32;
const INVALID_HAS_SPECIAL = 64;
const INVALID_PY_KEYWORD = 128;
const INVALID_EMPTY = 265;
}
}
#[cfg_attr(feature = "serialize", derive(Serialize, Deserialize))]
#[derive(Clone,Debug)]
pub struct DriverVar {
name: String,
target: DriverTarget,
ty: DriverVarType,
flag: DriverVarFlag,
curval: f32,
}
pub fn parse_driver_var(blend: &blender::Object) -> DriverVar {
let name = blend.get_str("name").unwrap().to_owned();
let target = blend.get_object("targets").unwrap();
let target = parse_driver_target(&target);
let ty = *blend.get("type").unwrap();
let flag = *blend.get("flag").unwrap();
let curval = *blend.get("curval").unwrap();
DriverVar {
name,
target,
ty,
flag,
curval,
}
}
impl DriverVar {
pub fn name(&self) -> &str {
&self.name
}
pub fn driver_target(&self) -> &DriverTarget {
&self.target
}
pub fn driver_target_object(&self) -> Option<&str> {
self.target.object.as_ref().map(|s| s.as_str())
}
pub fn driver_transchannel(&self) -> Option<TransformChannel> {
match self.ty {
DriverVarType::TransformChannel => Some(self.target.transchan),
_ => None,
}
}
pub fn flags(&self) -> DriverVarFlag {
self.flag
}
pub fn target_flags(&self) -> DriverTargetFlags {
self.target.flag
}
pub fn target_rna_path(&self) -> Option<&str> {
self.target.rna_path.as_ref().map(|s| s.as_str())
}
pub fn ty(&self) -> DriverVarType {
self.ty
}
pub fn pchan_name(&self) -> &str {
&self.target.pchan_name
}
pub fn value_at(&self, driver_trafo: &DriverTransformation) -> f32 {
match self.ty {
DriverVarType::TransformChannel => {
match self.target.transchan {
TransformChannel::LocX => driver_trafo.loc.x,
TransformChannel::LocY => driver_trafo.loc.y,
TransformChannel::LocZ => driver_trafo.loc.z,
TransformChannel::RotX => driver_trafo.rot.x,
TransformChannel::RotY => driver_trafo.rot.y,
TransformChannel::RotZ => driver_trafo.rot.z,
TransformChannel::ScaleX => driver_trafo.scale.x,
TransformChannel::ScaleY => driver_trafo.scale.y,
TransformChannel::ScaleZ => driver_trafo.scale.z,
_ => unreachable!(),
}
}
_ => todo!()
}
}
}
#[repr(i32)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[cfg_attr(feature = "serialize", derive(Serialize, Deserialize))]
enum DriverType {
Average = 0,
Python,
Sum,
Min,
Max,
}
#[cfg_attr(feature = "serialize", derive(Serialize, Deserialize))]
#[derive(Clone,Debug)]
pub struct ChannelDriver {
variables: Vec<DriverVar>,
curval: f32,
influence: f32,
ty: DriverType,
flag: i32,
}
impl ChannelDriver {
pub fn variables(&self) -> &[DriverVar] {
&self.variables
}
pub fn value_at(&self, driver_trafo: &[DriverTransformation]) -> f32 {
assert_eq!(driver_trafo.len(), self.variables.len());
match self.ty {
DriverType::Average => {
let sum = self.variables.iter()
.zip(driver_trafo)
.fold(0., |acc, (var, driver_trafo)| {
acc + var.value_at(driver_trafo)
});
sum / self.variables.len() as f32
}
DriverType::Sum => {
self.variables.iter()
.zip(driver_trafo)
.fold(0., |acc, (var, driver_trafo)| {
acc + var.value_at(driver_trafo)
})
}
DriverType::Min => {
self.variables.iter()
.zip(driver_trafo)
.map(|(var, driver_trafo)| var.value_at(driver_trafo))
.min_by_key(|value| *value as i32 * 10000)
.unwrap_or(0.)
}
DriverType::Max =>{
self.variables.iter()
.zip(driver_trafo)
.map(|(var, driver_trafo)| var.value_at(driver_trafo))
.max_by_key(|value| *value as i32 * 10000)
.unwrap_or(0.)
}
_ => todo!("Python driver expressions not supported yet :D")
}
}
}
fn parse_driver(driver: &blender::Object) -> ChannelDriver {
let variables = driver.get_list("variables").unwrap()
.iter()
.map(|var| parse_driver_var(&var))
.collect();
let curval = *driver.get("curval").unwrap();
let influence = *driver.get("influence").unwrap();
let ty = *driver.get("type").unwrap();
let flag = *driver.get("flag").unwrap();
ChannelDriver {
variables,
curval,
influence,
ty,
flag,
}
}
#[cfg_attr(feature = "serialize", derive(Serialize, Deserialize))]
#[derive(Clone,Debug)]
pub struct FCurve{
lookup: Vec<i32>,
bezt: Vec<BezTriple>,
fpt: Option<Vec<FPoint>>,
curval: f32,
array_index: i32,
rna_path: String,
path: String,
object: String,
component: Component,
flag: Flags,
extend: Extend,
color: Rgba<f32>,
min_frame: Option<isize>,
max_frame: Option<isize>,
modifiers: Vec<Modifier>,
driver: Option<ChannelDriver>,
}
impl FCurve{
pub fn rna_path(&self) -> &str{
&self.rna_path
}
pub fn path(&self) -> &str{
&self.path
}
pub fn object(&self) -> &str{
&self.object
}
pub fn component(&self) -> &Component{
&self.component
}
pub fn array_index(&self) -> usize{
self.array_index as usize
}
pub fn bezt(&self) -> &[BezTriple]{
&self.bezt
}
pub fn color(&self) -> &Rgba<f32>{
&self.color
}
pub fn extend(&self) -> Extend {
self.extend
}
pub fn flags(&self) -> Flags{
self.flag
}
pub fn is_enabled(&self) -> bool {
!self.flag.contains(Flags::MUTED) && !self.flag.contains(Flags::DISABLED)
}
pub fn bezt_at(&self, frame: f32) -> Result<&BezTriple, usize>{
let lookup = (frame * PRECISSION) as i32;
self.lookup
.binary_search(&lookup)
.map(|idx| &self.bezt[idx])
}
pub fn pnt_at(&self, frame: f32, precission: u32) -> Option<Result<&FPoint, usize>>{
self.fpt.as_ref().map(|fpt| fpt
.binary_search_by_key(&((frame * precission as f32) as isize), |pt| (pt.vec.x * precission as f32) as isize)
.map(|idx| &fpt[idx]))
}
pub fn driver(&self) -> Option<&ChannelDriver> {
self.driver.as_ref()
}
pub fn driver_value_at<I: Into<Option<isize>>>(&self, driver_trafo: &[DriverTransformation], in_frame: I, out_frame: I) -> Option<f32> {
if let Some(driver) = self.driver.as_ref() {
let value = driver.value_at(driver_trafo);
Some(self.value_at(value, in_frame, out_frame))
}else{
None
}
}
pub fn value_at<I: Into<Option<isize>>>(&self, frame: f32, in_frame: I, out_frame: I) -> f32{
if self.bezt.len() == 1 ||
(self.bezt.len() == 2 && self.bezt[0].from().y == self.bezt[1].from().y)
{
return self.bezt[0].from().y;
}
let in_frame = in_frame.into().unwrap_or(self.bezt[0].from().x as isize);
let out_frame = out_frame.into().unwrap_or(self.bezt.last().unwrap().from().x as isize);
let next;
let prev;
match self.bezt_at(frame) {
Ok(bezt) => {
return bezt.from().y;
}
Err(pos) => {
next = if pos < self.bezt.len(){
self.bezt[pos]
}else{
match self.extend {
Extend::Constant => *self.bezt_at(out_frame as f32)
.unwrap_or(self.bezt.last().unwrap()),
Extend::Linear if self.bezt.len() < 2 => *self.bezt_at(out_frame as f32)
.unwrap_or(self.bezt.last().unwrap()),
Extend::Linear => {
let prev = self.bezt_at((out_frame - 1) as f32)
.unwrap_or(&self.bezt[self.bezt.len() - 2]);
let mut next = *self.bezt_at(out_frame as f32)
.unwrap_or(self.bezt.last().unwrap());
let duration = next.from().x - prev.from().x;
let time = frame - prev.from().x;
let pct = time / duration;
let lerped = utils::lerp(prev.from().y, next.from().y, pct);
next.vec[1].y = lerped;
next.vec[1].x = time;
next
}
}
};
prev = if pos > 0 {
self.bezt[pos - 1]
}else{
match self.extend {
Extend::Constant => *self.bezt_at(in_frame as f32)
.unwrap_or(&self.bezt[0]),
Extend::Linear => {
let mut prev = *self.bezt_at(in_frame as f32)
.unwrap_or(self.bezt.first().unwrap());
let next = self.bezt_at((in_frame + 1) as f32)
.unwrap_or(self.bezt.last().unwrap());
let duration = next.from().x - prev.from().x;
let time = frame - prev.from().x;
let pct = time / duration;
let lerped = utils::lerp(prev.from().y, next.from().y, pct);
prev.vec[1].y = lerped;
prev.vec[1].x = time;
prev
}
}
};
}
}
let duration = next.from().x - prev.from().x;
if self.flag.contains(Flags::DISCRETE_VALUES) || duration == 0.{
prev.from().y
}else{
let time = frame - prev.from().x;
let pct = time / duration;
let v = match prev.ipo{
Interpolation::Bezier => {
utils::bezier_interpolate(prev.from().y, prev.cp2().y, next.cp1().y, next.from().y, pct)
}
Interpolation::Linear =>
utils::lerp(prev.from().y, next.from().y, pct),
Interpolation::Constant =>
prev.from().y,
Interpolation::Back | Interpolation::Bounce | Interpolation::Circ |
Interpolation::Cubic | Interpolation::Elastic | Interpolation::Expo |
Interpolation::Quad | Interpolation::Quart | Interpolation::Quint |
Interpolation::Sine => {
let begin = prev.from().y;
let change = next.from().y - begin;
let easing_fn = easing(prev.ipo, prev.easing, prev.back);
easing_fn(time,begin,change,duration)
}
};
if prev.from().y > next.from().y{
clamp(v, next.from().y, prev.from().y)
}else{
clamp(v, prev.from().y, next.from().y)
}
}
}
}
#[cfg_attr(feature = "serialize", derive(Serialize, Deserialize))]
#[derive(Clone,Debug,Hash,Eq,PartialEq)]
struct CurvesKey<'a>{
object: Cow<'a, str>,
component: Cow<'a, Component>,
}
#[cfg_attr(feature = "serialize", derive(Serialize, Deserialize))]
#[derive(Clone,Debug)]
pub struct Action{
name: String,
curves: HashMap<CurvesKey<'static>, Vec<FCurve>>,
fps: f32,
num_frames: isize,
min_frame: isize,
max_frame: isize,
}
fn key<'a>(object: &'a str, component: &'a Component) -> CurvesKey<'a>{
CurvesKey{
object: object.into(),
component: Cow::Borrowed(component),
}
}
impl Action{
pub fn name(&self) -> &str{
&self.name
}
fn _assert_rot_mode(&self, object: &str, component: &Component){
let ckey = key(object, component);
let found = self.curves.get(&ckey).is_some();
if !found {
match component {
Component::RotationQuat => {
let ckey = key(object, &Component::RotationEuler);
let found = self.curves.get(&ckey).is_some();
if found {
panic!("asked for quaternion but have euler for {}, action {}", object, self.name);
}
let ckey = key(object, &Component::RotationAxisAngle);
let found = self.curves.get(&ckey).is_some();
if found {
panic!("asked for quaternion but have axis angle for {}, action {}", object, self.name);
}
}
Component::RotationEuler=> {
let ckey = key(object, &Component::RotationQuat);
let found = self.curves.get(&ckey).is_some();
if found {
panic!("asked for euler but have quaternion for {}, action {}", object, self.name);
}
let ckey = key(object, &Component::RotationAxisAngle);
let found = self.curves.get(&ckey).is_some();
if found {
panic!("asked for euler but have axis angle for {}, action {}", object, self.name);
}
}
Component::RotationAxisAngle => {
let ckey = key(object, &Component::RotationQuat);
let found = self.curves.get(&ckey).is_some();
if found {
panic!("asked for axis angle but have quaternion for {}, action {}", object, self.name);
}
let ckey = key(object, &Component::RotationEuler);
let found = self.curves.get(&ckey).is_some();
if found {
panic!("asked for axis angle but have euler for {}, action {}", object, self.name);
}
}
_ => panic!("Asked for non rotation component {:?} for {}, action {}", component, object, self.name)
}
}
}
fn value_at_frame(&self, object: &str, component: &Component, frame: f32, current_value: &[f32], doloop: bool) -> Option<Vec<f32>>{
let values = self.curves.get(&key(object, component))?;
let new_value = values.into_iter()
.fold(current_value.to_vec(), |mut value, curve| {
if curve.is_enabled() {
let t = if doloop {
if let (Some(min), Some(max)) = (curve.min_frame, curve.max_frame){
ModifierCycle{
before_mode: CyclingMode::Cyclic,
after_mode: CyclingMode::Cyclic,
before_cycles: 0,
after_cycles: 0,
}.cycle(frame, Some(min.max(self.min_frame)), Some(max.min(self.max_frame)), curve).0
}else{
frame
}
}else{
frame
};
let (t, cycled_offset) = curve.modifiers.iter()
.filter_map(|modifier| if let ModifierData::Cycles(data) = modifier.data{
Some(data)
}else{
None
})
.next()
.map(|modifier| modifier.cycle(frame, Some(self.min_frame), Some(self.max_frame), curve))
.unwrap_or((t, None));
let idx = curve.array_index as usize;
let mut v = curve.value_at(t, self.min_frame, self.max_frame);
if let Some(offset) = cycled_offset {
v += offset;
}
value[idx] = v;
}
value
});
Some(new_value)
}
pub fn value_at(&self, object: &str, component: &Component, time: f32, current_value: &[f32], doloop: bool) -> Option<Vec<f32>>{
let frame = time * self.fps;
self.value_at_frame(object, component, frame, current_value, doloop)
}
pub fn location_at(&self, object: &str, t: f32, current_value: &Pnt3, doloop: bool) -> Option<Pnt3> {
self.value_at(object, &Component::Location, t, current_value.as_vec().as_slice(), doloop)
.map(|p| utils::to_vec3(&p).to_pnt())
}
pub fn scale_at(&self, object: &str, t: f32, current_value: &Vec3, doloop: bool) -> Option<Vec3> {
self.value_at(object, &Component::Scale, t, current_value.as_slice(), doloop)
.map(|p| utils::to_vec3(&p))
}
pub fn orientation_at(&self, object: &str, t: f32, current_value: &UnitQuat, doloop: bool) -> Option<UnitQuat> {
let q = current_value.coords;
let q = vec4(q.w, q.x, q.y, q.z);
let frame = t * self.fps;
let prev_frame = frame.floor();
let next_frame = frame.ceil();
let prev_value = self
.value_at_frame(object, &Component::RotationQuat, prev_frame, q.as_slice(), doloop)
.map(|q| utils::to_quat(&q));
let next_value = self
.value_at_frame(object, &Component::RotationQuat, next_frame, q.as_slice(), doloop)
.map(|q| utils::to_quat(&q));
let pct = frame - prev_frame;
prev_value.and_then(|prev| next_value.map(|next| prev.slerp(&next, pct)))
}
pub fn orientation_from_euler_at(&self, object: &str, t: f32, current_value: &UnitQuat, rot_order: utils::RotOrder, doloop: bool) -> Option<UnitQuat> {
let e = current_value.euler_angles();
let value = [e.0, e.1, e.2];
let frame = t * self.fps;
let prev_frame = frame.floor();
let next_frame = frame.ceil();
let prev_value = self.value_at_frame(object, &Component::RotationEuler, prev_frame, &value, doloop)
.map(|rot|{
crate::Rotation::Euler(
rot_order,
vec3(Rad(rot[0]), Rad(rot[1]), Rad(rot[2]))
).into_quat()
});
let next_value = self.value_at_frame(object, &Component::RotationEuler, next_frame, &value, doloop)
.map(|rot|{
crate::Rotation::Euler(
rot_order,
vec3(Rad(rot[0]), Rad(rot[1]), Rad(rot[2]))
).into_quat()
});
let pct = frame - prev_frame;
prev_value
.and_then(|prev| next_value.map(|next| prev.slerp(&next, pct)))
}
pub fn orientation_from_axis_angle_at(&self, object: &str, t: f32, current_value: &UnitQuat, doloop: bool) -> Option<UnitQuat> {
let axis = current_value.axis()?;
let angle = current_value.angle();
let e = [angle, axis.x, axis.y, axis.z];
self.value_at(object, &Component::RotationAxisAngle, t, &e, doloop)
.map(|e| {
let axis = Unit::new_normalize(vec3(e[1], e[2], e[3]));
let angle = e[0];
UnitQuat::from_axis_angle(&axis, angle)
})
}
pub fn rotation_at(&self, object: &str, t: f32, current_value: &crate::Rotation, doloop: bool) -> Option<crate::Rotation> {
match current_value{
crate::Rotation::Quat(q) => {
let q = q.coords;
let q = vec4(q.w, q.x, q.y, q.z);
let frame = t * self.fps;
let prev_frame = frame.floor();
let next_frame = frame.ceil();
let prev_value = self
.value_at_frame(object, &Component::RotationQuat, prev_frame, q.as_slice(), doloop)
.map(|q| utils::to_quat(&q));
let next_value = self
.value_at_frame(object, &Component::RotationQuat, next_frame, q.as_slice(), doloop)
.map(|q| utils::to_quat(&q));
let pct = frame - prev_frame;
prev_value
.and_then(|prev| next_value.map(|next| prev.slerp(&next, pct)))
.map(|q| crate::Rotation::Quat(q))
}
crate::Rotation::Euler(rot_order, rot) => {
let value = &[rot.x.value(), rot.y.value(), rot.z.value()];
let frame = t * self.fps;
let prev_frame = frame.floor();
let next_frame = frame.ceil();
let prev_value = self
.value_at_frame(object, &Component::RotationEuler, prev_frame, value, doloop)
.map(|rot|
utils::euler_to_quaternion(&vec3(Rad(rot[0]), Rad(rot[1]), Rad(rot[2])), *rot_order)
);
let next_value = self
.value_at_frame(object, &Component::RotationEuler, next_frame, value, doloop)
.map(|rot|
utils::euler_to_quaternion(&vec3(Rad(rot[0]), Rad(rot[1]), Rad(rot[2])), *rot_order)
);
let pct = frame - prev_frame;
prev_value
.and_then(|prev| next_value.map(|next| prev.slerp(&next, pct)))
.map(|q| {
let (x,y,z) = utils::to_euler(&q, *rot_order);
crate::Rotation::Euler(
*rot_order,
vec3!(x, y, z)
)
})
}
crate::Rotation::AxisAngle(axis, angle) => {
let e = [angle.value(), axis.x, axis.y, axis.z];
self.value_at(object, &Component::RotationAxisAngle, t, &e, doloop)
.map(|axis_angle|{
crate::Rotation::AxisAngle(
Unit::new_normalize(vec3(axis_angle[1], axis_angle[2], axis_angle[3])),
Rad(axis_angle[0])
)
})
}
}
}
pub fn keyblock_at(&self, keyblock: &str, t: f32, doloop: bool) -> Option<f32> {
self.value_at(keyblock, &Component::Value, t, &[0.], doloop).map(|v| v[0])
}
pub fn hide_view_at(&self, object: &str, t: f32, doloop: bool) -> Option<bool> {
self.value_at(object, &Component::HideView, t, &[0.], doloop).map(|v| v[0] != 0.)
}
pub fn hide_render_at(&self, object: &str, t: f32, doloop: bool) -> Option<bool> {
self.value_at(object, &Component::HideRender, t, &[0.], doloop).map(|v| v[0] != 0.)
}
pub fn min_time_s(&self) -> f32 {
self.min_frame as f32 / self.fps
}
pub fn max_time_s(&self) -> f32 {
(self.max_frame as f32 + 1. - f32::EPSILON) / self.fps
}
pub fn min_frame(&self) -> isize {
self.min_frame
}
pub fn max_frame(&self) -> isize {
self.max_frame
}
pub fn set_min_frame(&mut self, min: isize){
self.min_frame = min
}
pub fn set_max_frame(&mut self, max: isize){
self.max_frame = max
}
pub fn loop_time(&self, t: f32) -> f32 {
((t - self.min_time_s()) % self.duration()) + self.min_time_s()
}
pub fn clamp_time(&self, t: f32) -> f32 {
let max_t = self.max_time_s();
let min_t = self.min_time_s();
clamp(t, min_t, max_t)
}
pub fn duration(&self) -> f32 {
self.max_time_s() - self.min_time_s()
}
pub fn num_frames(&self) -> isize {
self.num_frames
}
pub fn original_fps(&self) -> f32{
self.fps
}
pub fn set_original_fps(&mut self, fps: f32){
self.fps = fps;
}
}
#[inline]
fn easing(interpolation: Interpolation, easing: Ease, back: f32) -> Box<dyn Fn(f32, f32, f32, f32) -> f32>{
match interpolation{
Interpolation::Back => {
match easing{
Ease::In => Box::new(move |time, begin, change, duration| back::ease_in_s(time, begin, change, duration, back)),
Ease::Out | Ease::Auto => Box::new(move |time, begin, change, duration| back::ease_out_s(time, begin, change, duration, back)),
Ease::InOut => Box::new(move |time, begin, change, duration| back::ease_in_out_s(time, begin, change, duration, back)),
}
},
Interpolation::Bounce => {
match easing{
Ease::In => Box::new(bounce::ease_in),
Ease::Out | Ease::Auto => Box::new(bounce::ease_out),
Ease::InOut => Box::new(bounce::ease_in_out),
}
},
Interpolation::Circ => {
match easing{
Ease::In | Ease::Auto => Box::new(circ::ease_in),
Ease::Out => Box::new(circ::ease_out),
Ease::InOut => Box::new(circ::ease_in_out),
}
},
Interpolation::Cubic => {
match easing{
Ease::In | Ease::Auto => Box::new(cubic::ease_in),
Ease::Out => Box::new(cubic::ease_out),
Ease::InOut => Box::new(cubic::ease_in_out),
}
},
Interpolation::Elastic => {
match easing{
Ease::In => Box::new(elastic::ease_in),
Ease::Out | Ease::Auto => Box::new(elastic::ease_out),
Ease::InOut => Box::new(elastic::ease_in_out),
}
},
Interpolation::Expo => {
match easing{
Ease::In | Ease::Auto => Box::new(expo::ease_in),
Ease::Out => Box::new(expo::ease_out),
Ease::InOut => Box::new(expo::ease_in_out),
}
},
Interpolation::Quad => {
match easing{
Ease::In | Ease::Auto => Box::new(quad::ease_in),
Ease::Out => Box::new(quad::ease_out),
Ease::InOut => Box::new(quad::ease_in_out),
}
},
Interpolation::Quart => {
match easing{
Ease::In | Ease::Auto => Box::new(quart::ease_in),
Ease::Out => Box::new(quart::ease_out),
Ease::InOut => Box::new(quart::ease_in_out),
}
},
Interpolation::Quint => {
match easing{
Ease::In | Ease::Auto => Box::new(quint::ease_in),
Ease::Out => Box::new(quint::ease_out),
Ease::InOut => Box::new(quint::ease_in_out),
}
},
Interpolation::Sine => {
match easing{
Ease::In | Ease::Auto => Box::new(sine::ease_in),
Ease::Out => Box::new(sine::ease_out),
Ease::InOut => Box::new(sine::ease_in_out),
}
},
_ => panic!("Only penner easings supported"),
}
}
pub fn parse_curve(curve: &blender::Object) -> FCurve{
let num_beztriples = curve.get::<i32>("totvert").unwrap();
let curval = *curve.get("curval").unwrap();
let rna_path = curve.get_str("rna_path").unwrap().to_string();
let array_index = *curve.get("array_index").unwrap();
let beztriples = curve
.get_data_slice::<BezTriple>("bezt", *num_beztriples as usize)
.unwrap()
.to_vec();
let fpt = curve.get_data_slice::<FPoint>("fpt", *num_beztriples as usize)
.map(|fpt| fpt.to_vec())
.ok();
let color = curve.get_slice("color").unwrap();
let flag = *curve.get("flag").unwrap();
let extend = *curve.get("extend").unwrap();
let modifiers = curve.get_list("modifiers").unwrap()
.into_iter()
.filter_map(|modifier|{
let ty = *modifier.get::<ModifierType>("type").unwrap();
if ty == ModifierType::Cycles {
let data = *modifier.get::<ModifierCycle>("data").unwrap();
let modifier = Modifier{
data: ModifierData::Cycles(data),
flag: *modifier.get::<ModifierFlags>("flag").unwrap(),
influence: *modifier.get::<f32>("influence").unwrap(),
start_frame: *modifier.get::<f32>("sfra").unwrap(),
end_frame: *modifier.get::<f32>("efra").unwrap(),
blend_in: *modifier.get::<f32>("blendin").unwrap(),
blend_out: *modifier.get::<f32>("blendout").unwrap(),
};
Some(modifier)
}else{
None
}
}).collect::<Vec<_>>();
let path: String = rna_path
.chars()
.take_while(|c| *c!='[')
.collect();
let object: String = rna_path
.chars()
.skip_while(|c| *c!='"')
.skip(1)
.take_while(|c| *c!='"')
.collect();
let driver = curve.get_object("driver")
.map(|driver| parse_driver(&driver))
.ok();
let component: String = if rna_path.contains("["){
rna_path
.chars()
.skip_while(|c| *c!='[')
.skip_while(|c| *c!=']')
.skip(2)
.collect()
}else{
rna_path.clone()
};
let component = match component.as_str(){
"location" => Component::Location,
"rotation_quaternion" => Component::RotationQuat,
"rotation_euler" => Component::RotationEuler,
"rotation_axis_angle" => Component::RotationAxisAngle,
"scale" => Component::Scale,
"value" => Component::Value,
"hide_view" => Component::HideView,
"hide_render" => Component::HideRender,
"hide" => Component::Hide,
_ => Component::Other(component)
};
let min_frame = beztriples.first()
.map(|bezt| bezt.from().x as isize);
let max_frame = beztriples.last()
.map(|bezt| bezt.from().x as isize);
let lookup = beztriples.iter().map(|bezt| (bezt.from().x * PRECISSION) as i32).collect();
FCurve{
bezt: beztriples,
lookup,
fpt,
curval: curval,
rna_path: rna_path,
path: path,
object: object,
component: component,
array_index: array_index,
flag,
extend: extend,
color: rgba!(color[0], color[1], color[2], 1.0),
min_frame,
max_frame,
modifiers,
driver
}
}
pub fn parse_action(action: blender::Object, fps: f32) -> Option<Action>{
if action.structure().name() == "bAction"{
let action_name = action.name().unwrap().to_string();
let curves = action.get_list("curves").unwrap();
let curves = curves.iter().map(|fcurve|{
let curve = parse_curve(&fcurve);
let key = CurvesKey{
object: curve.object.clone().into(),
component: Cow::Owned(curve.component.clone()),
};
(key, curve)
}).into_group_map();
let min_frame =
curves.values()
.filter_map(|curves| curves.iter()
.filter_map(|curve| curve.min_frame)
.min())
.min()
.unwrap_or(0);
let max_frame =
curves.values()
.filter_map(|curves| curves.iter()
.filter_map(|curve| curve.max_frame)
.max())
.max()
.unwrap_or(0);
let num_frames = max_frame - min_frame;
let curves = curves.into_iter().collect();
Some(Action{
name: action_name,
curves,
fps,
min_frame,
max_frame: max_frame,
num_frames,
})
}else{
None
}
}
pub fn parse_nla_tracks_for(obj: &blender::Object, fps: f32) -> Option<HashMap<String, Action>>{
obj.get_object("adt").ok().map(|animdata|{
let nlatracks = animdata.get_list("nla_tracks").unwrap();
nlatracks.iter().flat_map(|nlatrack|{
let nlastrips = nlatrack.get_list("strips").unwrap();
nlastrips.iter().filter_map(|nlastrip|{
nlastrip.get_object("act").ok()
.and_then(|action| parse_action(action, fps))
.map(|action| (action.name().to_string(), action))
})
}).collect()
})
}
pub fn parse_default_action_for(obj: &blender::Object, fps: f32) -> Option<Action>{
let animdata = obj.get_object("adt").ok()?;
let action = animdata.get_object("action").ok()?;
parse_action(action, fps)
}
pub fn default_action_name_for<'a>(
obj: &'a blender::Object,
library_id: &LibraryId,
libraries: &HashMap<LibraryId, blender::File>) -> Option<ObjectId>
{
let default_action = obj.get_object("adt").ok()?;
if let Some(lib_id) = library_id.linked_library_id(&default_action){
let library = &libraries[&lib_id];
let default_action = library.object_by_name(default_action.name().unwrap())?;
let action = default_action.get_object("action").ok()?;
let action_name = action.name().ok()?;
Some(ObjectId::new(lib_id, action_name))
}else{
let action = default_action.get_object("action").ok()?;
let action_name = action.name().ok()?;
Some(ObjectId::new(library_id.clone(), action_name))
}
}
pub fn parse_drivers_for(obj: &blender::Object) -> Vec<FCurve>{
if let Ok(animdata) = obj.get_object("adt") {
if let Ok(drivers) = animdata.get_list("drivers") {
return drivers.iter().map(|driver| parse_curve(&driver)).collect()
}
}
vec![]
}
pub fn fps_for_file(blend: &blender::File) -> f32 {
let render_data = blend.objects("Scene").into_iter()
.next()
.unwrap()
.get_object("r")
.unwrap();
let frs_sec = *render_data.get::<u16>("frs_sec").unwrap() as f32;
let frs_sec_base = *render_data.get::<f32>("frs_sec_base").unwrap();
frs_sec / frs_sec_base
}
pub fn parse_actions(
blend: &blender::File,
library_id: &LibraryId,
libraries: &HashMap<LibraryId, blender::File>) -> HashMap<ObjectId, Action>
{
let fps = fps_for_file(blend);
blend.objects("bAction").into_iter()
.filter_map(|action| {
if let Some(lib_id) = library_id.linked_library_id(&action){
let library = &libraries[&lib_id];
let action = library.object_by_name(action.name().unwrap())?;
let action = parse_action(action, fps)?;
let id = ObjectId::new(lib_id.clone(), action.name());
Some((id, action))
}else{
let action = parse_action(action, fps)?;
let id = ObjectId::new(library_id.clone(), action.name());
Some((id, action))
}
}).collect()
}