use blender;
use crate::model;
use std::mem;
use na::{Mat3, Mat4, Pnt3, Quat, ToVec, Unit, UnitQuat, Vec3, clamp, geometry::Rotation3, one, vec3};
use color::Rgb;
use angle::{Rad, Angle};
use crate::bones;
use std::ops::{Add, Mul, Sub};
use std::path::{Path, PathBuf};
use std::fmt::{self, Display};
use crate::curves;
#[inline]
pub fn to_vec3(m: &[f32]) -> Vec3{
vec3(m[0], m[1], m[2])
}
#[inline]
pub fn to_quat(q: &[f32]) -> UnitQuat{
UnitQuat::from_quaternion(Quat::new(q[0], q[1], q[2], q[3]))
}
#[cfg_attr(feature = "serialize", derive(Serialize, Deserialize))]
#[derive(Clone,Debug,Copy,PartialEq,Eq)]
#[allow(dead_code)]
#[repr(i16)]
pub enum RotMode {
Quat = 0,
EulerXYZ = 1,
EulerXZY = 2,
EulerYXZ = 3,
EulerYZX = 4,
EulerZXY = 5,
EulerZYX = 6,
AxisAngle = -1,
}
#[cfg_attr(feature = "serialize", derive(Serialize, Deserialize))]
#[derive(Clone,Debug,Copy,PartialEq)]
pub enum Rotation {
Quat(UnitQuat),
Euler(RotOrder, Vec3<Rad<f32>>),
AxisAngle(Unit<Vec3>, Rad<f32>),
}
impl RotMode{
pub fn to_euler_rotation(self, rot: Vec3<Rad<f32>>) -> Rotation{
Rotation::Euler(self.to_rot_order(), rot)
}
pub fn to_rot_order(self) -> RotOrder{
match self{
RotMode::EulerXYZ => RotOrder::XYZ,
RotMode::EulerXZY => RotOrder::XZY,
RotMode::EulerYXZ => RotOrder::YXZ,
RotMode::EulerYZX => RotOrder::YZX,
RotMode::EulerZXY => RotOrder::ZXY,
RotMode::EulerZYX => RotOrder::ZYX,
_ => panic!("Not an euler rotation")
}
}
}
impl Rotation{
pub fn into_quat(self) -> UnitQuat {
match self{
Rotation::Quat(q) => q,
Rotation::Euler(rot_order, rot) =>
euler_to_quaternion(&rot, rot_order),
Rotation::AxisAngle(axis, angle) =>
UnitQuat::from_axis_angle(&axis, angle.value()),
}
}
pub fn to_euler(&self) -> Vec3<Rad<f32>> {
match self {
Rotation::Quat(q) => {
let (roll, pitch, yaw) = q.euler_angles();
vec3(Rad(roll), Rad(pitch), Rad(yaw))
}
Rotation::Euler(_, euler)
=> *euler,
Rotation::AxisAngle(axis, angle)
=> Rotation::Quat(UnitQuat::from_axis_angle(axis, angle.value())).to_euler()
}
}
}
#[cfg_attr(feature = "serialize", derive(Serialize, Deserialize))]
#[derive(Clone,Debug)]
pub struct Transformations{
pub position: Pnt3,
pub rotation: Rotation,
pub drotation: Option<Rotation>,
pub scale: Vec3,
pub parent_inv: Option<Mat4>,
pub const_inv: Option<Mat4>,
pub parent_bone: Option<String>,
}
impl Transformations{
pub fn local_model_matrix(&self) -> Mat4{
trafo_from_parts(&self.position, &self.into_quat(), &self.scale)
}
pub fn into_quat(&self) -> UnitQuat {
if let Some(drot) = self.drotation{
drot.into_quat() * self.rotation.into_quat()
}else{
self.rotation.into_quat()
}
}
pub fn global_model_matrix(&self, parent: Option<&Mat4>) -> Mat4{
let local = self.local_model_matrix();
if let Some(parent) = parent{
if let Some(parent_inv) = self.parent_inv{
parent * parent_inv * local
}else{
parent * local
}
}else{
local
}
}
pub fn into_driver_transform(&self) -> curves::DriverTransformation {
let euler = self.rotation.to_euler();
curves::DriverTransformation {
loc: self.position,
rot: vec3!(euler.x.value(), euler.y.value(), euler.z.value()),
scale: self.scale,
}
}
}
pub fn trafo_from_parts(pos: &Pnt3, orientation: &UnitQuat, scale: &Vec3) -> Mat4{
let scale = Mat3::from_diagonal(scale);
let rot = orientation.to_rotation_matrix();
let mat = rot * scale;
Mat4::from_columns(&[
vec4!(mat.column(0), 0.),
vec4!(mat.column(1), 0.),
vec4!(mat.column(2), 0.),
vec4!(pos.to_vec(), 1.),
])
}
#[inline]
pub fn transformations(blend_obj: &blender::Object) -> Transformations{
let rot_mode: RotMode = *blend_obj.get("rotmode").unwrap();
let rotation;
let drotation;
match rot_mode{
RotMode::Quat => {
let q: &[f32;4] = blend_obj.get("quat").unwrap();
rotation = Rotation::Quat(to_quat(q));
let q: &[f32;4] = blend_obj.get("dquat").unwrap();
drotation = Rotation::Quat(to_quat(q));
}
RotMode::AxisAngle => {
let rotaxis: Unit<Vec3> = *blend_obj.get("rotAxis").unwrap();
let rotangle = *blend_obj.get("rotAngle").unwrap();
rotation = Rotation::AxisAngle(rotaxis, rotangle);
let rotaxis: Unit<Vec3> = *blend_obj.get("drotAxis").unwrap();
let rotangle = *blend_obj.get("drotAngle").unwrap();
drotation = Rotation::AxisAngle(rotaxis, rotangle);
}
_euler_rot_order => {
let rot = *blend_obj.get("rot").unwrap();
rotation = rot_mode.to_euler_rotation(rot);
let drot = *blend_obj.get("drot").unwrap();
drotation = rot_mode.to_euler_rotation(drot);
}
}
let pos: Pnt3 = *blend_obj.get("loc").unwrap();
let dpos: Vec3 = *blend_obj.get("dloc").unwrap();
let scale: Vec3 = *blend_obj.get("size").unwrap();
let dscale: Vec3 = *blend_obj.get("dscale").unwrap();
let mut position = pos + dpos;
let scale = scale.component_mul(&dscale);
let const_inv: Mat4 = *blend_obj.get("constinv").unwrap();
let const_inv = if const_inv == one::<Mat4>(){
None
}else{
Some(const_inv)
};
let parent_type: model::ParentType = *blend_obj.get("partype").unwrap();
let parent_inv: Option<Mat4>;
let parent_bone_name: Option<String>;
if let Ok(parent) = blend_obj.get_object("parent"){
if parent_type != model::ParentType::Bone {
parent_bone_name = None;
}else{
parent_bone_name = blend_obj.get_str("parsubstr").map(|parent_bone| parent_bone.to_owned()).ok();
fn search_bone<'a>(parent: &blender::Object<'a>, name: &str) -> Option<blender::Object<'a>>{
if parent.name().unwrap() == name {
return Some(parent.clone())
}
for child in parent.get_list("childbase").unwrap().iter() {
if let Some(bone) = search_bone(&child, name) {
return Some(bone)
}
}
None
}
let mut parent_bone = None;
for base in parent.get_object("data").unwrap().get_list("bonebase").unwrap().iter() {
if let Some(bone) = search_bone(&base, parent_bone_name.as_ref().unwrap()) {
parent_bone = Some(bone);
break;
}
}
let parent_bone = parent_bone
.expect(&format!(
"object parented to bone but couldn't find bone {}",
parent_bone_name.as_ref().unwrap()
));
if !parent_bone.get::<bones::Flags>("flag").unwrap().contains(bones::Flags::RELATIVE_PARENTING) {
let pose = parent.get_object("pose").unwrap();
let channel = pose.get_list("chanbase").unwrap().iter()
.find(|channel| channel.name().unwrap() == parent_bone_name.as_ref().unwrap())
.unwrap();
let pose_mat = channel.get::<Mat4>("pose_mat").unwrap();
let vec = pose_mat.column(1).xyz();
let length = *channel.get_object("bone").unwrap().get::<f32>("length").unwrap();
position += vec * length;
}
}
let blend_parent_inv = *blend_obj.get::<Mat4>("parentinv").unwrap();
if blend_parent_inv == one::<Mat4>() {
parent_inv = None
}else{
parent_inv = Some(blend_parent_inv);
}
}else{
parent_inv = None;
parent_bone_name = None;
}
Transformations{
position,
rotation,
drotation: Some(drotation),
scale,
parent_inv,
const_inv,
parent_bone: parent_bone_name,
}
}
#[cfg_attr(feature = "serialize", derive(Serialize, Deserialize))]
#[derive(Clone,Debug,Copy,PartialEq,Eq)]
pub enum RotOrder{
XYZ,
XZY,
YXZ,
YZX,
ZXY,
ZYX,
}
impl RotOrder {
pub fn inverse(self) -> RotOrder {
match self {
RotOrder::XYZ => RotOrder::ZYX,
RotOrder::XZY => RotOrder::YZX,
RotOrder::YXZ => RotOrder::ZXY,
RotOrder::YZX => RotOrder::XZY,
RotOrder::ZXY => RotOrder::YXZ,
RotOrder::ZYX => RotOrder::XYZ,
}
}
}
#[cfg_attr(feature = "serialize", derive(Serialize, Deserialize))]
struct AxisParity{
axis: Vec3<usize>,
parity: bool,
}
impl RotOrder{
fn to_axis_parity(self) -> AxisParity{
let axis_parity = match self{
RotOrder::XYZ => ([0usize, 1, 2], false),
RotOrder::XZY => ([0, 2, 1], true),
RotOrder::YXZ => ([1, 0, 2], true),
RotOrder::YZX => ([1, 2, 0], false),
RotOrder::ZXY => ([2, 0, 1], false),
RotOrder::ZYX => ([2, 1, 0], true),
};
AxisParity{
axis: vec3(axis_parity.0[0], axis_parity.0[1], axis_parity.0[2]),
parity: axis_parity.1,
}
}
}
pub fn euler_to_quaternion(rot: &Vec3<Rad<f32>>, rot_order: RotOrder) -> UnitQuat{
let r = rot_order.to_axis_parity();
let i = r.axis.x;
let j = r.axis.y;
let k = r.axis.z;
let ti = rot[i].value() as f64 * 0.5;
let tj = rot[j].value() as f64 * if r.parity { -0.5 } else { 0.5 };
let th = rot[k].value() as f64 * 0.5;
let ci = ti.cos();
let cj = tj.cos();
let ch = th.cos();
let si = ti.sin();
let sj = tj.sin();
let sh = th.sin();
let cc = ci * ch;
let cs = ci * sh;
let sc = si * ch;
let ss = si * sh;
let mut a: [f64;3] = unsafe{ mem::MaybeUninit::uninit().assume_init() };
a[i] = cj * sc - sj * cs;
a[j] = cj * ss + sj * cc;
a[k] = cj * cs - sj * sc;
let mut q: [f32; 4] = unsafe{ mem::MaybeUninit::uninit().assume_init() };
q[0] = (cj * cc + sj * ss) as f32;
q[1] = a[0] as f32;
q[2] = a[1] as f32;
q[3] = a[2] as f32;
if r.parity { q[j + 1] = -q[j + 1] };
to_quat(&q)
}
pub fn blackbody_old(kelvin: f32) -> Rgb<f32>{
let temp = kelvin / 100.;
let r = if temp <= 66.{
1.0
}else{
let r = temp - 60.;
let r = 329.698727446 * r.powf(-0.1332047592);
clamp( r / 255., 0.0, 1.0 )
};
let g = if temp <= 10.{
0.0
}else if temp <= 66.{
let g = temp;
let g = 99.4708025861 * g.ln() - 161.1195681661;
clamp( g / 255., 0.0, 1.0 )
}else{
let g = temp - 60.;
let g = 288.1221695283 * g.powf(-0.0755148492);
clamp( g / 255., 0.0, 1.0)
};
let b = if temp >= 66.{
1.0
}else if temp <= 19.{
0.0
}else{
let b = temp - 10.;
let b = 138.5177312231 * b.ln() - 305.0447927307;
clamp( b/255., 0.0, 1.0 )
};
rgb!(r,g,b)
}
pub fn blackbody(kelvin: f32) -> Rgb<f32>{
let temp = kelvin / 100.;
let r = if temp <= 66.{
1.0
}else{
let x = temp - 55.;
let a = 351.97690566805693;
let b = 0.114206453784165;
let c = -40.25366309332127;
let r = a + b * x + c * x.ln();
clamp( r / 255., 0.0, 1.0 )
};
let g = if temp <= 10.{
0.0
}else if temp <= 66.{
let a = -155.25485562709179;
let b = -0.44596950469579133;
let c = 104.49216199393888;
let x = temp - 2.;
let g = a + b * x + c * x.ln();
clamp( g / 255., 0.0, 1.0 )
}else{
let a = 325.4494125711974;
let b = 0.07943456536662342;
let c = -28.0852963507957;
let x = temp - 50.;
let g = a + b * x + c * x.ln();
clamp( g / 255., 0.0, 1.0 )
};
let b = if temp >= 66.{
1.0
}else if temp <= 19.{
0.0
}else{
let a = -254.76935184120902;
let b = 0.8274096064007395;
let c = 115.67994401066147;
let x = temp - 10.;
let b = a + b * x + c * x.ln();
clamp( b / 255., 0.0, 1.0 )
};
rgb!(r,g,b)
}
#[inline]
pub fn lerp<T>(p0: T, p1: T, pct: f32) -> T
where T: Add<T, Output = T> + Sub<T, Output = T> + Mul<f32, Output = T> + Clone
{
p0.clone() + (p1 - p0) * pct
}
#[inline]
pub fn rel_lerp<T: Sub<T, Output = T> + Mul<f32, Output = T>>(p0: T, p1: T, pct: f32) -> T{
(p1 - p0) * pct
}
pub fn bezier_interpolate(from: f32, cp1: f32, cp2: f32, to: f32, pct: f32) -> f32{
let c = (cp1 - from) * 3.0;
let b = (cp2 - cp1) * 3.0 - c;
let a = to - from - c - b;
let t = pct;
let t2 = t*t;
let t3 = t2*t;
a*t3 + b*t2 + c*t + from
}
#[inline]
pub fn to_euler(q: &UnitQuat, rot_order: RotOrder) -> (Rad<f32>, Rad<f32>, Rad<f32>){
let rot = q.to_rotation_matrix();
rot_to_euler(&rot, rot_order)
}
pub fn rot_to_euler(rot: &Rotation3<f32>, rot_order: RotOrder) -> (Rad<f32>, Rad<f32>, Rad<f32>) {
let r = rot_order.to_axis_parity();
let m = rot.matrix();
let i = r.axis.x;
let j = r.axis.y;
let k = r.axis.z;
let cy = m[(i,i)].hypot(m[(j,i)]);
let (x, y, z);
if cy > 16. * f32::EPSILON {
let x1 = m[(k,j)].atan2(m[(k,k)]);
let y1 = (-m[(k,i)]).atan2(cy);
let z1 = m[(j,i)].atan2(m[(i,i)]);
let x2 = (-m[(k,j)]).atan2(-m[(k,k)]);
let y2 = (-m[(k,i)]).atan2(-cy);
let z2 = (-m[(j,i)]).atan2(-m[(i,i)]);
let d1 = x1.abs() + y1.abs() + z1.abs();
let d2 = x2.abs() + y2.abs() + z2.abs();
if d1 > d2 {
x = x2;
y = y2;
z = z2;
}else{
x = x1;
y = y1;
z = z1;
}
}else{
x = (-m[(j,k)]).atan2(m[(j,j)]);
y = (-m[(k,i)]).atan2(cy);
z = 0.;
}
let (x,y,z) = match rot_order{
RotOrder::XYZ => (x,y,z),
RotOrder::XZY => (x,z,y),
RotOrder::YXZ => (y,x,z),
RotOrder::YZX => (z,x,y),
RotOrder::ZXY => (y,z,x),
RotOrder::ZYX => (z,y,x),
};
if r.parity {
(Rad(-x), Rad(-y), Rad(-z))
}else{
(Rad(x), Rad(y), Rad(z))
}
}
#[cfg(not(target_os="unknown"))]
#[macro_export]
macro_rules! time{
($descr: expr, $func: block) => (
{
use std::time;
let then = time::Instant::now();
let ret = $func;
let duration = time::Instant::now() - then;
debug!("{} time: {}ms", $descr, duration.as_secs() as f32 * 1000. + duration.subsec_nanos() as f32/1000000f32);
ret
}
);
}
#[cfg(target_os="unknown")]
#[macro_export]
macro_rules! time{
($descr: expr, $func: block) => ( $func );
}
#[cfg_attr(feature = "serialize", derive(Serialize, Deserialize))]
#[derive(Debug,Clone)]
pub enum Property{
String(String, String),
Int(String, i32),
Float(String, f32),
ArrayInt(String, Vec<i32>),
ArrayFloat(String, Vec<f32>),
ArrayDouble(String, Vec<f64>),
Group(String, Vec<Property>),
Double(String, f64),
}
impl<'a> From<blender::IdProperty<'a>> for Property{
fn from(p: blender::IdProperty<'a>) -> Property{
let name = p.name().to_owned();
match p.value(){
blender::IdPropertyValue::String(o) =>
Property::String(name, o.to_owned()),
blender::IdPropertyValue::Int(o) =>
Property::Int(name, o),
blender::IdPropertyValue::Float(o) =>
Property::Float(name, o),
blender::IdPropertyValue::ArrayInt(o) =>
Property::ArrayInt(name, o.to_vec()),
blender::IdPropertyValue::ArrayFloat(o) =>
Property::ArrayFloat(name, o.to_vec()),
blender::IdPropertyValue::ArrayDouble(o) =>
Property::ArrayDouble(name, o.to_vec()),
blender::IdPropertyValue::Group(o) =>
Property::Group(name, o.iter().map(|p| Property::from(p)).collect()),
blender::IdPropertyValue::Double(o) =>
Property::Double(name, o),
blender::IdPropertyValue::Id() =>
unimplemented!(),
blender::IdPropertyValue::IdArray() =>
unimplemented!(),
}
}
}
impl Property{
pub fn ty(&self) -> blender::PropertyType{
match self{
Property::String(_, _) => blender::PropertyType::String,
Property::Int(_, _) => blender::PropertyType::Int,
Property::Float(_, _) => blender::PropertyType::Float,
Property::ArrayInt(_, _) => blender::PropertyType::Array,
Property::ArrayFloat(_, _) => blender::PropertyType::Array,
Property::ArrayDouble(_, _) => blender::PropertyType::Array,
Property::Group(_, _) => blender::PropertyType::Group,
Property::Double(_, _) => blender::PropertyType::Double,
}
}
pub fn name(&self) -> &str {
match self{
Property::String(n, _) => &n,
Property::Int(n, _) => &n,
Property::Float(n, _) => &n,
Property::ArrayInt(n, _) => &n,
Property::ArrayFloat(n, _) => &n,
Property::ArrayDouble(n, _) => &n,
Property::Group(n, _) => &n,
Property::Double(n, _) => &n,
}
}
}
pub fn custom_properties(obj: &blender::Object) -> Vec<Property>{
obj.custom_properties()
.map(|g| g.iter()
.filter(|p| p.name() != "_RNA_UI")
.map(|p| Property::from(p))
.collect()
).unwrap_or_default()
}
#[derive(Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
#[cfg_attr(feature = "serialize", derive(Serialize, Deserialize))]
pub enum LibraryId{
Path(PathBuf),
Packed(Option<PathBuf>, String),
}
impl LibraryId {
pub fn join<L: Into<LibraryId>>(&self, other: L) -> LibraryId {
let other = other.into();
match other {
LibraryId::Path(_) => other,
LibraryId::Packed(ref path, ref name) => {
if path.is_some() {
other
}else{
match self {
LibraryId::Path(path)
=> LibraryId::Packed(Some(path.clone()), name.clone()),
LibraryId::Packed(path, parent_name)
=> LibraryId::Packed(path.clone(), parent_name.clone() + "/" + &name),
}
}
}
}
}
pub fn to_path(&self) -> PathBuf {
match self {
LibraryId::Path(path) => path.clone(),
LibraryId::Packed(Some(parent_path), name) => parent_path.join(name),
LibraryId::Packed(None, name) => PathBuf::from("//".to_owned() + name),
}
}
pub fn to_path_with_original_path<P: AsRef<Path>>(&self, p: P) -> PathBuf {
let original = p.as_ref();
match self {
LibraryId::Path(path) => path.clone(),
LibraryId::Packed(Some(parent_path), name) => parent_path.join(name),
LibraryId::Packed(None, name) => original.to_owned().join(name),
}
}
pub fn linked_library_id(&self, obj: &blender::Object) -> Option<LibraryId> {
if obj.structure().name() == "ID" {
let lib = obj.get_object("lib").unwrap();
let lib_name = &lib.get_str("name").unwrap()[2..];
let is_packed = lib.get_object("packedfile").is_ok();
if is_packed {
Some(self.join(lib_name))
}else{
let lib_path = match self {
LibraryId::Path(path) => if let Some(parent) = path.parent() {
parent.join(lib_name)
}else{
PathBuf::from(lib_name)
},
LibraryId::Packed(path, name)
=> if let Some(parent) = path.as_ref().and_then(|p| p.parent()) {
parent.join(lib_name)
}else{
PathBuf::from(lib_name)
}
};
Some(LibraryId::Path(lib_path))
}
}else{
None
}
}
pub fn object_id(&self, obj: &blender::Object) -> ObjectId {
if let Some(lib_id) = self.linked_library_id(obj) {
ObjectId::new(lib_id, obj.name().unwrap())
}else{
ObjectId::new(self.clone(), obj.name().unwrap())
}
}
}
impl From<&str> for LibraryId {
fn from(name: &str) -> LibraryId {
LibraryId::Packed(None, name.to_owned())
}
}
impl From<PathBuf> for LibraryId {
fn from(path: PathBuf) -> LibraryId {
LibraryId::Path(path)
}
}
impl Display for LibraryId {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
match self {
LibraryId::Path(p) =>
write!(fmt, "{}", p.display()),
LibraryId::Packed(Some(p), name) =>
write!(fmt, "packed file {} in blend file {}", name, p.display()),
LibraryId::Packed(None, name) =>
write!(fmt, "packed file {} in blend loaded from memory", name),
}
}
}
#[derive(Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
#[cfg_attr(feature = "serialize", derive(Serialize, Deserialize))]
pub struct ObjectId {
pub source_file: LibraryId,
pub name: String,
}
impl Display for ObjectId {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
match &self.source_file {
LibraryId::Path(p) =>
write!(fmt, "{} from {}", self.name, p.display()),
LibraryId::Packed(Some(p), name) =>
write!(fmt, "{} from packed file {} in blend file {}", self.name, name, p.display()),
LibraryId::Packed(None, name) =>
write!(fmt, "{} from packed file {} in blend loaded from memory", self.name, name),
}
}
}
impl ObjectId {
pub fn new<S: Into<String>>(library_id: LibraryId, name: S) -> ObjectId {
ObjectId{
source_file: library_id,
name: name.into(),
}
}
pub fn to_path(&self) -> PathBuf {
self.source_file.to_path().join(&self.name)
}
pub fn to_path_with_original_path<P: AsRef<Path>>(&self, p: P) -> PathBuf {
self.source_file.to_path_with_original_path(p).join(&self.name)
}
}