using System; using System.Linq; using UniGLTF; using UniGLTF.Utils; using UniJSON; using UnityEngine; namespace VRM { public class VRMExporter : gltfExporter { public const Axes Vrm0xSpecificationInverseAxis = Axes.Z; public static ExportingGltfData Export(GltfExportSettings configuration, GameObject go, ITextureSerializer textureSerializer) { var data = new ExportingGltfData(); using (var exporter = new VRMExporter(data, configuration, textureSerializer: textureSerializer)) { exporter.Prepare(go); exporter.Export(); } return data; } public readonly VRM.glTF_VRM_extensions VRM = new glTF_VRM_extensions(); public VRMExporter( ExportingGltfData data, GltfExportSettings exportSettings, IAnimationExporter animationExporter = null, IMaterialExporter materialExporter = null, ITextureSerializer textureSerializer = null ) : base( data, exportSettings, animationExporter: animationExporter, materialExporter: materialExporter ?? VrmMaterialExporterUtility.GetValidVrmMaterialExporter(), textureSerializer: textureSerializer ) { if (exportSettings == null) { throw new Exception($"VRM specification requires InverseAxis settings as {Vrm0xSpecificationInverseAxis}"); } if (exportSettings.InverseAxis != Vrm0xSpecificationInverseAxis) { // migration 用に reverseX を許す Debug.LogWarning($"VRM specification requires InverseAxis settings as {Vrm0xSpecificationInverseAxis}"); } _gltf.extensionsUsed.Add(glTF_VRM_extensions.ExtensionName); } public override void ExportExtensions(ITextureSerializer textureSerializer) { var getBone = UniHumanoid.Humanoid.Get_GetBoneTransform(Copy); UniHumanoid.AvatarDescription description = null; var isCreated = false; if (Copy.TryGetComponent(out var humanoid)) { if (humanoid != null) { description = humanoid.GetDescription(out isCreated); } } var nodes = Copy.transform.Traverse().Skip(1).ToList(); { if (description != null) { // use description VRM.humanoid.Apply(description, nodes); } if (isCreated) { GameObject.DestroyImmediate(description); } } // set humanoid bone mapping // var avatar = animator.avatar; foreach (HumanBodyBones key in CachedEnum.GetValues()) { if (key == HumanBodyBones.LastBone) { break; } var transform = getBone(key); if (transform != null) { var nodeIndex = nodes.IndexOf(transform); if (nodeIndex < 0) { Debug.LogError($"ヒューマンボーンが export 対象に含まれていない?", transform); } else { VRM.humanoid.SetNodeIndex(key, nodeIndex); } } } // morph if (Copy.TryGetComponent(out var master)) { var avatar = master.BlendShapeAvatar; if (avatar != null) { foreach (var x in avatar.Clips) { if (x == null) { continue; } VRM.blendShapeMaster.Add(x, this); } } } // secondary VRMSpringUtility.ExportSecondary(Copy.transform, Nodes, x => VRM.secondaryAnimation.colliderGroups.Add(x), x => VRM.secondaryAnimation.boneGroups.Add(x) ); #pragma warning disable 0618 // meta(obsolete) { if (Copy.TryGetComponent(out var meta)) { VRM.meta.author = meta.Author; VRM.meta.contactInformation = meta.ContactInformation; VRM.meta.title = meta.Title; if (meta.Thumbnail != null) { VRM.meta.texture = TextureExporter.RegisterExportingAsSRgb(meta.Thumbnail, needsAlpha: true); } VRM.meta.licenseType = meta.LicenseType; VRM.meta.otherLicenseUrl = meta.OtherLicenseUrl; VRM.meta.reference = meta.Reference; } } #pragma warning restore 0618 // meta { if (Copy.TryGetComponent(out var _meta) && _meta.Meta != null) { var meta = _meta.Meta; // info VRM.meta.version = meta.Version; VRM.meta.author = meta.Author; VRM.meta.contactInformation = meta.ContactInformation; VRM.meta.reference = meta.Reference; VRM.meta.title = meta.Title; if (meta.Thumbnail != null) { VRM.meta.texture = TextureExporter.RegisterExportingAsSRgb(meta.Thumbnail, needsAlpha: true); } // ussage permission VRM.meta.allowedUser = meta.AllowedUser; VRM.meta.violentUssage = meta.ViolentUssage; VRM.meta.sexualUssage = meta.SexualUssage; VRM.meta.commercialUssage = meta.CommercialUssage; VRM.meta.otherPermissionUrl = meta.OtherPermissionUrl; // distribution license VRM.meta.licenseType = meta.LicenseType; if (meta.LicenseType == LicenseType.Other) { VRM.meta.otherLicenseUrl = meta.OtherLicenseUrl; } } } // firstPerson if (Copy.TryGetComponent(out var firstPerson)) { if (firstPerson.FirstPersonBone != null) { VRM.firstPerson.firstPersonBone = Nodes.IndexOf(firstPerson.FirstPersonBone); VRM.firstPerson.firstPersonBoneOffset = firstPerson.FirstPersonOffset; VRM.firstPerson.meshAnnotations = firstPerson.Renderers.Select(x => new glTF_VRM_MeshAnnotation { mesh = Meshes.IndexOf(x.SharedMesh), firstPersonFlag = x.FirstPersonFlag.ToString(), }).ToList(); } // lookAt if (Copy.TryGetComponent(out var lookAtHead)) { if (Copy.TryGetComponent(out var boneApplyer)) { VRM.firstPerson.lookAtType = LookAtType.Bone; VRM.firstPerson.lookAtHorizontalInner.Apply(boneApplyer.HorizontalInner); VRM.firstPerson.lookAtHorizontalOuter.Apply(boneApplyer.HorizontalOuter); VRM.firstPerson.lookAtVerticalDown.Apply(boneApplyer.VerticalDown); VRM.firstPerson.lookAtVerticalUp.Apply(boneApplyer.VerticalUp); } else if (Copy.TryGetComponent(out var blendShapeApplyer)) { VRM.firstPerson.lookAtType = LookAtType.BlendShape; VRM.firstPerson.lookAtHorizontalOuter.Apply(blendShapeApplyer.Horizontal); VRM.firstPerson.lookAtVerticalDown.Apply(blendShapeApplyer.VerticalDown); VRM.firstPerson.lookAtVerticalUp.Apply(blendShapeApplyer.VerticalUp); } } } // materials foreach (var m in Materials) { VRM.materialProperties.Add(BuiltInVrmExtensionMaterialPropertyExporter.ExportMaterial(m, TextureExporter)); } // Serialize VRM var f = new JsonFormatter(); VRMSerializer.Serialize(f, VRM); var bytes = f.GetStoreBytes(); glTFExtensionExport.GetOrCreate(ref _gltf.extensions).Add("VRM", bytes); } } }