diff --git a/Cargo.lock b/Cargo.lock index b9817b6..0f533ed 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -970,6 +970,8 @@ name = "bevy_vrm" version = "0.0.1" dependencies = [ "bevy", + "goth-gltf", + "nanoserde", ] [[package]] @@ -1690,6 +1692,15 @@ dependencies = [ "xi-unicode", ] +[[package]] +name = "goth-gltf" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a321bce5d6962210f492c9eca623eaaa9d8e2a1a2495110339f3f2cd7463daa" +dependencies = [ + "nanoserde", +] + [[package]] name = "gpu-alloc" version = "0.5.4" @@ -2177,6 +2188,21 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "nanoserde" +version = "0.1.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a983d0b19ed0fcd803c4f04f9b20d5e6dd17e06d44d98742a0985ac45dab1bc" +dependencies = [ + "nanoserde-derive", +] + +[[package]] +name = "nanoserde-derive" +version = "0.1.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4dc96541767a4279572fdcf9f95af9cc1c9b2a2254e7a079203c81e206a9059" + [[package]] name = "ndk" version = "0.7.0" diff --git a/Cargo.toml b/Cargo.toml index f4081e7..47efe6c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -24,3 +24,5 @@ opt-level = 'z' [dependencies] bevy = "0.11.3" +goth-gltf = "0.1.1" +nanoserde = "0.1.35" diff --git a/src/extensions.rs b/src/extensions.rs new file mode 100644 index 0000000..43f506c --- /dev/null +++ b/src/extensions.rs @@ -0,0 +1,2 @@ +pub mod vrm0; +pub mod vrmc_vrm; diff --git a/src/extensions/vrm0.rs b/src/extensions/vrm0.rs new file mode 100644 index 0000000..584ec5f --- /dev/null +++ b/src/extensions/vrm0.rs @@ -0,0 +1,259 @@ +use nanoserde::{DeJson, SerJson}; + +#[derive(DeJson, SerJson, Debug, Clone)] +pub struct Vrm { + #[nserde(rename = "exporterVersion")] + pub exporter_version: Option, + #[nserde(rename = "specVersion")] + pub spec_version: String, + pub meta: Meta, + pub humanoid: Humanoid, + #[nserde(rename = "firstPerson")] + pub first_person: FirstPerson, + #[nserde(rename = "blendShapeMaster")] + pub blend_shape_master: BlendShapeMaster, + #[nserde(rename = "secondaryAnimation")] + pub secondary_animation: SecondaryAnimation, + #[nserde(rename = "materialProperties")] + pub material_properties: Vec, +} + +#[derive(DeJson, SerJson, Debug, Clone)] +pub struct Meta { + pub title: Option, + pub version: Option, + pub author: Option, + #[nserde(rename = "contactInformation")] + pub contact_information: Option, + pub reference: Option, + pub texture: Option, + #[nserde(rename = "allowedUserName")] + pub allowed_user_name: Option, + #[nserde(rename = "violentUssageName")] + pub violent_usage_name: Option, + #[nserde(rename = "sexualUssageName")] + pub sexual_usage_name: Option, + #[nserde(rename = "commercialUssageName")] + pub commercial_usage_name: Option, + #[nserde(rename = "otherPermissionUrl")] + pub other_permission_url: Option, + #[nserde(rename = "licenseName")] + pub license_name: Option, + #[nserde(rename = "otherLicenseUrl")] + pub other_license_url: Option, +} + +#[derive(DeJson, SerJson, Debug, Clone)] +pub struct Humanoid { + #[nserde(rename = "humanBones")] + pub human_bones: Vec, + #[nserde(rename = "armStretch")] + pub arm_stretch: f32, + #[nserde(rename = "legStretch")] + pub leg_stretch: f32, + #[nserde(rename = "upperArmTwist")] + pub upper_arm_twist: f32, + #[nserde(rename = "lowerArmTwist")] + pub lower_arm_twist: f32, + #[nserde(rename = "upperLegTwist")] + pub upper_leg_twist: f32, + #[nserde(rename = "lowerLegTwist")] + pub lower_leg_twist: f32, + #[nserde(rename = "feetSpacing")] + pub feet_spacing: f32, + #[nserde(rename = "hasTranslationDoF")] + pub has_translation_dof: bool, +} + +#[derive(DeJson, SerJson, Debug, Clone)] +pub struct Bone { + #[nserde(rename = "bone")] + pub name: String, + pub node: u32, + #[nserde(rename = "useDefaultValues")] + pub use_default_values: bool, +} + +#[derive(DeJson, SerJson, Debug, Clone)] +pub struct FirstPerson { + #[nserde(rename = "firstPersonBone")] + pub first_person_bone: u32, + #[nserde(rename = "firstPersonBoneOffset")] + pub first_person_bone_offset: Vec3, + // todo + #[nserde(rename = "meshAnnotations")] + pub mesh_annotations: Vec<()>, + #[nserde(rename = "lookAtTypeName")] + pub look_at_type_name: String, + #[nserde(rename = "lookAtHorizontalInner")] + pub look_at_horizontal_inner: LookAtCurve, + #[nserde(rename = "lookAtHorizontalOuter")] + pub look_at_horizontal_outer: LookAtCurve, + #[nserde(rename = "lookAtVerticalDown")] + pub look_at_vertical_down: LookAtCurve, + #[nserde(rename = "lookAtVerticalUp")] + pub look_at_vertical_up: LookAtCurve, +} + +#[derive(DeJson, SerJson, Debug, Clone)] +pub struct LookAtCurve { + pub curve: [u32; 8], + #[nserde(rename = "xRange")] + pub x_range: u32, + #[nserde(rename = "yRange")] + pub y_range: u32, +} + +#[derive(DeJson, SerJson, Debug, Clone)] +pub struct BlendShapeMaster { + #[nserde(rename = "blendShapeGroups")] + pub blend_shape_groups: Vec, +} + +#[derive(DeJson, SerJson, Debug, Clone)] +pub struct BlendShapeGroup { + pub name: String, + #[nserde(rename = "presetName")] + pub preset_name: String, + pub binds: Vec, + // todo + #[nserde(rename = "materialValues")] + pub material_values: Vec<()>, +} + +#[derive(DeJson, SerJson, Debug, Clone)] +pub struct Bind { + pub mesh: u32, + pub index: u32, + pub weight: u32, +} + +#[derive(DeJson, SerJson, Debug, Clone)] +pub struct SecondaryAnimation { + #[nserde(rename = "boneGroups")] + pub bone_groups: Vec, + #[nserde(rename = "colliderGroups")] + pub collider_groups: Vec, +} + +#[derive(DeJson, SerJson, Debug, Clone)] +pub struct BoneGroup { + pub comment: String, + pub stiffiness: f32, + #[nserde(rename = "gravityPower")] + pub gravity_power: f32, + #[nserde(rename = "gravityDir")] + pub gravity_dir: Vec3, + #[nserde(rename = "dragForce")] + pub drag_force: f32, + pub center: f32, + #[nserde(rename = "hitRadius")] + pub hit_radius: f32, + pub bones: Vec, + #[nserde(rename = "colliderGroups")] + pub collider_groups: Vec, +} + +#[derive(DeJson, SerJson, Debug, Clone)] +pub struct ColliderGroup { + pub node: u32, + pub colliders: Vec, +} + +#[derive(DeJson, SerJson, Debug, Clone)] +pub struct Collider { + pub offset: Vec3, + pub radius: f32, +} + +#[derive(DeJson, SerJson, Debug, Clone)] +pub struct Vec3 { + pub x: f32, + pub y: f32, + pub z: f32, +} + +#[derive(DeJson, SerJson, Debug, Clone)] +pub struct MaterialProperty { + pub name: String, + #[nserde(rename = "renderQueue")] + pub render_queue: u32, + pub shader: String, + #[nserde(rename = "floatProperties")] + pub float: FloatProperties, + #[nserde(rename = "vectorProperties")] + pub vector: VectorProperties, + #[nserde(rename = "textureProperties")] + pub texture: TextureProperties, + #[nserde(rename = "keywordMap")] + pub keyword_map: KeywordMap, + #[nserde(rename = "tagMap")] + pub tag_map: TagMap, +} + +#[derive(DeJson, SerJson, Debug, Clone)] +pub struct FloatProperties { + #[nserde(rename = "_ShadeShift")] + pub shade_shift: f32, + #[nserde(rename = "_ShadeToony")] + pub shade_toony: f32, + #[nserde(rename = "_Cutoff")] + pub cutoff: f32, + #[nserde(rename = "_IndirectLightIntensity")] + pub indirect_light_insensity: f32, + #[nserde(rename = "_OutlineWidth")] + pub outline_width: f32, +} + +#[derive(DeJson, SerJson, Debug, Clone)] +pub struct TextureProperties { + #[nserde(rename = "_MainTex")] + pub main_tex: u32, + #[nserde(rename = "_ShadeTexture")] + pub shade_texture: u32, + #[nserde(rename = "_BumpMap")] + pub bump_map: u32, + #[nserde(rename = "_SphereAdd")] + pub sphere_add: u32, + #[nserde(rename = "_EmissionMap")] + pub emission_map: u32, +} + +#[derive(DeJson, SerJson, Debug, Clone)] +pub struct VectorProperties { + #[nserde(rename = "_Color")] + pub color: [f32; 4], + #[nserde(rename = "_ShadeColor")] + pub shade_color: [f32; 4], + #[nserde(rename = "_OutlineColor")] + pub outline_color: [f32; 4], +} + +#[derive(DeJson, SerJson, Debug, Clone)] +pub struct TagMap { + #[nserde(rename = "RenderType")] + pub render_type: RenderType, +} + +#[derive(Clone, Debug, DeJson, SerJson)] +pub enum RenderType { + Transparent, + TransparentCutout, + Opaque, +} + +#[derive(DeJson, SerJson, Debug, Clone)] +pub struct KeywordMap { + #[nserde(rename = "_ALPHABLEND_ON")] + pub alpha_blend: Option, + #[nserde(rename = "_ALPHATEST_ON")] + pub alpha_test: Option, + #[nserde(rename = "_NORMALMAP")] + pub normal_map: Option, + #[nserde(rename = "MTOON_OUTLINE_COLOR_FIXED")] + pub outline_color_fixed: Option, + #[nserde(rename = "MTOON_OUTLINE_COLOR_MIXED")] + pub outline_color_mixed: Option, + #[nserde(rename = "MTOON_OUTLINE_WIDTH_WORLD")] + pub outline_width_world: Option, +} diff --git a/src/extensions/vrmc_vrm.rs b/src/extensions/vrmc_vrm.rs new file mode 100644 index 0000000..c19d65e --- /dev/null +++ b/src/extensions/vrmc_vrm.rs @@ -0,0 +1,63 @@ +use nanoserde::{DeJson, SerJson}; + +#[derive(DeJson, SerJson, Debug, Clone)] +pub struct VrmcVrm { + #[nserde(rename = "specVersion")] + pub spec_version: String, + pub meta: Meta, + pub humanoid: Humanoid, + #[nserde(rename = "firstPerson")] + pub first_person: Option, + #[nserde(rename = "lookAt")] + pub look_at: Option, + pub expressions: Option, +} + +#[derive(DeJson, SerJson, Debug, Clone)] +pub struct Meta { + pub name: String, + pub version: Option, + pub authors: Vec, + #[nserde(rename = "copyrightInformation")] + pub copy_right_information: Option, + #[nserde(rename = "contactInformation")] + pub contact_information: Option, + pub reference: Option>, + #[nserde(rename = "thirdPartyLicenses")] + pub third_party_licenses: Option, + #[nserde(rename = "thumbnailImage")] + pub thumbnail_image: Option, + #[nserde(rename = "licenseUrl")] + pub license_url: String, + #[nserde(rename = "avatarPermission")] + pub avatar_permission: String, + #[nserde(rename = "allowExcessivelyViolentUsage")] + pub allow_excessively_violent_usage: Option, + #[nserde(rename = "allowExcessivelySexualUsage")] + pub allow_excessively_sexual_usage: Option, + #[nserde(rename = "commercialUsage")] + pub commercial_usage: Option, + #[nserde(rename = "allowPoliticalOrReligiousUsage")] + pub allow_political_or_religious_usage: Option, + #[nserde(rename = "allowAntisocialOrHateUsage")] + pub allow_antisocial_or_hate_usage: Option, + #[nserde(rename = "creditNotation")] + pub credit_notation: Option, + #[nserde(rename = "allowRedistribution")] + pub allow_redistribution: Option, + pub modification: Option, + #[nserde(rename = "otherLicenseUrl")] + pub other_license_url: Option, +} + +#[derive(DeJson, SerJson, Debug, Clone)] +pub struct Humanoid {} + +#[derive(DeJson, SerJson, Debug, Clone)] +pub struct FirstPerson {} + +#[derive(DeJson, SerJson, Debug, Clone)] +pub struct LookAt {} + +#[derive(DeJson, SerJson, Debug, Clone)] +pub struct Expressions {} diff --git a/src/lib.rs b/src/lib.rs index e52a33b..091641a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,5 +1,6 @@ use bevy::prelude::*; +mod extensions; pub mod loader; pub struct VRMPlugin; diff --git a/src/loader.rs b/src/loader.rs index 598f269..8eb7674 100644 --- a/src/loader.rs +++ b/src/loader.rs @@ -1,6 +1,11 @@ +use std::fmt::Debug; + use bevy::{ - asset::AssetLoader, gltf::GltfLoader, render::texture::CompressedImageFormats, utils::HashMap, + asset::AssetLoader, gltf::GltfLoader, prelude::*, render::texture::CompressedImageFormats, + utils::HashMap, }; +use goth_gltf::default_extensions; +use nanoserde::{DeJson, DeJsonErr}; #[derive(Default)] pub struct VRMLoader; @@ -16,10 +21,54 @@ impl AssetLoader for VRMLoader { supported_compressed_formats: CompressedImageFormats::default(), }; - Box::pin(async move { Ok(gltf_loader.load(bytes, load_context).await?) }) + Box::pin(async move { + gltf_loader.load(bytes, load_context).await?; + load_vrm_extensions(bytes).await?; + Ok(()) + }) } fn extensions(&self) -> &[&str] { &["vrm"] } } + +#[derive(Default, Debug, Clone, DeJson)] +pub struct RootExtensions { + #[nserde(rename = "KHR_lights_punctual")] + pub khr_lights_punctual: Option, + #[nserde(rename = "VRM")] + pub vrm0: Option, + #[nserde(rename = "VRMC_vrm")] + pub vrmc_vrm: Option, +} + +#[derive(Debug, Default, Clone, Copy, DeJson)] +pub struct Extensions; + +impl goth_gltf::Extensions for Extensions { + type RootExtensions = RootExtensions; + type TextureExtensions = default_extensions::TextureExtensions; + type TextureInfoExtensions = default_extensions::TextureInfoExtensions; + type MaterialExtensions = default_extensions::MaterialExtensions; + type BufferExtensions = default_extensions::BufferExtensions; + type NodeExtensions = default_extensions::NodeExtensions; + type NodeExtras = default_extensions::NodeExtras; + type BufferViewExtensions = default_extensions::BufferViewExtensions; +} + +async fn load_vrm_extensions(bytes: &[u8]) -> Result<(), DeJsonErr> { + info!("load_vrm_extensions"); + + let (gltf, _): (goth_gltf::Gltf, _) = goth_gltf::Gltf::from_bytes(&bytes)?; + + if let Some(vrm0) = gltf.extensions.vrm0 { + info!("Found VRM 0.0 extension: {:?}", vrm0.meta.title); + } else if let Some(vrmc_vrm) = gltf.extensions.vrmc_vrm { + info!("Found VRM 1.0 extension: {:?}", vrmc_vrm.meta.name); + } else { + info!("No VRM extension found"); + } + + Ok(()) +}