Generate skeleton attention and genepool catalogs (#49)
Some checks failed
Native code generation / deterministic (push) Successful in 5m4s
Imaging and meshing gate / native (push) Failing after 17s
JPEG 2000 feature / linux (push) Failing after 59s
Skia feature / linux (push) Failing after 1m31s

This commit is contained in:
2026-08-09 09:28:40 +00:00
parent 5f83ce61e1
commit 4a32025e37
18 changed files with 7018 additions and 423 deletions

View File

@@ -25,11 +25,17 @@ jobs:
run: | run: |
cargo test -p libremetaverse --test generated_catalogs cargo test -p libremetaverse --test generated_catalogs
cargo test -p libremetaverse-compat-tests --test appearance_visual_semantics cargo test -p libremetaverse-compat-tests --test appearance_visual_semantics
- name: Test generated skeleton, attention, and genepool catalogs
run: |
cargo test -p libremetaverse --test generated_avatar_catalogs
cargo test -p libremetaverse --lib linden_skeleton
cargo test -p libremetaverse --lib joint_base_support_category
cargo test -p libremetaverse --lib joint_get_aliases_list
- name: Regenerate twice without reference checkout access - name: Regenerate twice without reference checkout access
run: | run: |
cargo run -p libremetaverse-codegen -- generate cargo run -p libremetaverse-codegen -- generate
cargo run -p libremetaverse-codegen -- generate cargo run -p libremetaverse-codegen -- generate
git diff --exit-code -- codegen/generated crates/libremetaverse/src/packet_catalog.rs crates/libremetaverse/src/visual_catalog.rs crates/libremetaverse/src/foliage_catalog.rs git diff --exit-code -- codegen/generated crates/libremetaverse/src/packet_catalog.rs crates/libremetaverse/src/visual_catalog.rs crates/libremetaverse/src/foliage_catalog.rs crates/libremetaverse/src/skeleton_catalog.rs crates/libremetaverse/src/attention_catalog.rs crates/libremetaverse/src/genepool_catalog.rs
- name: Lint native generator - name: Lint native generator
run: | run: |
cargo clippy -p libremetaverse-codegen --all-targets -- -D warnings cargo clippy -p libremetaverse-codegen --all-targets -- -D warnings

1
Cargo.lock generated
View File

@@ -368,6 +368,7 @@ dependencies = [
"libremetaverse-imaging", "libremetaverse-imaging",
"libremetaverse-structured-data", "libremetaverse-structured-data",
"libremetaverse-types", "libremetaverse-types",
"roxmltree",
] ]
[[package]] [[package]]

View File

@@ -243,3 +243,10 @@ relationships, skeletal/volume morphs, and every foliage rendering property.
Schema validation rejects malformed or ambiguous input, while sorted visual-ID Schema validation rejects malformed or ambiguous input, while sorted visual-ID
and source-indexed foliage lookups preserve the ordering observable in the and source-indexed foliage lookups preserve the ordering observable in the
reference APIs and packet bytes. reference APIs and packet bytes.
The remaining avatar-data generators emit the full 133-bone/26-collision-volume
skeleton hierarchy, both masculine and feminine versions of the default and
updated 11-slot attention tables, and all 24 genepool archetypes with 3,360
visual-parameter values. Native skeleton traversal, alias lookup, expanded mesh
joint lists, custom XML loading, attention indexing, and archetype lookup now
implement the mapped behavior directly in Rust. Each checked-in catalog retains
its own input hash and license provenance.

View File

@@ -4,7 +4,7 @@ Generated by `python3 tools/generate_api_shims.py`; do not edit by hand.
| Assembly | Types | Members | Status | | Assembly | Types | Members | Status |
|---|---:|---:|---| |---|---:|---:|---|
| `LibreMetaverse` | 2,711 | 27,281 | native implementation: 16 types / 10,554 members; remaining surface is callable failure-only shims | | `LibreMetaverse` | 2,711 | 27,281 | native implementation: 26 types / 10,604 members; remaining surface is callable failure-only shims |
| `LibreMetaverse.Imaging.Abstractions` | 3 | 20 | native implementation: 3 types / 20 members; no generated shims remain | | `LibreMetaverse.Imaging.Abstractions` | 3 | 20 | native implementation: 3 types / 20 members; no generated shims remain |
| `LibreMetaverse.Imaging.Skia` | 1 | 3 | native implementation: 1 type / 3 members; no generated shims remain | | `LibreMetaverse.Imaging.Skia` | 1 | 3 | native implementation: 1 type / 3 members; no generated shims remain |
| `LibreMetaverse.LslTools` | 164 | 768 | callable failure-only shim | | `LibreMetaverse.LslTools` | 164 | 768 | callable failure-only shim |

View File

@@ -91,6 +91,32 @@ constant-time indexed lookup. Golden fixtures cover all catalog counts,
representative complete records, public getters/setters, appearance decoding, representative complete records, public getters/setters, appearance decoding,
and every generated relationship category. and every generated relationship category.
## Skeleton, attentions, and genepool
The skeleton generator validates the pinned hierarchy root, declared counts,
finite three-component transforms, unique joint names and aliases, collision
volumes, support categories, and child element schema. Its checked-in
[`skeleton_catalog.rs`](../crates/libremetaverse/src/skeleton_catalog.rs) output
constructs all 133 joints and 26 collision volumes in source hierarchy order.
The native skeleton records also implement depth-first enumeration, canonical
and alias lookup, expanded mesh-joint lists, the built-in loader, and validated
custom XML loading without a serializer or managed runtime.
The attention generator validates both pinned attention inputs independently,
requiring masculine and feminine sets with each of the nine named parameters
exactly once. The generated
[`attention_catalog.rs`](../crates/libremetaverse/src/attention_catalog.rs)
keeps the eleven `LookAtType` slots in enum-discriminant order, including the
observable zero-initialized `None` and `Clear` entries.
The genepool generator validates unique archetype names and parameter IDs,
finite values, and each ignored-but-schema-relevant texture UUID and slot. The
generated [`genepool_catalog.rs`](../crates/libremetaverse/src/genepool_catalog.rs)
preserves source archetype and parameter order for all 24 archetypes and 3,360
parameter values; exact-name lookup returns the original source index. Separate
generated headers retain the input hashes and BSD-3-Clause provenance for the
skeleton, both attention files, and genepool asset.
## Commands ## Commands
Regenerate every currently registered Rust output using only checked-in data: Regenerate every currently registered Rust output using only checked-in data:

View File

@@ -17,6 +17,7 @@ bcdec_rs = { version = "0.2.0", optional = true }
libremetaverse-imaging = { path = "../libremetaverse-imaging" } libremetaverse-imaging = { path = "../libremetaverse-imaging" }
libremetaverse-structured-data = { path = "../libremetaverse-structured-data" } libremetaverse-structured-data = { path = "../libremetaverse-structured-data" }
libremetaverse-types = { path = "../libremetaverse-types" } libremetaverse-types = { path = "../libremetaverse-types" }
roxmltree = "0.21.1"
[lints] [lints]
workspace = true workspace = true

View File

