use na::{Pnt3, Vec3, ToVec, vec4, clamp};
use crate::utils::{rel_lerp, LibraryId, ObjectId};
use crate::mesh::Mesh;
use crate::trimesh::{TriMesh, VertexExt, Vertex4Ext};
use crate::curves;
use crate::scene::SceneData;
use hashbrown::HashMap;
#[cfg(feature="rayon")]
use rayon::prelude::*;
#[cfg(feature="rayon")]
use rayon::iter::IntoParallelIterator;
bitflags!{
#[cfg_attr(feature = "serialize", derive(Serialize, Deserialize))]
pub struct Flags: u16{
const EXPAND = 1;
}
}
#[cfg_attr(feature = "serialize", derive(Serialize, Deserialize))]
#[derive(Clone,Debug,Copy,PartialEq,Eq)]
#[repr(u8)]
pub enum Ty{
Normal = 0,
Relative = 1,
}
#[cfg_attr(feature = "serialize", derive(Serialize, Deserialize))]
#[derive(Clone,Debug,Copy,PartialEq,Eq)]
#[repr(u16)]
pub enum BlockTy{
Linear = 0,
Cardinal = 1,
Bspline = 2,
CatmullRom = 3,
}
bitflags!{
#[cfg_attr(feature = "serialize", derive(Serialize, Deserialize))]
pub struct BlockFlags: u16{
const MUTE = 1 << 0;
const SEL = 1 << 1;
const LOCKED = 1 << 2;
}
}
#[cfg_attr(feature = "serialize", derive(Serialize, Deserialize))]
#[derive(Clone,Debug)]
pub enum BlockData{
Pnt(Vec<Pnt3>),
}
#[cfg_attr(feature = "serialize", derive(Serialize, Deserialize))]
#[derive(Clone,Debug)]
pub struct Block{
name: String,
vgroup: String,
influence: f32,
min: f32,
max: f32,
ty: BlockTy,
flag: BlockFlags,
relative: u16,
uid: i32,
data: BlockData,
}
impl Block {
pub fn is_enabled(&self) -> bool {
!self.flag.contains(BlockFlags::MUTE)
}
}
#[cfg_attr(feature = "serialize", derive(Serialize, Deserialize))]
#[derive(Clone,Debug)]
pub struct Key{
name: String,
default_action_name: Option<ObjectId>,
flags: Flags,
ty: Ty,
blocks: Vec<Block>,
drivers: Vec<curves::FCurve>
}
pub fn parse(
key: &blender::Object,
library_id: LibraryId,
libraries: &HashMap<LibraryId, blender::File>,
scene_data: &mut SceneData) -> Key
{
let elemstr = key.get_slice::<u8>("elemstr");
let elemsize = *key.get::<i32>("elemsize").unwrap();
let flags = *key.get::<Flags>("flag").unwrap();
let ty = *key.get::<Ty>("type").unwrap();
let keyblocks = key.get_list("block").unwrap();
let name = key.name().unwrap().to_owned();
let keyblocks = keyblocks.into_iter();
let default_action_name = curves::default_action_name_for(
key,
&library_id,
libraries
);
if let Some(default_action_id) = default_action_name.as_ref() {
if !scene_data.actions.contains_key(default_action_id) {
let library = &libraries[&default_action_id.source_file];
let fps = curves::fps_for_file(library);
let default_action = library.object_by_name(&default_action_id.name).unwrap();
let default_action = curves::parse_action(default_action, fps).unwrap();
scene_data.actions.insert(default_action_id.clone(), default_action);
}
}
let blocks = keyblocks.map(|keyblock: blender::Object| {
let influence = *keyblock.get::<f32>("curval").unwrap();
let min = *keyblock.get::<f32>("slidermin").unwrap();
let max = *keyblock.get::<f32>("slidermax").unwrap();
let blockty = *keyblock.get::<BlockTy>("type").unwrap();
let relative = *keyblock.get::<u16>("relative").unwrap();
let flag = *keyblock.get::<BlockFlags>("flag").unwrap();
let num_elem = *keyblock.get::<i32>("totelem").unwrap();
let uid = *keyblock.get::<i32>("uid").unwrap();
let data = keyblock.get_data_slice::<Pnt3>("data", num_elem as usize).unwrap();
let name = keyblock.name().unwrap();
let vgroup = keyblock.get_str("vgroup").unwrap();
Block{
name: name.to_owned(),
vgroup: vgroup.to_owned(),
influence,
min,
max,
ty: blockty,
flag,
relative,
uid,
data: BlockData::Pnt(data.to_vec()),
}
}).collect();
let drivers = curves::parse_drivers_for(key);
Key{
name,
default_action_name,
flags,
ty,
blocks,
drivers,
}
}
impl Key{
pub fn name(&self) -> &str{
&self.name
}
pub fn default_action(&self) -> Option<&ObjectId>{
self.default_action_name.as_ref()
}
pub fn to_trimesh(
self,
blendmesh: &blender::Object,
library_id: LibraryId,
mesh: &Mesh,
vertex_groups: &[String],
libraries: &HashMap<LibraryId, blender::File>,
scene_data: &mut SceneData) -> TrimeshKey
{
let blocks = self.blocks.into_iter()
.map(|block| block.to_trimesh(
blendmesh,
library_id.clone(),
mesh,
vertex_groups,
libraries,
scene_data,
))
.collect();
TrimeshKey{
name: self.name,
default_action_name: self.default_action_name,
flags: self.flags,
ty: self.ty,
blocks,
drivers: self.drivers,
}
}
pub fn drivers(&self) -> &[curves::FCurve] {
&self.drivers
}
}
impl Block {
fn to_trimesh(
self,
blendmesh: &blender::Object,
library_id: LibraryId,
mesh: &Mesh,
vertex_groups: &[String],
libraries: &HashMap<LibraryId, blender::File>,
scene_data: &mut SceneData,) -> TrimeshBlock
{
let BlockData::Pnt(data) = self.data;
assert_eq!(data.len(), mesh.mvert.len());
let mut mesh = mesh.clone();
for (mvert, pnt) in mesh.mvert.iter_mut().zip(data.into_iter()){
mvert.position = pnt.to_vec();
}
let trimesh = TriMesh::from(
blendmesh,
library_id,
&mesh,
libraries,
scene_data,
);
let data = trimesh
.original_vertices()
.iter()
.map(|v| v.position)
.collect();
let mut weights = None;
let mut vgroup_idx = None;
let mut weight_groups = vec![];
if self.vgroup != "" {
let vgroup = &self.vgroup;
if let Some(vgroup) = vertex_groups.iter().position(|g| g == vgroup) {
weights = Some(trimesh.original_vertices()
.iter()
.map(|v| {
let dvert = &mesh.dvert[v.original_idx as usize];
let dw = &mesh.dweights[dvert.dw_start .. dvert.dw_end];
dw.iter()
.find(|w| w.def_nr == vgroup as i32)
.map(|w| w.weight)
.unwrap_or(0.0)
}).collect::<Vec<_>>());
let mut last = None;
for (i, w) in weights.as_ref().unwrap().iter().enumerate() {
if *w > 0. && last.is_none() {
last = Some(i)
}else if *w == 0. && last.is_some() {
let s = last .unwrap();
weight_groups.push(s..i);
last = None;
}
}
if let Some(last) = last {
weight_groups.push(last .. weights.as_ref().unwrap().len());
}
vgroup_idx = Some(vgroup);
}
}
TrimeshBlock{
name: self.name,
vgroup: self.vgroup,
vgroup_idx,
influence: self.influence,
min: self.min,
max: self.max,
ty: self.ty,
flag: self.flag,
relative: self.relative,
uid: self.uid,
data,
weights,
weight_groups,
}
}
}
#[cfg_attr(feature = "serialize", derive(Serialize, Deserialize))]
#[derive(Clone,Debug)]
pub struct TrimeshBlock{
name: String,
vgroup: String,
vgroup_idx: Option<usize>,
influence: f32,
min: f32,
max: f32,
ty: BlockTy,
flag: BlockFlags,
relative: u16,
uid: i32,
data: Vec<Vec3>,
weights: Option<Vec<f32>>,
weight_groups: Vec<std::ops::Range<usize>>,
}
impl TrimeshBlock {
pub fn is_enabled(&self) -> bool {
!self.flag.contains(BlockFlags::MUTE)
}
pub fn data(&self) -> &[Vec3] {
&self.data
}
}
#[cfg_attr(feature = "serialize", derive(Serialize, Deserialize))]
#[derive(Clone,Debug)]
pub struct TrimeshKey{
name: String,
default_action_name: Option<ObjectId>,
flags: Flags,
ty: Ty,
blocks: Vec<TrimeshBlock>,
drivers: Vec<curves::FCurve>,
}
impl TrimeshKey{
pub fn name(&self) -> &str{
&self.name
}
pub fn default_action(&self) -> Option<&ObjectId>{
self.default_action_name.as_ref()
}
pub fn keyblock(&self, key: &str) -> Option<&TrimeshBlock> {
self.blocks.iter().find(|block| block.name == key)
}
pub fn keyblock_vgroup(&self, key: &str) -> Option<&str> {
self.keyblock(key).map(|b| b.vgroup.as_str())
}
pub fn basis(&self) -> Option<&TrimeshBlock>{
self.blocks.get(0)
}
#[cfg(not(feature="rayon"))]
pub fn apply<'a, V>(&self, key: &str, dst: &mut [V], pct: f32)
where V: VertexExt + 'a
{
let block = self.keyblock(key).unwrap();
if !block.is_enabled() {
return
}
let pct = clamp(pct, block.min, block.max);
let to_apply = &block.data;
match self.ty{
Ty::Relative => {
let basis = &self.blocks[block.relative as usize].data;
if let Some(weights) = block.weights.as_ref() {
block.weight_groups.iter().for_each(|range| {
for (((basis, to), vertex), weight) in basis[range.clone()].iter()
.zip(&to_apply[range.clone()])
.zip(&mut dst[range.clone()])
.zip(&weights[range.clone()])
{
*vertex.position_mut() += rel_lerp(*basis, *to, pct * *weight);
}
});
}else{
for (vertex, (basis, to)) in dst.into_iter()
.zip(basis.iter().zip(to_apply.iter()))
{
*vertex.position_mut() = *vertex.position() + rel_lerp(*basis, *to, pct);
}
}
}
Ty::Normal => {
unimplemented!()
}
}
}
#[cfg(feature="rayon")]
pub fn apply<'a, V>(&self, key: &str, dst: &mut [V], pct: f32)
where V: VertexExt + Send + Sync + 'a,
{
let block = self.keyblock(key).unwrap();
if !block.is_enabled() {
return
}
let pct = clamp(pct, block.min, block.max);
let to_apply = &block.data;
match self.ty{
Ty::Relative => {
let basis = &self.blocks[block.relative as usize].data;
if let Some(weights) = block.weights.as_ref() {
block.weight_groups.iter().for_each(|range| {
for (((basis, to), vertex), weight) in basis[range.clone()].iter()
.zip(&to_apply[range.clone()])
.zip(&mut dst[range.clone()])
.zip(&weights[range.clone()])
{
*vertex.position_mut() += rel_lerp(*basis, *to, pct * *weight);
}
});
}else{
dst.into_par_iter()
.zip(basis.par_iter())
.zip(to_apply.par_iter())
.for_each(|((vertex, basis), to)|
{
*vertex.position_mut() = *vertex.position() + rel_lerp(*basis, *to, pct);
});
}
}
Ty::Normal => {
unimplemented!()
}
}
}
#[cfg(not(feature="rayon"))]
pub fn apply4<'a, V>(&self, key: &str, dst: &mut [V], pct: f32)
where V: Vertex4Ext + 'a
{
let block = self.keyblock(key).unwrap();
if !block.is_enabled() {
return
}
let pct = clamp(pct, block.min, block.max);
let to_apply = &block.data;
match self.ty{
Ty::Relative => {
let basis = &self.blocks[block.relative as usize].data;
if let Some(weights) = block.weights.as_ref() {
block.weight_groups.iter().for_each(|range| {
for (((basis, to), vertex), weight) in basis[range.clone()].iter()
.zip(&to_apply[range.clone()])
.zip(&mut dst[range.clone()])
.zip(&weights[range.clone()])
{
*vertex.position_mut() += vec4!(rel_lerp(*basis, *to, pct * *weight), 0.);
}
});
}else{
for (vertex, (basis, to)) in dst.into_iter()
.zip(basis.iter().zip(to_apply.iter()))
{
*vertex.position_mut() = *vertex.position() + vec4!(rel_lerp(*basis, *to, pct), 0.);
}
}
}
Ty::Normal => {
unimplemented!()
}
}
}
#[cfg(feature="rayon")]
pub fn apply4<'a, V>(&self, key: &str, dst: &mut [V], pct: f32)
where V: Vertex4Ext + Send + Sync + 'a,
{
let block = self.keyblock(key).unwrap();
if !block.is_enabled() {
return
}
let pct = clamp(pct, block.min, block.max);
let to_apply = &block.data;
match self.ty{
Ty::Relative => {
let basis = &self.blocks[block.relative as usize].data;
if let Some(weights) = block.weights.as_ref() {
block.weight_groups.iter().for_each(|range| {
for (((basis, to), vertex), weight) in basis[range.clone()].iter()
.zip(&to_apply[range.clone()])
.zip(&mut dst[range.clone()])
.zip(&weights[range.clone()])
{
*vertex.position_mut() += vec4!(rel_lerp(*basis, *to, pct * *weight), 0.);
}
});
}else{
dst.into_par_iter()
.zip(basis.par_iter())
.zip(to_apply.par_iter())
.for_each(|((vertex, basis), to)|
{
*vertex.position_mut() = *vertex.position() + vec4!(rel_lerp(*basis, *to, pct), 0.);
});
}
}
Ty::Normal => {
unimplemented!()
}
}
}
pub fn block_names(&self) -> impl Iterator<Item = &String>{
self.blocks.iter().map(|block| &block.name)
}
pub fn drivers(&self) -> &[curves::FCurve] {
&self.drivers
}
}