Streamingle_URP/Assets/External/VRM/Runtime/IO/VRMImporterContext.cs

374 lines
13 KiB
C#

using System;
using System.Linq;
using System.Collections.Generic;
using UniGLTF;
using UnityEngine;
using UniJSON;
using System.Threading.Tasks;
using VRMShaders;
using Object = UnityEngine.Object;
namespace VRM
{
public class VRMImporterContext : ImporterContext
{
VRMData _data;
public VRM.glTF_VRM_extensions VRM
{
get
{
return _data.VrmExtension;
}
}
public VRMImporterContext(
VRMData data,
IReadOnlyDictionary<SubAssetKey, Object> externalObjectMap = null,
ITextureDeserializer textureDeserializer = null,
IMaterialDescriptorGenerator materialGenerator = null)
: base(data.Data, externalObjectMap, textureDeserializer, materialGenerator ?? new VRMMaterialDescriptorGenerator(data.VrmExtension))
{
_data = data;
TextureDescriptorGenerator = new VrmTextureDescriptorGenerator(Data, VRM);
}
#region OnLoad
protected override async Task OnLoadHierarchy(IAwaitCaller awaitCaller, Func<string, IDisposable> MeasureTime)
{
Root.name = "VRM";
using (MeasureTime("VRM LoadMeta"))
{
await LoadMetaAsync(awaitCaller);
}
await awaitCaller.NextFrame();
using (MeasureTime("VRM LoadHumanoid"))
{
LoadHumanoid();
}
await awaitCaller.NextFrame();
using (MeasureTime("VRM LoadBlendShapeMaster"))
{
LoadBlendShapeMaster();
}
await awaitCaller.NextFrame();
using (MeasureTime("VRM LoadSecondary"))
{
VRMSpringUtility.LoadSecondary(Root.transform, Nodes,
VRM.secondaryAnimation);
}
await awaitCaller.NextFrame();
using (MeasureTime("VRM LoadFirstPerson"))
{
LoadFirstPerson();
}
}
async Task LoadMetaAsync(IAwaitCaller awaitCaller)
{
if (awaitCaller == null)
{
throw new ArgumentNullException();
}
var meta = await ReadMetaAsync(awaitCaller);
var _meta = Root.AddComponent<VRMMeta>();
_meta.Meta = meta;
Meta = meta;
}
void LoadFirstPerson()
{
var firstPerson = Root.AddComponent<VRMFirstPerson>();
var gltfFirstPerson = VRM.firstPerson;
if (gltfFirstPerson.firstPersonBone != -1)
{
firstPerson.FirstPersonBone = Nodes[gltfFirstPerson.firstPersonBone];
firstPerson.FirstPersonOffset = gltfFirstPerson.firstPersonBoneOffset;
}
else
{
// fallback
firstPerson.SetDefault();
firstPerson.FirstPersonOffset = gltfFirstPerson.firstPersonBoneOffset;
}
firstPerson.TraverseRenderers(this);
// LookAt
var lookAtHead = Root.AddComponent<VRMLookAtHead>();
lookAtHead.OnImported(this);
}
void LoadBlendShapeMaster()
{
BlendShapeAvatar = ScriptableObject.CreateInstance<BlendShapeAvatar>();
BlendShapeAvatar.name = "BlendShape";
var transformMeshTable = new Dictionary<Mesh, Transform>();
foreach (var transform in Root.transform.Traverse())
{
if (transform.GetSharedMesh() != null)
{
transformMeshTable.Add(transform.GetSharedMesh(), transform);
}
}
var blendShapeList = VRM.blendShapeMaster.blendShapeGroups;
if (blendShapeList != null && blendShapeList.Count > 0)
{
foreach (var x in blendShapeList)
{
BlendShapeAvatar.Clips.Add(LoadBlendShapeBind(x, transformMeshTable));
}
}
var proxy = Root.AddComponent<VRMBlendShapeProxy>();
BlendShapeAvatar.CreateDefaultPreset();
proxy.BlendShapeAvatar = BlendShapeAvatar;
}
BlendShapeClip LoadBlendShapeBind(glTF_VRM_BlendShapeGroup group, Dictionary<Mesh, Transform> transformMeshTable)
{
var asset = ScriptableObject.CreateInstance<BlendShapeClip>();
var groupName = group.name;
var prefix = "BlendShape.";
while (groupName.FastStartsWith(prefix))
{
groupName = groupName.Substring(prefix.Length);
}
asset.name = "BlendShape." + groupName;
if (group != null)
{
asset.BlendShapeName = groupName;
asset.Preset = CacheEnum.TryParseOrDefault<BlendShapePreset>(group.presetName, true);
asset.IsBinary = group.isBinary;
if (asset.Preset == BlendShapePreset.Unknown)
{
// fallback
asset.Preset = CacheEnum.TryParseOrDefault<BlendShapePreset>(group.name, true);
}
asset.Values = group.binds.Select(x =>
{
var mesh = Meshes[x.mesh].Mesh;
var node = transformMeshTable[mesh];
var relativePath = UniGLTF.UnityExtensions.RelativePathFrom(node, Root.transform);
return new BlendShapeBinding
{
RelativePath = relativePath,
Index = x.index,
Weight = x.weight,
};
})
.ToArray();
asset.MaterialValues = group.materialValues.Select(x =>
{
var value = new Vector4();
for (int i = 0; i < x.targetValue.Length; ++i)
{
switch (i)
{
case 0: value.x = x.targetValue[0]; break;
case 1: value.y = x.targetValue[1]; break;
case 2: value.z = x.targetValue[2]; break;
case 3: value.w = x.targetValue[3]; break;
}
}
var material = MaterialFactory.Materials
.Select(y => y.Asset)
.FirstOrDefault(y => y.name == x.materialName);
var propertyName = x.propertyName;
if (x.propertyName.FastEndsWith("_ST_S")
|| x.propertyName.FastEndsWith("_ST_T"))
{
propertyName = x.propertyName.Substring(0, x.propertyName.Length - 2);
}
var binding = default(MaterialValueBinding?);
if (material != null)
{
try
{
binding = new MaterialValueBinding
{
MaterialName = x.materialName,
ValueName = x.propertyName,
TargetValue = value,
BaseValue = material.GetColor(propertyName),
};
}
catch (Exception)
{
// do nothing
}
}
return binding;
})
.Where(x => x.HasValue)
.Select(x => x.Value)
.ToArray();
}
return asset;
}
static String ToHumanBoneName(HumanBodyBones b)
{
foreach (var x in HumanTrait.BoneName)
{
if (x.Replace(" ", "") == b.ToString())
{
return x;
}
}
throw new KeyNotFoundException();
}
static SkeletonBone ToSkeletonBone(Transform t)
{
var sb = new SkeletonBone();
sb.name = t.name;
sb.position = t.localPosition;
sb.rotation = t.localRotation;
sb.scale = t.localScale;
return sb;
}
private void LoadHumanoid()
{
AvatarDescription = VRM.humanoid.ToDescription(Nodes);
AvatarDescription.name = "AvatarDescription";
HumanoidAvatar = AvatarDescription.CreateAvatar(Root.transform);
if (!HumanoidAvatar.isValid || !HumanoidAvatar.isHuman)
{
throw new Exception("fail to create avatar");
}
HumanoidAvatar.name = "VrmAvatar";
var humanoid = Root.AddComponent<VRMHumanoidDescription>();
humanoid.Avatar = HumanoidAvatar;
humanoid.Description = AvatarDescription;
var animator = Root.GetComponent<Animator>();
if (animator == null)
{
animator = Root.AddComponent<Animator>();
}
animator.avatar = HumanoidAvatar;
// default としてとりあえず設定する
// https://docs.unity3d.com/ScriptReference/Renderer-probeAnchor.html
var head = animator.GetBoneTransform(HumanBodyBones.Head);
foreach (var smr in animator.GetComponentsInChildren<SkinnedMeshRenderer>())
{
smr.probeAnchor = head;
}
}
#endregion
public UniHumanoid.AvatarDescription AvatarDescription;
public Avatar HumanoidAvatar;
public BlendShapeAvatar BlendShapeAvatar;
public VRMMetaObject Meta;
public async Task<VRMMetaObject> ReadMetaAsync(IAwaitCaller awaitCaller, bool createThumbnail = false)
{
if (awaitCaller == null)
{
throw new ArgumentNullException();
}
var meta = ScriptableObject.CreateInstance<VRMMetaObject>();
meta.name = "Meta";
meta.ExporterVersion = VRM.exporterVersion;
var gltfMeta = VRM.meta;
meta.Version = gltfMeta.version; // model version
meta.Author = gltfMeta.author;
meta.ContactInformation = gltfMeta.contactInformation;
meta.Reference = gltfMeta.reference;
meta.Title = gltfMeta.title;
if (gltfMeta.texture >= 0)
{
var (key, param) = GltfTextureImporter.CreateSrgb(Data, gltfMeta.texture, Vector2.zero, Vector2.one);
meta.Thumbnail = await TextureFactory.GetTextureAsync(param, awaitCaller) as Texture2D;
}
meta.AllowedUser = gltfMeta.allowedUser;
meta.ViolentUssage = gltfMeta.violentUssage;
meta.SexualUssage = gltfMeta.sexualUssage;
meta.CommercialUssage = gltfMeta.commercialUssage;
meta.OtherPermissionUrl = gltfMeta.otherPermissionUrl;
meta.LicenseType = gltfMeta.licenseType;
meta.OtherLicenseUrl = gltfMeta.otherLicenseUrl;
return meta;
}
public override void TransferOwnership(TakeResponsibilityForDestroyObjectFunc take)
{
// VRM-0 は SubAssetKey を使っていないので default で済ます
// VRM 固有のリソース(ScriptableObject)
take(default, HumanoidAvatar);
HumanoidAvatar = null;
take(default, Meta);
Meta = null;
take(default, AvatarDescription);
AvatarDescription = null;
foreach (var x in BlendShapeAvatar.Clips)
{
take(default, x);
{
// do nothing
}
}
take(default, BlendShapeAvatar);
BlendShapeAvatar = null;
// GLTF のリソース
base.TransferOwnership(take);
}
public override void Dispose()
{
// VRM specific
if (HumanoidAvatar != null)
{
UnityObjectDestoyer.DestroyRuntimeOrEditor(HumanoidAvatar);
}
if (Meta != null)
{
UnityObjectDestoyer.DestroyRuntimeOrEditor(Meta);
}
if (AvatarDescription != null)
{
UnityObjectDestoyer.DestroyRuntimeOrEditor(AvatarDescription);
}
if (BlendShapeAvatar != null)
{
foreach (var clip in BlendShapeAvatar.Clips)
{
UnityObjectDestoyer.DestroyRuntimeOrEditor(clip);
}
UnityObjectDestoyer.DestroyRuntimeOrEditor(BlendShapeAvatar);
}
base.Dispose();
}
}
}