UniVRM/Assets/VRM/Runtime/IO/VRMExporter.cs
2024-09-26 15:50:09 +09:00

233 lines
9.0 KiB
C#

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<VRMHumanoidDescription>(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<HumanBodyBones>())
{
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<VRMBlendShapeProxy>(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<VRMMetaInformation>(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<VRMMeta>(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<VRMFirstPerson>(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(),
})
.Where(x => x.mesh != -1)
.ToList();
}
// lookAt
if (Copy.TryGetComponent<VRMLookAtHead>(out var lookAtHead))
{
if (Copy.TryGetComponent<VRMLookAtBoneApplyer>(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<VRMLookAtBlendShapeApplyer>(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);
}
}
}