mirror of
https://github.com/storytold/storyteller-bevy.git
synced 2026-10-16 00:03:29 +00:00
a1779fb1ec
This PR adds `cargo fmt -- --check`, `nx lint ...` and `nx test ...` as GitHub Actions. This will cause future PRs to automatically fail when formatting issues, Clippy warnings, or test failures are present in the changed code.
627 lines
20 KiB
Rust
627 lines
20 KiB
Rust
#![allow(unused)]
|
|
|
|
use crate::{
|
|
errors::{LoadJointsError, LoadMotionError},
|
|
joint::{JointData, JointName},
|
|
Axis, Bvh, Channel, ChannelType, EnumeratedLines,
|
|
};
|
|
use bstr::ByteSlice;
|
|
use lexical::parse;
|
|
use std::time::Duration;
|
|
|
|
/*
|
|
use nom::{
|
|
alt, char, delimited, digit, do_parse, map, map_res, named, opt, pair, recognize, space, tag,
|
|
take_while, parse, ws, Err as NomErr, IResult,
|
|
};
|
|
*/
|
|
use smallvec::{smallvec, SmallVec};
|
|
use std::{convert::TryFrom, mem, str};
|
|
|
|
/*
|
|
named! {
|
|
unsigned_float(&[u8]) -> f64,
|
|
map!(
|
|
recognize!(alt!(
|
|
delimited!(digit, tag!("."), opt!(digit))
|
|
| delimited!(opt!(digit), tag!("."), digit)
|
|
)),
|
|
lexical::parse
|
|
)
|
|
}
|
|
|
|
named! {
|
|
float(&[u8]) -> f64,
|
|
map!(
|
|
pair!(
|
|
opt!(alt!(tag!("+") | tag!("-"))),
|
|
unsigned_float
|
|
),
|
|
|(sign, value): (Option<&[u8]>, f64)| {
|
|
sign.and_then(|s|
|
|
if s[0] == (b'-') {
|
|
Some(-1f64)
|
|
} else {
|
|
None
|
|
})
|
|
.unwrap_or(1f64) * value
|
|
}
|
|
)
|
|
}
|
|
|
|
named! {
|
|
unsigned_int(&[u8]) -> u64,
|
|
map!(
|
|
recognize!(
|
|
delimited!(space, digit, space)
|
|
),
|
|
lexical::parse
|
|
)
|
|
}
|
|
|
|
named! {
|
|
token(&[u8]) -> &[u8],
|
|
delimited!(
|
|
space,
|
|
take_while!(|ch| char::from(ch).is_alphanumeric()),
|
|
space
|
|
)
|
|
}
|
|
|
|
named!(single_channel(&[u8]) -> ChannelType,
|
|
map_res!(
|
|
alt!(
|
|
tag!("Xposition") |
|
|
tag!("Yposition") |
|
|
tag!("Zposition") |
|
|
tag!("Xrotation") |
|
|
tag!("Yrotation") |
|
|
tag!("Zrotation")
|
|
),
|
|
ChannelType::from_bytes
|
|
)
|
|
);
|
|
|
|
named! {
|
|
offset(&[u8]) -> Vector3<f32>,
|
|
ws!(
|
|
do_parse!(
|
|
tag!(b"OFFSET") >>
|
|
x: float >>
|
|
y: float >>
|
|
z: float >>
|
|
(Vector3 { x: x as f32, y: y as f32, z: z as f32 })
|
|
)
|
|
)
|
|
}
|
|
|
|
named! {
|
|
end_site(&[u8]) -> Vector3<f32>,
|
|
ws!(
|
|
do_parse!(
|
|
tag!(b"End") >>
|
|
tag!(b"Site") >>
|
|
end_site: delimited!(char!('{'), offset, char!('}')) >>
|
|
(end_site)
|
|
)
|
|
)
|
|
}
|
|
*/
|
|
/*
|
|
fn heirarchy<'a>(data: &'a [u8]) -> IResult<&'a [u8], Vec<JointData>> {
|
|
let mut joints = Vec::new();
|
|
let mut num_channels = 0usize;
|
|
let mut joint_index = 0usize;
|
|
|
|
let mut channels = |data: &'a [u8]| -> IResult<&'a [u8], SmallVec<[Channel; 6]>> {
|
|
let (mut data, expected_num_channels) = do_parse!(
|
|
data,
|
|
tag!(b"CHANNELS") >> num_channels: unsigned_int >> (num_channels)
|
|
)?;
|
|
|
|
let mut out_channels = smallvec![];
|
|
for i in 0..expected_num_channels {
|
|
let (new_data, channel_ty) = single_channel(data)?;
|
|
let channel = Channel::new(channel_ty, num_channels);
|
|
num_channels += 1;
|
|
data = new_data;
|
|
}
|
|
|
|
Ok((&*data, out_channels))
|
|
};
|
|
|
|
struct JointMembers {
|
|
name: JointName,
|
|
offset: Vector3<f32>,
|
|
channels: SmallVec<[Channel; 6]>,
|
|
end_site: Option<Vector3<f32>>,
|
|
}
|
|
|
|
let joint_members = |mut data: &'a [u8]| -> IResult<&'a [u8], JointMembers> {
|
|
let (new_data, name) =
|
|
do_parse!(data, tag!(b"Joint") >> joint_name: token >> (joint_name))?;
|
|
|
|
let mut offset_and_channels =
|
|
|data: &'a [u8]| -> IResult<&'a [u8], (Vector3<f32>, SmallVec<[Channel; 6]>)> {
|
|
do_parse!(data, offst: offset >> chans: channels >> (offst, chans))
|
|
};
|
|
|
|
let opt_end_site = |data: &'a [u8]| -> IResult<&'a [u8], Option<Vector3<f32>>> {
|
|
match end_site(data) {
|
|
Ok((data, site)) => Ok((data, Some(site))),
|
|
Err(NomErr::Error(_)) => Ok((data, None)),
|
|
// @TODO: is this right?
|
|
Err(e) => Err(e),
|
|
}
|
|
};
|
|
|
|
let (data, members) = do_parse!(
|
|
data,
|
|
name: token
|
|
>> char!('{')
|
|
>> offst_and_chans: offset_and_channels
|
|
>> end_site: opt_end_site
|
|
>> (name, offst_and_chans.0, offst_and_chans.1, end_site)
|
|
)?;
|
|
|
|
let members = JointMembers {
|
|
name: JointName::from(members.0),
|
|
offset: members.1,
|
|
channels: members.2,
|
|
end_site: members.3,
|
|
};
|
|
|
|
Ok((data, members))
|
|
};
|
|
|
|
/*
|
|
parse!(data,
|
|
do_parse!(
|
|
tag!("HIERARCHY") >>
|
|
opt!(
|
|
tag!("Root") >>
|
|
root_joint_name: token >>
|
|
char!('{') >>
|
|
root_offset: offset >>
|
|
root_channels: channels >>
|
|
char!('}') >>
|
|
()
|
|
) >>
|
|
()
|
|
)
|
|
);
|
|
*/
|
|
Ok((data, joints))
|
|
}
|
|
|
|
named! {
|
|
frame_metadata(&[u8]) -> (std::time::Duration, usize),
|
|
ws!(do_parse!(
|
|
tag!("Frames") >>
|
|
char!(':') >>
|
|
num_frames: unsigned_int >>
|
|
tag!("Frame") >>
|
|
tag!("Time") >>
|
|
char!(':') >>
|
|
frame_time: unsigned_float >>
|
|
(Duration::from_secs_f64(frame_time), num_frames as usize)
|
|
))
|
|
}
|
|
|
|
fn motion_section(
|
|
data: &[u8],
|
|
num_channels: usize,
|
|
) -> IResult<&[u8], (std::time::Duration, usize, Vec<f32>)> {
|
|
let (frame_time, num_frames) = frame_metadata(data)?;
|
|
let motion_data = Vec::with_capacity(num_channels * num_frames);
|
|
|
|
for f in 0..num_frames {
|
|
let chomped = do_parse!(data, take_while!(|ch| char::from(ch).is_ascii_whitespace()));
|
|
let (output, motion_val) = float(chomped);
|
|
motion_data.push(motion_val);
|
|
data = output;
|
|
}
|
|
|
|
Ok((data, (frame_time, num_frames, motion_data)))
|
|
}
|
|
*/
|
|
|
|
impl Bvh {
|
|
// @TODO: Remove panics
|
|
/// Logic for parsing the data from a `BufRead`.
|
|
pub(crate) fn read_joints(
|
|
&mut self,
|
|
lines: &mut EnumeratedLines<'_>,
|
|
) -> Result<(), LoadJointsError> {
|
|
const HEIRARCHY_KEYWORD: &[u8] = b"HIERARCHY";
|
|
|
|
const ROOT_KEYWORD: &[u8] = b"ROOT";
|
|
const JOINT_KEYWORD: &[u8] = b"JOINT";
|
|
const ENDSITE_KEYWORDS: &[&[u8]] = &[b"End", b"Site"];
|
|
|
|
const OPEN_BRACE: &[u8] = b"{";
|
|
const CLOSE_BRACE: &[u8] = b"}";
|
|
|
|
const OFFSET_KEYWORD: &[u8] = b"OFFSET";
|
|
const CHANNELS_KEYWORD: &[u8] = b"CHANNELS";
|
|
|
|
#[derive(Debug, Eq, PartialEq)]
|
|
enum ParseMode {
|
|
NotStarted,
|
|
InHeirarchy,
|
|
Finished,
|
|
}
|
|
|
|
#[allow(unused)]
|
|
#[derive(Eq, PartialEq)]
|
|
enum NextExpectedLine {
|
|
Hierarchy,
|
|
Channels,
|
|
Offset,
|
|
OpeningBrace,
|
|
ClosingBrace,
|
|
JointName,
|
|
RootName,
|
|
}
|
|
|
|
let mut joints = vec![];
|
|
let mut curr_mode = ParseMode::NotStarted;
|
|
let mut curr_channel = 0usize;
|
|
let (mut curr_index, mut curr_depth) = (0usize, 0usize);
|
|
let mut next_expected_line = NextExpectedLine::Hierarchy;
|
|
|
|
let mut curr_joint = JointData::empty_root();
|
|
let mut in_end_site = false;
|
|
let mut pushed_end_site_joint = false;
|
|
|
|
#[inline]
|
|
fn get_parent_index(joints: &[JointData], for_depth: usize) -> usize {
|
|
joints
|
|
.iter()
|
|
.rev()
|
|
.find(|jd| jd.depth() == for_depth.saturating_sub(2))
|
|
.and_then(|jd| jd.private_data().map(|p| p.self_index))
|
|
.unwrap_or(0)
|
|
}
|
|
|
|
for (line_num, line) in lines {
|
|
let line = line?;
|
|
let line = line.trim();
|
|
|
|
let mut tokens = line.fields_with(|c: char| c.is_ascii_whitespace() || c == ':');
|
|
|
|
let first_token = match tokens.next() {
|
|
Some(tok) => tok,
|
|
None => continue,
|
|
};
|
|
|
|
match first_token.as_bytes() {
|
|
HEIRARCHY_KEYWORD => {
|
|
if curr_mode != ParseMode::NotStarted {
|
|
panic!("Unexpected hierarchy");
|
|
}
|
|
curr_mode = ParseMode::InHeirarchy;
|
|
next_expected_line = NextExpectedLine::RootName;
|
|
}
|
|
ROOT_KEYWORD => {
|
|
if curr_mode != ParseMode::InHeirarchy
|
|
|| next_expected_line != NextExpectedLine::RootName
|
|
{
|
|
panic!("Unexpected root: {:?}", curr_mode);
|
|
}
|
|
|
|
if let Some(name) = tokens.next() {
|
|
curr_joint.set_name(name);
|
|
} else {
|
|
panic!("Missing root name!");
|
|
}
|
|
}
|
|
OPEN_BRACE => {
|
|
curr_depth += 1;
|
|
}
|
|
CLOSE_BRACE => {
|
|
curr_depth -= 1;
|
|
if curr_depth == 0 {
|
|
// We have closed the brace of the root joint.
|
|
curr_mode = ParseMode::Finished;
|
|
}
|
|
|
|
if in_end_site {
|
|
if let JointData::Child {
|
|
ref mut private, ..
|
|
} = curr_joint
|
|
{
|
|
private.self_index = curr_index;
|
|
private.parent_index = get_parent_index(&joints, curr_depth);
|
|
private.depth = curr_depth - 1;
|
|
}
|
|
|
|
let new_joint = mem::replace(&mut curr_joint, JointData::empty_child());
|
|
joints.push(new_joint);
|
|
curr_index += 1;
|
|
in_end_site = false;
|
|
pushed_end_site_joint = true;
|
|
}
|
|
}
|
|
kw if kw == ENDSITE_KEYWORDS[0] => {
|
|
if tokens.next() == Some(ENDSITE_KEYWORDS[1]) {
|
|
in_end_site = true;
|
|
} else {
|
|
panic!("Unexpected end keyword");
|
|
}
|
|
}
|
|
JOINT_KEYWORD => {
|
|
if curr_mode != ParseMode::InHeirarchy {
|
|
panic!("Unexpected Joint");
|
|
}
|
|
|
|
if !pushed_end_site_joint {
|
|
if let JointData::Child {
|
|
ref mut private, ..
|
|
} = curr_joint
|
|
{
|
|
private.self_index = curr_index;
|
|
private.parent_index = get_parent_index(&joints, curr_depth);
|
|
private.depth = curr_depth - 1;
|
|
}
|
|
|
|
let new_joint = mem::replace(&mut curr_joint, JointData::empty_child());
|
|
joints.push(new_joint);
|
|
|
|
curr_index += 1;
|
|
} else {
|
|
pushed_end_site_joint = false;
|
|
}
|
|
|
|
if let Some(mut name) = tokens.next() {
|
|
curr_joint.set_name(name);
|
|
} else {
|
|
panic!("Missing joint name!");
|
|
}
|
|
}
|
|
OFFSET_KEYWORD => {
|
|
if curr_mode != ParseMode::InHeirarchy {
|
|
return Err(LoadJointsError::UnexpectedOffsetSection { line: line_num });
|
|
}
|
|
|
|
let mut offset = [0.0, 0.0, 0.0];
|
|
|
|
macro_rules! parse_axis {
|
|
($axis_field:literal, $axis_enum:ident) => {
|
|
if let Some(tok) = tokens.next() {
|
|
offset[$axis_field] =
|
|
parse(tok).map_err(|e| LoadJointsError::ParseOffsetError {
|
|
parse_float_error: e,
|
|
axis: Axis::$axis_enum,
|
|
line: line_num,
|
|
})?;
|
|
} else {
|
|
return Err(LoadJointsError::MissingOffsetAxis {
|
|
axis: Axis::$axis_enum,
|
|
line: line_num,
|
|
});
|
|
}
|
|
};
|
|
}
|
|
|
|
parse_axis!(0, X);
|
|
parse_axis!(1, Y);
|
|
parse_axis!(2, Z);
|
|
|
|
curr_joint.set_offset(offset, in_end_site);
|
|
}
|
|
CHANNELS_KEYWORD => {
|
|
if curr_mode != ParseMode::InHeirarchy {
|
|
return Err(LoadJointsError::UnexpectedChannelsSection { line: line_num });
|
|
}
|
|
|
|
let num_channels: usize = tokens
|
|
.next()
|
|
.ok_or(LoadJointsError::ParseNumChannelsError {
|
|
error: None,
|
|
line: line_num,
|
|
})
|
|
.and_then(|tok| match parse(tok) {
|
|
Ok(c) => Ok(c),
|
|
Err(e) => Err(LoadJointsError::ParseNumChannelsError {
|
|
error: Some(e),
|
|
line: line_num,
|
|
}),
|
|
})?;
|
|
|
|
let mut channels: SmallVec<[Channel; 6]> = Default::default();
|
|
channels.reserve(num_channels);
|
|
|
|
for tok in tokens {
|
|
let channel_ty = ChannelType::try_from(tok).map_err(|e| {
|
|
LoadJointsError::ParseChannelError {
|
|
error: e,
|
|
line: line_num,
|
|
}
|
|
})?;
|
|
let channel = Channel::new(channel_ty, curr_channel);
|
|
curr_channel += 1;
|
|
channels.push(channel);
|
|
}
|
|
|
|
curr_joint.set_channels(channels);
|
|
}
|
|
_ => {}
|
|
}
|
|
|
|
if curr_mode == ParseMode::Finished {
|
|
break;
|
|
}
|
|
}
|
|
|
|
if curr_mode != ParseMode::Finished {
|
|
return Err(LoadJointsError::MissingRoot);
|
|
}
|
|
|
|
self.joints = joints;
|
|
self.num_channels = curr_channel;
|
|
|
|
Ok(())
|
|
}
|
|
|
|
pub(crate) fn read_motion(
|
|
&mut self,
|
|
lines: &mut EnumeratedLines<'_>,
|
|
) -> Result<(), LoadMotionError> {
|
|
const MOTION_KEYWORD: &[u8] = b"MOTION";
|
|
const FRAMES_KEYWORD: &[u8] = b"Frames";
|
|
const FRAME_TIME_KEYWORDS: &[&[u8]] = &[b"Frame", b"Time:"];
|
|
|
|
macro_rules! last_line_num {
|
|
() => {
|
|
lines.last_enumerator().unwrap_or(0)
|
|
};
|
|
}
|
|
|
|
lines
|
|
.next_non_empty_line()
|
|
.ok_or(LoadMotionError::MissingMotionSection {
|
|
line: last_line_num!(),
|
|
})
|
|
.and_then(|(line_num, line)| {
|
|
let line = line?;
|
|
let line = line.trim();
|
|
if line == MOTION_KEYWORD {
|
|
Ok(())
|
|
} else {
|
|
Err(LoadMotionError::MissingMotionSection { line: line_num })
|
|
}
|
|
})?;
|
|
|
|
let num_frames = lines
|
|
.next_non_empty_line()
|
|
.ok_or(LoadMotionError::MissingNumFrames {
|
|
parse_error: None,
|
|
line: last_line_num!(),
|
|
})
|
|
.and_then(|(line_num, line)| {
|
|
let line = line?;
|
|
let line = line.trim();
|
|
let mut tokens = line.fields_with(|c: char| c.is_ascii_whitespace() || c == ':');
|
|
|
|
if tokens.next() != Some(FRAMES_KEYWORD) {
|
|
return Err(LoadMotionError::MissingNumFrames {
|
|
parse_error: None,
|
|
line: line_num,
|
|
});
|
|
}
|
|
|
|
let parse_num_frames = |token: Option<&[u8]>| {
|
|
if let Some(num_frames) = token.and_then(|b| str::from_utf8(b).ok()) {
|
|
parse::<usize, _>(num_frames)
|
|
.map_err(|e| LoadMotionError::MissingNumFrames {
|
|
parse_error: Some(e),
|
|
line: line_num,
|
|
})
|
|
.map_err(Into::into)
|
|
} else {
|
|
Err(LoadMotionError::MissingNumFrames {
|
|
parse_error: None,
|
|
line: line_num,
|
|
})
|
|
}
|
|
};
|
|
|
|
match tokens.next() {
|
|
Some(tok) if tok == b":" => parse_num_frames(tokens.next()),
|
|
Some(tok) => parse_num_frames(Some(tok)),
|
|
None => Err(LoadMotionError::MissingNumFrames {
|
|
parse_error: None,
|
|
line: line_num,
|
|
}),
|
|
}
|
|
})?;
|
|
|
|
self.frame_time = lines
|
|
.next_non_empty_line()
|
|
.ok_or(LoadMotionError::MissingFrameTime {
|
|
parse_error: None,
|
|
line: last_line_num!(),
|
|
})
|
|
.and_then(|(line_num, line)| {
|
|
let line = line?;
|
|
let mut tokens = line.fields();
|
|
|
|
let frame_time_kw = tokens.next();
|
|
if frame_time_kw == FRAME_TIME_KEYWORDS.first().copied() {
|
|
// do nothing
|
|
} else {
|
|
return Err(LoadMotionError::MissingFrameTime {
|
|
parse_error: None,
|
|
line: line_num,
|
|
});
|
|
}
|
|
|
|
let frame_time_kw = tokens.next();
|
|
if frame_time_kw == FRAME_TIME_KEYWORDS.get(1).copied() {
|
|
// do nothing
|
|
} else {
|
|
return Err(LoadMotionError::MissingFrameTime {
|
|
parse_error: None,
|
|
line: line_num,
|
|
});
|
|
}
|
|
|
|
let parse_frame_time = |token: Option<&[u8]>| {
|
|
if let Some(frame_time) = token {
|
|
let frame_time_secs = parse::<f64, _>(frame_time).map_err(|e| {
|
|
LoadMotionError::MissingFrameTime {
|
|
parse_error: Some(e),
|
|
line: line_num,
|
|
}
|
|
})?;
|
|
Ok(Duration::from_secs_f64(frame_time_secs))
|
|
} else {
|
|
Err(LoadMotionError::MissingFrameTime {
|
|
parse_error: None,
|
|
line: line_num,
|
|
})
|
|
}
|
|
};
|
|
|
|
match tokens.next() {
|
|
Some(tok) if tok == b":" => parse_frame_time(tokens.next()),
|
|
Some(tok) => parse_frame_time(Some(tok)),
|
|
None => Err(LoadMotionError::MissingNumFrames {
|
|
parse_error: None,
|
|
line: line_num,
|
|
}),
|
|
}
|
|
})?;
|
|
|
|
let expected_total_motion_values = self.num_channels * num_frames;
|
|
|
|
self.motion_values.reserve(expected_total_motion_values);
|
|
|
|
for (line_num, line) in lines {
|
|
let line = line?;
|
|
let tokens = line.fields();
|
|
for (channel_index, token) in tokens.enumerate() {
|
|
let motion =
|
|
parse::<f32, _>(token).map_err(|e| LoadMotionError::ParseMotionSection {
|
|
parse_error: e,
|
|
channel_index,
|
|
line: line_num,
|
|
})?;
|
|
self.motion_values.push(motion);
|
|
}
|
|
}
|
|
|
|
if self.motion_values.len() != self.num_channels * num_frames {
|
|
return Err(LoadMotionError::MotionCountMismatch {
|
|
actual_total_motion_values: self.motion_values.len(),
|
|
expected_total_motion_values,
|
|
expected_num_frames: num_frames,
|
|
expected_num_clips: self.num_channels,
|
|
});
|
|
}
|
|
|
|
Ok(())
|
|
}
|
|
}
|