@@ -0,0 +1,147 @@
// @generated by libremetaverse-codegen; DO NOT EDIT.
// Regenerate: cargo run -p libremetaverse-codegen -- generate
// Generator: attentions
// Source: codegen/inputs/attentions.xml sha256=bdbc4ca02dd3bde78b86e796f7eb4ada0000e47be9a127e35d2b77f3010a20af license=BSD-3-Clause
// Source: codegen/inputs/attentionsN.xml sha256=80055e12a04d05ef5bb16e8c542cd6c218735f8c72f7e48317b8a34282d1ac5b license=BSD-3-Clause
#![allow(
clippy::missing_errors_doc,
clippy::must_use_candidate,
clippy::should_implement_trait
)]
use std::sync::OnceLock;
#[derive(Clone, Debug, Default, PartialEq)]
pub struct AttentionData {
priority: f32,
timeout: f32,
}
impl AttentionData {
pub fn priority(&self) -> f32 {
self.priority
}
pub fn set_priority(&mut self, value: f32) {
self.priority = value;
}
pub fn timeout(&self) -> f32 {
self.timeout
}
pub fn set_timeout(&mut self, value: f32) {
self.timeout = value;
}
}
#[derive(Clone, Debug, Default, PartialEq)]
pub struct AttentionSet {
name: String,
entries: Vec<AttentionData>,
}
impl AttentionSet {
pub fn get(&self, type_: crate::LookAtType) -> Result<AttentionData, crate::Error> {
self.entries.get(type_ as usize).cloned().ok_or(crate::Error::IndexOutOfRange)
}
pub fn entries(&self) -> Vec<AttentionData> {
self.entries.clone()
}
pub fn set_entries(&mut self, value: Vec<AttentionData>) {
self.entries = value;
}
pub fn name(&self) -> String {
self.name.clone()
}
pub fn set_name(&mut self, value: String) {
self.name = value;
}
}
fn default_sets() -> &'static [AttentionSet] {
static SETS: OnceLock<Vec<AttentionSet>> = OnceLock::new();
SETS.get_or_init(|| {
vec![
AttentionSet { name : "Masculine".to_owned(), entries :
vec![AttentionData { priority : 0.0_f32, timeout : 0.0_f32 },
AttentionData { priority : 1.0_f32, timeout : 3.0_f32 }, AttentionData {
priority : 3.0_f32, timeout : 4.0_f32 }, AttentionData { priority :
2.0_f32, timeout : 2.0_f32 }, AttentionData { priority : 3.0_f32, timeout
: 4.0_f32 }, AttentionData { priority : 4.0_f32, timeout : 1.0_f32 },
AttentionData { priority : 0.0_f32, timeout : - 1.0_f32 }, AttentionData
{ priority : 6.0_f32, timeout : - 1.0_f32 }, AttentionData { priority :
6.0_f32, timeout : - 1.0_f32 }, AttentionData { priority : 7.0_f32,
timeout : - 1.0_f32 }, AttentionData { priority : 0.0_f32, timeout :
0.0_f32 }] }, AttentionSet { name : "Feminine".to_owned(), entries :
vec![AttentionData { priority : 0.0_f32, timeout : 0.0_f32 },
AttentionData { priority : 1.0_f32, timeout : 3.0_f32 }, AttentionData {
priority : 3.0_f32, timeout : 4.0_f32 }, AttentionData { priority :
2.0_f32, timeout : 2.0_f32 }, AttentionData { priority : 3.0_f32, timeout
: 4.0_f32 }, AttentionData { priority : 4.0_f32, timeout : 1.0_f32 },
AttentionData { priority : 0.0_f32, timeout : - 1.0_f32 }, AttentionData
{ priority : 6.0_f32, timeout : - 1.0_f32 }, AttentionData { priority :
6.0_f32, timeout : - 1.0_f32 }, AttentionData { priority : 7.0_f32,
timeout : - 1.0_f32 }, AttentionData { priority : 0.0_f32, timeout :
0.0_f32 }] }
]
})
.as_slice()
}
fn updated_sets() -> &'static [AttentionSet] {
static SETS: OnceLock<Vec<AttentionSet>> = OnceLock::new();
SETS.get_or_init(|| {
vec![
AttentionSet { name : "Masculine".to_owned(), entries :
vec![AttentionData { priority : 0.0_f32, timeout : 0.0_f32 },
AttentionData { priority : 1.0_f32, timeout : 3.0_f32 }, AttentionData {
priority : 3.0_f32, timeout : 4.0_f32 }, AttentionData { priority :
2.0_f32, timeout : 2.0_f32 }, AttentionData { priority : 3.0_f32, timeout
: 3.0_f32 }, AttentionData { priority : 4.0_f32, timeout : 1.0_f32 },
AttentionData { priority : 5.0_f32, timeout : 3.0_f32 }, AttentionData {
priority : 3.5_f32, timeout : - 1.0_f32 }, AttentionData { priority :
3.5_f32, timeout : 20.0_f32 }, AttentionData { priority : 7.0_f32,
timeout : - 1.0_f32 }, AttentionData { priority : 0.0_f32, timeout :
0.0_f32 }] }, AttentionSet { name : "Feminine".to_owned(), entries :
vec![AttentionData { priority : 0.0_f32, timeout : 0.0_f32 },
AttentionData { priority : 1.0_f32, timeout : 3.0_f32 }, AttentionData {
priority : 3.0_f32, timeout : 4.0_f32 }, AttentionData { priority :
2.0_f32, timeout : 2.0_f32 }, AttentionData { priority : 3.0_f32, timeout
: 6.0_f32 }, AttentionData { priority : 4.0_f32, timeout : 1.0_f32 },
AttentionData { priority : 5.0_f32, timeout : 6.0_f32 }, AttentionData {
priority : 2.5_f32, timeout : - 1.0_f32 }, AttentionData { priority :
2.5_f32, timeout : 10.0_f32 }, AttentionData { priority : 7.0_f32,
timeout : - 1.0_f32 }, AttentionData { priority : 0.0_f32, timeout :
0.0_f32 }] }
]
})
.as_slice()
}
pub struct LindenAttentions;
impl LindenAttentions {
pub fn default() -> Vec<AttentionSet> {
default_sets().to_vec()
}
pub fn updated() -> Vec<AttentionSet> {
updated_sets().to_vec()
}
pub fn default_masculine() -> AttentionSet {
default_sets()[0].clone()
}
pub fn default_feminine() -> AttentionSet {
default_sets()[1].clone()
}
pub fn updated_masculine() -> AttentionSet {
updated_sets()[0].clone()
}
pub fn updated_feminine() -> AttentionSet {
updated_sets()[1].clone()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn generated_attention_shape() {
assert_eq!(default_sets().len(), 2);
assert_eq!(updated_sets().len(), 2);
assert!(
default_sets().iter().chain(updated_sets()).all(| set | set.entries.len() ==
11)
);
assert_eq!(default_sets() [0].name, "Masculine");
assert_eq!(default_sets() [1].name, "Feminine");
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -4789,33 +4789,10 @@ impl AppearanceSetEventArgs {
} }
/// C# type: `T:LibreMetaverse.ArchetypeParam`. /// C# type: `T:LibreMetaverse.ArchetypeParam`.
pub struct ArchetypeParam; /// C# member: `P:LibreMetaverse.ArchetypeParam.Id`.
impl ArchetypeParam { /// C# member: `P:LibreMetaverse.ArchetypeParam.Name`.
/// C# member: `P:LibreMetaverse.ArchetypeParam.Id`. /// C# member: `P:LibreMetaverse.ArchetypeParam.Value`.
pub fn id(&self) -> i32 { pub use crate::genepool_catalog::ArchetypeParam;
libremetaverse_types::unimplemented_api!("P:LibreMetaverse.ArchetypeParam.Id")
}
/// Setter for C# member: `P:LibreMetaverse.ArchetypeParam.Id`.
pub fn set_id(&mut self, value: i32) {
libremetaverse_types::unimplemented_api!("P:LibreMetaverse.ArchetypeParam.Id")
}
/// C# member: `P:LibreMetaverse.ArchetypeParam.Name`.
pub fn name(&self) -> String {
libremetaverse_types::unimplemented_api!("P:LibreMetaverse.ArchetypeParam.Name")
}
/// Setter for C# member: `P:LibreMetaverse.ArchetypeParam.Name`.
pub fn set_name(&mut self, value: String) {
libremetaverse_types::unimplemented_api!("P:LibreMetaverse.ArchetypeParam.Name")
}
/// C# member: `P:LibreMetaverse.ArchetypeParam.Value`.
pub fn value(&self) -> f32 {
libremetaverse_types::unimplemented_api!("P:LibreMetaverse.ArchetypeParam.Value")
}
/// Setter for C# member: `P:LibreMetaverse.ArchetypeParam.Value`.
pub fn set_value(&mut self, value: f32) {
libremetaverse_types::unimplemented_api!("P:LibreMetaverse.ArchetypeParam.Value")
}
}
/// C# type: `T:LibreMetaverse.AssetCache`. /// C# type: `T:LibreMetaverse.AssetCache`.
pub struct AssetCache { pub struct AssetCache {
@@ -5420,55 +5397,15 @@ impl AttachedSoundGainChangeEventArgs {
} }
/// C# type: `T:LibreMetaverse.AttentionData`. /// C# type: `T:LibreMetaverse.AttentionData`.
pub struct AttentionData; /// C# member: `P:LibreMetaverse.AttentionData.Priority`.
impl AttentionData { /// C# member: `P:LibreMetaverse.AttentionData.Timeout`.
/// C# member: `P:LibreMetaverse.AttentionData.Priority`. pub use crate::attention_catalog::AttentionData;
pub fn priority(&self) -> f32 {
libremetaverse_types::unimplemented_api!("P:LibreMetaverse.AttentionData.Priority")
}
/// Setter for C# member: `P:LibreMetaverse.AttentionData.Priority`.
pub fn set_priority(&mut self, value: f32) {
libremetaverse_types::unimplemented_api!("P:LibreMetaverse.AttentionData.Priority")
}
/// C# member: `P:LibreMetaverse.AttentionData.Timeout`.
pub fn timeout(&self) -> f32 {
libremetaverse_types::unimplemented_api!("P:LibreMetaverse.AttentionData.Timeout")
}
/// Setter for C# member: `P:LibreMetaverse.AttentionData.Timeout`.
pub fn set_timeout(&mut self, value: f32) {
libremetaverse_types::unimplemented_api!("P:LibreMetaverse.AttentionData.Timeout")
}
}
/// C# type: `T:LibreMetaverse.AttentionSet`. /// C# type: `T:LibreMetaverse.AttentionSet`.
pub struct AttentionSet; /// C# member: `M:LibreMetaverse.AttentionSet.Get(LibreMetaverse.LookAtType)`.
impl AttentionSet { /// C# member: `P:LibreMetaverse.AttentionSet.Entries`.
/// C# member: `M:LibreMetaverse.AttentionSet.Get(LibreMetaverse.LookAtType)`. /// C# member: `P:LibreMetaverse.AttentionSet.Name`.
pub fn get( pub use crate::attention_catalog::AttentionSet;
&self,
type_: libremetaverse::LookAtType,
) -> Result<libremetaverse::AttentionData, crate::Error> {
libremetaverse_types::not_implemented(
"M:LibreMetaverse.AttentionSet.Get(LibreMetaverse.LookAtType)",
)
}
/// C# member: `P:LibreMetaverse.AttentionSet.Entries`.
pub fn entries(&self) -> Vec<libremetaverse::AttentionData> {
libremetaverse_types::unimplemented_api!("P:LibreMetaverse.AttentionSet.Entries")
}
/// Setter for C# member: `P:LibreMetaverse.AttentionSet.Entries`.
pub fn set_entries(&mut self, value: Vec<libremetaverse::AttentionData>) {
libremetaverse_types::unimplemented_api!("P:LibreMetaverse.AttentionSet.Entries")
}
/// C# member: `P:LibreMetaverse.AttentionSet.Name`.
pub fn name(&self) -> String {
libremetaverse_types::unimplemented_api!("P:LibreMetaverse.AttentionSet.Name")
}
/// Setter for C# member: `P:LibreMetaverse.AttentionSet.Name`.
pub fn set_name(&mut self, value: String) {
libremetaverse_types::unimplemented_api!("P:LibreMetaverse.AttentionSet.Name")
}
}
/// C# type: `T:LibreMetaverse.Avatar`. /// C# type: `T:LibreMetaverse.Avatar`.
pub struct Avatar { pub struct Avatar {
@@ -10936,42 +10873,15 @@ impl GLTFMaterialUpdate {
} }
/// C# type: `T:LibreMetaverse.Genepool`. /// C# type: `T:LibreMetaverse.Genepool`.
pub struct Genepool; /// C# member: `F:LibreMetaverse.Genepool.Archetypes`.
impl Genepool { /// C# member: `M:LibreMetaverse.Genepool.FindIndex(System.String)`.
/// C# member: `F:LibreMetaverse.Genepool.Archetypes`. /// C# member: `M:LibreMetaverse.Genepool.Get(System.Int32)`.
pub fn archetypes() -> Vec<libremetaverse::GenepoolArchetype> { pub use crate::genepool_catalog::Genepool;
libremetaverse_types::unimplemented_api!("F:LibreMetaverse.Genepool.Archetypes")
}
/// C# member: `M:LibreMetaverse.Genepool.FindIndex(System.String)`.
pub fn find_index(name: String) -> Result<i32, crate::Error> {
libremetaverse_types::not_implemented("M:LibreMetaverse.Genepool.FindIndex(System.String)")
}
/// C# member: `M:LibreMetaverse.Genepool.Get(System.Int32)`.
pub fn get(index: i32) -> Result<libremetaverse::GenepoolArchetype, crate::Error> {
libremetaverse_types::not_implemented("M:LibreMetaverse.Genepool.Get(System.Int32)")
}
}
/// C# type: `T:LibreMetaverse.GenepoolArchetype`. /// C# type: `T:LibreMetaverse.GenepoolArchetype`.
pub struct GenepoolArchetype; /// C# member: `P:LibreMetaverse.GenepoolArchetype.Name`.
impl GenepoolArchetype { /// C# member: `P:LibreMetaverse.GenepoolArchetype.Params`.
/// C# member: `P:LibreMetaverse.GenepoolArchetype.Name`. pub use crate::genepool_catalog::GenepoolArchetype;
pub fn name(&self) -> String {
libremetaverse_types::unimplemented_api!("P:LibreMetaverse.GenepoolArchetype.Name")
}
/// Setter for C# member: `P:LibreMetaverse.GenepoolArchetype.Name`.
pub fn set_name(&mut self, value: String) {
libremetaverse_types::unimplemented_api!("P:LibreMetaverse.GenepoolArchetype.Name")
}
/// C# member: `P:LibreMetaverse.GenepoolArchetype.Params`.
pub fn params(&self) -> Vec<libremetaverse::ArchetypeParam> {
libremetaverse_types::unimplemented_api!("P:LibreMetaverse.GenepoolArchetype.Params")
}
/// Setter for C# member: `P:LibreMetaverse.GenepoolArchetype.Params`.
pub fn set_params(&mut self, value: Vec<libremetaverse::ArchetypeParam>) {
libremetaverse_types::unimplemented_api!("P:LibreMetaverse.GenepoolArchetype.Params")
}
}
/// C# type: `T:LibreMetaverse.GenericStreamingMessageEventArgs`. /// C# type: `T:LibreMetaverse.GenericStreamingMessageEventArgs`.
pub struct GenericStreamingMessageEventArgs; pub struct GenericStreamingMessageEventArgs;
@@ -17214,41 +17124,13 @@ impl LegacyEnvironmentEventArgs {
} }
/// C# type: `T:LibreMetaverse.LindenAttentions`. /// C# type: `T:LibreMetaverse.LindenAttentions`.
pub struct LindenAttentions; /// C# member: `F:LibreMetaverse.LindenAttentions.Default`.
impl LindenAttentions { /// C# member: `F:LibreMetaverse.LindenAttentions.Updated`.
/// C# member: `F:LibreMetaverse.LindenAttentions.Default`. /// C# member: `P:LibreMetaverse.LindenAttentions.DefaultFeminine`.
pub fn default() -> Vec<libremetaverse::AttentionSet> { /// C# member: `P:LibreMetaverse.LindenAttentions.DefaultMasculine`.
libremetaverse_types::unimplemented_api!("F:LibreMetaverse.LindenAttentions.Default") /// C# member: `P:LibreMetaverse.LindenAttentions.UpdatedFeminine`.
} /// C# member: `P:LibreMetaverse.LindenAttentions.UpdatedMasculine`.
/// C# member: `F:LibreMetaverse.LindenAttentions.Updated`. pub use crate::attention_catalog::LindenAttentions;
pub fn updated() -> Vec<libremetaverse::AttentionSet> {
libremetaverse_types::unimplemented_api!("F:LibreMetaverse.LindenAttentions.Updated")
}
/// C# member: `P:LibreMetaverse.LindenAttentions.DefaultFeminine`.
pub fn default_feminine() -> libremetaverse::AttentionSet {
libremetaverse_types::unimplemented_api!(
"P:LibreMetaverse.LindenAttentions.DefaultFeminine"
)
}
/// C# member: `P:LibreMetaverse.LindenAttentions.DefaultMasculine`.
pub fn default_masculine() -> libremetaverse::AttentionSet {
libremetaverse_types::unimplemented_api!(
"P:LibreMetaverse.LindenAttentions.DefaultMasculine"
)
}
/// C# member: `P:LibreMetaverse.LindenAttentions.UpdatedFeminine`.
pub fn updated_feminine() -> libremetaverse::AttentionSet {
libremetaverse_types::unimplemented_api!(
"P:LibreMetaverse.LindenAttentions.UpdatedFeminine"
)
}
/// C# member: `P:LibreMetaverse.LindenAttentions.UpdatedMasculine`.
pub fn updated_masculine() -> libremetaverse::AttentionSet {
libremetaverse_types::unimplemented_api!(
"P:LibreMetaverse.LindenAttentions.UpdatedMasculine"
)
}
}
/// C# type: `T:LibreMetaverse.LiveParticle`. /// C# type: `T:LibreMetaverse.LiveParticle`.
pub struct LiveParticle { pub struct LiveParticle {
@@ -170382,15 +170264,8 @@ pub mod rendering {
impl BoneTransform {} impl BoneTransform {}
/// C# type: `T:LibreMetaverse.Rendering.CollisionVolume`. /// C# type: `T:LibreMetaverse.Rendering.CollisionVolume`.
pub struct CollisionVolume; /// C# member: `M:LibreMetaverse.Rendering.CollisionVolume.#ctor`.
impl CollisionVolume { pub use crate::skeleton::CollisionVolume;
/// C# member: `M:LibreMetaverse.Rendering.CollisionVolume.#ctor`.
pub fn new() -> Result<Self, crate::Error> {
libremetaverse_types::not_implemented(
"M:LibreMetaverse.Rendering.CollisionVolume.#ctor",
)
}
}
/// C# type: `T:LibreMetaverse.Rendering.DetailLevel`. /// C# type: `T:LibreMetaverse.Rendering.DetailLevel`.
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
@@ -170667,150 +170542,27 @@ pub mod rendering {
} }
/// C# type: `T:LibreMetaverse.Rendering.Joint`. /// C# type: `T:LibreMetaverse.Rendering.Joint`.
pub struct Joint { /// C# member: `M:LibreMetaverse.Rendering.Joint.#ctor`.
/// Rust composition for C# base type `LibreMetaverse.Rendering.JointBase`. /// C# member: `M:LibreMetaverse.Rendering.Joint.GetAliasesList`.
pub base: libremetaverse::rendering::JointBase, /// C# member: `P:LibreMetaverse.Rendering.Joint.aliases`.
} /// C# member: `P:LibreMetaverse.Rendering.Joint.bone`.
impl Joint { /// C# member: `P:LibreMetaverse.Rendering.Joint.collision_volume`.
/// C# member: `M:LibreMetaverse.Rendering.Joint.#ctor`. /// C# member: `P:LibreMetaverse.Rendering.Joint.connected`.
pub fn new() -> Result<Self, crate::Error> { /// C# member: `P:LibreMetaverse.Rendering.Joint.pivot`.
libremetaverse_types::not_implemented("M:LibreMetaverse.Rendering.Joint.#ctor") pub use crate::skeleton::Joint;
}
/// C# member: `M:LibreMetaverse.Rendering.Joint.GetAliasesList`.
pub fn get_aliases_list(&self) -> Result<Vec<String>, crate::Error> {
libremetaverse_types::not_implemented("M:LibreMetaverse.Rendering.Joint.GetAliasesList")
}
/// C# member: `P:LibreMetaverse.Rendering.Joint.aliases`.
pub fn aliases(&self) -> String {
libremetaverse_types::unimplemented_api!("P:LibreMetaverse.Rendering.Joint.aliases")
}
/// Setter for C# member: `P:LibreMetaverse.Rendering.Joint.aliases`.
pub fn set_aliases(&mut self, value: String) {
libremetaverse_types::unimplemented_api!("P:LibreMetaverse.Rendering.Joint.aliases")
}
/// C# member: `P:LibreMetaverse.Rendering.Joint.bone`.
pub fn bone(&self) -> Option<Vec<libremetaverse::rendering::Joint>> {
libremetaverse_types::unimplemented_api!("P:LibreMetaverse.Rendering.Joint.bone")
}
/// Setter for C# member: `P:LibreMetaverse.Rendering.Joint.bone`.
pub fn set_bone(&mut self, value: Option<Vec<libremetaverse::rendering::Joint>>) {
libremetaverse_types::unimplemented_api!("P:LibreMetaverse.Rendering.Joint.bone")
}
/// C# member: `P:LibreMetaverse.Rendering.Joint.collision_volume`.
pub fn collision_volume(&self) -> Vec<libremetaverse::rendering::CollisionVolume> {
libremetaverse_types::unimplemented_api!(
"P:LibreMetaverse.Rendering.Joint.collision_volume"
)
}
/// Setter for C# member: `P:LibreMetaverse.Rendering.Joint.collision_volume`.
pub fn set_collision_volume(
&mut self,
value: Vec<libremetaverse::rendering::CollisionVolume>,
) {
libremetaverse_types::unimplemented_api!(
"P:LibreMetaverse.Rendering.Joint.collision_volume"
)
}
/// C# member: `P:LibreMetaverse.Rendering.Joint.connected`.
pub fn connected(&self) -> bool {
libremetaverse_types::unimplemented_api!("P:LibreMetaverse.Rendering.Joint.connected")
}
/// Setter for C# member: `P:LibreMetaverse.Rendering.Joint.connected`.
pub fn set_connected(&mut self, value: bool) {
libremetaverse_types::unimplemented_api!("P:LibreMetaverse.Rendering.Joint.connected")
}
/// C# member: `P:LibreMetaverse.Rendering.Joint.pivot`.
pub fn pivot(&self) -> Vec<f32> {
libremetaverse_types::unimplemented_api!("P:LibreMetaverse.Rendering.Joint.pivot")
}
/// Setter for C# member: `P:LibreMetaverse.Rendering.Joint.pivot`.
pub fn set_pivot(&mut self, value: Vec<f32>) {
libremetaverse_types::unimplemented_api!("P:LibreMetaverse.Rendering.Joint.pivot")
}
}
/// C# type: `T:LibreMetaverse.Rendering.JointBase`. /// C# type: `T:LibreMetaverse.Rendering.JointBase`.
pub struct JointBase; /// C# member: `M:LibreMetaverse.Rendering.JointBase.#ctor`.
impl JointBase { /// C# member: `P:LibreMetaverse.Rendering.JointBase.SupportCategory`.
/// C# member: `M:LibreMetaverse.Rendering.JointBase.#ctor`. /// C# member: `P:LibreMetaverse.Rendering.JointBase.end`.
pub fn new() -> Result<Self, crate::Error> { /// C# member: `P:LibreMetaverse.Rendering.JointBase.group`.
libremetaverse_types::not_implemented("M:LibreMetaverse.Rendering.JointBase.#ctor") /// C# member: `P:LibreMetaverse.Rendering.JointBase.name`.
} /// C# member: `P:LibreMetaverse.Rendering.JointBase.pos`.
/// C# member: `P:LibreMetaverse.Rendering.JointBase.SupportCategory`. /// C# member: `P:LibreMetaverse.Rendering.JointBase.reposition`.
pub fn support_category(&self) -> libremetaverse::rendering::JointSupportCategory { /// C# member: `P:LibreMetaverse.Rendering.JointBase.rot`.
libremetaverse_types::unimplemented_api!( /// C# member: `P:LibreMetaverse.Rendering.JointBase.scale`.
"P:LibreMetaverse.Rendering.JointBase.SupportCategory" /// C# member: `P:LibreMetaverse.Rendering.JointBase.support`.
) pub use crate::skeleton::JointBase;
}
/// C# member: `P:LibreMetaverse.Rendering.JointBase.end`.
pub fn end(&self) -> Vec<f32> {
libremetaverse_types::unimplemented_api!("P:LibreMetaverse.Rendering.JointBase.end")
}
/// Setter for C# member: `P:LibreMetaverse.Rendering.JointBase.end`.
pub fn set_end(&mut self, value: Vec<f32>) {
libremetaverse_types::unimplemented_api!("P:LibreMetaverse.Rendering.JointBase.end")
}
/// C# member: `P:LibreMetaverse.Rendering.JointBase.group`.
pub fn group(&self) -> String {
libremetaverse_types::unimplemented_api!("P:LibreMetaverse.Rendering.JointBase.group")
}
/// Setter for C# member: `P:LibreMetaverse.Rendering.JointBase.group`.
pub fn set_group(&mut self, value: String) {
libremetaverse_types::unimplemented_api!("P:LibreMetaverse.Rendering.JointBase.group")
}
/// C# member: `P:LibreMetaverse.Rendering.JointBase.name`.
pub fn name(&self) -> String {
libremetaverse_types::unimplemented_api!("P:LibreMetaverse.Rendering.JointBase.name")
}
/// Setter for C# member: `P:LibreMetaverse.Rendering.JointBase.name`.
pub fn set_name(&mut self, value: String) {
libremetaverse_types::unimplemented_api!("P:LibreMetaverse.Rendering.JointBase.name")
}
/// C# member: `P:LibreMetaverse.Rendering.JointBase.pos`.
pub fn pos(&self) -> Vec<f32> {
libremetaverse_types::unimplemented_api!("P:LibreMetaverse.Rendering.JointBase.pos")
}
/// Setter for C# member: `P:LibreMetaverse.Rendering.JointBase.pos`.
pub fn set_pos(&mut self, value: Vec<f32>) {
libremetaverse_types::unimplemented_api!("P:LibreMetaverse.Rendering.JointBase.pos")
}
/// C# member: `P:LibreMetaverse.Rendering.JointBase.reposition`.
pub fn reposition(&self) -> bool {
libremetaverse_types::unimplemented_api!(
"P:LibreMetaverse.Rendering.JointBase.reposition"
)
}
/// Setter for C# member: `P:LibreMetaverse.Rendering.JointBase.reposition`.
pub fn set_reposition(&mut self, value: bool) {
libremetaverse_types::unimplemented_api!(
"P:LibreMetaverse.Rendering.JointBase.reposition"
)
}
/// C# member: `P:LibreMetaverse.Rendering.JointBase.rot`.
pub fn rot(&self) -> Vec<f32> {
libremetaverse_types::unimplemented_api!("P:LibreMetaverse.Rendering.JointBase.rot")
}
/// Setter for C# member: `P:LibreMetaverse.Rendering.JointBase.rot`.
pub fn set_rot(&mut self, value: Vec<f32>) {
libremetaverse_types::unimplemented_api!("P:LibreMetaverse.Rendering.JointBase.rot")
}
/// C# member: `P:LibreMetaverse.Rendering.JointBase.scale`.
pub fn scale(&self) -> Vec<f32> {
libremetaverse_types::unimplemented_api!("P:LibreMetaverse.Rendering.JointBase.scale")
}
/// Setter for C# member: `P:LibreMetaverse.Rendering.JointBase.scale`.
pub fn set_scale(&mut self, value: Vec<f32>) {
libremetaverse_types::unimplemented_api!("P:LibreMetaverse.Rendering.JointBase.scale")
}
/// C# member: `P:LibreMetaverse.Rendering.JointBase.support`.
pub fn support(&self) -> String {
libremetaverse_types::unimplemented_api!("P:LibreMetaverse.Rendering.JointBase.support")
}
/// Setter for C# member: `P:LibreMetaverse.Rendering.JointBase.support`.
pub fn set_support(&mut self, value: String) {
libremetaverse_types::unimplemented_api!("P:LibreMetaverse.Rendering.JointBase.support")
}
}
/// C# type: `T:LibreMetaverse.Rendering.JointSupportCategory`. /// C# type: `T:LibreMetaverse.Rendering.JointSupportCategory`.
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
@@ -171284,133 +171036,20 @@ pub mod rendering {
} }
/// C# type: `T:LibreMetaverse.Rendering.LindenSkeleton`. /// C# type: `T:LibreMetaverse.Rendering.LindenSkeleton`.
pub struct LindenSkeleton; /// C# member: `M:LibreMetaverse.Rendering.LindenSkeleton.#ctor`.
impl LindenSkeleton { /// C# member: `M:LibreMetaverse.Rendering.LindenSkeleton.BuildExpandedJointList(System.Collections.Generic.IEnumerable{System.String})`.
/// C# member: `M:LibreMetaverse.Rendering.LindenSkeleton.#ctor`. /// C# member: `M:LibreMetaverse.Rendering.LindenSkeleton.BuildJointDictionary`.
pub fn new() -> Result<Self, crate::Error> { /// C# member: `M:LibreMetaverse.Rendering.LindenSkeleton.GetAllJoints`.
libremetaverse_types::not_implemented("M:LibreMetaverse.Rendering.LindenSkeleton.#ctor") /// C# member: `M:LibreMetaverse.Rendering.LindenSkeleton.GetBone(System.String)`.
} /// C# member: `M:LibreMetaverse.Rendering.LindenSkeleton.GetDefault`.
/// C# member: `M:LibreMetaverse.Rendering.LindenSkeleton.BuildExpandedJointList(System.Collections.Generic.IEnumerable{System.String})`. /// C# member: `M:LibreMetaverse.Rendering.LindenSkeleton.Load`.
pub fn build_expanded_joint_list( /// C# member: `M:LibreMetaverse.Rendering.LindenSkeleton.Load(System.String)`.
&self, /// C# member: `P:LibreMetaverse.Rendering.LindenSkeleton.bone`.
joints_filter: Box<dyn Iterator<Item = String>>, /// C# member: `P:LibreMetaverse.Rendering.LindenSkeleton.num_bones`.
) -> Result<Vec<String>, crate::Error> { /// C# member: `P:LibreMetaverse.Rendering.LindenSkeleton.num_collision_volumes`.
libremetaverse_types::not_implemented( /// C# member: `P:LibreMetaverse.Rendering.LindenSkeleton.version`.
"M:LibreMetaverse.Rendering.LindenSkeleton.BuildExpandedJointList(System.Collections.Generic.IEnumerable{System.String})", /// C# member: `P:LibreMetaverse.Rendering.LindenSkeleton.versionSpecified`.
) pub use crate::skeleton::LindenSkeleton;
}
/// C# member: `M:LibreMetaverse.Rendering.LindenSkeleton.BuildJointDictionary`.
pub fn build_joint_dictionary(
&self,
) -> Result<
std::collections::HashMap<String, std::sync::Arc<libremetaverse::rendering::Joint>>,
crate::Error,
> {
libremetaverse_types::not_implemented(
"M:LibreMetaverse.Rendering.LindenSkeleton.BuildJointDictionary",
)
}
/// C# member: `M:LibreMetaverse.Rendering.LindenSkeleton.GetAllJoints`.
pub fn get_all_joints(
&self,
) -> Result<
Box<dyn Iterator<Item = std::sync::Arc<libremetaverse::rendering::Joint>>>,
crate::Error,
> {
libremetaverse_types::not_implemented(
"M:LibreMetaverse.Rendering.LindenSkeleton.GetAllJoints",
)
}
/// C# member: `M:LibreMetaverse.Rendering.LindenSkeleton.GetBone(System.String)`.
pub fn get_bone(
&self,
name_or_alias: String,
) -> Result<Option<std::sync::Arc<libremetaverse::rendering::Joint>>, crate::Error>
{
libremetaverse_types::not_implemented(
"M:LibreMetaverse.Rendering.LindenSkeleton.GetBone(System.String)",
)
}
/// C# member: `M:LibreMetaverse.Rendering.LindenSkeleton.GetDefault`.
pub fn get_default() -> Result<libremetaverse::rendering::LindenSkeleton, crate::Error> {
libremetaverse_types::not_implemented(
"M:LibreMetaverse.Rendering.LindenSkeleton.GetDefault",
)
}
/// C# member: `M:LibreMetaverse.Rendering.LindenSkeleton.Load`.
pub fn load_with_method() -> Result<libremetaverse::rendering::LindenSkeleton, crate::Error>
{
libremetaverse_types::not_implemented("M:LibreMetaverse.Rendering.LindenSkeleton.Load")
}
/// C# member: `M:LibreMetaverse.Rendering.LindenSkeleton.Load(System.String)`.
pub fn load_with_string(
file_name: Option<String>,
) -> Result<libremetaverse::rendering::LindenSkeleton, crate::Error> {
libremetaverse_types::not_implemented(
"M:LibreMetaverse.Rendering.LindenSkeleton.Load(System.String)",
)
}
/// C# member: `P:LibreMetaverse.Rendering.LindenSkeleton.bone`.
pub fn bone(&self) -> libremetaverse::rendering::Joint {
libremetaverse_types::unimplemented_api!(
"P:LibreMetaverse.Rendering.LindenSkeleton.bone"
)
}
/// Setter for C# member: `P:LibreMetaverse.Rendering.LindenSkeleton.bone`.
pub fn set_bone(&mut self, value: libremetaverse::rendering::Joint) {
libremetaverse_types::unimplemented_api!(
"P:LibreMetaverse.Rendering.LindenSkeleton.bone"
)
}
/// C# member: `P:LibreMetaverse.Rendering.LindenSkeleton.num_bones`.
pub fn num_bones(&self) -> String {
libremetaverse_types::unimplemented_api!(
"P:LibreMetaverse.Rendering.LindenSkeleton.num_bones"
)
}
/// Setter for C# member: `P:LibreMetaverse.Rendering.LindenSkeleton.num_bones`.
pub fn set_num_bones(&mut self, value: String) {
libremetaverse_types::unimplemented_api!(
"P:LibreMetaverse.Rendering.LindenSkeleton.num_bones"
)
}
/// C# member: `P:LibreMetaverse.Rendering.LindenSkeleton.num_collision_volumes`.
pub fn num_collision_volumes(&self) -> String {
libremetaverse_types::unimplemented_api!(
"P:LibreMetaverse.Rendering.LindenSkeleton.num_collision_volumes"
)
}
/// Setter for C# member: `P:LibreMetaverse.Rendering.LindenSkeleton.num_collision_volumes`.
pub fn set_num_collision_volumes(&mut self, value: String) {
libremetaverse_types::unimplemented_api!(
"P:LibreMetaverse.Rendering.LindenSkeleton.num_collision_volumes"
)
}
/// C# member: `P:LibreMetaverse.Rendering.LindenSkeleton.version`.
pub fn version(&self) -> f32 {
libremetaverse_types::unimplemented_api!(
"P:LibreMetaverse.Rendering.LindenSkeleton.version"
)
}
/// Setter for C# member: `P:LibreMetaverse.Rendering.LindenSkeleton.version`.
pub fn set_version(&mut self, value: f32) {
libremetaverse_types::unimplemented_api!(
"P:LibreMetaverse.Rendering.LindenSkeleton.version"
)
}
/// C# member: `P:LibreMetaverse.Rendering.LindenSkeleton.versionSpecified`.
pub fn version_specified(&self) -> bool {
libremetaverse_types::unimplemented_api!(
"P:LibreMetaverse.Rendering.LindenSkeleton.versionSpecified"
)
}
/// Setter for C# member: `P:LibreMetaverse.Rendering.LindenSkeleton.versionSpecified`.
pub fn set_version_specified(&mut self, value: bool) {
libremetaverse_types::unimplemented_api!(
"P:LibreMetaverse.Rendering.LindenSkeleton.versionSpecified"
)
}
}
/// C# type: `T:LibreMetaverse.Rendering.Mesh`. /// C# type: `T:LibreMetaverse.Rendering.Mesh`.
pub struct Mesh { pub struct Mesh {

View File

@@ -2,15 +2,22 @@
extern crate self as libremetaverse; extern crate self as libremetaverse;
#[rustfmt::skip] // Deterministic machine output is formatted by the pinned generator.
mod attention_catalog;
mod bit_pack; mod bit_pack;
#[rustfmt::skip] // Deterministic machine output is formatted by the pinned generator. #[rustfmt::skip] // Deterministic machine output is formatted by the pinned generator.
mod foliage_catalog; mod foliage_catalog;
mod generated; mod generated;
#[rustfmt::skip] // Deterministic machine output is formatted by the pinned generator.
mod genepool_catalog;
mod j2k; mod j2k;
mod message_codec; mod message_codec;
#[rustfmt::skip] #[rustfmt::skip]
pub mod packet_catalog; pub mod packet_catalog;
mod packet_wire; mod packet_wire;
mod skeleton;
#[rustfmt::skip] // Deterministic machine output is formatted by the pinned generator.
mod skeleton_catalog;
mod targa; mod targa;
#[rustfmt::skip] // Deterministic machine output is formatted by the pinned generator. #[rustfmt::skip] // Deterministic machine output is formatted by the pinned generator.
mod visual_catalog; mod visual_catalog;

View File

@@ -0,0 +1,580 @@
//! Native skeleton records, traversal helpers, and custom XML loading.
#![allow(clippy::missing_errors_doc)] // Public signatures are fixed by the compatibility catalog.
#![allow(clippy::must_use_candidate)] // Getters mirror C# properties.
#![allow(clippy::needless_pass_by_value)] // Owned strings are fixed by the compatibility API.
#![allow(clippy::unnecessary_wraps)] // Constructors mirror fallible mapped signatures.
use roxmltree::{Document, Node};
use std::collections::{HashMap, HashSet};
use std::sync::Arc;
#[derive(Clone, Debug, Default, PartialEq)]
pub struct JointBase {
name: String,
pos: Vec<f32>,
rot: Vec<f32>,
scale: Vec<f32>,
group: String,
support: String,
end: Vec<f32>,
reposition: bool,
}
impl JointBase {
pub fn new() -> Result<Self, crate::Error> {
Ok(Self::default())
}
pub fn support_category(&self) -> crate::rendering::JointSupportCategory {
if self.support.eq_ignore_ascii_case("extended") {
crate::rendering::JointSupportCategory::Extended
} else {
crate::rendering::JointSupportCategory::Base
}
}
pub fn end(&self) -> Vec<f32> {
self.end.clone()
}
pub fn set_end(&mut self, value: Vec<f32>) {
self.end = value;
}
pub fn group(&self) -> String {
self.group.clone()
}
pub fn set_group(&mut self, value: String) {
self.group = value;
}
pub fn name(&self) -> String {
self.name.clone()
}
pub fn set_name(&mut self, value: String) {
self.name = value;
}
pub fn pos(&self) -> Vec<f32> {
self.pos.clone()
}
pub fn set_pos(&mut self, value: Vec<f32>) {
self.pos = value;
}
pub fn reposition(&self) -> bool {
self.reposition
}
pub fn set_reposition(&mut self, value: bool) {
self.reposition = value;
}
pub fn rot(&self) -> Vec<f32> {
self.rot.clone()
}
pub fn set_rot(&mut self, value: Vec<f32>) {
self.rot = value;
}
pub fn scale(&self) -> Vec<f32> {
self.scale.clone()
}
pub fn set_scale(&mut self, value: Vec<f32>) {
self.scale = value;
}
pub fn support(&self) -> String {
self.support.clone()
}
pub fn set_support(&mut self, value: String) {
self.support = value;
}
#[allow(clippy::too_many_arguments)]
pub(crate) fn from_parts(
name: String,
pos: Vec<f32>,
rot: Vec<f32>,
scale: Vec<f32>,
group: String,
support: String,
end: Vec<f32>,
reposition: bool,
) -> Self {
Self {
name,
pos,
rot,
scale,
group,
support,
end,
reposition,
}
}
}
#[derive(Clone, Debug, Default, PartialEq)]
pub struct CollisionVolume {
pub base: JointBase,
}
impl CollisionVolume {
pub fn new() -> Result<Self, crate::Error> {
Ok(Self::default())
}
pub(crate) fn from_base(base: JointBase) -> Self {
Self { base }
}
}
#[derive(Clone, Debug, PartialEq)]
pub struct Joint {
pub base: JointBase,
collision_volume: Vec<CollisionVolume>,
bone: Option<Vec<Joint>>,
pivot: Vec<f32>,
aliases: String,
connected: bool,
}
impl Default for Joint {
fn default() -> Self {
Self {
base: JointBase::default(),
collision_volume: Vec::new(),
bone: Some(Vec::new()),
pivot: Vec::new(),
aliases: String::new(),
connected: false,
}
}
}
impl Joint {
pub fn new() -> Result<Self, crate::Error> {
Ok(Self::default())
}
pub fn get_aliases_list(&self) -> Result<Vec<String>, crate::Error> {
Ok(self
.aliases
.split_ascii_whitespace()
.map(str::to_owned)
.collect())
}
pub fn aliases(&self) -> String {
self.aliases.clone()
}
pub fn set_aliases(&mut self, value: String) {
self.aliases = value;
}
pub fn bone(&self) -> Option<Vec<Self>> {
self.bone.clone()
}
pub fn set_bone(&mut self, value: Option<Vec<Self>>) {
self.bone = value;
}
pub fn collision_volume(&self) -> Vec<CollisionVolume> {
self.collision_volume.clone()
}
pub fn set_collision_volume(&mut self, value: Vec<CollisionVolume>) {
self.collision_volume = value;
}
pub fn connected(&self) -> bool {
self.connected
}
pub fn set_connected(&mut self, value: bool) {
self.connected = value;
}
pub fn pivot(&self) -> Vec<f32> {
self.pivot.clone()
}
pub fn set_pivot(&mut self, value: Vec<f32>) {
self.pivot = value;
}
pub(crate) fn from_parts(
base: JointBase,
collision_volume: Vec<CollisionVolume>,
bone: Vec<Self>,
pivot: Vec<f32>,
aliases: String,
connected: bool,
) -> Self {
Self {
base,
collision_volume,
bone: Some(bone),
pivot,
aliases,
connected,
}
}
}
#[derive(Clone, Debug, PartialEq)]
pub struct LindenSkeleton {
bone: Joint,
version: f32,
version_specified: bool,
num_bones: String,
num_collision_volumes: String,
}
impl Default for LindenSkeleton {
fn default() -> Self {
Self {
bone: Joint::default(),
version: 0.0,
version_specified: false,
num_bones: String::new(),
num_collision_volumes: String::new(),
}
}
}
impl LindenSkeleton {
pub fn new() -> Result<Self, crate::Error> {
Ok(Self::default())
}
pub fn build_expanded_joint_list(
&self,
joints_filter: Box<dyn Iterator<Item = String>>,
) -> Result<Vec<String>, crate::Error> {
let filter = joints_filter.collect::<Vec<_>>();
let mut expanded = Vec::new();
if let Some(children) = &self.bone.bone {
for child in children {
expand_joint(child, &self.bone.base.name, &mut expanded, &filter);
}
}
Ok(expanded)
}
pub fn build_joint_dictionary(&self) -> Result<HashMap<String, Arc<Joint>>, crate::Error> {
let mut dictionary = HashMap::new();
for joint in collect_joints(&self.bone) {
if !joint.base.name.is_empty() {
dictionary.insert(joint.base.name.clone(), Arc::clone(&joint));
}
for alias in joint.aliases.split_ascii_whitespace() {
if !alias.is_empty() {
dictionary.insert(alias.to_owned(), Arc::clone(&joint));
}
}
}
Ok(dictionary)
}
pub fn get_all_joints(&self) -> Result<Box<dyn Iterator<Item = Arc<Joint>>>, crate::Error> {
Ok(Box::new(collect_joints(&self.bone).into_iter()))
}
pub fn get_bone(&self, name_or_alias: String) -> Result<Option<Arc<Joint>>, crate::Error> {
if name_or_alias.is_empty() {
return Ok(None);
}
Ok(self.build_joint_dictionary()?.remove(&name_or_alias))
}
pub fn get_default() -> Result<Self, crate::Error> {
Ok(crate::skeleton_catalog::default_skeleton())
}
pub fn load_with_method() -> Result<Self, crate::Error> {
Self::get_default()
}
pub fn load_with_string(file_name: Option<String>) -> Result<Self, crate::Error> {
let Some(file_name) = file_name else {
return Self::get_default();
};
let text = std::fs::read_to_string(file_name).map_err(|_| crate::Error::Argument)?;
parse_skeleton_xml(&text)
}
pub fn bone(&self) -> Joint {
self.bone.clone()
}
pub fn set_bone(&mut self, value: Joint) {
self.bone = value;
}
pub fn num_bones(&self) -> String {
self.num_bones.clone()
}
pub fn set_num_bones(&mut self, value: String) {
self.num_bones = value;
}
pub fn num_collision_volumes(&self) -> String {
self.num_collision_volumes.clone()
}
pub fn set_num_collision_volumes(&mut self, value: String) {
self.num_collision_volumes = value;
}
pub fn version(&self) -> f32 {
self.version
}
pub fn set_version(&mut self, value: f32) {
self.version = value;
}
pub fn version_specified(&self) -> bool {
self.version_specified
}
pub fn set_version_specified(&mut self, value: bool) {
self.version_specified = value;
}
pub(crate) fn from_parts(
bone: Joint,
version: f32,
version_specified: bool,
num_bones: String,
num_collision_volumes: String,
) -> Self {
Self {
bone,
version,
version_specified,
num_bones,
num_collision_volumes,
}
}
}
fn collect_joints(root: &Joint) -> Vec<Arc<Joint>> {
fn visit(joint: &Joint, output: &mut Vec<Arc<Joint>>) {
output.push(Arc::new(joint.clone()));
if let Some(children) = &joint.bone {
for child in children {
visit(child, output);
}
}
}
let mut output = Vec::new();
visit(root, &mut output);
output
}
fn expand_joint(
current: &Joint,
effective_parent: &str,
expanded: &mut Vec<String>,
filter: &[String],
) {
let mut next_effective_parent = effective_parent;
if filter.iter().any(|name| name == &current.base.name) {
let parent_matches_last = expanded.last().is_some_and(|last| last == effective_parent);
if !parent_matches_last && filter.iter().any(|name| name == effective_parent) {
expanded.push(effective_parent.to_owned());
}
expanded.push(current.base.name.clone());
next_effective_parent = &current.base.name;
}
if let Some(children) = &current.bone {
for child in children {
expand_joint(child, next_effective_parent, expanded, filter);
}
}
}
fn parse_error(position: usize) -> crate::Error {
crate::Error::Parse {
position,
context: "avatar skeleton XML",
}
}
fn node_position(node: Node<'_, '_>) -> usize {
node.range().start
}
fn required_attribute<'a>(node: Node<'a, '_>, name: &str) -> Result<&'a str, crate::Error> {
node.attribute(name)
.filter(|value| !value.is_empty())
.ok_or_else(|| parse_error(node_position(node)))
}
fn vector_attribute(node: Node<'_, '_>, name: &str) -> Result<Vec<f32>, crate::Error> {
let Some(value) = node.attribute(name) else {
return Ok(Vec::new());
};
let values = value
.split_ascii_whitespace()
.map(str::parse::<f32>)
.collect::<Result<Vec<_>, _>>()
.map_err(|_| parse_error(node_position(node)))?;
if values.len() != 3 || values.iter().any(|value| !value.is_finite()) {
return Err(parse_error(node_position(node)));
}
Ok(values)
}
fn boolean_attribute(node: Node<'_, '_>, name: &str) -> Result<bool, crate::Error> {
match node.attribute(name) {
None => Ok(false),
Some(value) if value.eq_ignore_ascii_case("true") => Ok(true),
Some(value) if value.eq_ignore_ascii_case("false") => Ok(false),
Some(_) => Err(parse_error(node_position(node))),
}
}
fn parse_base(node: Node<'_, '_>) -> Result<JointBase, crate::Error> {
let support = node.attribute("support").unwrap_or_default();
if !support.is_empty()
&& !support.eq_ignore_ascii_case("base")
&& !support.eq_ignore_ascii_case("extended")
{
return Err(parse_error(node_position(node)));
}
Ok(JointBase::from_parts(
required_attribute(node, "name")?.to_owned(),
vector_attribute(node, "pos")?,
vector_attribute(node, "rot")?,
vector_attribute(node, "scale")?,
node.attribute("group").unwrap_or_default().to_owned(),
support.to_owned(),
vector_attribute(node, "end")?,
boolean_attribute(node, "reposition")?,
))
}
fn parse_joint(node: Node<'_, '_>, names: &mut HashSet<String>) -> Result<Joint, crate::Error> {
if !node.has_tag_name("bone") {
return Err(parse_error(node_position(node)));
}
let base = parse_base(node)?;
if !names.insert(base.name.clone()) {
return Err(parse_error(node_position(node)));
}
let aliases = node.attribute("aliases").unwrap_or_default().to_owned();
for alias in aliases.split_ascii_whitespace() {
if !names.insert(alias.to_owned()) {
return Err(parse_error(node_position(node)));
}
}
let mut collision_volumes = Vec::new();
let mut bones = Vec::new();
for child in node.children().filter(Node::is_element) {
if child.has_tag_name("collision_volume") {
if child.children().any(|node| node.is_element()) {
return Err(parse_error(node_position(child)));
}
collision_volumes.push(CollisionVolume::from_base(parse_base(child)?));
} else if child.has_tag_name("bone") {
bones.push(parse_joint(child, names)?);
} else {
return Err(parse_error(node_position(child)));
}
}
Ok(Joint::from_parts(
base,
collision_volumes,
bones,
vector_attribute(node, "pivot")?,
aliases,
boolean_attribute(node, "connected")?,
))
}
pub(crate) fn parse_skeleton_xml(text: &str) -> Result<LindenSkeleton, crate::Error> {
let document = Document::parse(text).map_err(|_| parse_error(0))?;
let root = document.root_element();
if !root.has_tag_name("linden_skeleton") {
return Err(parse_error(node_position(root)));
}
let version_text = required_attribute(root, "version")?;
let version = version_text
.parse::<f32>()
.map_err(|_| parse_error(node_position(root)))?;
if !version.is_finite() {
return Err(parse_error(node_position(root)));
}
let num_bones = required_attribute(root, "num_bones")?.to_owned();
let expected_bones = num_bones
.parse::<usize>()
.map_err(|_| parse_error(node_position(root)))?;
let num_collision_volumes = required_attribute(root, "num_collision_volumes")?.to_owned();
let expected_collision_volumes = num_collision_volumes
.parse::<usize>()
.map_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 (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)));
}
Ok(LindenSkeleton::from_parts(
bone,
version,
true,
num_bones,
num_collision_volumes,
))
}
pub(crate) fn skeleton_counts(root: &Joint) -> (usize, usize) {
let mut bones = 1;
let mut collision_volumes = root.collision_volume.len();
if let Some(children) = &root.bone {
for child in children {
let (child_bones, child_collision_volumes) = skeleton_counts(child);
bones += child_bones;
collision_volumes += child_collision_volumes;
}
}
(bones, collision_volumes)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn runtime_parser_rejects_count_mismatches_and_unknown_children() {
let wrong_count = "<linden_skeleton version=\"1\" num_bones=\"2\" num_collision_volumes=\"0\"><bone name=\"root\"/></linden_skeleton>";
assert!(matches!(
parse_skeleton_xml(wrong_count),
Err(crate::Error::Parse { .. })
));
let unknown = "<linden_skeleton version=\"1\" num_bones=\"1\" num_collision_volumes=\"0\"><bone name=\"root\"><unknown/></bone></linden_skeleton>";
assert!(matches!(
parse_skeleton_xml(unknown),
Err(crate::Error::Parse { .. })
));
}
}

View File

@@ -0,0 +1,996 @@
// @generated by libremetaverse-codegen; DO NOT EDIT.
// Regenerate: cargo run -p libremetaverse-codegen -- generate
// Generator: skeleton
// Source: codegen/inputs/avatar_skeleton.xml sha256=addb3cc4cd582ed4343d778e06daf90c6b77b61b4ecb4a20d4bf202c2b93908a license=BSD-3-Clause
#![allow(clippy::float_cmp, clippy::too_many_lines, clippy::unreadable_literal)]
pub(crate) fn default_skeleton() -> crate::skeleton::LindenSkeleton {
crate::skeleton::LindenSkeleton::from_parts(
crate::skeleton::Joint::from_parts(
crate::skeleton::JointBase::from_parts(
"mPelvis".to_owned(),
vec![0.0_f32, 0.0_f32, 1.067_f32],
vec![0.0_f32, 0.0_f32, 0.0_f32],
vec![1.0_f32, 1.0_f32, 1.0_f32],
"Torso".to_owned(),
"base".to_owned(),
vec![0.0_f32, 0.0_f32, 0.084_f32],
true,
),
vec![
crate ::skeleton::CollisionVolume::from_base(crate
::skeleton::JointBase::from_parts("PELVIS".to_owned(), vec![- 0.01_f32,
0.0_f32,- 0.02_f32], vec![0.0_f32, 8.0_f32, 0.0_f32], vec![0.12_f32,
0.16_f32, 0.17_f32], "Collision".to_owned(), "base".to_owned(),
vec![0.03_f32, 0.0_f32, 0.095_f32], true)), crate
::skeleton::CollisionVolume::from_base(crate
::skeleton::JointBase::from_parts("BUTT".to_owned(), vec![- 0.06_f32,
0.0_f32,- 0.1_f32], vec![0.0_f32, 0.0_f32, 0.0_f32], vec![0.1_f32,
0.1_f32, 0.1_f32], "Collision".to_owned(), "base".to_owned(), vec![-
0.1_f32, 0.0_f32, 0.0_f32], true))
],
vec![
crate ::skeleton::Joint::from_parts(crate
::skeleton::JointBase::from_parts("mSpine1".to_owned(), vec![0.0_f32,
0.0_f32, 0.084_f32], vec![0.0_f32, 0.0_f32, 0.0_f32], vec![1.0_f32,
1.0_f32, 1.0_f32], "Spine".to_owned(), "extended".to_owned(),
vec![0.0_f32, 0.0_f32,- 0.084_f32], false), vec![], vec![crate
::skeleton::Joint::from_parts(crate
::skeleton::JointBase::from_parts("mSpine2".to_owned(), vec![0.0_f32,
0.0_f32,- 0.084_f32], vec![0.0_f32, 0.0_f32, 0.0_f32], vec![1.0_f32,
1.0_f32, 1.0_f32], "Spine".to_owned(), "extended".to_owned(),
vec![0.0_f32, 0.0_f32, 0.084_f32], false), vec![], vec![crate
::skeleton::Joint::from_parts(crate
::skeleton::JointBase::from_parts("mTorso".to_owned(), vec![0.0_f32,
0.0_f32, 0.084_f32], vec![0.0_f32, 0.0_f32, 0.0_f32], vec![1.0_f32,
1.0_f32, 1.0_f32], "Torso".to_owned(), "base".to_owned(), vec![-
0.015_f32, 0.0_f32, 0.205_f32], false), vec![crate
::skeleton::CollisionVolume::from_base(crate
::skeleton::JointBase::from_parts("BELLY".to_owned(), vec![0.028_f32,
0.0_f32, 0.04_f32], vec![0.0_f32, 8.0_f32, 0.0_f32], vec![0.09_f32,
0.13_f32, 0.15_f32], "Collision".to_owned(), "base".to_owned(),
vec![0.028_f32, 0.0_f32, 0.094_f32], true)), crate
::skeleton::CollisionVolume::from_base(crate
::skeleton::JointBase::from_parts("LEFT_HANDLE".to_owned(), vec![0.0_f32,
0.1_f32, 0.058_f32], vec![0.0_f32, 0.0_f32, 0.0_f32], vec![0.05_f32,
0.05_f32, 0.05_f32], "Collision".to_owned(), "base".to_owned(),
vec![0.0_f32, 0.1_f32, 0.0_f32], true)), crate
::skeleton::CollisionVolume::from_base(crate
::skeleton::JointBase::from_parts("RIGHT_HANDLE".to_owned(),
vec![0.0_f32,- 0.1_f32, 0.058_f32], vec![0.0_f32, 0.0_f32, 0.0_f32],
vec![0.05_f32, 0.05_f32, 0.05_f32], "Collision".to_owned(), "base"
.to_owned(), vec![0.0_f32,- 0.1_f32, 0.0_f32], true)), crate
::skeleton::CollisionVolume::from_base(crate
::skeleton::JointBase::from_parts("LOWER_BACK".to_owned(), vec![0.0_f32,
0.0_f32, 0.023_f32], vec![0.0_f32, 0.0_f32, 0.0_f32], vec![0.09_f32,
0.13_f32, 0.15_f32], "Collision".to_owned(), "base".to_owned(), vec![-
0.1_f32, 0.0_f32, 0.0_f32], true))], vec![crate
::skeleton::Joint::from_parts(crate
::skeleton::JointBase::from_parts("mSpine3".to_owned(), vec![- 0.015_f32,
0.0_f32, 0.205_f32], vec![0.0_f32, 0.0_f32, 0.0_f32], vec![1.0_f32,
1.0_f32, 1.0_f32], "Spine".to_owned(), "extended".to_owned(),
vec![0.015_f32, 0.0_f32,- 0.205_f32], false), vec![], vec![crate
::skeleton::Joint::from_parts(crate
::skeleton::JointBase::from_parts("mSpine4".to_owned(), vec![0.015_f32,
0.0_f32,- 0.205_f32], vec![0.0_f32, 0.0_f32, 0.0_f32], vec![1.0_f32,
1.0_f32, 1.0_f32], "Spine".to_owned(), "extended".to_owned(), vec![-
0.015_f32, 0.0_f32, 0.205_f32], false), vec![], vec![crate
::skeleton::Joint::from_parts(crate
::skeleton::JointBase::from_parts("mChest".to_owned(), vec![- 0.015_f32,
0.0_f32, 0.205_f32], vec![0.0_f32, 0.0_f32, 0.0_f32], vec![1.0_f32,
1.0_f32, 1.0_f32], "Torso".to_owned(), "base".to_owned(), vec![-
0.01_f32, 0.0_f32, 0.25_f32], false), vec![crate
::skeleton::CollisionVolume::from_base(crate
::skeleton::JointBase::from_parts("CHEST".to_owned(), vec![0.028_f32,
0.0_f32, 0.07_f32], vec![0.0_f32,- 10.0_f32, 0.0_f32], vec![0.11_f32,
0.15_f32, 0.2_f32], "Collision".to_owned(), "base".to_owned(), vec![-
0.096_f32, 0.0_f32, 0.152_f32], true)), crate
::skeleton::CollisionVolume::from_base(crate
::skeleton::JointBase::from_parts("LEFT_PEC".to_owned(), vec![0.119_f32,
0.082_f32, 0.042_f32], vec![0.0_f32, 4.29_f32, 0.0_f32], vec![0.05_f32,
0.05_f32, 0.05_f32], "Collision".to_owned(), "base".to_owned(),
vec![0.08_f32, 0.0_f32,- 0.006_f32], true)), crate
::skeleton::CollisionVolume::from_base(crate
::skeleton::JointBase::from_parts("RIGHT_PEC".to_owned(),
vec![0.119_f32,- 0.082_f32, 0.042_f32], vec![0.0_f32, 4.29_f32, 0.0_f32],
vec![0.05_f32, 0.05_f32, 0.05_f32], "Collision".to_owned(), "base"
.to_owned(), vec![0.08_f32, 0.0_f32,- 0.006_f32], true)), crate
::skeleton::CollisionVolume::from_base(crate
::skeleton::JointBase::from_parts("UPPER_BACK".to_owned(), vec![0.0_f32,
0.0_f32, 0.017_f32], vec![0.0_f32, 0.0_f32, 0.0_f32], vec![0.09_f32,
0.13_f32, 0.15_f32], "Collision".to_owned(), "base".to_owned(), vec![-
0.1_f32, 0.0_f32, 0.0_f32], true))], vec![crate
::skeleton::Joint::from_parts(crate
::skeleton::JointBase::from_parts("mNeck".to_owned(), vec![- 0.01_f32,
0.0_f32, 0.251_f32], vec![0.0_f32, 0.0_f32, 0.0_f32], vec![1.0_f32,
1.0_f32, 1.0_f32], "Torso".to_owned(), "base".to_owned(), vec![0.0_f32,
0.0_f32, 0.077_f32], false), vec![crate
::skeleton::CollisionVolume::from_base(crate
::skeleton::JointBase::from_parts("NECK".to_owned(), vec![0.0_f32,
0.0_f32, 0.02_f32], vec![0.0_f32, 0.0_f32, 0.0_f32], vec![0.05_f32,
0.06_f32, 0.08_f32], "Collision".to_owned(), "base".to_owned(),
vec![0.0_f32, 0.0_f32, 0.08_f32], false))], vec![crate
::skeleton::Joint::from_parts(crate
::skeleton::JointBase::from_parts("mHead".to_owned(), vec![0.0_f32,-
0.0_f32, 0.076_f32], vec![0.0_f32, 0.0_f32, 0.0_f32], vec![1.0_f32,
1.0_f32, 1.0_f32], "Torso".to_owned(), "base".to_owned(), vec![0.0_f32,
0.0_f32, 0.079_f32], false), vec![crate
::skeleton::CollisionVolume::from_base(crate
::skeleton::JointBase::from_parts("HEAD".to_owned(), vec![0.02_f32,
0.0_f32, 0.07_f32], vec![0.0_f32, 0.0_f32, 0.0_f32], vec![0.11_f32,
0.09_f32, 0.12_f32], "Collision".to_owned(), "base".to_owned(),
vec![0.0_f32, 0.0_f32, 0.1_f32], true))], vec![crate
::skeleton::Joint::from_parts(crate
::skeleton::JointBase::from_parts("mSkull".to_owned(), vec![0.0_f32,
0.0_f32, 0.079_f32], vec![0.0_f32, 0.0_f32, 0.0_f32], vec![1.0_f32,
1.0_f32, 1.0_f32], "Extra".to_owned(), "base".to_owned(), vec![0.0_f32,
0.0_f32, 0.033_f32], true), vec![], vec![], vec![0.0_f32, 0.0_f32,
0.079_f32], "figureHair avatar_mSkull".to_owned(), false), crate
::skeleton::Joint::from_parts(crate
::skeleton::JointBase::from_parts("mEyeRight".to_owned(),
vec![0.098_f32,- 0.036_f32, 0.079_f32], vec![0.0_f32, 0.0_f32,- 0.0_f32],
vec![1.0_f32, 1.0_f32, 1.0_f32], "Extra".to_owned(), "base".to_owned(),
vec![0.025_f32, 0.0_f32, 0.0_f32], true), vec![], vec![],
vec![0.098466_f32,- 0.036_f32, 0.079_f32], "avatar_mEyeRight".to_owned(),
false), crate ::skeleton::Joint::from_parts(crate
::skeleton::JointBase::from_parts("mEyeLeft".to_owned(), vec![0.098_f32,
0.036_f32, 0.079_f32], vec![0.0_f32,- 0.0_f32, 0.0_f32], vec![1.0_f32,
1.0_f32, 1.0_f32], "Extra".to_owned(), "base".to_owned(), vec![0.025_f32,
0.0_f32, 0.0_f32], true), vec![], vec![], vec![0.098461_f32, 0.036_f32,
0.079_f32], "avatar_mEyeLeft".to_owned(), false), crate
::skeleton::Joint::from_parts(crate
::skeleton::JointBase::from_parts("mFaceRoot".to_owned(), vec![0.025_f32,
0.0_f32, 0.045_f32], vec![0.0_f32, 0.0_f32, 0.0_f32], vec![1.0_f32,
1.0_f32, 1.0_f32], "Face".to_owned(), "extended".to_owned(),
vec![0.02_f32, 0.0_f32, 0.0_f32], false), vec![], vec![crate
::skeleton::Joint::from_parts(crate
::skeleton::JointBase::from_parts("mFaceEyeAltRight".to_owned(),
vec![0.073_f32,- 0.036_f32, 0.034_f32], vec![0.0_f32, 0.0_f32, 0.0_f32],
vec![1.0_f32, 1.0_f32, 1.0_f32], "Face".to_owned(), "extended"
.to_owned(), vec![0.025_f32, 0.0_f32, 0.0_f32], true), vec![], vec![],
vec![0.073466_f32,- 0.036_f32, 0.03393_f32], "avatar_mFaceEyeAltRight"
.to_owned(), false), crate ::skeleton::Joint::from_parts(crate
::skeleton::JointBase::from_parts("mFaceEyeAltLeft".to_owned(),
vec![0.073_f32, 0.036_f32, 0.034_f32], vec![0.0_f32, 0.0_f32, 0.0_f32],
vec![1.0_f32, 1.0_f32, 1.0_f32], "Face".to_owned(), "extended"
.to_owned(), vec![0.025_f32, 0.0_f32, 0.0_f32], true), vec![], vec![],
vec![0.073461_f32, 0.036_f32, 0.03393_f32], "avatar_mFaceEyeAltLeft"
.to_owned(), false), crate ::skeleton::Joint::from_parts(crate
::skeleton::JointBase::from_parts("mFaceForeheadLeft".to_owned(),
vec![0.061_f32, 0.035_f32, 0.083_f32], vec![0.0_f32, 0.0_f32, 0.0_f32],
vec![1.0_f32, 1.0_f32, 1.0_f32], "Face".to_owned(), "extended"
.to_owned(), vec![0.024_f32, 0.004_f32, 0.018_f32], true), vec![],
vec![], vec![0.061_f32, 0.035_f32, 0.083_f32], "avatar_mFaceForeheadLeft"
.to_owned(), false), crate ::skeleton::Joint::from_parts(crate
::skeleton::JointBase::from_parts("mFaceForeheadRight".to_owned(),
vec![0.061_f32,- 0.035_f32, 0.083_f32], vec![0.0_f32, 0.0_f32, 0.0_f32],
vec![1.0_f32, 1.0_f32, 1.0_f32], "Face".to_owned(), "extended"
.to_owned(), vec![0.024_f32,- 0.004_f32, 0.018_f32], true), vec![],
vec![], vec![0.061_f32,- 0.035_f32, 0.083_f32],
"avatar_mFaceForeheadRight".to_owned(), false), crate
::skeleton::Joint::from_parts(crate
::skeleton::JointBase::from_parts("mFaceEyebrowOuterLeft".to_owned(),
vec![0.064_f32, 0.051_f32, 0.048_f32], vec![0.0_f32, 0.0_f32, 0.0_f32],
vec![1.0_f32, 1.0_f32, 1.0_f32], "Eyes".to_owned(), "extended"
.to_owned(), vec![0.023_f32, 0.013_f32, 0.0_f32], true), vec![], vec![],
vec![0.064_f32, 0.051_f32, 0.048_f32], "avatar_mFaceEyebrowOuterLeft"
.to_owned(), false), crate ::skeleton::Joint::from_parts(crate
::skeleton::JointBase::from_parts("mFaceEyebrowCenterLeft".to_owned(),
vec![0.07_f32, 0.043_f32, 0.056_f32], vec![0.0_f32, 0.0_f32, 0.0_f32],
vec![1.0_f32, 1.0_f32, 1.0_f32], "Eyes".to_owned(), "extended"
.to_owned(), vec![0.027_f32, 0.0_f32, 0.0_f32], true), vec![], vec![],
vec![0.07_f32, 0.043_f32, 0.056_f32], "avatar_mFaceEyebrowCenterLeft"
.to_owned(), false), crate ::skeleton::Joint::from_parts(crate
::skeleton::JointBase::from_parts("mFaceEyebrowInnerLeft".to_owned(),
vec![0.075_f32, 0.022_f32, 0.051_f32], vec![0.0_f32, 0.0_f32, 0.0_f32],
vec![1.0_f32, 1.0_f32, 1.0_f32], "Eyes".to_owned(), "extended"
.to_owned(), vec![0.026_f32, 0.0_f32, 0.0_f32], true), vec![], vec![],
vec![0.075_f32, 0.022_f32, 0.051_f32], "avatar_mFaceEyebrowInnerLeft"
.to_owned(), false), crate ::skeleton::Joint::from_parts(crate
::skeleton::JointBase::from_parts("mFaceEyebrowOuterRight".to_owned(),
vec![0.064_f32,- 0.051_f32, 0.048_f32], vec![0.0_f32, 0.0_f32, 0.0_f32],
vec![1.0_f32, 1.0_f32, 1.0_f32], "Eyes".to_owned(), "extended"
.to_owned(), vec![0.023_f32,- 0.013_f32, 0.0_f32], true), vec![], vec![],
vec![0.064_f32,- 0.051_f32, 0.048_f32], "avatar_mFaceEyebrowOuterRight"
.to_owned(), false), crate ::skeleton::Joint::from_parts(crate
::skeleton::JointBase::from_parts("mFaceEyebrowCenterRight".to_owned(),
vec![0.07_f32,- 0.043_f32, 0.056_f32], vec![0.0_f32, 0.0_f32, 0.0_f32],
vec![1.0_f32, 1.0_f32, 1.0_f32], "Eyes".to_owned(), "extended"
.to_owned(), vec![0.027_f32, 0.0_f32, 0.0_f32], true), vec![], vec![],
vec![0.07_f32,- 0.043_f32, 0.056_f32], "avatar_mFaceEyebrowCenterRight"
.to_owned(), false), crate ::skeleton::Joint::from_parts(crate
::skeleton::JointBase::from_parts("mFaceEyebrowInnerRight".to_owned(),
vec![0.075_f32,- 0.022_f32, 0.051_f32], vec![0.0_f32, 0.0_f32, 0.0_f32],
vec![1.0_f32, 1.0_f32, 1.0_f32], "Eyes".to_owned(), "extended"
.to_owned(), vec![0.026_f32, 0.0_f32, 0.0_f32], true), vec![], vec![],
vec![0.075_f32,- 0.022_f32, 0.051_f32], "avatar_mFaceEyebrowInnerRight"
.to_owned(), false), crate ::skeleton::Joint::from_parts(crate
::skeleton::JointBase::from_parts("mFaceEyeLidUpperLeft".to_owned(),
vec![0.073_f32, 0.036_f32, 0.034_f32], vec![0.0_f32, 0.0_f32, 0.0_f32],
vec![1.0_f32, 1.0_f32, 1.0_f32], "Eyes".to_owned(), "extended"
.to_owned(), vec![0.027_f32, 0.0_f32, 0.005_f32], true), vec![], vec![],
vec![0.073_f32, 0.036_f32, 0.034_f32], "avatar_mFaceEyeLidUpperLeft"
.to_owned(), false), crate ::skeleton::Joint::from_parts(crate
::skeleton::JointBase::from_parts("mFaceEyeLidLowerLeft".to_owned(),
vec![0.073_f32, 0.036_f32, 0.034_f32], vec![0.0_f32, 0.0_f32, 0.0_f32],
vec![1.0_f32, 1.0_f32, 1.0_f32], "Eyes".to_owned(), "extended"
.to_owned(), vec![0.024_f32, 0.0_f32,- 0.007_f32], true), vec![], vec![],
vec![0.073_f32, 0.036_f32, 0.034_f32], "avatar_mFaceEyeLidLowerLeft"
.to_owned(), false), crate ::skeleton::Joint::from_parts(crate
::skeleton::JointBase::from_parts("mFaceEyeLidUpperRight".to_owned(),
vec![0.073_f32,- 0.036_f32, 0.034_f32], vec![0.0_f32, 0.0_f32, 0.0_f32],
vec![1.0_f32, 1.0_f32, 1.0_f32], "Eyes".to_owned(), "extended"
.to_owned(), vec![0.027_f32, 0.0_f32, 0.005_f32], true), vec![], vec![],
vec![0.073_f32,- 0.036_f32, 0.034_f32], "avatar_mFaceEyeLidUpperRight"
.to_owned(), false), crate ::skeleton::Joint::from_parts(crate
::skeleton::JointBase::from_parts("mFaceEyeLidLowerRight".to_owned(),
vec![0.073_f32,- 0.036_f32, 0.034_f32], vec![0.0_f32, 0.0_f32, 0.0_f32],
vec![1.0_f32, 1.0_f32, 1.0_f32], "Eyes".to_owned(), "extended"
.to_owned(), vec![0.024_f32, 0.0_f32,- 0.007_f32], true), vec![], vec![],
vec![0.073_f32,- 0.036_f32, 0.034_f32], "avatar_mFaceEyeLidLowerRight"
.to_owned(), false), crate ::skeleton::Joint::from_parts(crate
::skeleton::JointBase::from_parts("mFaceEar1Left".to_owned(),
vec![0.0_f32, 0.08_f32, 0.002_f32], vec![0.0_f32, 0.0_f32, 0.0_f32],
vec![1.0_f32, 1.0_f32, 1.0_f32], "Ears".to_owned(), "extended"
.to_owned(), vec![- 0.019_f32, 0.018_f32, 0.025_f32], true), vec![],
vec![crate ::skeleton::Joint::from_parts(crate
::skeleton::JointBase::from_parts("mFaceEar2Left".to_owned(), vec![-
0.019_f32, 0.018_f32, 0.025_f32], vec![0.0_f32, 0.0_f32, 0.0_f32],
vec![1.0_f32, 1.0_f32, 1.0_f32], "Ears".to_owned(), "extended"
.to_owned(), vec![0.0_f32, 0.0_f32, 0.033_f32], true), vec![], vec![],
vec![- 0.019_f32, 0.018_f32, 0.025_f32], "avatar_mFaceEar2Left"
.to_owned(), true)], vec![0.0_f32, 0.08_f32, 0.002_f32],
"avatar_mFaceEar1Left".to_owned(), false), crate
::skeleton::Joint::from_parts(crate
::skeleton::JointBase::from_parts("mFaceEar1Right".to_owned(),
vec![0.0_f32,- 0.08_f32, 0.002_f32], vec![0.0_f32, 0.0_f32, 0.0_f32],
vec![1.0_f32, 1.0_f32, 1.0_f32], "Ears".to_owned(), "extended"
.to_owned(), vec![- 0.019_f32,- 0.018_f32, 0.025_f32], true), vec![],
vec![crate ::skeleton::Joint::from_parts(crate
::skeleton::JointBase::from_parts("mFaceEar2Right".to_owned(), vec![-
0.019_f32,- 0.018_f32, 0.025_f32], vec![0.0_f32, 0.0_f32, 0.0_f32],
vec![1.0_f32, 1.0_f32, 1.0_f32], "Ears".to_owned(), "extended"
.to_owned(), vec![0.0_f32, 0.0_f32, 0.033_f32], true), vec![], vec![],
vec![- 0.019_f32,- 0.018_f32, 0.025_f32], "avatar_mFaceEar2Right"
.to_owned(), true)], vec![0.0_f32,- 0.08_f32, 0.002_f32],
"avatar_mFaceEar1Right".to_owned(), false), crate
::skeleton::Joint::from_parts(crate
::skeleton::JointBase::from_parts("mFaceNoseLeft".to_owned(),
vec![0.086_f32, 0.015_f32,- 0.004_f32], vec![0.0_f32, 0.0_f32, 0.0_f32],
vec![1.0_f32, 1.0_f32, 1.0_f32], "Face".to_owned(), "extended"
.to_owned(), vec![0.015_f32, 0.004_f32, 0.0_f32], true), vec![], vec![],
vec![0.086_f32, 0.015_f32,- 0.004_f32], "avatar_mFaceNoseLeft"
.to_owned(), false), crate ::skeleton::Joint::from_parts(crate
::skeleton::JointBase::from_parts("mFaceNoseCenter".to_owned(),
vec![0.102_f32, 0.0_f32, 0.0_f32], vec![0.0_f32, 0.0_f32, 0.0_f32],
vec![1.0_f32, 1.0_f32, 1.0_f32], "Face".to_owned(), "extended"
.to_owned(), vec![0.025_f32, 0.0_f32, 0.0_f32], true), vec![], vec![],
vec![0.102_f32, 0.0_f32, 0.0_f32], "avatar_mFaceNoseCenter".to_owned(),
false), crate ::skeleton::Joint::from_parts(crate
::skeleton::JointBase::from_parts("mFaceNoseRight".to_owned(),
vec![0.086_f32,- 0.015_f32,- 0.004_f32], vec![0.0_f32, 0.0_f32, 0.0_f32],
vec![1.0_f32, 1.0_f32, 1.0_f32], "Face".to_owned(), "extended"
.to_owned(), vec![0.015_f32,- 0.004_f32, 0.0_f32], true), vec![], vec![],
vec![0.086_f32,- 0.015_f32,- 0.004_f32], "avatar_mFaceNoseRight"
.to_owned(), false), crate ::skeleton::Joint::from_parts(crate
::skeleton::JointBase::from_parts("mFaceCheekLowerLeft".to_owned(),
vec![0.05_f32, 0.034_f32,- 0.031_f32], vec![0.0_f32, 0.0_f32, 0.0_f32],
vec![1.0_f32, 1.0_f32, 1.0_f32], "Face".to_owned(), "extended"
.to_owned(), vec![0.013_f32, 0.03_f32, 0.0_f32], true), vec![], vec![],
vec![0.05_f32, 0.034_f32,- 0.031_f32], "avatar_mFaceCheekLowerLeft"
.to_owned(), false), crate ::skeleton::Joint::from_parts(crate
::skeleton::JointBase::from_parts("mFaceCheekUpperLeft".to_owned(),
vec![0.07_f32, 0.034_f32,- 0.005_f32], vec![0.0_f32, 0.0_f32, 0.0_f32],
vec![1.0_f32, 1.0_f32, 1.0_f32], "Face".to_owned(), "extended"
.to_owned(), vec![0.022_f32, 0.015_f32, 0.0_f32], true), vec![], vec![],
vec![0.07_f32, 0.034_f32,- 0.005_f32], "avatar_mFaceCheekUpperLeft"
.to_owned(), false), crate ::skeleton::Joint::from_parts(crate
::skeleton::JointBase::from_parts("mFaceCheekLowerRight".to_owned(),
vec![0.05_f32,- 0.034_f32,- 0.031_f32], vec![0.0_f32, 0.0_f32, 0.0_f32],
vec![1.0_f32, 1.0_f32, 1.0_f32], "Face".to_owned(), "extended"
.to_owned(), vec![0.013_f32,- 0.03_f32, 0.0_f32], true), vec![], vec![],
vec![0.05_f32,- 0.034_f32,- 0.031_f32], "avatar_mFaceCheekLowerRight"
.to_owned(), false), crate ::skeleton::Joint::from_parts(crate
::skeleton::JointBase::from_parts("mFaceCheekUpperRight".to_owned(),
vec![0.07_f32,- 0.034_f32,- 0.005_f32], vec![0.0_f32, 0.0_f32, 0.0_f32],
vec![1.0_f32, 1.0_f32, 1.0_f32], "Face".to_owned(), "extended"
.to_owned(), vec![0.022_f32,- 0.015_f32, 0.0_f32], true), vec![], vec![],
vec![0.07_f32,- 0.034_f32,- 0.005_f32], "avatar_mFaceCheekUpperRight"
.to_owned(), false), crate ::skeleton::Joint::from_parts(crate
::skeleton::JointBase::from_parts("mFaceJaw".to_owned(), vec![-
0.001_f32, 0.0_f32,- 0.015_f32], vec![0.0_f32, 0.0_f32, 0.0_f32],
vec![1.0_f32, 1.0_f32, 1.0_f32], "Mouth".to_owned(), "extended"
.to_owned(), vec![0.059_f32, 0.0_f32,- 0.039_f32], false), vec![],
vec![crate ::skeleton::Joint::from_parts(crate
::skeleton::JointBase::from_parts("mFaceChin".to_owned(), vec![0.074_f32,
0.0_f32,- 0.054_f32], vec![0.0_f32, 0.0_f32, 0.0_f32], vec![1.0_f32,
1.0_f32, 1.0_f32], "Mouth".to_owned(), "extended".to_owned(),
vec![0.021_f32, 0.0_f32,- 0.018_f32], true), vec![], vec![],
vec![0.074_f32, 0.0_f32,- 0.054_f32], "avatar_mFaceChin".to_owned(),
false), crate ::skeleton::Joint::from_parts(crate
::skeleton::JointBase::from_parts("mFaceTeethLower".to_owned(),
vec![0.021_f32, 0.0_f32,- 0.039_f32], vec![0.0_f32, 0.0_f32, 0.0_f32],
vec![1.0_f32, 1.0_f32, 1.0_f32], "Mouth".to_owned(), "extended"
.to_owned(), vec![0.035_f32, 0.0_f32, 0.0_f32], true), vec![], vec![crate
::skeleton::Joint::from_parts(crate
::skeleton::JointBase::from_parts("mFaceLipLowerLeft".to_owned(),
vec![0.045_f32, 0.0_f32, 0.0_f32], vec![0.0_f32, 0.0_f32, 0.0_f32],
vec![1.0_f32, 1.0_f32, 1.0_f32], "Lips".to_owned(), "extended"
.to_owned(), vec![0.034_f32, 0.017_f32, 0.005_f32], true), vec![],
vec![], vec![0.045_f32, 0.0_f32, 0.0_f32], "avatar_mFaceLipLowerLeft"
.to_owned(), false), crate ::skeleton::Joint::from_parts(crate
::skeleton::JointBase::from_parts("mFaceLipLowerRight".to_owned(),
vec![0.045_f32, 0.0_f32, 0.0_f32], vec![0.0_f32, 0.0_f32, 0.0_f32],
vec![1.0_f32, 1.0_f32, 1.0_f32], "Lips".to_owned(), "extended"
.to_owned(), vec![0.034_f32,- 0.017_f32, 0.005_f32], true), vec![],
vec![], vec![0.045_f32, 0.0_f32, 0.0_f32], "avatar_mFaceLipLowerRight"
.to_owned(), false), crate ::skeleton::Joint::from_parts(crate
::skeleton::JointBase::from_parts("mFaceLipLowerCenter".to_owned(),
vec![0.045_f32, 0.0_f32, 0.0_f32], vec![0.0_f32, 0.0_f32, 0.0_f32],
vec![1.0_f32, 1.0_f32, 1.0_f32], "Lips".to_owned(), "extended"
.to_owned(), vec![0.04_f32, 0.0_f32, 0.002_f32], true), vec![], vec![],
vec![0.045_f32, 0.0_f32, 0.0_f32], "avatar_mFaceLipLowerCenter"
.to_owned(), false), crate ::skeleton::Joint::from_parts(crate
::skeleton::JointBase::from_parts("mFaceTongueBase".to_owned(),
vec![0.039_f32, 0.0_f32, 0.005_f32], vec![0.0_f32, 0.0_f32, 0.0_f32],
vec![1.0_f32, 1.0_f32, 1.0_f32], "Mouth".to_owned(), "extended"
.to_owned(), vec![0.022_f32, 0.0_f32, 0.007_f32], false), vec![],
vec![crate ::skeleton::Joint::from_parts(crate
::skeleton::JointBase::from_parts("mFaceTongueTip".to_owned(),
vec![0.022_f32, 0.0_f32, 0.007_f32], vec![0.0_f32, 0.0_f32, 0.0_f32],
vec![1.0_f32, 1.0_f32, 1.0_f32], "Mouth".to_owned(), "extended"
.to_owned(), vec![0.01_f32, 0.0_f32, 0.0_f32], true), vec![], vec![],
vec![0.022_f32, 0.0_f32, 0.007_f32], "avatar_mFaceTongueTip".to_owned(),
true)], vec![0.039_f32, 0.0_f32, 0.005_f32], "avatar_mFaceTongueBase"
.to_owned(), false)], vec![0.021_f32, 0.0_f32,- 0.039_f32],
"avatar_mFaceTeethLower".to_owned(), false)], vec![- 0.001_f32, 0.0_f32,-
0.015_f32], "avatar_mFaceJaw".to_owned(), false), crate
::skeleton::Joint::from_parts(crate
::skeleton::JointBase::from_parts("mFaceJawShaper".to_owned(),
vec![0.0_f32, 0.0_f32, 0.0_f32], vec![0.0_f32, 0.0_f32, 0.0_f32],
vec![1.0_f32, 1.0_f32, 1.0_f32], "Face".to_owned(), "extended"
.to_owned(), vec![- 0.017_f32, 0.0_f32, 0.0_f32], true), vec![], vec![],
vec![0.0_f32, 0.0_f32, 0.0_f32], "avatar_mFaceJawShaper".to_owned(),
false), crate ::skeleton::Joint::from_parts(crate
::skeleton::JointBase::from_parts("mFaceForeheadCenter".to_owned(),
vec![0.069_f32, 0.0_f32, 0.065_f32], vec![0.0_f32, 0.0_f32, 0.0_f32],
vec![1.0_f32, 1.0_f32, 1.0_f32], "Face".to_owned(), "extended"
.to_owned(), vec![0.036_f32, 0.0_f32, 0.0_f32], true), vec![], vec![],
vec![0.069_f32, 0.0_f32, 0.065_f32], "avatar_mFaceForeheadCenter"
.to_owned(), false), crate ::skeleton::Joint::from_parts(crate
::skeleton::JointBase::from_parts("mFaceNoseBase".to_owned(),
vec![0.094_f32, 0.0_f32,- 0.016_f32], vec![0.0_f32, 0.0_f32, 0.0_f32],
vec![1.0_f32, 1.0_f32, 1.0_f32], "Nose".to_owned(), "extended"
.to_owned(), vec![0.014_f32, 0.0_f32, 0.0_f32], true), vec![], vec![],
vec![0.094_f32, 0.0_f32,- 0.016_f32], "avatar_mFaceNoseBase".to_owned(),
false), crate ::skeleton::Joint::from_parts(crate
::skeleton::JointBase::from_parts("mFaceTeethUpper".to_owned(),
vec![0.02_f32, 0.0_f32,- 0.03_f32], vec![0.0_f32, 0.0_f32, 0.0_f32],
vec![1.0_f32, 1.0_f32, 1.0_f32], "Mouth".to_owned(), "extended"
.to_owned(), vec![0.035_f32, 0.0_f32, 0.0_f32], true), vec![], vec![crate
::skeleton::Joint::from_parts(crate
::skeleton::JointBase::from_parts("mFaceLipUpperLeft".to_owned(),
vec![0.045_f32, 0.0_f32,- 0.003_f32], vec![0.0_f32, 0.0_f32, 0.0_f32],
vec![1.0_f32, 1.0_f32, 1.0_f32], "Lips".to_owned(), "extended"
.to_owned(), vec![0.041_f32, 0.015_f32, 0.0_f32], true), vec![], vec![],
vec![0.045_f32, 0.0_f32,- 0.003_f32], "avatar_mFaceLipUpperLeft"
.to_owned(), false), crate ::skeleton::Joint::from_parts(crate
::skeleton::JointBase::from_parts("mFaceLipUpperRight".to_owned(),
vec![0.045_f32, 0.0_f32,- 0.003_f32], vec![0.0_f32, 0.0_f32, 0.0_f32],
vec![1.0_f32, 1.0_f32, 1.0_f32], "Lips".to_owned(), "extended"
.to_owned(), vec![0.041_f32,- 0.015_f32, 0.0_f32], true), vec![], vec![],
vec![0.045_f32, 0.0_f32,- 0.003_f32], "avatar_mFaceLipUpperRight"
.to_owned(), false), crate ::skeleton::Joint::from_parts(crate
::skeleton::JointBase::from_parts("mFaceLipCornerLeft".to_owned(),
vec![0.028_f32,- 0.019_f32,- 0.01_f32], vec![0.0_f32, 0.0_f32, 0.0_f32],
vec![1.0_f32, 1.0_f32, 1.0_f32], "Lips".to_owned(), "extended"
.to_owned(), vec![0.045_f32, 0.051_f32, 0.0_f32], true), vec![], vec![],
vec![0.028_f32,- 0.019_f32,- 0.01_f32], "avatar_mFaceLipCornerLeft"
.to_owned(), false), crate ::skeleton::Joint::from_parts(crate
::skeleton::JointBase::from_parts("mFaceLipCornerRight".to_owned(),
vec![0.028_f32, 0.019_f32,- 0.01_f32], vec![0.0_f32, 0.0_f32, 0.0_f32],
vec![1.0_f32, 1.0_f32, 1.0_f32], "Lips".to_owned(), "extended"
.to_owned(), vec![0.045_f32,- 0.051_f32, 0.0_f32], true), vec![], vec![],
vec![0.028_f32, 0.019_f32,- 0.01_f32], "avatar_mFaceLipCornerRight"
.to_owned(), false), crate ::skeleton::Joint::from_parts(crate
::skeleton::JointBase::from_parts("mFaceLipUpperCenter".to_owned(),
vec![0.045_f32, 0.0_f32,- 0.003_f32], vec![0.0_f32, 0.0_f32, 0.0_f32],
vec![1.0_f32, 1.0_f32, 1.0_f32], "Lips".to_owned(), "extended"
.to_owned(), vec![0.043_f32, 0.0_f32, 0.002_f32], true), vec![], vec![],
vec![0.045_f32, 0.0_f32,- 0.003_f32], "avatar_mFaceLipUpperCenter"
.to_owned(), false)], vec![0.02_f32, 0.0_f32,- 0.03_f32],
"avatar_mFaceTeethUpper".to_owned(), false), crate
::skeleton::Joint::from_parts(crate
::skeleton::JointBase::from_parts("mFaceEyecornerInnerLeft".to_owned(),
vec![0.075_f32, 0.017_f32, 0.032_f32], vec![0.0_f32, 0.0_f32, 0.0_f32],
vec![1.0_f32, 1.0_f32, 1.0_f32], "Face".to_owned(), "extended"
.to_owned(), vec![0.016_f32, 0.0_f32, 0.0_f32], true), vec![], vec![],
vec![0.075_f32, 0.017_f32, 0.032_f32], "avatar_mFaceEyecornerInnerLeft"
.to_owned(), false), crate ::skeleton::Joint::from_parts(crate
::skeleton::JointBase::from_parts("mFaceEyecornerInnerRight".to_owned(),
vec![0.075_f32,- 0.017_f32, 0.032_f32], vec![0.0_f32, 0.0_f32, 0.0_f32],
vec![1.0_f32, 1.0_f32, 1.0_f32], "Face".to_owned(), "extended"
.to_owned(), vec![0.016_f32, 0.0_f32, 0.0_f32], true), vec![], vec![],
vec![0.075_f32,- 0.017_f32, 0.032_f32], "avatar_mFaceEyecornerInnerRight"
.to_owned(), false), crate ::skeleton::Joint::from_parts(crate
::skeleton::JointBase::from_parts("mFaceNoseBridge".to_owned(),
vec![0.091_f32, 0.0_f32, 0.02_f32], vec![0.0_f32, 0.0_f32, 0.0_f32],
vec![1.0_f32, 1.0_f32, 1.0_f32], "Nose".to_owned(), "extended"
.to_owned(), vec![0.015_f32, 0.0_f32, 0.008_f32], true), vec![], vec![],
vec![0.091_f32, 0.0_f32, 0.02_f32], "avatar_mFaceNoseBridge".to_owned(),
false)], vec![0.025_f32, 0.0_f32, 0.045_f32], "avatar_mFaceRoot"
.to_owned(), false)], vec![0.0_f32,- 0.0_f32, 0.07563_f32],
"head avatar_mHead".to_owned(), true)], vec![- 0.009507_f32, 0.0_f32,
0.251108_f32], "neck avatar_mNeck".to_owned(), true), crate
::skeleton::Joint::from_parts(crate
::skeleton::JointBase::from_parts("mCollarLeft".to_owned(), vec![-
0.021_f32, 0.085_f32, 0.165_f32], vec![0.0_f32, 0.0_f32, 0.0_f32],
vec![1.0_f32, 1.0_f32, 1.0_f32], "Arms".to_owned(), "base".to_owned(),
vec![0.0_f32, 0.079_f32, 0.0_f32], false), vec![crate
::skeleton::CollisionVolume::from_base(crate
::skeleton::JointBase::from_parts("L_CLAVICLE".to_owned(), vec![0.02_f32,
0.0_f32, 0.02_f32], vec![0.0_f32, 0.0_f32, 0.0_f32], vec![0.07_f32,
0.14_f32, 0.05_f32], "Collision".to_owned(), "base".to_owned(),
vec![0.0_f32, 0.1_f32, 0.0_f32], true))], vec![crate
::skeleton::Joint::from_parts(crate
::skeleton::JointBase::from_parts("mShoulderLeft".to_owned(),
vec![0.0_f32, 0.079_f32,- 0.0_f32], vec![0.0_f32, 0.0_f32, 0.0_f32],
vec![1.0_f32, 1.0_f32, 1.0_f32], "Arms".to_owned(), "base".to_owned(),
vec![0.0_f32, 0.247_f32, 0.0_f32], false), vec![crate
::skeleton::CollisionVolume::from_base(crate
::skeleton::JointBase::from_parts("L_UPPER_ARM".to_owned(), vec![0.0_f32,
0.12_f32, 0.01_f32], vec![- 5.0_f32, 0.0_f32, 0.0_f32], vec![0.05_f32,
0.17_f32, 0.05_f32], "Collision".to_owned(), "base".to_owned(),
vec![0.0_f32, 0.13_f32,- 0.003_f32], true))], vec![crate
::skeleton::Joint::from_parts(crate
::skeleton::JointBase::from_parts("mElbowLeft".to_owned(), vec![0.0_f32,
0.248_f32, 0.0_f32], vec![0.0_f32, 0.0_f32, 0.0_f32], vec![1.0_f32,
1.0_f32, 1.0_f32], "Arms".to_owned(), "base".to_owned(), vec![0.0_f32,
0.205_f32, 0.0_f32], false), vec![crate
::skeleton::CollisionVolume::from_base(crate
::skeleton::JointBase::from_parts("L_LOWER_ARM".to_owned(), vec![0.0_f32,
0.1_f32, 0.0_f32], vec![- 3.0_f32, 0.0_f32, 0.0_f32], vec![0.04_f32,
0.14_f32, 0.04_f32], "Collision".to_owned(), "base".to_owned(),
vec![0.0_f32, 0.1_f32,- 0.001_f32], true))], vec![crate
::skeleton::Joint::from_parts(crate
::skeleton::JointBase::from_parts("mWristLeft".to_owned(), vec![-
0.0_f32, 0.205_f32, 0.0_f32], vec![0.0_f32, 0.0_f32, 0.0_f32],
vec![1.0_f32, 1.0_f32, 1.0_f32], "Arms".to_owned(), "base".to_owned(),
vec![0.0_f32, 0.06_f32, 0.0_f32], false), vec![crate
::skeleton::CollisionVolume::from_base(crate
::skeleton::JointBase::from_parts("L_HAND".to_owned(), vec![0.01_f32,
0.05_f32, 0.0_f32], vec![- 3.0_f32, 0.0_f32,- 10.0_f32], vec![0.05_f32,
0.08_f32, 0.03_f32], "Collision".to_owned(), "base".to_owned(),
vec![0.005_f32, 0.049_f32,- 0.001_f32], false))], vec![crate
::skeleton::Joint::from_parts(crate
::skeleton::JointBase::from_parts("mHandMiddle1Left".to_owned(),
vec![0.013_f32, 0.101_f32, 0.015_f32], vec![0.0_f32, 0.0_f32, 0.0_f32],
vec![1.0_f32, 1.0_f32, 1.0_f32], "Hand".to_owned(), "extended"
.to_owned(), vec![- 0.001_f32, 0.04_f32,- 0.006_f32], false), vec![],
vec![crate ::skeleton::Joint::from_parts(crate
::skeleton::JointBase::from_parts("mHandMiddle2Left".to_owned(), vec![-
0.001_f32, 0.04_f32,- 0.006_f32], vec![0.0_f32, 0.0_f32, 0.0_f32],
vec![1.0_f32, 1.0_f32, 1.0_f32], "Hand".to_owned(), "extended"
.to_owned(), vec![- 0.001_f32, 0.049_f32,- 0.008_f32], false), vec![],
vec![crate ::skeleton::Joint::from_parts(crate
::skeleton::JointBase::from_parts("mHandMiddle3Left".to_owned(), vec![-
0.001_f32, 0.049_f32,- 0.008_f32], vec![0.0_f32, 0.0_f32, 0.0_f32],
vec![1.0_f32, 1.0_f32, 1.0_f32], "Hand".to_owned(), "extended"
.to_owned(), vec![- 0.002_f32, 0.033_f32,- 0.006_f32], false), vec![],
vec![], vec![- 0.001_f32, 0.049_f32,- 0.008_f32],
"avatar_mHandMiddle3Left".to_owned(), true)], vec![- 0.001_f32,
0.04_f32,- 0.006_f32], "avatar_mHandMiddle2Left".to_owned(), true)],
vec![0.013_f32, 0.101_f32, 0.015_f32], "avatar_mHandMiddle1Left"
.to_owned(), false), crate ::skeleton::Joint::from_parts(crate
::skeleton::JointBase::from_parts("mHandIndex1Left".to_owned(),
vec![0.038_f32, 0.097_f32, 0.015_f32], vec![0.0_f32, 0.0_f32, 0.0_f32],
vec![1.0_f32, 1.0_f32, 1.0_f32], "Hand".to_owned(), "extended"
.to_owned(), vec![0.017_f32, 0.036_f32,- 0.006_f32], false), vec![],
vec![crate ::skeleton::Joint::from_parts(crate
::skeleton::JointBase::from_parts("mHandIndex2Left".to_owned(),
vec![0.017_f32, 0.036_f32,- 0.006_f32], vec![0.0_f32, 0.0_f32, 0.0_f32],
vec![1.0_f32, 1.0_f32, 1.0_f32], "Hand".to_owned(), "extended"
.to_owned(), vec![0.014_f32, 0.032_f32,- 0.006_f32], false), vec![],
vec![crate ::skeleton::Joint::from_parts(crate
::skeleton::JointBase::from_parts("mHandIndex3Left".to_owned(),
vec![0.014_f32, 0.032_f32,- 0.006_f32], vec![0.0_f32, 0.0_f32, 0.0_f32],
vec![1.0_f32, 1.0_f32, 1.0_f32], "Hand".to_owned(), "extended"
.to_owned(), vec![0.011_f32, 0.025_f32,- 0.004_f32], false), vec![],
vec![], vec![0.014_f32, 0.032_f32,- 0.006_f32], "avatar_mHandIndex3Left"
.to_owned(), true)], vec![0.017_f32, 0.036_f32,- 0.006_f32],
"avatar_mHandIndex2Left".to_owned(), true)], vec![0.038_f32, 0.097_f32,
0.015_f32], "avatar_mHandIndex1Left".to_owned(), false), crate
::skeleton::Joint::from_parts(crate
::skeleton::JointBase::from_parts("mHandRing1Left".to_owned(), vec![-
0.01_f32, 0.099_f32, 0.009_f32], vec![0.0_f32, 0.0_f32, 0.0_f32],
vec![1.0_f32, 1.0_f32, 1.0_f32], "Hand".to_owned(), "extended"
.to_owned(), vec![- 0.013_f32, 0.038_f32,- 0.008_f32], false), vec![],
vec![crate ::skeleton::Joint::from_parts(crate
::skeleton::JointBase::from_parts("mHandRing2Left".to_owned(), vec![-
0.013_f32, 0.038_f32,- 0.008_f32], vec![0.0_f32, 0.0_f32, 0.0_f32],
vec![1.0_f32, 1.0_f32, 1.0_f32], "Hand".to_owned(), "extended"
.to_owned(), vec![- 0.013_f32, 0.04_f32,- 0.009_f32], false), vec![],
vec![crate ::skeleton::Joint::from_parts(crate
::skeleton::JointBase::from_parts("mHandRing3Left".to_owned(), vec![-
0.013_f32, 0.04_f32,- 0.009_f32], vec![0.0_f32, 0.0_f32, 0.0_f32],
vec![1.0_f32, 1.0_f32, 1.0_f32], "Hand".to_owned(), "extended"
.to_owned(), vec![- 0.01_f32, 0.028_f32,- 0.006_f32], false), vec![],
vec![], vec![- 0.013_f32, 0.04_f32,- 0.009_f32], "avatar_mHandRing3Left"
.to_owned(), true)], vec![- 0.013_f32, 0.038_f32,- 0.008_f32],
"avatar_mHandRing2Left".to_owned(), true)], vec![- 0.01_f32, 0.099_f32,
0.009_f32], "avatar_mHandRing1Left".to_owned(), false), crate
::skeleton::Joint::from_parts(crate
::skeleton::JointBase::from_parts("mHandPinky1Left".to_owned(), vec![-
0.031_f32, 0.095_f32, 0.003_f32], vec![0.0_f32, 0.0_f32, 0.0_f32],
vec![1.0_f32, 1.0_f32, 1.0_f32], "Hand".to_owned(), "extended"
.to_owned(), vec![- 0.024_f32, 0.025_f32,- 0.006_f32], false), vec![],
vec![crate ::skeleton::Joint::from_parts(crate
::skeleton::JointBase::from_parts("mHandPinky2Left".to_owned(), vec![-
0.024_f32, 0.025_f32,- 0.006_f32], vec![0.0_f32, 0.0_f32, 0.0_f32],
vec![1.0_f32, 1.0_f32, 1.0_f32], "Hand".to_owned(), "extended"
.to_owned(), vec![- 0.015_f32, 0.018_f32,- 0.004_f32], false), vec![],
vec![crate ::skeleton::Joint::from_parts(crate
::skeleton::JointBase::from_parts("mHandPinky3Left".to_owned(), vec![-
0.015_f32, 0.018_f32,- 0.004_f32], vec![0.0_f32, 0.0_f32, 0.0_f32],
vec![1.0_f32, 1.0_f32, 1.0_f32], "Hand".to_owned(), "extended"
.to_owned(), vec![- 0.013_f32, 0.016_f32,- 0.004_f32], false), vec![],
vec![], vec![- 0.015_f32, 0.018_f32,- 0.004_f32],
"avatar_mHandPinky3Left".to_owned(), true)], vec![- 0.024_f32,
0.025_f32,- 0.006_f32], "avatar_mHandPinky2Left".to_owned(), true)],
vec![- 0.031_f32, 0.095_f32, 0.003_f32], "avatar_mHandPinky1Left"
.to_owned(), false), crate ::skeleton::Joint::from_parts(crate
::skeleton::JointBase::from_parts("mHandThumb1Left".to_owned(),
vec![0.031_f32, 0.026_f32, 0.004_f32], vec![0.0_f32, 0.0_f32, 0.0_f32],
vec![1.0_f32, 1.0_f32, 1.0_f32], "Hand".to_owned(), "extended"
.to_owned(), vec![0.028_f32, 0.032_f32, 0.0_f32], false), vec![],
vec![crate ::skeleton::Joint::from_parts(crate
::skeleton::JointBase::from_parts("mHandThumb2Left".to_owned(),
vec![0.028_f32, 0.032_f32,- 0.001_f32], vec![0.0_f32, 0.0_f32, 0.0_f32],
vec![1.0_f32, 1.0_f32, 1.0_f32], "Hand".to_owned(), "extended"
.to_owned(), vec![0.023_f32, 0.031_f32, 0.0_f32], false), vec![],
vec![crate ::skeleton::Joint::from_parts(crate
::skeleton::JointBase::from_parts("mHandThumb3Left".to_owned(),
vec![0.023_f32, 0.031_f32,- 0.001_f32], vec![0.0_f32, 0.0_f32, 0.0_f32],
vec![1.0_f32, 1.0_f32, 1.0_f32], "Hand".to_owned(), "extended"
.to_owned(), vec![0.015_f32, 0.025_f32, 0.0_f32], false), vec![], vec![],
vec![0.023_f32, 0.031_f32,- 0.001_f32], "avatar_mHandThumb3Left"
.to_owned(), true)], vec![0.028_f32, 0.032_f32,- 0.001_f32],
"avatar_mHandThumb2Left".to_owned(), true)], vec![0.031_f32, 0.026_f32,
0.004_f32], "avatar_mHandThumb1Left".to_owned(), false)], vec![- 0.0_f32,
0.204846_f32, 0.0_f32], "lHand avatar_mWristLeft".to_owned(), true)],
vec![0.0_f32, 0.248_f32, 0.0_f32], "lForeArm avatar_mElbowLeft"
.to_owned(), true)], vec![0.0_f32, 0.079_f32,- 0.0_f32],
"lShldr avatar_mShoulderLeft".to_owned(), true)], vec![- 0.020927_f32,
0.084665_f32, 0.165396_f32], "lCollar avatar_mCollarLeft".to_owned(),
false), crate ::skeleton::Joint::from_parts(crate
::skeleton::JointBase::from_parts("mCollarRight".to_owned(), vec![-
0.021_f32,- 0.085_f32, 0.165_f32], vec![0.0_f32, 0.0_f32, 0.0_f32],
vec![1.0_f32, 1.0_f32, 1.0_f32], "Arms".to_owned(), "base".to_owned(),
vec![0.0_f32,- 0.079_f32, 0.0_f32], false), vec![crate
::skeleton::CollisionVolume::from_base(crate
::skeleton::JointBase::from_parts("R_CLAVICLE".to_owned(), vec![0.02_f32,
0.0_f32, 0.02_f32], vec![0.0_f32, 0.0_f32, 0.0_f32], vec![0.07_f32,
0.14_f32, 0.05_f32], "Collision".to_owned(), "base".to_owned(),
vec![0.0_f32,- 0.1_f32, 0.0_f32], true))], vec![crate
::skeleton::Joint::from_parts(crate
::skeleton::JointBase::from_parts("mShoulderRight".to_owned(),
vec![0.0_f32,- 0.079_f32,- 0.0_f32], vec![0.0_f32, 0.0_f32, 0.0_f32],
vec![1.0_f32, 1.0_f32, 1.0_f32], "Arms".to_owned(), "base".to_owned(),
vec![0.0_f32,- 0.247_f32, 0.0_f32], false), vec![crate
::skeleton::CollisionVolume::from_base(crate
::skeleton::JointBase::from_parts("R_UPPER_ARM".to_owned(),
vec![0.0_f32,- 0.12_f32, 0.01_f32], vec![5.0_f32, 0.0_f32, 0.0_f32],
vec![0.05_f32, 0.17_f32, 0.05_f32], "Collision".to_owned(), "base"
.to_owned(), vec![0.0_f32,- 0.13_f32,- 0.003_f32], true))], vec![crate
::skeleton::Joint::from_parts(crate
::skeleton::JointBase::from_parts("mElbowRight".to_owned(),
vec![0.0_f32,- 0.248_f32,- 0.0_f32], vec![0.0_f32, 0.0_f32, 0.0_f32],
vec![1.0_f32, 1.0_f32, 1.0_f32], "Arms".to_owned(), "base".to_owned(),
vec![0.0_f32,- 0.205_f32, 0.0_f32], false), vec![crate
::skeleton::CollisionVolume::from_base(crate
::skeleton::JointBase::from_parts("R_LOWER_ARM".to_owned(),
vec![0.0_f32,- 0.1_f32, 0.0_f32], vec![3.0_f32, 0.0_f32, 0.0_f32],
vec![0.04_f32, 0.14_f32, 0.04_f32], "Collision".to_owned(), "base"
.to_owned(), vec![0.0_f32,- 0.1_f32,- 0.001_f32], true))], vec![crate
::skeleton::Joint::from_parts(crate
::skeleton::JointBase::from_parts("mWristRight".to_owned(),
vec![0.0_f32,- 0.205_f32,- 0.0_f32], vec![0.0_f32, 0.0_f32, 0.0_f32],
vec![1.0_f32, 1.0_f32, 1.0_f32], "Arms".to_owned(), "base".to_owned(),
vec![0.0_f32,- 0.06_f32, 0.0_f32], false), vec![crate
::skeleton::CollisionVolume::from_base(crate
::skeleton::JointBase::from_parts("R_HAND".to_owned(), vec![0.01_f32,-
0.05_f32, 0.0_f32], vec![3.0_f32, 0.0_f32, 10.0_f32], vec![0.05_f32,
0.08_f32, 0.03_f32], "Collision".to_owned(), "base".to_owned(),
vec![0.005_f32,- 0.049_f32,- 0.001_f32], false))], vec![crate
::skeleton::Joint::from_parts(crate
::skeleton::JointBase::from_parts("mHandMiddle1Right".to_owned(),
vec![0.013_f32,- 0.101_f32, 0.015_f32], vec![0.0_f32, 0.0_f32, 0.0_f32],
vec![1.0_f32, 1.0_f32, 1.0_f32], "Hand".to_owned(), "extended"
.to_owned(), vec![- 0.001_f32,- 0.04_f32,- 0.006_f32], false), vec![],
vec![crate ::skeleton::Joint::from_parts(crate
::skeleton::JointBase::from_parts("mHandMiddle2Right".to_owned(), vec![-
0.001_f32,- 0.04_f32,- 0.006_f32], vec![0.0_f32, 0.0_f32, 0.0_f32],
vec![1.0_f32, 1.0_f32, 1.0_f32], "Hand".to_owned(), "extended"
.to_owned(), vec![- 0.001_f32,- 0.049_f32,- 0.008_f32], false), vec![],
vec![crate ::skeleton::Joint::from_parts(crate
::skeleton::JointBase::from_parts("mHandMiddle3Right".to_owned(), vec![-
0.001_f32,- 0.049_f32,- 0.008_f32], vec![0.0_f32, 0.0_f32, 0.0_f32],
vec![1.0_f32, 1.0_f32, 1.0_f32], "Hand".to_owned(), "extended"
.to_owned(), vec![- 0.002_f32,- 0.033_f32,- 0.006_f32], false), vec![],
vec![], vec![- 0.001_f32,- 0.049_f32,- 0.008_f32],
"avatar_mHandMiddle3Right".to_owned(), true)], vec![- 0.001_f32,-
0.04_f32,- 0.006_f32], "avatar_mHandMiddle2Right".to_owned(), true)],
vec![0.013_f32,- 0.101_f32, 0.015_f32], "avatar_mHandMiddle1Right"
.to_owned(), false), crate ::skeleton::Joint::from_parts(crate
::skeleton::JointBase::from_parts("mHandIndex1Right".to_owned(),
vec![0.038_f32,- 0.097_f32, 0.015_f32], vec![0.0_f32, 0.0_f32, 0.0_f32],
vec![1.0_f32, 1.0_f32, 1.0_f32], "Hand".to_owned(), "extended"
.to_owned(), vec![0.017_f32,- 0.036_f32,- 0.006_f32], false), vec![],
vec![crate ::skeleton::Joint::from_parts(crate
::skeleton::JointBase::from_parts("mHandIndex2Right".to_owned(),
vec![0.017_f32,- 0.036_f32,- 0.006_f32], vec![0.0_f32, 0.0_f32, 0.0_f32],
vec![1.0_f32, 1.0_f32, 1.0_f32], "Hand".to_owned(), "extended"
.to_owned(), vec![0.014_f32,- 0.032_f32,- 0.006_f32], false), vec![],
vec![crate ::skeleton::Joint::from_parts(crate
::skeleton::JointBase::from_parts("mHandIndex3Right".to_owned(),
vec![0.014_f32,- 0.032_f32,- 0.006_f32], vec![0.0_f32, 0.0_f32, 0.0_f32],
vec![1.0_f32, 1.0_f32, 1.0_f32], "Hand".to_owned(), "extended"
.to_owned(), vec![0.011_f32,- 0.025_f32,- 0.004_f32], false), vec![],
vec![], vec![0.014_f32,- 0.032_f32,- 0.006_f32],
"avatar_mHandIndex3Right".to_owned(), true)], vec![0.017_f32,-
0.036_f32,- 0.006_f32], "avatar_mHandIndex2Right".to_owned(), true)],
vec![0.038_f32,- 0.097_f32, 0.015_f32], "avatar_mHandIndex1Right"
.to_owned(), false), crate ::skeleton::Joint::from_parts(crate
::skeleton::JointBase::from_parts("mHandRing1Right".to_owned(), vec![-
0.01_f32,- 0.099_f32, 0.009_f32], vec![0.0_f32, 0.0_f32, 0.0_f32],
vec![1.0_f32, 1.0_f32, 1.0_f32], "Hand".to_owned(), "extended"
.to_owned(), vec![- 0.013_f32,- 0.038_f32,- 0.008_f32], false), vec![],
vec![crate ::skeleton::Joint::from_parts(crate
::skeleton::JointBase::from_parts("mHandRing2Right".to_owned(), vec![-
0.013_f32,- 0.038_f32,- 0.008_f32], vec![0.0_f32, 0.0_f32, 0.0_f32],
vec![1.0_f32, 1.0_f32, 1.0_f32], "Hand".to_owned(), "extended"
.to_owned(), vec![- 0.013_f32,- 0.04_f32,- 0.009_f32], false), vec![],
vec![crate ::skeleton::Joint::from_parts(crate
::skeleton::JointBase::from_parts("mHandRing3Right".to_owned(), vec![-
0.013_f32,- 0.04_f32,- 0.009_f32], vec![0.0_f32, 0.0_f32, 0.0_f32],
vec![1.0_f32, 1.0_f32, 1.0_f32], "Hand".to_owned(), "extended"
.to_owned(), vec![- 0.01_f32,- 0.028_f32,- 0.006_f32], false), vec![],
vec![], vec![- 0.013_f32,- 0.04_f32,- 0.009_f32],
"avatar_mHandRing3Right".to_owned(), true)], vec![- 0.013_f32,-
0.038_f32,- 0.008_f32], "avatar_mHandRing2Right".to_owned(), true)],
vec![- 0.01_f32,- 0.099_f32, 0.009_f32], "avatar_mHandRing1Right"
.to_owned(), false), crate ::skeleton::Joint::from_parts(crate
::skeleton::JointBase::from_parts("mHandPinky1Right".to_owned(), vec![-
0.031_f32,- 0.095_f32, 0.003_f32], vec![0.0_f32, 0.0_f32, 0.0_f32],
vec![1.0_f32, 1.0_f32, 1.0_f32], "Hand".to_owned(), "extended"
.to_owned(), vec![- 0.024_f32,- 0.025_f32,- 0.006_f32], false), vec![],
vec![crate ::skeleton::Joint::from_parts(crate
::skeleton::JointBase::from_parts("mHandPinky2Right".to_owned(), vec![-
0.024_f32,- 0.025_f32,- 0.006_f32], vec![0.0_f32, 0.0_f32, 0.0_f32],
vec![1.0_f32, 1.0_f32, 1.0_f32], "Hand".to_owned(), "extended"
.to_owned(), vec![- 0.015_f32,- 0.018_f32,- 0.004_f32], false), vec![],
vec![crate ::skeleton::Joint::from_parts(crate
::skeleton::JointBase::from_parts("mHandPinky3Right".to_owned(), vec![-
0.015_f32,- 0.018_f32,- 0.004_f32], vec![0.0_f32, 0.0_f32, 0.0_f32],
vec![1.0_f32, 1.0_f32, 1.0_f32], "Hand".to_owned(), "extended"
.to_owned(), vec![- 0.013_f32,- 0.016_f32,- 0.004_f32], false), vec![],
vec![], vec![- 0.015_f32,- 0.018_f32,- 0.004_f32],
"avatar_mHandPinky3Right".to_owned(), true)], vec![- 0.024_f32,-
0.025_f32,- 0.006_f32], "avatar_mHandPinky2Right".to_owned(), true)],
vec![- 0.031_f32,- 0.095_f32, 0.003_f32], "avatar_mHandPinky1Right"
.to_owned(), false), crate ::skeleton::Joint::from_parts(crate
::skeleton::JointBase::from_parts("mHandThumb1Right".to_owned(),
vec![0.031_f32,- 0.026_f32, 0.004_f32], vec![0.0_f32, 0.0_f32, 0.0_f32],
vec![1.0_f32, 1.0_f32, 1.0_f32], "Hand".to_owned(), "extended"
.to_owned(), vec![0.028_f32,- 0.032_f32, 0.0_f32], false), vec![],
vec![crate ::skeleton::Joint::from_parts(crate
::skeleton::JointBase::from_parts("mHandThumb2Right".to_owned(),
vec![0.028_f32,- 0.032_f32,- 0.001_f32], vec![0.0_f32, 0.0_f32, 0.0_f32],
vec![1.0_f32, 1.0_f32, 1.0_f32], "Hand".to_owned(), "extended"
.to_owned(), vec![0.023_f32,- 0.031_f32, 0.0_f32], false), vec![],
vec![crate ::skeleton::Joint::from_parts(crate
::skeleton::JointBase::from_parts("mHandThumb3Right".to_owned(),
vec![0.023_f32,- 0.031_f32,- 0.001_f32], vec![0.0_f32, 0.0_f32, 0.0_f32],
vec![1.0_f32, 1.0_f32, 1.0_f32], "Hand".to_owned(), "extended"
.to_owned(), vec![0.015_f32,- 0.025_f32, 0.0_f32], false), vec![],
vec![], vec![0.023_f32,- 0.031_f32,- 0.001_f32],
"avatar_mHandThumb3Right".to_owned(), true)], vec![0.028_f32,-
0.032_f32,- 0.001_f32], "avatar_mHandThumb2Right".to_owned(), true)],
vec![0.031_f32,- 0.026_f32, 0.004_f32], "avatar_mHandThumb1Right"
.to_owned(), false)], vec![- 0.0_f32,- 0.205_f32,- 0.0_f32],
"rHand avatar_mWristRight".to_owned(), true)], vec![0.0_f32,- 0.248_f32,-
0.0_f32], "rForeArm avatar_mElbowRight".to_owned(), true)],
vec![0.0_f32,- 0.079418_f32,- 0.0_f32], "rShldr avatar_mShoulderRight"
.to_owned(), true)], vec![- 0.020927_f32,- 0.085_f32, 0.165396_f32],
"rCollar avatar_mCollarRight".to_owned(), false), crate
::skeleton::Joint::from_parts(crate
::skeleton::JointBase::from_parts("mWingsRoot".to_owned(), vec![-
0.014_f32, 0.0_f32, 0.0_f32], vec![0.0_f32, 0.0_f32, 0.0_f32],
vec![1.0_f32, 1.0_f32, 1.0_f32], "Wing".to_owned(), "extended"
.to_owned(), vec![- 0.061_f32, 0.0_f32, 0.0_f32], false), vec![],
vec![crate ::skeleton::Joint::from_parts(crate
::skeleton::JointBase::from_parts("mWing1Left".to_owned(), vec![-
0.099_f32, 0.105_f32, 0.181_f32], vec![0.0_f32, 0.0_f32, 0.0_f32],
vec![1.0_f32, 1.0_f32, 1.0_f32], "Wing".to_owned(), "extended"
.to_owned(), vec![- 0.168_f32, 0.169_f32, 0.067_f32], true), vec![],
vec![crate ::skeleton::Joint::from_parts(crate
::skeleton::JointBase::from_parts("mWing2Left".to_owned(), vec![-
0.168_f32, 0.169_f32, 0.067_f32], vec![0.0_f32, 0.0_f32, 0.0_f32],
vec![1.0_f32, 1.0_f32, 1.0_f32], "Wing".to_owned(), "extended"
.to_owned(), vec![- 0.181_f32, 0.183_f32, 0.0_f32], true), vec![],
vec![crate ::skeleton::Joint::from_parts(crate
::skeleton::JointBase::from_parts("mWing3Left".to_owned(), vec![-
0.181_f32, 0.183_f32, 0.0_f32], vec![0.0_f32, 0.0_f32, 0.0_f32],
vec![1.0_f32, 1.0_f32, 1.0_f32], "Wing".to_owned(), "extended"
.to_owned(), vec![- 0.171_f32, 0.173_f32, 0.0_f32], true), vec![],
vec![crate ::skeleton::Joint::from_parts(crate
::skeleton::JointBase::from_parts("mWing4Left".to_owned(), vec![-
0.171_f32, 0.173_f32, 0.0_f32], vec![0.0_f32, 0.0_f32, 0.0_f32],
vec![1.0_f32, 1.0_f32, 1.0_f32], "Wing".to_owned(), "extended"
.to_owned(), vec![- 0.146_f32, 0.132_f32, 0.0_f32], true), vec![],
vec![], vec![- 0.171_f32, 0.173_f32, 0.0_f32], "avatar_mWing4Left"
.to_owned(), true), crate ::skeleton::Joint::from_parts(crate
::skeleton::JointBase::from_parts("mWing4FanLeft".to_owned(), vec![-
0.171_f32, 0.173_f32, 0.0_f32], vec![0.0_f32, 0.0_f32, 0.0_f32],
vec![1.0_f32, 1.0_f32, 1.0_f32], "Wing".to_owned(), "extended"
.to_owned(), vec![- 0.068_f32, 0.062_f32,- 0.159_f32], true), vec![],
vec![], vec![- 0.171_f32, 0.173_f32, 0.0_f32], "avatar_mWing4FanLeft"
.to_owned(), true)], vec![- 0.181_f32, 0.183_f32, 0.0_f32],
"avatar_mWing3Left".to_owned(), true)], vec![- 0.168_f32, 0.169_f32,
0.067_f32], "avatar_mWing2Left".to_owned(), true)], vec![- 0.099_f32,
0.105_f32, 0.181_f32], "avatar_mWing1Left".to_owned(), false), crate
::skeleton::Joint::from_parts(crate
::skeleton::JointBase::from_parts("mWing1Right".to_owned(), vec![-
0.099_f32,- 0.105_f32, 0.181_f32], vec![0.0_f32, 0.0_f32, 0.0_f32],
vec![1.0_f32, 1.0_f32, 1.0_f32], "Wing".to_owned(), "extended"
.to_owned(), vec![- 0.168_f32,- 0.169_f32, 0.067_f32], true), vec![],
vec![crate ::skeleton::Joint::from_parts(crate
::skeleton::JointBase::from_parts("mWing2Right".to_owned(), vec![-
0.168_f32,- 0.169_f32, 0.067_f32], vec![0.0_f32, 0.0_f32, 0.0_f32],
vec![1.0_f32, 1.0_f32, 1.0_f32], "Wing".to_owned(), "extended"
.to_owned(), vec![- 0.181_f32,- 0.183_f32, 0.0_f32], true), vec![],
vec![crate ::skeleton::Joint::from_parts(crate
::skeleton::JointBase::from_parts("mWing3Right".to_owned(), vec![-
0.181_f32,- 0.183_f32, 0.0_f32], vec![0.0_f32, 0.0_f32, 0.0_f32],
vec![1.0_f32, 1.0_f32, 1.0_f32], "Wing".to_owned(), "extended"
.to_owned(), vec![- 0.171_f32,- 0.173_f32, 0.0_f32], true), vec![],
vec![crate ::skeleton::Joint::from_parts(crate
::skeleton::JointBase::from_parts("mWing4Right".to_owned(), vec![-
0.171_f32,- 0.173_f32, 0.0_f32], vec![0.0_f32, 0.0_f32, 0.0_f32],
vec![1.0_f32, 1.0_f32, 1.0_f32], "Wing".to_owned(), "extended"
.to_owned(), vec![- 0.146_f32,- 0.132_f32, 0.0_f32], true), vec![],
vec![], vec![- 0.171_f32,- 0.173_f32, 0.0_f32], "avatar_mWing4Right"
.to_owned(), true), crate ::skeleton::Joint::from_parts(crate
::skeleton::JointBase::from_parts("mWing4FanRight".to_owned(), vec![-
0.171_f32,- 0.173_f32, 0.0_f32], vec![0.0_f32, 0.0_f32, 0.0_f32],
vec![1.0_f32, 1.0_f32, 1.0_f32], "Wing".to_owned(), "extended"
.to_owned(), vec![- 0.068_f32,- 0.062_f32,- 0.159_f32], true), vec![],
vec![], vec![- 0.171_f32,- 0.173_f32, 0.0_f32], "avatar_mWing4FanRight"
.to_owned(), true)], vec![- 0.181_f32,- 0.183_f32, 0.0_f32],
"avatar_mWing3Right".to_owned(), true)], vec![- 0.168_f32,- 0.169_f32,
0.067_f32], "avatar_mWing2Right".to_owned(), true)], vec![- 0.099_f32,-
0.105_f32, 0.181_f32], "avatar_mWing1Right".to_owned(), false)], vec![-
0.014_f32, 0.0_f32, 0.0_f32], "avatar_mWingsRoot".to_owned(), false)],
vec![- 0.015368_f32, 0.0_f32, 0.204877_f32], "chest avatar_mChest"
.to_owned(), true)], vec![0.015368_f32, 0.0_f32,- 0.204877_f32],
"avatar_mSpine4".to_owned(), true)], vec![- 0.015368_f32, 0.0_f32,
0.204877_f32], "avatar_mSpine3".to_owned(), true)], vec![0.0_f32,
0.0_f32, 0.084073_f32], "abdomen avatar_mTorso".to_owned(), true)],
vec![0.0_f32, 0.0_f32,- 0.084073_f32], "avatar_mSpine2".to_owned(),
true)], vec![0.0_f32, 0.0_f32, 0.084073_f32], "avatar_mSpine1"
.to_owned(), true), crate ::skeleton::Joint::from_parts(crate
::skeleton::JointBase::from_parts("mHipRight".to_owned(),
vec![0.034_f32,- 0.129_f32,- 0.041_f32], vec![0.0_f32, 0.0_f32, 0.0_f32],
vec![1.0_f32, 1.0_f32, 1.0_f32], "Legs".to_owned(), "base".to_owned(),
vec![- 0.001_f32, 0.049_f32,- 0.491_f32], true), vec![crate
::skeleton::CollisionVolume::from_base(crate
::skeleton::JointBase::from_parts("R_UPPER_LEG".to_owned(), vec![-
0.02_f32, 0.05_f32,- 0.22_f32], vec![0.0_f32, 0.0_f32, 0.0_f32],
vec![0.09_f32, 0.09_f32, 0.32_f32], "Collision".to_owned(), "base"
.to_owned(), vec![0.0_f32, 0.0_f32,- 0.2_f32], true))], vec![crate
::skeleton::Joint::from_parts(crate
::skeleton::JointBase::from_parts("mKneeRight".to_owned(), vec![-
0.001_f32, 0.049_f32,- 0.491_f32], vec![0.0_f32, 0.0_f32, 0.0_f32],
vec![1.0_f32, 1.0_f32, 1.0_f32], "Legs".to_owned(), "base".to_owned(),
vec![- 0.029_f32, 0.0_f32,- 0.469_f32], false), vec![crate
::skeleton::CollisionVolume::from_base(crate
::skeleton::JointBase::from_parts("R_LOWER_LEG".to_owned(), vec![-
0.02_f32, 0.0_f32,- 0.2_f32], vec![0.0_f32, 0.0_f32, 0.0_f32],
vec![0.06_f32, 0.06_f32, 0.25_f32], "Collision".to_owned(), "base"
.to_owned(), vec![- 0.01_f32, 0.0_f32,- 0.15_f32], true))], vec![crate
::skeleton::Joint::from_parts(crate
::skeleton::JointBase::from_parts("mAnkleRight".to_owned(), vec![-
0.029_f32, 0.0_f32,- 0.468_f32], vec![0.0_f32, 0.0_f32, 0.0_f32],
vec![1.0_f32, 1.0_f32, 1.0_f32], "Legs".to_owned(), "base".to_owned(),
vec![0.112_f32, 0.0_f32,- 0.061_f32], false), vec![crate
::skeleton::CollisionVolume::from_base(crate
::skeleton::JointBase::from_parts("R_FOOT".to_owned(), vec![0.077_f32,
0.0_f32,- 0.041_f32], vec![0.0_f32, 10.0_f32, 0.0_f32], vec![0.13_f32,
0.05_f32, 0.05_f32], "Collision".to_owned(), "base".to_owned(),
vec![0.089_f32, 0.0_f32,- 0.026_f32], true))], vec![crate
::skeleton::Joint::from_parts(crate
::skeleton::JointBase::from_parts("mFootRight".to_owned(),
vec![0.112_f32,- 0.0_f32,- 0.061_f32], vec![0.0_f32, 0.0_f32, 0.0_f32],
vec![1.0_f32, 1.0_f32, 1.0_f32], "Extra".to_owned(), "base".to_owned(),
vec![0.105_f32,- 0.01_f32, 0.0_f32], true), vec![], vec![crate
::skeleton::Joint::from_parts(crate
::skeleton::JointBase::from_parts("mToeRight".to_owned(), vec![0.109_f32,
0.0_f32, 0.0_f32], vec![0.0_f32, 0.0_f32, 0.0_f32], vec![1.0_f32,
1.0_f32, 1.0_f32], "Extra".to_owned(), "base".to_owned(), vec![0.02_f32,
0.0_f32, 0.0_f32], false), vec![], vec![], vec![0.105399_f32,-
0.010408_f32,- 0.000104_f32], "avatar_mToeRight".to_owned(), false)],
vec![0.111956_f32,- 0.0_f32,- 0.060637_f32], "avatar_mFootRight"
.to_owned(), true)], vec![- 0.028869_f32, 0.0_f32,- 0.468494_f32],
"rFoot avatar_mAnkleRight".to_owned(), true)], vec![- 0.00078_f32,
0.048635_f32,- 0.490922_f32], "rShin avatar_mKneeRight".to_owned(),
true)], vec![0.03362_f32,- 0.128806_f32,- 0.041086_f32],
"rThigh avatar_mHipRight".to_owned(), false), crate
::skeleton::Joint::from_parts(crate
::skeleton::JointBase::from_parts("mHipLeft".to_owned(), vec![0.034_f32,
0.127_f32,- 0.041_f32], vec![0.0_f32, 0.0_f32, 0.0_f32], vec![1.0_f32,
1.0_f32, 1.0_f32], "Legs".to_owned(), "base".to_owned(), vec![-
0.001_f32,- 0.046_f32,- 0.491_f32], true), vec![crate
::skeleton::CollisionVolume::from_base(crate
::skeleton::JointBase::from_parts("L_UPPER_LEG".to_owned(), vec![-
0.02_f32,- 0.05_f32,- 0.22_f32], vec![0.0_f32, 0.0_f32, 0.0_f32],
vec![0.09_f32, 0.09_f32, 0.32_f32], "Collision".to_owned(), "base"
.to_owned(), vec![0.0_f32, 0.0_f32,- 0.2_f32], true))], vec![crate
::skeleton::Joint::from_parts(crate
::skeleton::JointBase::from_parts("mKneeLeft".to_owned(), vec![-
0.001_f32,- 0.046_f32,- 0.491_f32], vec![0.0_f32, 0.0_f32, 0.0_f32],
vec![1.0_f32, 1.0_f32, 1.0_f32], "Legs".to_owned(), "base".to_owned(),
vec![- 0.029_f32, 0.001_f32,- 0.469_f32], false), vec![crate
::skeleton::CollisionVolume::from_base(crate
::skeleton::JointBase::from_parts("L_LOWER_LEG".to_owned(), vec![-
0.02_f32, 0.0_f32,- 0.2_f32], vec![0.0_f32, 0.0_f32, 0.0_f32],
vec![0.06_f32, 0.06_f32, 0.25_f32], "Collision".to_owned(), "base"
.to_owned(), vec![- 0.01_f32, 0.0_f32,- 0.15_f32], true))], vec![crate
::skeleton::Joint::from_parts(crate
::skeleton::JointBase::from_parts("mAnkleLeft".to_owned(), vec![-
0.029_f32, 0.001_f32,- 0.468_f32], vec![0.0_f32, 0.0_f32, 0.0_f32],
vec![1.0_f32, 1.0_f32, 1.0_f32], "Legs".to_owned(), "base".to_owned(),
vec![0.112_f32, 0.0_f32,- 0.061_f32], false), vec![crate
::skeleton::CollisionVolume::from_base(crate
::skeleton::JointBase::from_parts("L_FOOT".to_owned(), vec![0.077_f32,
0.0_f32,- 0.041_f32], vec![0.0_f32, 10.0_f32, 0.0_f32], vec![0.13_f32,
0.05_f32, 0.05_f32], "Collision".to_owned(), "base".to_owned(),
vec![0.089_f32, 0.0_f32,- 0.026_f32], true))], vec![crate
::skeleton::Joint::from_parts(crate
::skeleton::JointBase::from_parts("mFootLeft".to_owned(),
vec![0.112_f32,- 0.0_f32,- 0.061_f32], vec![0.0_f32, 0.0_f32, 0.0_f32],
vec![1.0_f32, 1.0_f32, 1.0_f32], "Extra".to_owned(), "base".to_owned(),
vec![0.105_f32, 0.008_f32, 0.001_f32], true), vec![], vec![crate
::skeleton::Joint::from_parts(crate
::skeleton::JointBase::from_parts("mToeLeft".to_owned(), vec![0.109_f32,
0.0_f32, 0.0_f32], vec![0.0_f32, 0.0_f32, 0.0_f32], vec![1.0_f32,
1.0_f32, 1.0_f32], "Extra".to_owned(), "base".to_owned(), vec![0.02_f32,
0.0_f32, 0.0_f32], false), vec![], vec![], vec![0.105387_f32,
0.00827_f32, 0.000871_f32], "avatar_mToeLeft".to_owned(), false)],
vec![0.111956_f32,- 0.0_f32,- 0.06062_f32], "avatar_mFootLeft"
.to_owned(), true)], vec![- 0.028887_f32, 0.001378_f32,- 0.468449_f32],
"lFoot avatar_mAnkleLeft".to_owned(), true)], vec![- 0.000887_f32,-
0.045568_f32,- 0.491053_f32], "lShin avatar_mKneeLeft".to_owned(),
true)], vec![0.033757_f32, 0.126765_f32,- 0.040998_f32],
"lThigh avatar_mHipLeft".to_owned(), false), crate
::skeleton::Joint::from_parts(crate
::skeleton::JointBase::from_parts("mTail1".to_owned(), vec![- 0.116_f32,
0.0_f32, 0.047_f32], vec![0.0_f32, 0.0_f32, 0.0_f32], vec![1.0_f32,
1.0_f32, 1.0_f32], "Tail".to_owned(), "extended".to_owned(), vec![-
0.197_f32, 0.0_f32, 0.0_f32], true), vec![], vec![crate
::skeleton::Joint::from_parts(crate
::skeleton::JointBase::from_parts("mTail2".to_owned(), vec![- 0.197_f32,
0.0_f32, 0.0_f32], vec![0.0_f32, 0.0_f32, 0.0_f32], vec![1.0_f32,
1.0_f32, 1.0_f32], "Tail".to_owned(), "extended".to_owned(), vec![-
0.168_f32, 0.0_f32, 0.0_f32], true), vec![], vec![crate
::skeleton::Joint::from_parts(crate
::skeleton::JointBase::from_parts("mTail3".to_owned(), vec![- 0.168_f32,
0.0_f32, 0.0_f32], vec![0.0_f32, 0.0_f32, 0.0_f32], vec![1.0_f32,
1.0_f32, 1.0_f32], "Tail".to_owned(), "extended".to_owned(), vec![-
0.142_f32, 0.0_f32, 0.0_f32], true), vec![], vec![crate
::skeleton::Joint::from_parts(crate
::skeleton::JointBase::from_parts("mTail4".to_owned(), vec![- 0.142_f32,
0.0_f32, 0.0_f32], vec![0.0_f32, 0.0_f32, 0.0_f32], vec![1.0_f32,
1.0_f32, 1.0_f32], "Tail".to_owned(), "extended".to_owned(), vec![-
0.112_f32, 0.0_f32, 0.0_f32], true), vec![], vec![crate
::skeleton::Joint::from_parts(crate
::skeleton::JointBase::from_parts("mTail5".to_owned(), vec![- 0.112_f32,
0.0_f32, 0.0_f32], vec![0.0_f32, 0.0_f32, 0.0_f32], vec![1.0_f32,
1.0_f32, 1.0_f32], "Tail".to_owned(), "extended".to_owned(), vec![-
0.094_f32, 0.0_f32, 0.0_f32], true), vec![], vec![crate
::skeleton::Joint::from_parts(crate
::skeleton::JointBase::from_parts("mTail6".to_owned(), vec![- 0.094_f32,
0.0_f32, 0.0_f32], vec![0.0_f32, 0.0_f32, 0.0_f32], vec![1.0_f32,
1.0_f32, 1.0_f32], "Tail".to_owned(), "extended".to_owned(), vec![-
0.089_f32, 0.0_f32, 0.0_f32], true), vec![], vec![], vec![- 0.094_f32,
0.0_f32, 0.0_f32], "avatar_mTail6".to_owned(), true)], vec![- 0.112_f32,
0.0_f32, 0.0_f32], "avatar_mTail5".to_owned(), true)], vec![- 0.142_f32,
0.0_f32, 0.0_f32], "avatar_mTail4".to_owned(), true)], vec![- 0.168_f32,
0.0_f32, 0.0_f32], "avatar_mTail3".to_owned(), true)], vec![- 0.197_f32,
0.0_f32, 0.0_f32], "avatar_mTail2".to_owned(), true)], vec![- 0.116_f32,
0.0_f32, 0.047_f32], "avatar_mTail1".to_owned(), false), crate
::skeleton::Joint::from_parts(crate
::skeleton::JointBase::from_parts("mGroin".to_owned(), vec![0.064_f32,
0.0_f32,- 0.097_f32], vec![0.0_f32, 0.0_f32, 0.0_f32], vec![1.0_f32,
1.0_f32, 1.0_f32], "Groin".to_owned(), "extended".to_owned(),
vec![0.004_f32, 0.0_f32,- 0.066_f32], false), vec![], vec![],
vec![0.064_f32, 0.0_f32,- 0.097_f32], "avatar_mGroin".to_owned(), false),
crate ::skeleton::Joint::from_parts(crate
::skeleton::JointBase::from_parts("mHindLimbsRoot".to_owned(), vec![-
0.2_f32, 0.0_f32, 0.084_f32], vec![0.0_f32, 0.0_f32, 0.0_f32],
vec![1.0_f32, 1.0_f32, 1.0_f32], "Limb".to_owned(), "extended"
.to_owned(), vec![- 0.204_f32, 0.0_f32, 0.0_f32], false), vec![],
vec![crate ::skeleton::Joint::from_parts(crate
::skeleton::JointBase::from_parts("mHindLimb1Left".to_owned(), vec![-
0.204_f32, 0.129_f32,- 0.125_f32], vec![0.0_f32, 0.0_f32, 0.0_f32],
vec![1.0_f32, 1.0_f32, 1.0_f32], "Limb".to_owned(), "extended"
.to_owned(), vec![0.002_f32,- 0.046_f32,- 0.491_f32], true), vec![],
vec![crate ::skeleton::Joint::from_parts(crate
::skeleton::JointBase::from_parts("mHindLimb2Left".to_owned(),
vec![0.002_f32,- 0.046_f32,- 0.491_f32], vec![0.0_f32, 0.0_f32, 0.0_f32],
vec![1.0_f32, 1.0_f32, 1.0_f32], "Limb".to_owned(), "extended"
.to_owned(), vec![- 0.03_f32,- 0.003_f32,- 0.468_f32], false), vec![],
vec![crate ::skeleton::Joint::from_parts(crate
::skeleton::JointBase::from_parts("mHindLimb3Left".to_owned(), vec![-
0.03_f32,- 0.003_f32,- 0.468_f32], vec![0.0_f32, 0.0_f32, 0.0_f32],
vec![1.0_f32, 1.0_f32, 1.0_f32], "Limb".to_owned(), "extended"
.to_owned(), vec![0.112_f32, 0.0_f32,- 0.061_f32], false), vec![],
vec![crate ::skeleton::Joint::from_parts(crate
::skeleton::JointBase::from_parts("mHindLimb4Left".to_owned(),
vec![0.112_f32, 0.0_f32,- 0.061_f32], vec![0.0_f32, 0.0_f32, 0.0_f32],
vec![1.0_f32, 1.0_f32, 1.0_f32], "Limb".to_owned(), "extended"
.to_owned(), vec![0.105_f32, 0.008_f32, 0.0_f32], true), vec![], vec![],
vec![0.112_f32, 0.0_f32,- 0.061_f32], "avatar_mHindLimb4Left".to_owned(),
true)], vec![- 0.03_f32,- 0.003_f32,- 0.468_f32], "avatar_mHindLimb3Left"
.to_owned(), true)], vec![0.002_f32,- 0.046_f32,- 0.491_f32],
"avatar_mHindLimb2Left".to_owned(), true)], vec![- 0.204_f32, 0.129_f32,-
0.125_f32], "avatar_mHindLimb1Left".to_owned(), false), crate
::skeleton::Joint::from_parts(crate
::skeleton::JointBase::from_parts("mHindLimb1Right".to_owned(), vec![-
0.204_f32,- 0.129_f32,- 0.125_f32], vec![0.0_f32, 0.0_f32, 0.0_f32],
vec![1.0_f32, 1.0_f32, 1.0_f32], "Limb".to_owned(), "extended"
.to_owned(), vec![0.002_f32, 0.046_f32,- 0.491_f32], true), vec![],
vec![crate ::skeleton::Joint::from_parts(crate
::skeleton::JointBase::from_parts("mHindLimb2Right".to_owned(),
vec![0.002_f32, 0.046_f32,- 0.491_f32], vec![0.0_f32, 0.0_f32, 0.0_f32],
vec![1.0_f32, 1.0_f32, 1.0_f32], "Limb".to_owned(), "extended"
.to_owned(), vec![- 0.03_f32, 0.003_f32,- 0.468_f32], false), vec![],
vec![crate ::skeleton::Joint::from_parts(crate
::skeleton::JointBase::from_parts("mHindLimb3Right".to_owned(), vec![-
0.03_f32, 0.003_f32,- 0.468_f32], vec![0.0_f32, 0.0_f32, 0.0_f32],
vec![1.0_f32, 1.0_f32, 1.0_f32], "Limb".to_owned(), "extended"
.to_owned(), vec![0.112_f32, 0.0_f32,- 0.061_f32], false), vec![],
vec![crate ::skeleton::Joint::from_parts(crate
::skeleton::JointBase::from_parts("mHindLimb4Right".to_owned(),
vec![0.112_f32, 0.0_f32,- 0.061_f32], vec![0.0_f32, 0.0_f32, 0.0_f32],
vec![1.0_f32, 1.0_f32, 1.0_f32], "Limb".to_owned(), "extended"
.to_owned(), vec![0.105_f32,- 0.008_f32, 0.0_f32], true), vec![], vec![],
vec![0.112_f32, 0.0_f32,- 0.061_f32], "avatar_mHindLimb4Right"
.to_owned(), true)], vec![- 0.03_f32, 0.003_f32,- 0.468_f32],
"avatar_mHindLimb3Right".to_owned(), true)], vec![0.002_f32, 0.046_f32,-
0.491_f32], "avatar_mHindLimb2Right".to_owned(), true)], vec![-
0.204_f32,- 0.129_f32,- 0.125_f32], "avatar_mHindLimb1Right".to_owned(),
false)], vec![- 0.2_f32, 0.0_f32, 0.084_f32], "avatar_mHindLimbsRoot"
.to_owned(), false)
],
vec![0.0_f32, 0.0_f32, 1.067015_f32],
"hip avatar_mPelvis".to_owned(),
false,
),
2.0_f32,
false,
"133".to_owned(),
"26".to_owned(),
)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn generated_default_shape() {
let skeleton = default_skeleton();
assert_eq!(skeleton.version(), 2.0);
assert_eq!(skeleton.num_bones(), "133");
assert_eq!(skeleton.num_collision_volumes(), "26");
assert_eq!(skeleton.bone().base.name(), "mPelvis");
}
}

View File

@@ -0,0 +1,208 @@
//! Golden fixtures for skeleton, attention, and genepool generation.
#![allow(clippy::float_cmp)] // Golden XML values must retain their exact f32 representation.
use libremetaverse::rendering::Joint;
use libremetaverse::{Genepool, LindenAttentions, LookAtType};
fn skeleton_counts(root: &Joint) -> (usize, usize) {
let mut bones = 1;
let mut collision_volumes = root.collision_volume().len();
if let Some(children) = root.bone() {
for child in children {
let (child_bones, child_collision_volumes) = skeleton_counts(&child);
bones += child_bones;
collision_volumes += child_collision_volumes;
}
}
(bones, collision_volumes)
}
#[test]
fn default_skeleton_matches_the_complete_pinned_hierarchy() {
let skeleton = libremetaverse::rendering::LindenSkeleton::get_default().unwrap();
assert_eq!(skeleton.version(), 2.0);
assert!(!skeleton.version_specified());
assert_eq!(skeleton.num_bones(), "133");
assert_eq!(skeleton.num_collision_volumes(), "26");
assert_eq!(skeleton_counts(&skeleton.bone()), (133, 26));
assert_eq!(skeleton.get_all_joints().unwrap().count(), 133);
let pelvis = skeleton.bone();
assert_eq!(pelvis.base.name(), "mPelvis");
assert_eq!(pelvis.base.pos(), vec![0.0, 0.0, 1.067]);
assert_eq!(pelvis.base.rot(), vec![0.0, 0.0, 0.0]);
assert_eq!(pelvis.base.scale(), vec![1.0, 1.0, 1.0]);
assert_eq!(pelvis.base.end(), vec![0.0, 0.0, 0.084]);
assert_eq!(pelvis.pivot(), vec![0.0, 0.0, 1.067_015]);
assert_eq!(pelvis.base.group(), "Torso");
assert_eq!(pelvis.base.support(), "base");
assert!(pelvis.base.reposition());
assert!(!pelvis.connected());
assert_eq!(
pelvis.get_aliases_list().unwrap(),
["hip", "avatar_mPelvis"]
);
assert_eq!(pelvis.collision_volume().len(), 2);
assert_eq!(pelvis.collision_volume()[0].base.name(), "PELVIS");
let by_alias = skeleton.get_bone("hip".to_owned()).unwrap().unwrap();
assert_eq!(by_alias.base.name(), "mPelvis");
assert_eq!(
libremetaverse::rendering::LindenSkeleton::load_with_method().unwrap(),
skeleton
);
assert_eq!(
libremetaverse::rendering::LindenSkeleton::load_with_string(None).unwrap(),
skeleton
);
}
#[test]
fn skeleton_records_preserve_mutable_mapped_properties() {
let mut base = libremetaverse::rendering::JointBase::new().unwrap();
base.set_name("joint".to_owned());
base.set_pos(vec![1.0, 2.0, 3.0]);
base.set_rot(vec![4.0, 5.0, 6.0]);
base.set_scale(vec![7.0, 8.0, 9.0]);
base.set_group("group".to_owned());
base.set_support("extended".to_owned());
base.set_end(vec![10.0, 11.0, 12.0]);
base.set_reposition(true);
assert_eq!(base.name(), "joint");
assert_eq!(base.pos(), vec![1.0, 2.0, 3.0]);
assert_eq!(base.rot(), vec![4.0, 5.0, 6.0]);
assert_eq!(base.scale(), vec![7.0, 8.0, 9.0]);
assert_eq!(base.group(), "group");
assert_eq!(base.support(), "extended");
assert_eq!(base.end(), vec![10.0, 11.0, 12.0]);
assert!(base.reposition());
let mut joint = libremetaverse::rendering::Joint::new().unwrap();
joint.base = base;
joint.set_aliases("one two".to_owned());
joint.set_pivot(vec![13.0, 14.0, 15.0]);
joint.set_connected(true);
joint.set_collision_volume(vec![
libremetaverse::rendering::CollisionVolume::new().unwrap(),
]);
joint.set_bone(None);
assert_eq!(joint.aliases(), "one two");
assert_eq!(joint.pivot(), vec![13.0, 14.0, 15.0]);
assert!(joint.connected());
assert_eq!(joint.collision_volume().len(), 1);
assert!(joint.bone().is_none());
let mut skeleton = libremetaverse::rendering::LindenSkeleton::new().unwrap();
skeleton.set_bone(joint);
skeleton.set_version(3.0);
skeleton.set_version_specified(true);
skeleton.set_num_bones("1".to_owned());
skeleton.set_num_collision_volumes("1".to_owned());
assert_eq!(skeleton.version(), 3.0);
assert!(skeleton.version_specified());
assert_eq!(skeleton.num_bones(), "1");
assert_eq!(skeleton.num_collision_volumes(), "1");
}
#[test]
fn attention_catalogs_match_default_and_updated_fixtures() {
let default = LindenAttentions::default();
let updated = LindenAttentions::updated();
assert_eq!(default.len(), 2);
assert_eq!(updated.len(), 2);
assert_eq!(default[0].name(), "Masculine");
assert_eq!(default[1].name(), "Feminine");
assert!(
default
.iter()
.chain(&updated)
.all(|set| set.entries().len() == 11)
);
let none = default[0].get(LookAtType::None).unwrap();
assert_eq!(none.priority(), 0.0);
assert_eq!(none.timeout(), 0.0);
let clear = default[0].get(LookAtType::Clear).unwrap();
assert_eq!(clear.priority(), 0.0);
assert_eq!(clear.timeout(), 0.0);
let default_conversation = default[0].get(LookAtType::Conversation).unwrap();
assert_eq!(default_conversation.priority(), 0.0);
assert_eq!(default_conversation.timeout(), -1.0);
let updated_conversation = updated[0].get(LookAtType::Conversation).unwrap();
assert_eq!(updated_conversation.priority(), 5.0);
assert_eq!(updated_conversation.timeout(), 3.0);
assert_eq!(
LindenAttentions::updated_feminine()
.get(LookAtType::Focus)
.unwrap()
.timeout(),
10.0
);
assert_eq!(LindenAttentions::default_masculine(), default[0]);
assert_eq!(LindenAttentions::default_feminine(), default[1]);
assert_eq!(LindenAttentions::updated_masculine(), updated[0]);
assert_eq!(LindenAttentions::updated_feminine(), updated[1]);
let mut changed = updated_conversation;
changed.set_priority(8.0);
changed.set_timeout(9.0);
assert_eq!(changed.priority(), 8.0);
assert_eq!(changed.timeout(), 9.0);
let mut set = default[0].clone();
set.set_name("Changed".to_owned());
set.set_entries(vec![changed.clone()]);
assert_eq!(set.name(), "Changed");
assert_eq!(set.entries(), vec![changed]);
assert_eq!(
set.get(LookAtType::Clear),
Err(libremetaverse::Error::IndexOutOfRange)
);
}
#[test]
fn genepool_matches_every_pinned_archetype_and_supports_lookup() {
let archetypes = Genepool::archetypes();
assert_eq!(archetypes.len(), 24);
assert!(
archetypes
.iter()
.all(|archetype| archetype.params().len() == 140)
);
assert_eq!(
archetypes
.iter()
.map(|archetype| archetype.params().len())
.sum::<usize>(),
3360
);
assert_eq!(archetypes[0].name(), "M W Skinny");
assert_eq!(Genepool::find_index("M W Skinny".to_owned()).unwrap(), 0);
assert_eq!(Genepool::find_index("missing".to_owned()).unwrap(), -1);
assert_eq!(Genepool::get(0).unwrap(), archetypes[0]);
assert_eq!(
Genepool::get(-1),
Err(libremetaverse::Error::IndexOutOfRange)
);
assert_eq!(
Genepool::get(24),
Err(libremetaverse::Error::IndexOutOfRange)
);
let mut first = archetypes[0].params()[0].clone();
assert_eq!(first.id(), 1);
assert_eq!(first.name(), "Big_Brow");
assert_eq!(first.value(), 1.54);
first.set_id(99);
first.set_name("Changed".to_owned());
first.set_value(0.25);
assert_eq!(first.id(), 99);
assert_eq!(first.name(), "Changed");
assert_eq!(first.value(), 0.25);
let mut archetype = archetypes[0].clone();
archetype.set_name("Changed archetype".to_owned());
archetype.set_params(vec![first.clone()]);
assert_eq!(archetype.name(), "Changed archetype");
assert_eq!(archetype.params(), vec![first]);
}

View File

@@ -193,6 +193,7 @@ dependencies = [
"libremetaverse-imaging", "libremetaverse-imaging",
"libremetaverse-structured-data", "libremetaverse-structured-data",
"libremetaverse-types", "libremetaverse-types",
"roxmltree",
] ]
[[package]] [[package]]
@@ -372,6 +373,15 @@ version = "6.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf" checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf"
[[package]]
name = "roxmltree"
version = "0.21.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f1964b10c76125c36f8afe190065a4bf9a87bf324842c05701330bba9f1cacbb"
dependencies = [
"memchr",
]
[[package]] [[package]]
name = "rustversion" name = "rustversion"
version = "1.0.23" version = "1.0.23"

View File

@@ -0,0 +1,881 @@
//! Schema-validated generators for skeleton, attention, and genepool catalogs.
use super::{InputSpec, generated_rust, normalize_text, verify_inputs};
use roxmltree::{Document, Node};
use std::collections::BTreeSet;
use std::fs;
use std::path::Path;
#[derive(Clone, Debug, PartialEq)]
struct JointBaseRecord {
name: String,
pos: Vec<f32>,
rot: Vec<f32>,
scale: Vec<f32>,
group: String,
support: String,
end: Vec<f32>,
reposition: bool,
}
#[derive(Clone, Debug, PartialEq)]
struct CollisionRecord {
base: JointBaseRecord,
}
#[derive(Clone, Debug, PartialEq)]
struct JointRecord {
base: JointBaseRecord,
collision_volumes: Vec<CollisionRecord>,
bones: Vec<JointRecord>,
pivot: Vec<f32>,
aliases: String,
connected: bool,
}
#[derive(Clone, Debug, PartialEq)]
struct SkeletonRecord {
version: f32,
num_bones: String,
num_collision_volumes: String,
root: JointRecord,
}
#[derive(Clone, Copy, Debug, PartialEq)]
struct AttentionRecord {
priority: f32,
timeout: f32,
}
#[derive(Clone, Debug, PartialEq)]
struct AttentionSetRecord {
name: String,
entries: [AttentionRecord; 11],
}
#[derive(Clone, Debug, PartialEq)]
struct ArchetypeParamRecord {
id: i32,
name: String,
value: f32,
}
#[derive(Clone, Debug, PartialEq)]
struct ArchetypeRecord {
name: String,
params: Vec<ArchetypeParamRecord>,
}
const ATTENTION_NAMES: [&str; 9] = [
"idle",
"auto_listen",
"freelook",
"respond",
"hover",
"conversation",
"select",
"focus",
"mouselook",
];
fn location(path: &str, node: Node<'_, '_>, code: &str, message: &str) -> String {
let position = node.document().text_pos_at(node.range().start);
format!(
"{path}:{}:{}: error[{code}]: {message}",
position.row, position.col
)
}
fn required_attribute(
path: &str,
node: Node<'_, '_>,
name: &str,
code: &str,
) -> Result<String, String> {
node.attribute(name)
.filter(|value| !value.is_empty())
.map(str::to_owned)
.ok_or_else(|| {
location(
path,
node,
code,
&format!("<{}> requires attribute {name:?}", node.tag_name().name()),
)
})
}
fn parse_f32(path: &str, node: Node<'_, '_>, name: &str, code: &str) -> Result<f32, String> {
let value = required_attribute(path, node, name, code)?;
let parsed = value.parse::<f32>().map_err(|_| {
location(
path,
node,
code,
&format!("attribute {name:?} must be a finite float, found {value:?}"),
)
})?;
if parsed.is_finite() {
Ok(parsed)
} else {
Err(location(
path,
node,
code,
&format!("attribute {name:?} must be a finite float, found {value:?}"),
))
}
}
fn parse_i32(path: &str, node: Node<'_, '_>, name: &str, code: &str) -> Result<i32, String> {
let value = required_attribute(path, node, name, code)?;
value.parse::<i32>().map_err(|_| {
location(
path,
node,
code,
&format!("attribute {name:?} must be a signed 32-bit integer, found {value:?}"),
)
})
}
fn parse_usize(path: &str, node: Node<'_, '_>, name: &str, code: &str) -> Result<usize, String> {
let value = required_attribute(path, node, name, code)?;
value.parse::<usize>().map_err(|_| {
location(
path,
node,
code,
&format!("attribute {name:?} must be a non-negative integer, found {value:?}"),
)
})
}
fn vector_attribute(
path: &str,
node: Node<'_, '_>,
name: &str,
code: &str,
) -> Result<Vec<f32>, String> {
let Some(value) = node.attribute(name) else {
return Ok(Vec::new());
};
let values = value
.split_ascii_whitespace()
.map(str::parse::<f32>)
.collect::<Result<Vec<_>, _>>()
.map_err(|_| {
location(
path,
node,
code,
&format!("attribute {name:?} must contain three finite floats"),
)
})?;
if values.len() != 3 || values.iter().any(|value| !value.is_finite()) {
return Err(location(
path,
node,
code,
&format!("attribute {name:?} must contain three finite floats"),
));
}
Ok(values)
}
fn boolean_attribute(
path: &str,
node: Node<'_, '_>,
name: &str,
code: &str,
) -> Result<bool, String> {
match node.attribute(name) {
None => Ok(false),
Some(value) if value.eq_ignore_ascii_case("true") => Ok(true),
Some(value) if value.eq_ignore_ascii_case("false") => Ok(false),
Some(value) => Err(location(
path,
node,
code,
&format!("attribute {name:?} must be true or false, found {value:?}"),
)),
}
}
fn parse_joint_base(
path: &str,
node: Node<'_, '_>,
name_code: &str,
) -> Result<JointBaseRecord, String> {
let support = node.attribute("support").unwrap_or_default().to_owned();
if !support.is_empty()
&& !support.eq_ignore_ascii_case("base")
&& !support.eq_ignore_ascii_case("extended")
{
return Err(location(
path,
node,
"SK018",
&format!("unknown skeleton support category {support:?}"),
));
}
Ok(JointBaseRecord {
name: required_attribute(path, node, "name", name_code)?,
pos: vector_attribute(path, node, "pos", "SK011")?,
rot: vector_attribute(path, node, "rot", "SK012")?,
scale: vector_attribute(path, node, "scale", "SK013")?,
group: node.attribute("group").unwrap_or_default().to_owned(),
support,
end: vector_attribute(path, node, "end", "SK014")?,
reposition: boolean_attribute(path, node, "reposition", "SK015")?,
})
}
fn parse_joint(
path: &str,
node: Node<'_, '_>,
identifiers: &mut BTreeSet<String>,
collision_names: &mut BTreeSet<String>,
) -> Result<JointRecord, String> {
let base = parse_joint_base(path, node, "SK010")?;
if !identifiers.insert(base.name.clone()) {
return Err(location(
path,
node,
"SK016",
&format!("duplicate skeleton joint identifier {:?}", base.name),
));
}
let aliases = node.attribute("aliases").unwrap_or_default().to_owned();
for alias in aliases.split_ascii_whitespace() {
if !identifiers.insert(alias.to_owned()) {
return Err(location(
path,
node,
"SK016",
&format!("duplicate skeleton joint identifier {alias:?}"),
));
}
}
let mut collision_volumes = Vec::new();
let mut bones = Vec::new();
for child in node.children().filter(Node::is_element) {
if child.has_tag_name("collision_volume") {
if child.children().any(|node| node.is_element()) {
return Err(location(
path,
child,
"SK019",
"<collision_volume> may not contain child elements",
));
}
let collision = CollisionRecord {
base: parse_joint_base(path, child, "SK017")?,
};
if !collision_names.insert(collision.base.name.clone()) {
return Err(location(
path,
child,
"SK020",
&format!("duplicate collision-volume name {:?}", collision.base.name),
));
}
collision_volumes.push(collision);
} else if child.has_tag_name("bone") {
bones.push(parse_joint(path, child, identifiers, collision_names)?);
} else {
return Err(location(
path,
child,
"SK021",
"<bone> may contain only <bone> and <collision_volume> elements",
));
}
}
Ok(JointRecord {
base,
collision_volumes,
bones,
pivot: vector_attribute(path, node, "pivot", "SK022")?,
aliases,
connected: boolean_attribute(path, node, "connected", "SK023")?,
})
}
fn joint_counts(root: &JointRecord) -> (usize, usize) {
let mut bones = 1;
let mut collision_volumes = root.collision_volumes.len();
for child in &root.bones {
let (child_bones, child_collision_volumes) = joint_counts(child);
bones += child_bones;
collision_volumes += child_collision_volumes;
}
(bones, collision_volumes)
}
fn parse_skeleton(path: &str, text: &str) -> Result<SkeletonRecord, String> {
let document = Document::parse(text).map_err(|error| format!("{path}: {error}"))?;
let root = document.root_element();
if !root.has_tag_name("linden_skeleton") {
return Err(location(
path,
root,
"SK001",
"root element must be <linden_skeleton>",
));
}
let version = parse_f32(path, root, "version", "SK002")?;
let expected_bones = parse_usize(path, root, "num_bones", "SK003")?;
let expected_collision_volumes = parse_usize(path, root, "num_collision_volumes", "SK004")?;
let children = root.children().filter(Node::is_element).collect::<Vec<_>>();
if children.len() != 1 || !children[0].has_tag_name("bone") {
return Err(location(
path,
root,
"SK005",
"skeleton requires exactly one root <bone>",
));
}
let mut identifiers = BTreeSet::new();
let mut collision_names = BTreeSet::new();
let joint = parse_joint(path, children[0], &mut identifiers, &mut collision_names)?;
let (actual_bones, actual_collision_volumes) = joint_counts(&joint);
if actual_bones != expected_bones || actual_collision_volumes != expected_collision_volumes {
return Err(location(
path,
root,
"SK006",
&format!(
"declared skeleton counts are {expected_bones} bones/{expected_collision_volumes} collision volumes but parsed {actual_bones}/{actual_collision_volumes}"
),
));
}
Ok(SkeletonRecord {
version,
num_bones: expected_bones.to_string(),
num_collision_volumes: expected_collision_volumes.to_string(),
root: joint,
})
}
fn attention_index(name: &str) -> Option<usize> {
ATTENTION_NAMES
.iter()
.position(|candidate| candidate.eq_ignore_ascii_case(name))
.map(|index| index + 1)
}
fn parse_attentions(path: &str, text: &str) -> Result<Vec<AttentionSetRecord>, String> {
let document = Document::parse(text).map_err(|error| format!("{path}: {error}"))?;
let root = document.root_element();
if !root.has_tag_name("linden_attentions") {
return Err(location(
path,
root,
"AT001",
"root element must be <linden_attentions>",
));
}
let _version = parse_f32(path, root, "version", "AT002")?;
let genders = root.children().filter(Node::is_element).collect::<Vec<_>>();
if genders.len() != 2 {
return Err(location(
path,
root,
"AT003",
"attention catalog requires Masculine and Feminine gender sets",
));
}
let mut sets = Vec::new();
for (gender_index, gender) in genders.into_iter().enumerate() {
if !gender.has_tag_name("gender") {
return Err(location(
path,
gender,
"AT004",
"<linden_attentions> may contain only <gender> elements",
));
}
let name = required_attribute(path, gender, "name", "AT005")?;
let expected_name = if gender_index == 0 {
"Masculine"
} else {
"Feminine"
};
if name != expected_name {
return Err(location(
path,
gender,
"AT006",
&format!("gender set {gender_index} must be named {expected_name:?}"),
));
}
let mut entries = [AttentionRecord {
priority: 0.0,
timeout: 0.0,
}; 11];
let mut seen = BTreeSet::new();
for param in gender.children().filter(Node::is_element) {
if !param.has_tag_name("param") {
return Err(location(
path,
param,
"AT007",
"<gender> may contain only <param> elements",
));
}
let attention = required_attribute(path, param, "attention", "AT008")?;
let Some(index) = attention_index(&attention) else {
return Err(location(
path,
param,
"AT009",
&format!("unknown attention name {attention:?}"),
));
};
if !seen.insert(index) {
return Err(location(
path,
param,
"AT010",
&format!("duplicate attention name {attention:?}"),
));
}
entries[index] = AttentionRecord {
priority: parse_f32(path, param, "priority", "AT011")?,
timeout: parse_f32(path, param, "timeout", "AT012")?,
};
}
if seen.len() != ATTENTION_NAMES.len() {
return Err(location(
path,
gender,
"AT013",
"gender set must define each of the nine attention parameters exactly once",
));
}
sets.push(AttentionSetRecord { name, entries });
}
Ok(sets)
}
fn parse_genepool(path: &str, text: &str) -> Result<Vec<ArchetypeRecord>, String> {
let document = Document::parse(text).map_err(|error| format!("{path}: {error}"))?;
let root = document.root_element();
if !root.has_tag_name("linden_genepool") {
return Err(location(
path,
root,
"GP001",
"root element must be <linden_genepool>",
));
}
let _version = parse_f32(path, root, "version", "GP002")?;
let mut names = BTreeSet::new();
let mut archetypes = Vec::new();
for archetype in root.children().filter(Node::is_element) {
if !archetype.has_tag_name("archetype") {
return Err(location(
path,
archetype,
"GP003",
"<linden_genepool> may contain only <archetype> elements",
));
}
let name = required_attribute(path, archetype, "name", "GP004")?
.trim()
.to_owned();
if name.is_empty() || !names.insert(name.clone()) {
return Err(location(
path,
archetype,
"GP005",
&format!("empty or duplicate archetype name {name:?}"),
));
}
let mut ids = BTreeSet::new();
let mut texture_slots = BTreeSet::new();
let mut params = Vec::new();
for child in archetype.children().filter(Node::is_element) {
if child.has_tag_name("texture") {
let slot = parse_i32(path, child, "te", "GP013")?;
let uuid = required_attribute(path, child, "uuid", "GP014")?;
if !texture_slots.insert(slot) {
return Err(location(
path,
child,
"GP015",
&format!("duplicate texture-entry slot {slot} in archetype {name:?}"),
));
}
if !valid_uuid(&uuid) {
return Err(location(
path,
child,
"GP016",
&format!("invalid archetype texture UUID {uuid:?}"),
));
}
continue;
}
if !child.has_tag_name("param") {
return Err(location(
path,
child,
"GP006",
"<archetype> may contain only <param> and <texture> elements",
));
}
let id = parse_i32(path, child, "id", "GP007")?;
if !ids.insert(id) {
return Err(location(
path,
child,
"GP008",
&format!("duplicate parameter ID {id} in archetype {name:?}"),
));
}
params.push(ArchetypeParamRecord {
id,
name: required_attribute(path, child, "name", "GP009")?,
value: parse_f32(path, child, "value", "GP010")?,
});
}
if params.is_empty() {
return Err(location(
path,
archetype,
"GP011",
"archetype must contain at least one parameter",
));
}
archetypes.push(ArchetypeRecord { name, params });
}
if archetypes.is_empty() {
return Err(location(
path,
root,
"GP012",
"genepool must contain at least one archetype",
));
}
Ok(archetypes)
}
fn valid_uuid(value: &str) -> bool {
value.len() == 36
&& value.bytes().enumerate().all(|(index, byte)| match index {
8 | 13 | 18 | 23 => byte == b'-',
_ => byte.is_ascii_hexdigit(),
})
}
fn rust_f32(value: f32) -> String {
format!("{value:?}_f32")
}
fn vector_expression(values: &[f32]) -> String {
format!(
"vec![{}]",
values
.iter()
.map(|value| rust_f32(*value))
.collect::<Vec<_>>()
.join(",")
)
}
fn base_expression(base: &JointBaseRecord) -> String {
format!(
"crate::skeleton::JointBase::from_parts({:?}.to_owned(), {}, {}, {}, {:?}.to_owned(), {:?}.to_owned(), {}, {})",
base.name,
vector_expression(&base.pos),
vector_expression(&base.rot),
vector_expression(&base.scale),
base.group,
base.support,
vector_expression(&base.end),
base.reposition
)
}
fn joint_expression(joint: &JointRecord) -> String {
let collision_volumes = joint
.collision_volumes
.iter()
.map(|volume| {
format!(
"crate::skeleton::CollisionVolume::from_base({})",
base_expression(&volume.base)
)
})
.collect::<Vec<_>>()
.join(",");
let bones = joint
.bones
.iter()
.map(joint_expression)
.collect::<Vec<_>>()
.join(",");
format!(
"crate::skeleton::Joint::from_parts({}, vec![{collision_volumes}], vec![{bones}], {}, {:?}.to_owned(), {})",
base_expression(&joint.base),
vector_expression(&joint.pivot),
joint.aliases,
joint.connected
)
}
fn render_skeleton(skeleton: &SkeletonRecord) -> String {
format!(
"#![allow(clippy::float_cmp, clippy::too_many_lines, clippy::unreadable_literal)]\n\n\
pub(crate) fn default_skeleton() -> crate::skeleton::LindenSkeleton {{\n\
crate::skeleton::LindenSkeleton::from_parts({}, {}, false, {:?}.to_owned(), {:?}.to_owned())\n\
}}\n\n\
#[cfg(test)] mod tests {{ use super::*; #[test] fn generated_default_shape() {{ let skeleton = default_skeleton(); assert_eq!(skeleton.version(), 2.0); assert_eq!(skeleton.num_bones(), \"133\"); assert_eq!(skeleton.num_collision_volumes(), \"26\"); assert_eq!(skeleton.bone().base.name(), \"mPelvis\"); }} }}\n",
joint_expression(&skeleton.root),
rust_f32(skeleton.version),
skeleton.num_bones,
skeleton.num_collision_volumes
)
}
fn render_attentions(default: &[AttentionSetRecord], updated: &[AttentionSetRecord]) -> String {
fn sets_expression(sets: &[AttentionSetRecord]) -> String {
sets.iter()
.map(|set| {
let entries = set
.entries
.iter()
.map(|entry| {
format!(
"AttentionData {{ priority: {}, timeout: {} }}",
rust_f32(entry.priority),
rust_f32(entry.timeout)
)
})
.collect::<Vec<_>>()
.join(",");
format!(
"AttentionSet {{ name: {:?}.to_owned(), entries: vec![{entries}] }}",
set.name
)
})
.collect::<Vec<_>>()
.join(",")
}
format!(
"#![allow(clippy::missing_errors_doc, clippy::must_use_candidate, clippy::should_implement_trait)]\n\n\
use std::sync::OnceLock;\n\n\
#[derive(Clone, Debug, Default, PartialEq)] pub struct AttentionData {{ priority: f32, timeout: f32 }}\n\
impl AttentionData {{ pub fn priority(&self) -> f32 {{ self.priority }} pub fn set_priority(&mut self, value: f32) {{ self.priority = value; }} pub fn timeout(&self) -> f32 {{ self.timeout }} pub fn set_timeout(&mut self, value: f32) {{ self.timeout = value; }} }}\n\n\
#[derive(Clone, Debug, Default, PartialEq)] pub struct AttentionSet {{ name: String, entries: Vec<AttentionData> }}\n\
impl AttentionSet {{ pub fn get(&self, type_: crate::LookAtType) -> Result<AttentionData, crate::Error> {{ self.entries.get(type_ as usize).cloned().ok_or(crate::Error::IndexOutOfRange) }} pub fn entries(&self) -> Vec<AttentionData> {{ self.entries.clone() }} pub fn set_entries(&mut self, value: Vec<AttentionData>) {{ self.entries = value; }} pub fn name(&self) -> String {{ self.name.clone() }} pub fn set_name(&mut self, value: String) {{ self.name = value; }} }}\n\n\
fn default_sets() -> &'static [AttentionSet] {{ static SETS: OnceLock<Vec<AttentionSet>> = OnceLock::new(); SETS.get_or_init(|| vec![{}]).as_slice() }}\n\
fn updated_sets() -> &'static [AttentionSet] {{ static SETS: OnceLock<Vec<AttentionSet>> = OnceLock::new(); SETS.get_or_init(|| vec![{}]).as_slice() }}\n\n\
pub struct LindenAttentions; impl LindenAttentions {{ pub fn default() -> Vec<AttentionSet> {{ default_sets().to_vec() }} pub fn updated() -> Vec<AttentionSet> {{ updated_sets().to_vec() }} pub fn default_masculine() -> AttentionSet {{ default_sets()[0].clone() }} pub fn default_feminine() -> AttentionSet {{ default_sets()[1].clone() }} pub fn updated_masculine() -> AttentionSet {{ updated_sets()[0].clone() }} pub fn updated_feminine() -> AttentionSet {{ updated_sets()[1].clone() }} }}\n\n\
#[cfg(test)] mod tests {{ use super::*; #[test] fn generated_attention_shape() {{ assert_eq!(default_sets().len(), 2); assert_eq!(updated_sets().len(), 2); assert!(default_sets().iter().chain(updated_sets()).all(|set| set.entries.len() == 11)); assert_eq!(default_sets()[0].name, \"Masculine\"); assert_eq!(default_sets()[1].name, \"Feminine\"); }} }}\n",
sets_expression(default),
sets_expression(updated)
)
}
fn render_genepool(archetypes: &[ArchetypeRecord]) -> String {
let values = archetypes
.iter()
.map(|archetype| {
let params = archetype
.params
.iter()
.map(|param| {
format!(
"ArchetypeParam {{ id: {}, name: {:?}.to_owned(), value: {} }}",
param.id,
param.name,
rust_f32(param.value)
)
})
.collect::<Vec<_>>()
.join(",");
format!(
"GenepoolArchetype {{ name: {:?}.to_owned(), params: vec![{params}] }}",
archetype.name
)
})
.collect::<Vec<_>>()
.join(",");
format!(
"#![allow(clippy::missing_errors_doc, clippy::must_use_candidate, clippy::needless_pass_by_value, clippy::too_many_lines)]\n\n\
use std::sync::OnceLock;\n\n\
#[derive(Clone, Debug, Default, PartialEq)] pub struct ArchetypeParam {{ id: i32, name: String, value: f32 }}\n\
impl ArchetypeParam {{ pub fn id(&self) -> i32 {{ self.id }} pub fn set_id(&mut self, value: i32) {{ self.id = value; }} pub fn name(&self) -> String {{ self.name.clone() }} pub fn set_name(&mut self, value: String) {{ self.name = value; }} pub fn value(&self) -> f32 {{ self.value }} pub fn set_value(&mut self, value: f32) {{ self.value = value; }} }}\n\n\
#[derive(Clone, Debug, Default, PartialEq)] pub struct GenepoolArchetype {{ name: String, params: Vec<ArchetypeParam> }}\n\
impl GenepoolArchetype {{ pub fn name(&self) -> String {{ self.name.clone() }} pub fn set_name(&mut self, value: String) {{ self.name = value; }} pub fn params(&self) -> Vec<ArchetypeParam> {{ self.params.clone() }} pub fn set_params(&mut self, value: Vec<ArchetypeParam>) {{ self.params = value; }} }}\n\n\
fn generated_archetypes() -> &'static [GenepoolArchetype] {{ static ARCHETYPES: OnceLock<Vec<GenepoolArchetype>> = OnceLock::new(); ARCHETYPES.get_or_init(|| vec![{values}]).as_slice() }}\n\n\
pub struct Genepool; impl Genepool {{ pub fn archetypes() -> Vec<GenepoolArchetype> {{ generated_archetypes().to_vec() }} pub fn find_index(name: String) -> Result<i32, crate::Error> {{ let Some(index) = generated_archetypes().iter().position(|archetype| archetype.name == name) else {{ return Ok(-1); }}; i32::try_from(index).map_err(|_| crate::Error::IndexOutOfRange) }} pub fn get(index: i32) -> Result<GenepoolArchetype, crate::Error> {{ usize::try_from(index).ok().and_then(|index| generated_archetypes().get(index)).cloned().ok_or(crate::Error::IndexOutOfRange) }} }}\n\n\
#[cfg(test)] mod tests {{ use super::*; #[test] fn generated_genepool_shape() {{ let archetypes = generated_archetypes(); assert_eq!(archetypes.len(), 24); assert_eq!(archetypes.iter().map(|archetype| archetype.params.len()).sum::<usize>(), 3360); assert_eq!(archetypes[0].name, \"M W Skinny\"); }} }}\n"
)
}
fn load_input<'a>(inventory: &'a super::Inventory, id: &str) -> Result<&'a InputSpec, String> {
inventory
.inputs
.iter()
.find(|input| input.id == id)
.ok_or_else(|| format!("source inventory has no {id} input"))
}
fn load_text(root: &Path, input: &InputSpec) -> Result<String, String> {
let path = root.join(&input.vendored_path);
let bytes = fs::read(&path).map_err(|error| format!("{}: {error}", path.display()))?;
normalize_text(&input.vendored_path, &bytes).map_err(|error| error.to_string())
}
fn format_body(body: &str, name: &str) -> Result<String, String> {
let syntax = syn::parse_file(body)
.map_err(|error| format!("generated {name} Rust is invalid: {error}"))?;
Ok(prettyplease::unparse(&syntax))
}
pub(super) fn skeleton_catalog_bytes(root: &Path) -> Result<Vec<u8>, String> {
let inventory = super::load_inventory(root)?;
verify_inputs(root, &inventory)?;
let input = load_input(&inventory, "avatar_skeleton")?;
let skeleton = parse_skeleton(&input.vendored_path, &load_text(root, input)?)?;
let body = format_body(&render_skeleton(&skeleton), "skeleton")?;
Ok(generated_rust("skeleton", &[input], &body))
}
pub(super) fn attention_catalog_bytes(root: &Path) -> Result<Vec<u8>, String> {
let inventory = super::load_inventory(root)?;
verify_inputs(root, &inventory)?;
let default_input = load_input(&inventory, "attentions")?;
let updated_input = load_input(&inventory, "attentions_updated")?;
let default = parse_attentions(
&default_input.vendored_path,
&load_text(root, default_input)?,
)?;
let updated = parse_attentions(
&updated_input.vendored_path,
&load_text(root, updated_input)?,
)?;
let body = format_body(&render_attentions(&default, &updated), "attention")?;
Ok(generated_rust(
"attentions",
&[default_input, updated_input],
&body,
))
}
pub(super) fn genepool_catalog_bytes(root: &Path) -> Result<Vec<u8>, String> {
let inventory = super::load_inventory(root)?;
verify_inputs(root, &inventory)?;
let input = load_input(&inventory, "genepool")?;
let archetypes = parse_genepool(&input.vendored_path, &load_text(root, input)?)?;
let body = format_body(&render_genepool(&archetypes), "genepool")?;
Ok(generated_rust("genepool", &[input], &body))
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn skeleton_schema_rejects_bad_counts_vectors_and_duplicate_names() {
let bad_count = "<linden_skeleton version=\"1\" num_bones=\"2\" num_collision_volumes=\"0\"><bone name=\"root\"/></linden_skeleton>";
assert!(
parse_skeleton("bad.xml", bad_count)
.unwrap_err()
.contains("error[SK006]")
);
let bad_vector = "<linden_skeleton version=\"1\" num_bones=\"1\" num_collision_volumes=\"0\"><bone name=\"root\" pos=\"1 2\"/></linden_skeleton>";
assert!(
parse_skeleton("bad.xml", bad_vector)
.unwrap_err()
.contains("error[SK011]")
);
let duplicate = "<linden_skeleton version=\"1\" num_bones=\"2\" num_collision_volumes=\"0\"><bone name=\"root\"><bone name=\"root\"/></bone></linden_skeleton>";
assert!(
parse_skeleton("bad.xml", duplicate)
.unwrap_err()
.contains("error[SK016]")
);
}
#[test]
fn attention_schema_rejects_unknown_and_incomplete_sets() {
let unknown = "<linden_attentions version=\"1\"><gender name=\"Masculine\"><param attention=\"unknown\" priority=\"1\" timeout=\"1\"/></gender><gender name=\"Feminine\"/></linden_attentions>";
assert!(
parse_attentions("bad.xml", unknown)
.unwrap_err()
.contains("error[AT009]")
);
let incomplete = "<linden_attentions version=\"1\"><gender name=\"Masculine\"/><gender name=\"Feminine\"/></linden_attentions>";
assert!(
parse_attentions("bad.xml", incomplete)
.unwrap_err()
.contains("error[AT013]")
);
}
#[test]
fn genepool_schema_rejects_bad_values_and_duplicate_ids() {
let bad_value = "<linden_genepool version=\"1\"><archetype name=\"A\"><param id=\"1\" name=\"P\" value=\"NaN\"/></archetype></linden_genepool>";
assert!(
parse_genepool("bad.xml", bad_value)
.unwrap_err()
.contains("error[GP010]")
);
let duplicate = "<linden_genepool version=\"1\"><archetype name=\"A\"><param id=\"1\" name=\"P\" value=\"0\"/><param id=\"1\" name=\"Q\" value=\"1\"/></archetype></linden_genepool>";
assert!(
parse_genepool("bad.xml", duplicate)
.unwrap_err()
.contains("error[GP008]")
);
}
#[test]
fn pinned_avatar_catalog_semantics_have_stable_digests() {
let root = super::super::workspace_root();
let inventory = super::super::load_inventory(&root).expect("pinned inventory");
let skeleton_input = load_input(&inventory, "avatar_skeleton").expect("skeleton input");
let skeleton = parse_skeleton(
&skeleton_input.vendored_path,
&load_text(&root, skeleton_input).expect("skeleton text"),
)
.expect("skeleton");
let attentions_input = load_input(&inventory, "attentions").expect("attentions input");
let updated_input =
load_input(&inventory, "attentions_updated").expect("updated attentions input");
let default = parse_attentions(
&attentions_input.vendored_path,
&load_text(&root, attentions_input).expect("attentions text"),
)
.expect("attentions");
let updated = parse_attentions(
&updated_input.vendored_path,
&load_text(&root, updated_input).expect("updated attentions text"),
)
.expect("updated attentions");
let genepool_input = load_input(&inventory, "genepool").expect("genepool input");
let genepool = parse_genepool(
&genepool_input.vendored_path,
&load_text(&root, genepool_input).expect("genepool text"),
)
.expect("genepool");
assert_eq!(
super::super::sha256(format!("{skeleton:#?}").as_bytes()),
"912dc35e06ac8f2eed48819f340ea18ab6173437cc251cda52081bc8445db4d6"
);
assert_eq!(
super::super::sha256(format!("{default:#?}\n{updated:#?}").as_bytes()),
"a0343e5d473ce2bfa29985e5d98afb066bf282b8786ba099aa76457b0b3d8af8"
);
assert_eq!(
super::super::sha256(format!("{genepool:#?}").as_bytes()),
"5e33fecab1a9501113096ee5ec38f52eadb7deb3de05a7c50c42e00c15c24a5b"
);
}
}

