Implement avatar animation and skinning (#67)
This commit is contained in:
@@ -9,6 +9,11 @@ use roxmltree::{Document, Node};
|
||||
use std::collections::{HashMap, HashSet};
|
||||
use std::sync::Arc;
|
||||
|
||||
const MAX_SKELETON_BYTES: u64 = 8 * 1024 * 1024;
|
||||
const MAX_SKELETON_BONES: usize = 512;
|
||||
const MAX_COLLISION_VOLUMES: usize = 512;
|
||||
const MAX_SKELETON_DEPTH: usize = 128;
|
||||
|
||||
#[derive(Clone, Debug, Default, PartialEq)]
|
||||
pub struct JointBase {
|
||||
name: String,
|
||||
@@ -310,7 +315,17 @@ impl LindenSkeleton {
|
||||
let Some(file_name) = file_name else {
|
||||
return Self::get_default();
|
||||
};
|
||||
if std::fs::metadata(&file_name)
|
||||
.map_err(|_| crate::Error::Argument)?
|
||||
.len()
|
||||
> MAX_SKELETON_BYTES
|
||||
{
|
||||
return Err(crate::Error::Argument);
|
||||
}
|
||||
let text = std::fs::read_to_string(file_name).map_err(|_| crate::Error::Argument)?;
|
||||
if text.len() as u64 > MAX_SKELETON_BYTES {
|
||||
return Err(crate::Error::Argument);
|
||||
}
|
||||
parse_skeleton_xml(&text)
|
||||
}
|
||||
|
||||
@@ -469,10 +484,20 @@ fn parse_base(node: Node<'_, '_>) -> Result<JointBase, crate::Error> {
|
||||
))
|
||||
}
|
||||
|
||||
fn parse_joint(node: Node<'_, '_>, names: &mut HashSet<String>) -> Result<Joint, crate::Error> {
|
||||
fn parse_joint(
|
||||
node: Node<'_, '_>,
|
||||
names: &mut HashSet<String>,
|
||||
depth: usize,
|
||||
bone_count: &mut usize,
|
||||
collision_volume_count: &mut usize,
|
||||
) -> Result<Joint, crate::Error> {
|
||||
if !node.has_tag_name("bone") {
|
||||
return Err(parse_error(node_position(node)));
|
||||
}
|
||||
if depth > MAX_SKELETON_DEPTH || *bone_count >= MAX_SKELETON_BONES {
|
||||
return Err(parse_error(node_position(node)));
|
||||
}
|
||||
*bone_count += 1;
|
||||
let base = parse_base(node)?;
|
||||
if !names.insert(base.name.clone()) {
|
||||
return Err(parse_error(node_position(node)));
|
||||
@@ -487,12 +512,22 @@ fn parse_joint(node: Node<'_, '_>, names: &mut HashSet<String>) -> Result<Joint,
|
||||
let mut bones = Vec::new();
|
||||
for child in node.children().filter(Node::is_element) {
|
||||
if child.has_tag_name("collision_volume") {
|
||||
if *collision_volume_count >= MAX_COLLISION_VOLUMES {
|
||||
return Err(parse_error(node_position(child)));
|
||||
}
|
||||
if child.children().any(|node| node.is_element()) {
|
||||
return Err(parse_error(node_position(child)));
|
||||
}
|
||||
collision_volumes.push(CollisionVolume::from_base(parse_base(child)?));
|
||||
*collision_volume_count += 1;
|
||||
} else if child.has_tag_name("bone") {
|
||||
bones.push(parse_joint(child, names)?);
|
||||
bones.push(parse_joint(
|
||||
child,
|
||||
names,
|
||||
depth + 1,
|
||||
bone_count,
|
||||
collision_volume_count,
|
||||
)?);
|
||||
} else {
|
||||
return Err(parse_error(node_position(child)));
|
||||
}
|
||||
@@ -508,6 +543,9 @@ fn parse_joint(node: Node<'_, '_>, names: &mut HashSet<String>) -> Result<Joint,
|
||||
}
|
||||
|
||||
pub(crate) fn parse_skeleton_xml(text: &str) -> Result<LindenSkeleton, crate::Error> {
|
||||
if text.len() as u64 > MAX_SKELETON_BYTES {
|
||||
return Err(parse_error(0));
|
||||
}
|
||||
let document = Document::parse(text).map_err(|_| parse_error(0))?;
|
||||
let root = document.root_element();
|
||||
if !root.has_tag_name("linden_skeleton") {
|
||||
@@ -528,12 +566,23 @@ pub(crate) fn parse_skeleton_xml(text: &str) -> Result<LindenSkeleton, crate::Er
|
||||
let expected_collision_volumes = num_collision_volumes
|
||||
.parse::<usize>()
|
||||
.map_err(|_| parse_error(node_position(root)))?;
|
||||
if expected_bones > MAX_SKELETON_BONES || expected_collision_volumes > MAX_COLLISION_VOLUMES {
|
||||
return Err(parse_error(node_position(root)));
|
||||
}
|
||||
let root_bones = root.children().filter(Node::is_element).collect::<Vec<_>>();
|
||||
if root_bones.len() != 1 || !root_bones[0].has_tag_name("bone") {
|
||||
return Err(parse_error(node_position(root)));
|
||||
}
|
||||
let mut names = HashSet::new();
|
||||
let bone = parse_joint(root_bones[0], &mut names)?;
|
||||
let mut bone_count = 0;
|
||||
let mut collision_volume_count = 0;
|
||||
let bone = parse_joint(
|
||||
root_bones[0],
|
||||
&mut names,
|
||||
0,
|
||||
&mut bone_count,
|
||||
&mut collision_volume_count,
|
||||
)?;
|
||||
let (actual_bones, actual_collision_volumes) = skeleton_counts(&bone);
|
||||
if actual_bones != expected_bones || actual_collision_volumes != expected_collision_volumes {
|
||||
return Err(parse_error(node_position(root)));
|
||||
|
||||
Reference in New Issue
Block a user