View File

@@ -11,6 +11,7 @@ use std::fmt::Write as _;
use std::fs; use std::fs;
use std::path::{Component, Path, PathBuf}; use std::path::{Component, Path, PathBuf};
mod avatar_catalog;
mod xml_catalog; mod xml_catalog;
pub const INVENTORY_PATH: &str = "codegen/sources.json"; pub const INVENTORY_PATH: &str = "codegen/sources.json";
@@ -18,6 +19,9 @@ pub const MANIFEST_OUTPUT: &str = "codegen/generated/source_manifest.rs";
pub const PACKET_OUTPUT: &str = "crates/libremetaverse/src/packet_catalog.rs"; pub const PACKET_OUTPUT: &str = "crates/libremetaverse/src/packet_catalog.rs";
pub const VISUAL_OUTPUT: &str = "crates/libremetaverse/src/visual_catalog.rs"; pub const VISUAL_OUTPUT: &str = "crates/libremetaverse/src/visual_catalog.rs";
pub const FOLIAGE_OUTPUT: &str = "crates/libremetaverse/src/foliage_catalog.rs"; pub const FOLIAGE_OUTPUT: &str = "crates/libremetaverse/src/foliage_catalog.rs";
pub const SKELETON_OUTPUT: &str = "crates/libremetaverse/src/skeleton_catalog.rs";
pub const ATTENTION_OUTPUT: &str = "crates/libremetaverse/src/attention_catalog.rs";
pub const GENEPOOL_OUTPUT: &str = "crates/libremetaverse/src/genepool_catalog.rs";
pub const PUBLIC_API_PATH: &str = "api/public-api.json"; pub const PUBLIC_API_PATH: &str = "api/public-api.json";
const RUST_KEYWORDS: &[&str] = &[ const RUST_KEYWORDS: &[&str] = &[
"as", "break", "const", "continue", "crate", "else", "enum", "extern", "false", "fn", "for", "as", "break", "const", "continue", "crate", "else", "enum", "extern", "false", "fn", "for",
@@ -2055,6 +2059,18 @@ pub fn regenerate(root: &Path, check: bool) -> Result<(), String> {
root.join(FOLIAGE_OUTPUT), root.join(FOLIAGE_OUTPUT),
xml_catalog::foliage_catalog_bytes(root)?, xml_catalog::foliage_catalog_bytes(root)?,
), ),
(
root.join(SKELETON_OUTPUT),
avatar_catalog::skeleton_catalog_bytes(root)?,
),
(
root.join(ATTENTION_OUTPUT),
avatar_catalog::attention_catalog_bytes(root)?,
),
(
root.join(GENEPOOL_OUTPUT),
avatar_catalog::genepool_catalog_bytes(root)?,
),
]; ];
if check { if check {
for (path, expected) in outputs { for (path, expected) in outputs {
@@ -2099,6 +2115,21 @@ mod tests {
let second_foliage = let second_foliage =
xml_catalog::foliage_catalog_bytes(&root).expect("second foliage generation"); xml_catalog::foliage_catalog_bytes(&root).expect("second foliage generation");
assert_eq!(first_foliage, second_foliage); assert_eq!(first_foliage, second_foliage);
let first_skeleton =
avatar_catalog::skeleton_catalog_bytes(&root).expect("first skeleton generation");
let second_skeleton =
avatar_catalog::skeleton_catalog_bytes(&root).expect("second skeleton generation");
assert_eq!(first_skeleton, second_skeleton);
let first_attention =
avatar_catalog::attention_catalog_bytes(&root).expect("first attention generation");
let second_attention =
avatar_catalog::attention_catalog_bytes(&root).expect("second attention generation");
assert_eq!(first_attention, second_attention);
let first_genepool =
avatar_catalog::genepool_catalog_bytes(&root).expect("first genepool generation");
let second_genepool =
avatar_catalog::genepool_catalog_bytes(&root).expect("second genepool generation");
assert_eq!(first_genepool, second_genepool);
} }
#[test] #[test]

View File

@@ -38,10 +38,16 @@ TARGETS = {
# implementations. The generated module keeps catalog markers and re-exports # implementations. The generated module keeps catalog markers and re-exports
# the hand-written type so coverage remains deterministic. # the hand-written type so coverage remains deterministic.
NATIVE_TYPES = { NATIVE_TYPES = {
"T:LibreMetaverse.ArchetypeParam": "crate::genepool_catalog::ArchetypeParam",
"T:LibreMetaverse.AttentionData": "crate::attention_catalog::AttentionData",
"T:LibreMetaverse.AttentionSet": "crate::attention_catalog::AttentionSet",
"T:LibreMetaverse.BitPack": "crate::bit_pack::BitPack", "T:LibreMetaverse.BitPack": "crate::bit_pack::BitPack",
"T:LibreMetaverse.DrivenParamInfo": "crate::visual_catalog::DrivenParamInfo", "T:LibreMetaverse.DrivenParamInfo": "crate::visual_catalog::DrivenParamInfo",
"T:LibreMetaverse.GrassDefinition": "crate::foliage_catalog::GrassDefinition", "T:LibreMetaverse.GrassDefinition": "crate::foliage_catalog::GrassDefinition",
"T:LibreMetaverse.GrassDefinitions": "crate::foliage_catalog::GrassDefinitions", "T:LibreMetaverse.GrassDefinitions": "crate::foliage_catalog::GrassDefinitions",
"T:LibreMetaverse.Genepool": "crate::genepool_catalog::Genepool",
"T:LibreMetaverse.GenepoolArchetype": "crate::genepool_catalog::GenepoolArchetype",
"T:LibreMetaverse.LindenAttentions": "crate::attention_catalog::LindenAttentions",
"T:LibreMetaverse.Packets.PacketType": "crate::packet_catalog::PacketType", "T:LibreMetaverse.Packets.PacketType": "crate::packet_catalog::PacketType",
"T:LibreMetaverse.PrimMesher.Coord": "crate::prim_mesher::Coord", "T:LibreMetaverse.PrimMesher.Coord": "crate::prim_mesher::Coord",
"T:LibreMetaverse.PrimMesher.Face": "crate::prim_mesher::Face", "T:LibreMetaverse.PrimMesher.Face": "crate::prim_mesher::Face",
@@ -111,6 +117,10 @@ NATIVE_TYPES = {
"T:LibreMetaverse.VisualParam": "crate::visual_catalog::VisualParam", "T:LibreMetaverse.VisualParam": "crate::visual_catalog::VisualParam",
"T:LibreMetaverse.VisualParams": "crate::visual_catalog::VisualParams", "T:LibreMetaverse.VisualParams": "crate::visual_catalog::VisualParams",
"T:LibreMetaverse.VolumeMorphInfo": "crate::visual_catalog::VolumeMorphInfo", "T:LibreMetaverse.VolumeMorphInfo": "crate::visual_catalog::VolumeMorphInfo",
"T:LibreMetaverse.Rendering.CollisionVolume": "crate::skeleton::CollisionVolume",
"T:LibreMetaverse.Rendering.Joint": "crate::skeleton::Joint",
"T:LibreMetaverse.Rendering.JointBase": "crate::skeleton::JointBase",
"T:LibreMetaverse.Rendering.LindenSkeleton": "crate::skeleton::LindenSkeleton",
} }
# Native declarations whose cataloged method implementations are still emitted # Native declarations whose cataloged method implementations are still emitted

View File

@@ -980,15 +980,18 @@ def validate_generated_shims() -> None:
"not_implemented(", "not_implemented(",
"NotImplemented::new(", "NotImplemented::new(",
"crate::byte_order::", "crate::byte_order::",
"crate::attention_catalog::",
"crate::binary::", "crate::binary::",
"crate::dispatch::", "crate::dispatch::",
"crate::foliage_catalog::", "crate::foliage_catalog::",
"crate::genepool_catalog::",
"crate::json_codec::", "crate::json_codec::",
"crate::message_codec::", "crate::message_codec::",
"crate::notation::", "crate::notation::",
"crate::packet_catalog::", "crate::packet_catalog::",
"crate::packet_wire::", "crate::packet_wire::",
"crate::protobuf::", "crate::protobuf::",
"crate::skeleton::",
"crate::visual_catalog::", "crate::visual_catalog::",
"crate::xml_codec::", "crate::xml_codec::",
"stored_visual_params", "stored_visual_params",