using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using UniGLTF; using UniGLTF.Utils; using UnityEngine; using VRMShaders; namespace UniVRM10 { /// /// VrmLib.Model から UnityPrefab を構築する /// public class Vrm10Importer : UniGLTF.ImporterContext { private readonly Vrm10Data m_vrm; /// VrmLib.Model の オブジェクトと UnityEngine.Object のマッピングを記録する private readonly ModelMap m_map = new ModelMap(); private readonly bool m_useControlRig; private VrmLib.Model m_model; private IReadOnlyDictionary m_externalMap; private Avatar m_humanoid; private VRM10Object m_vrmObject; private List<(ExpressionPreset Preset, VRM10Expression Clip)> m_expressions = new List<(ExpressionPreset, VRM10Expression)>(); public Vrm10Importer( Vrm10Data vrm, IReadOnlyDictionary externalObjectMap = null, ITextureDeserializer textureDeserializer = null, IMaterialDescriptorGenerator materialGenerator = null, bool useControlRig = false ) : base(vrm.Data, externalObjectMap, textureDeserializer) { if (vrm == null) { throw new ArgumentNullException("vrm"); } m_vrm = vrm; m_useControlRig = useControlRig; TextureDescriptorGenerator = new Vrm10TextureDescriptorGenerator(Data); MaterialDescriptorGenerator = materialGenerator ?? new BuiltInVrm10MaterialDescriptorGenerator(); m_externalMap = externalObjectMap; if (m_externalMap == null) { m_externalMap = new Dictionary(); } } static void AssignHumanoid(List nodes, UniGLTF.Extensions.VRMC_vrm.HumanBone humanBone, VrmLib.HumanoidBones key) { if (nodes == null) { throw new ArgumentNullException("nodes"); } if (humanBone != null && humanBone.Node.HasValue) { var index = humanBone.Node.Value; if (index >= 0 && index < nodes.Count) { nodes[index].HumanoidBone = key; } else { throw new IndexOutOfRangeException("AssignHumanoid"); } } } public override async Task LoadAsync(IAwaitCaller awaitCaller, Func MeasureTime = null) { if (awaitCaller == null) { throw new ArgumentNullException(); } // NOTE: VRM データに対して、Load 前に必要なヘビーな変換処理を行う. // ヘビーなため、別スレッドで Run する. await awaitCaller.Run(() => { // bin に対して右手左手変換を破壊的に実行することに注意 !(bin が変換済みになる) m_model = ModelReader.Read(Data); // assign humanoid bones if (m_vrm.VrmExtension.Humanoid is UniGLTF.Extensions.VRMC_vrm.Humanoid humanoid) { AssignHumanoid(m_model.Nodes, humanoid.HumanBones.Hips, VrmLib.HumanoidBones.hips); AssignHumanoid(m_model.Nodes, humanoid.HumanBones.LeftUpperLeg, VrmLib.HumanoidBones.leftUpperLeg); AssignHumanoid(m_model.Nodes, humanoid.HumanBones.RightUpperLeg, VrmLib.HumanoidBones.rightUpperLeg); AssignHumanoid(m_model.Nodes, humanoid.HumanBones.LeftLowerLeg, VrmLib.HumanoidBones.leftLowerLeg); AssignHumanoid(m_model.Nodes, humanoid.HumanBones.RightLowerLeg, VrmLib.HumanoidBones.rightLowerLeg); AssignHumanoid(m_model.Nodes, humanoid.HumanBones.LeftFoot, VrmLib.HumanoidBones.leftFoot); AssignHumanoid(m_model.Nodes, humanoid.HumanBones.RightFoot, VrmLib.HumanoidBones.rightFoot); AssignHumanoid(m_model.Nodes, humanoid.HumanBones.Spine, VrmLib.HumanoidBones.spine); AssignHumanoid(m_model.Nodes, humanoid.HumanBones.Chest, VrmLib.HumanoidBones.chest); AssignHumanoid(m_model.Nodes, humanoid.HumanBones.Neck, VrmLib.HumanoidBones.neck); AssignHumanoid(m_model.Nodes, humanoid.HumanBones.Head, VrmLib.HumanoidBones.head); AssignHumanoid(m_model.Nodes, humanoid.HumanBones.LeftShoulder, VrmLib.HumanoidBones.leftShoulder); AssignHumanoid(m_model.Nodes, humanoid.HumanBones.RightShoulder, VrmLib.HumanoidBones.rightShoulder); AssignHumanoid(m_model.Nodes, humanoid.HumanBones.LeftUpperArm, VrmLib.HumanoidBones.leftUpperArm); AssignHumanoid(m_model.Nodes, humanoid.HumanBones.RightUpperArm, VrmLib.HumanoidBones.rightUpperArm); AssignHumanoid(m_model.Nodes, humanoid.HumanBones.LeftLowerArm, VrmLib.HumanoidBones.leftLowerArm); AssignHumanoid(m_model.Nodes, humanoid.HumanBones.RightLowerArm, VrmLib.HumanoidBones.rightLowerArm); AssignHumanoid(m_model.Nodes, humanoid.HumanBones.LeftHand, VrmLib.HumanoidBones.leftHand); AssignHumanoid(m_model.Nodes, humanoid.HumanBones.RightHand, VrmLib.HumanoidBones.rightHand); AssignHumanoid(m_model.Nodes, humanoid.HumanBones.LeftToes, VrmLib.HumanoidBones.leftToes); AssignHumanoid(m_model.Nodes, humanoid.HumanBones.RightToes, VrmLib.HumanoidBones.rightToes); AssignHumanoid(m_model.Nodes, humanoid.HumanBones.LeftEye, VrmLib.HumanoidBones.leftEye); AssignHumanoid(m_model.Nodes, humanoid.HumanBones.RightEye, VrmLib.HumanoidBones.rightEye); AssignHumanoid(m_model.Nodes, humanoid.HumanBones.Jaw, VrmLib.HumanoidBones.jaw); AssignHumanoid(m_model.Nodes, humanoid.HumanBones.LeftThumbMetacarpal, VrmLib.HumanoidBones.leftThumbMetacarpal); AssignHumanoid(m_model.Nodes, humanoid.HumanBones.LeftThumbProximal, VrmLib.HumanoidBones.leftThumbProximal); AssignHumanoid(m_model.Nodes, humanoid.HumanBones.LeftThumbDistal, VrmLib.HumanoidBones.leftThumbDistal); AssignHumanoid(m_model.Nodes, humanoid.HumanBones.LeftIndexProximal, VrmLib.HumanoidBones.leftIndexProximal); AssignHumanoid(m_model.Nodes, humanoid.HumanBones.LeftIndexIntermediate, VrmLib.HumanoidBones.leftIndexIntermediate); AssignHumanoid(m_model.Nodes, humanoid.HumanBones.LeftIndexDistal, VrmLib.HumanoidBones.leftIndexDistal); AssignHumanoid(m_model.Nodes, humanoid.HumanBones.LeftMiddleProximal, VrmLib.HumanoidBones.leftMiddleProximal); AssignHumanoid(m_model.Nodes, humanoid.HumanBones.LeftMiddleIntermediate, VrmLib.HumanoidBones.leftMiddleIntermediate); AssignHumanoid(m_model.Nodes, humanoid.HumanBones.LeftMiddleDistal, VrmLib.HumanoidBones.leftMiddleDistal); AssignHumanoid(m_model.Nodes, humanoid.HumanBones.LeftRingProximal, VrmLib.HumanoidBones.leftRingProximal); AssignHumanoid(m_model.Nodes, humanoid.HumanBones.LeftRingIntermediate, VrmLib.HumanoidBones.leftRingIntermediate); AssignHumanoid(m_model.Nodes, humanoid.HumanBones.LeftRingDistal, VrmLib.HumanoidBones.leftRingDistal); AssignHumanoid(m_model.Nodes, humanoid.HumanBones.LeftLittleProximal, VrmLib.HumanoidBones.leftLittleProximal); AssignHumanoid(m_model.Nodes, humanoid.HumanBones.LeftLittleIntermediate, VrmLib.HumanoidBones.leftLittleIntermediate); AssignHumanoid(m_model.Nodes, humanoid.HumanBones.LeftLittleDistal, VrmLib.HumanoidBones.leftLittleDistal); AssignHumanoid(m_model.Nodes, humanoid.HumanBones.RightThumbMetacarpal, VrmLib.HumanoidBones.rightThumbMetacarpal); AssignHumanoid(m_model.Nodes, humanoid.HumanBones.RightThumbProximal, VrmLib.HumanoidBones.rightThumbProximal); AssignHumanoid(m_model.Nodes, humanoid.HumanBones.RightThumbDistal, VrmLib.HumanoidBones.rightThumbDistal); AssignHumanoid(m_model.Nodes, humanoid.HumanBones.RightIndexProximal, VrmLib.HumanoidBones.rightIndexProximal); AssignHumanoid(m_model.Nodes, humanoid.HumanBones.RightIndexIntermediate, VrmLib.HumanoidBones.rightIndexIntermediate); AssignHumanoid(m_model.Nodes, humanoid.HumanBones.RightIndexDistal, VrmLib.HumanoidBones.rightIndexDistal); AssignHumanoid(m_model.Nodes, humanoid.HumanBones.RightMiddleProximal, VrmLib.HumanoidBones.rightMiddleProximal); AssignHumanoid(m_model.Nodes, humanoid.HumanBones.RightMiddleIntermediate, VrmLib.HumanoidBones.rightMiddleIntermediate); AssignHumanoid(m_model.Nodes, humanoid.HumanBones.RightMiddleDistal, VrmLib.HumanoidBones.rightMiddleDistal); AssignHumanoid(m_model.Nodes, humanoid.HumanBones.RightRingProximal, VrmLib.HumanoidBones.rightRingProximal); AssignHumanoid(m_model.Nodes, humanoid.HumanBones.RightRingIntermediate, VrmLib.HumanoidBones.rightRingIntermediate); AssignHumanoid(m_model.Nodes, humanoid.HumanBones.RightRingDistal, VrmLib.HumanoidBones.rightRingDistal); AssignHumanoid(m_model.Nodes, humanoid.HumanBones.RightLittleProximal, VrmLib.HumanoidBones.rightLittleProximal); AssignHumanoid(m_model.Nodes, humanoid.HumanBones.RightLittleIntermediate, VrmLib.HumanoidBones.rightLittleIntermediate); AssignHumanoid(m_model.Nodes, humanoid.HumanBones.RightLittleDistal, VrmLib.HumanoidBones.rightLittleDistal); AssignHumanoid(m_model.Nodes, humanoid.HumanBones.UpperChest, VrmLib.HumanoidBones.upperChest); } }); return await base.LoadAsync(awaitCaller, MeasureTime); } /// /// VrmLib.Model から 構築する /// /// /// protected override async Task LoadGeometryAsync(IAwaitCaller awaitCaller, Func MeasureTime) { // fill assets for (int i = 0; i < m_model.Materials.Count; ++i) { var src = m_model.Materials[i]; var dst = MaterialFactory.Materials[i].Asset; } await awaitCaller.NextFrame(); // mesh for (int i = 0; i < m_model.MeshGroups.Count; ++i) { var src = m_model.MeshGroups[i]; UnityEngine.Mesh mesh = default; if (src.Meshes.Count == 1) { mesh = MeshImporterShared.LoadSharedMesh(src.Meshes[0], src.Skin); } else { // 頂点バッファの連結が必用 // VRM-1 はこっち // https://github.com/vrm-c/UniVRM/issues/800 mesh = MeshImporterDivided.LoadDivided(src); } mesh.name = src.Name; m_map.Meshes.Add(src, mesh); Meshes.Add(new MeshWithMaterials { Mesh = mesh, Materials = src.Meshes[0].Submeshes.Select(x => MaterialFactory.Materials[x.Material].Asset).ToArray(), }); await awaitCaller.NextFrame(); } // node: recursive CreateNodes(m_model.Root, null, m_map.Nodes); for (int i = 0; i < m_model.Nodes.Count; ++i) { Nodes.Add(m_map.Nodes[m_model.Nodes[i]].transform); } await awaitCaller.NextFrame(); if (Root == null) { Root = m_map.Nodes[m_model.Root]; } else { // replace var modelRoot = m_map.Nodes[m_model.Root]; foreach (Transform child in modelRoot.transform) { child.SetParent(Root.transform, true); } m_map.Nodes[m_model.Root] = Root; } await awaitCaller.NextFrame(); // renderer var map = m_map; foreach (var (node, go) in map.Nodes.Select(kv => (kv.Key, kv.Value))) { if (node.MeshGroup is null) { continue; } CreateRenderer(node, go, map, MaterialFactory.Materials); await awaitCaller.NextFrame(); } } protected override async Task OnLoadHierarchy(IAwaitCaller awaitCaller, Func MeasureTime) { Root.name = "VRM1"; // humanoid var humanoid = Root.AddComponent(); humanoid.AssignBones(m_map.Nodes.Select(x => (ToUnity(x.Key.HumanoidBone.GetValueOrDefault()), x.Value.transform))); m_humanoid = humanoid.CreateAvatar(); m_humanoid.name = "humanoid"; var animator = Root.AddComponent(); animator.avatar = m_humanoid; // VrmController var controller = Root.AddComponent(); controller.InitializeAtRuntime(m_useControlRig); controller.enabled = false; // vrm controller.Vrm = await LoadVrmAsync(awaitCaller, m_vrm.VrmExtension); // springBone if (UniGLTF.Extensions.VRMC_springBone.GltfDeserializer.TryGet(Data.GLTF.extensions, out UniGLTF.Extensions.VRMC_springBone.VRMC_springBone springBone)) { await LoadSpringBoneAsync(awaitCaller, controller, springBone); } // constraint await LoadConstraintAsync(awaitCaller, controller); // Hierarchyの構築が終わるまで遅延させる controller.enabled = true; } VRM10Expression GetOrLoadExpression(in SubAssetKey key, ExpressionPreset preset, UniGLTF.Extensions.VRMC_vrm.Expression expression) { VRM10Expression clip = default; if (m_externalMap.TryGetValue(key, out UnityEngine.Object expressionObj)) { clip = expressionObj as VRM10Expression; } else { if (expression == null) { // default empty expression expression = new UniGLTF.Extensions.VRMC_vrm.Expression { IsBinary = false, OverrideBlink = UniGLTF.Extensions.VRMC_vrm.ExpressionOverrideType.none, OverrideLookAt = UniGLTF.Extensions.VRMC_vrm.ExpressionOverrideType.none, OverrideMouth = UniGLTF.Extensions.VRMC_vrm.ExpressionOverrideType.none, }; } clip = ScriptableObject.CreateInstance(); clip.name = key.Name; clip.IsBinary = expression.IsBinary.GetValueOrDefault(); clip.OverrideBlink = expression.OverrideBlink; clip.OverrideLookAt = expression.OverrideLookAt; clip.OverrideMouth = expression.OverrideMouth; if (expression.MorphTargetBinds != null) { clip.MorphTargetBindings = expression.MorphTargetBinds?.Select(x => x.Build10(Root, m_map, m_model)) .ToArray(); } else { clip.MorphTargetBindings = new MorphTargetBinding[] { }; } if (expression.MaterialColorBinds != null) { clip.MaterialColorBindings = expression.MaterialColorBinds.Select(x => x.Build10(MaterialFactory.Materials)) .Where(x => x.HasValue) .Select(x => x.Value) .ToArray(); } else { clip.MaterialColorBindings = new MaterialColorBinding[] { }; } if (expression.TextureTransformBinds != null) { clip.MaterialUVBindings = expression?.TextureTransformBinds?.Select(x => x.Build10(MaterialFactory.Materials)) .Where(x => x.HasValue) .Select(x => x.Value) .ToArray(); } else { clip.MaterialUVBindings = new MaterialUVBinding[] { }; } m_expressions.Add((preset, clip)); } return clip; } public async Task LoadVrmThumbnailAsync(IAwaitCaller awaitCaller = null) { if (awaitCaller == null) { awaitCaller = new ImmediateCaller(); } if (Vrm10TextureDescriptorGenerator.TryGetMetaThumbnailTextureImportParam(Data, m_vrm.VrmExtension, out (SubAssetKey, VRMShaders.TextureDescriptor Param) kv)) { var texture = await TextureFactory.GetTextureAsync(kv.Param, awaitCaller); return texture as Texture2D; } else { return null; } } async Task LoadVrmAsync(IAwaitCaller awaitCaller, UniGLTF.Extensions.VRMC_vrm.VRMC_vrm vrmExtension) { if (m_externalMap.TryGetValue(VRM10Object.SubAssetKey, out UnityEngine.Object obj) && obj is VRM10Object vrm) { // use external object map return vrm; } // create new object m_vrmObject = vrm = ScriptableObject.CreateInstance(); vrm.name = VRM10Object.SubAssetKey.Name; // meta if (vrmExtension.Meta != null) { var src = vrmExtension.Meta; var meta = new VRM10ObjectMeta(); vrm.Meta = meta; meta.Name = src.Name; meta.Version = src.Version; meta.CopyrightInformation = src.CopyrightInformation; meta.ContactInformation = src.ContactInformation; meta.ThirdPartyLicenses = src.ThirdPartyLicenses; // avatar meta.AvatarPermission = src.AvatarPermission; meta.ViolentUsage = src.AllowExcessivelyViolentUsage.GetValueOrDefault(); meta.SexualUsage = src.AllowExcessivelySexualUsage.GetValueOrDefault(); meta.CommercialUsage = src.CommercialUsage; meta.PoliticalOrReligiousUsage = src.AllowPoliticalOrReligiousUsage.GetValueOrDefault(); meta.AntisocialOrHateUsage = src.AllowAntisocialOrHateUsage.GetValueOrDefault(); // redistribution meta.CreditNotation = src.CreditNotation; meta.Redistribution = src.AllowRedistribution.GetValueOrDefault(); meta.Modification = src.Modification; meta.OtherLicenseUrl = src.OtherLicenseUrl; // if (src.References != null) { meta.References.AddRange(src.References); } if (src.Authors != null) { meta.Authors.AddRange(src.Authors); } var tex2D = await LoadVrmThumbnailAsync(awaitCaller); if (tex2D != null) { meta.Thumbnail = tex2D; } } // expression { vrm.Expression.Happy = GetOrLoadExpression(ExpressionKey.Happy.SubAssetKey, ExpressionPreset.happy, vrmExtension.Expressions?.Preset?.Happy); vrm.Expression.Angry = GetOrLoadExpression(ExpressionKey.Angry.SubAssetKey, ExpressionPreset.angry, vrmExtension.Expressions?.Preset?.Angry); vrm.Expression.Sad = GetOrLoadExpression(ExpressionKey.Sad.SubAssetKey, ExpressionPreset.sad, vrmExtension.Expressions?.Preset?.Sad); vrm.Expression.Relaxed = GetOrLoadExpression(ExpressionKey.Relaxed.SubAssetKey, ExpressionPreset.relaxed, vrmExtension.Expressions?.Preset?.Relaxed); vrm.Expression.Surprised = GetOrLoadExpression(ExpressionKey.Surprised.SubAssetKey, ExpressionPreset.surprised, vrmExtension.Expressions?.Preset?.Surprised); vrm.Expression.Aa = GetOrLoadExpression(ExpressionKey.Aa.SubAssetKey, ExpressionPreset.aa, vrmExtension.Expressions?.Preset?.Aa); vrm.Expression.Ih = GetOrLoadExpression(ExpressionKey.Ih.SubAssetKey, ExpressionPreset.ih, vrmExtension.Expressions?.Preset?.Ih); vrm.Expression.Ou = GetOrLoadExpression(ExpressionKey.Ou.SubAssetKey, ExpressionPreset.ou, vrmExtension.Expressions?.Preset?.Ou); vrm.Expression.Ee = GetOrLoadExpression(ExpressionKey.Ee.SubAssetKey, ExpressionPreset.ee, vrmExtension.Expressions?.Preset?.Ee); vrm.Expression.Oh = GetOrLoadExpression(ExpressionKey.Oh.SubAssetKey, ExpressionPreset.oh, vrmExtension.Expressions?.Preset?.Oh); vrm.Expression.Blink = GetOrLoadExpression(ExpressionKey.Blink.SubAssetKey, ExpressionPreset.blink, vrmExtension.Expressions?.Preset?.Blink); vrm.Expression.BlinkLeft = GetOrLoadExpression(ExpressionKey.BlinkLeft.SubAssetKey, ExpressionPreset.blinkLeft, vrmExtension.Expressions?.Preset?.BlinkLeft); vrm.Expression.BlinkRight = GetOrLoadExpression(ExpressionKey.BlinkRight.SubAssetKey, ExpressionPreset.blinkRight, vrmExtension.Expressions?.Preset?.BlinkRight); vrm.Expression.LookUp = GetOrLoadExpression(ExpressionKey.LookUp.SubAssetKey, ExpressionPreset.lookUp, vrmExtension.Expressions?.Preset?.LookUp); vrm.Expression.LookDown = GetOrLoadExpression(ExpressionKey.LookDown.SubAssetKey, ExpressionPreset.lookDown, vrmExtension.Expressions?.Preset?.LookDown); vrm.Expression.LookLeft = GetOrLoadExpression(ExpressionKey.LookLeft.SubAssetKey, ExpressionPreset.lookLeft, vrmExtension.Expressions?.Preset?.LookLeft); vrm.Expression.LookRight = GetOrLoadExpression(ExpressionKey.LookRight.SubAssetKey, ExpressionPreset.lookRight, vrmExtension.Expressions?.Preset?.LookRight); vrm.Expression.Neutral = GetOrLoadExpression(ExpressionKey.Neutral.SubAssetKey, ExpressionPreset.neutral, vrmExtension.Expressions?.Preset?.Neutral); if (vrmExtension?.Expressions?.Custom != null) { foreach (var (name, expression) in vrmExtension.Expressions.Custom.Select(kv => (kv.Key, kv.Value))) { var key = ExpressionKey.CreateCustom(name); var preset = ExpressionPreset.custom; var clip = GetOrLoadExpression(key.SubAssetKey, preset, expression); if (clip != null) { vrm.Expression.AddClip(preset, clip); } } } } // lookat if (vrmExtension.LookAt != null) { var src = vrmExtension.LookAt; vrm.LookAt.LookAtType = src.Type; if (src.OffsetFromHeadBone != null) { vrm.LookAt.OffsetFromHead = new Vector3(src.OffsetFromHeadBone[0], src.OffsetFromHeadBone[1], src.OffsetFromHeadBone[2]).ReverseX(); } if (src.RangeMapHorizontalInner != null) { vrm.LookAt.HorizontalInner = new CurveMapper(src.RangeMapHorizontalInner.InputMaxValue.Value, src.RangeMapHorizontalInner.OutputScale.Value); } if (src.RangeMapHorizontalOuter != null) { vrm.LookAt.HorizontalOuter = new CurveMapper(src.RangeMapHorizontalOuter.InputMaxValue.Value, src.RangeMapHorizontalOuter.OutputScale.Value); } if (src.RangeMapVerticalUp != null) { vrm.LookAt.VerticalUp = new CurveMapper(src.RangeMapVerticalUp.InputMaxValue.Value, src.RangeMapVerticalUp.OutputScale.Value); } if (src.RangeMapVerticalDown != null) { vrm.LookAt.VerticalDown = new CurveMapper(src.RangeMapVerticalDown.InputMaxValue.Value, src.RangeMapVerticalDown.OutputScale.Value); } } // firstPerson if (vrmExtension.FirstPerson != null && vrmExtension.FirstPerson.MeshAnnotations != null) { var fp = vrmExtension.FirstPerson; foreach (var x in fp.MeshAnnotations) { var node = Nodes[x.Node.Value]; var relative = node.RelativePathFrom(Root.transform); vrm.FirstPerson.Renderers.Add(new RendererFirstPersonFlags { FirstPersonFlag = x.Type, Renderer = relative, }); } } else { // default 値を割り当てる foreach (var smr in Root.GetComponentsInChildren()) { var relative = smr.transform.RelativePathFrom(Root.transform); vrm.FirstPerson.Renderers.Add(new RendererFirstPersonFlags { FirstPersonFlag = UniGLTF.Extensions.VRMC_vrm.FirstPersonType.auto, Renderer = relative, }); } } // 設定の無い renderer に auto を割り当てる foreach (var smr in Root.GetComponentsInChildren()) { var relative = smr.transform.RelativePathFrom(Root.transform); if (!vrm.FirstPerson.Renderers.Any(x => x.Renderer == relative)) { vrm.FirstPerson.Renderers.Add(new RendererFirstPersonFlags { FirstPersonFlag = UniGLTF.Extensions.VRMC_vrm.FirstPersonType.auto, Renderer = relative, }); } } return vrm; } async Task LoadSpringBoneAsync(IAwaitCaller awaitCaller, Vrm10Instance controller, UniGLTF.Extensions.VRMC_springBone.VRMC_springBone gltfVrmSpringBone) { await awaitCaller.NextFrame(); // colliders var colliders = new List(); if (gltfVrmSpringBone.Colliders != null) { foreach (var c in gltfVrmSpringBone.Colliders) { var collider = Nodes[c.Node.Value].gameObject.AddComponent(); colliders.Add(collider); if (c.Shape.Capsule is UniGLTF.Extensions.VRMC_springBone.ColliderShapeCapsule capsule) { collider.ColliderType = VRM10SpringBoneColliderTypes.Capsule; collider.Offset = Vector3InvertX(capsule.Offset); collider.Tail = Vector3InvertX(capsule.Tail); collider.Radius = capsule.Radius.Value; } else if (c.Shape.Sphere is UniGLTF.Extensions.VRMC_springBone.ColliderShapeSphere sphere) { collider.ColliderType = VRM10SpringBoneColliderTypes.Sphere; collider.Offset = Vector3InvertX(sphere.Offset); collider.Radius = sphere.Radius.Value; } else { throw new Vrm10Exception("unknown shape"); } } var secondary = Root.transform.Find("secondary"); if (secondary == null) { secondary = new GameObject("secondary").transform; secondary.SetParent(Root.transform, false); } // colliderGroup if (gltfVrmSpringBone.ColliderGroups != null) { foreach (var g in gltfVrmSpringBone.ColliderGroups) { var colliderGroup = secondary.gameObject.AddComponent(); controller.SpringBone.ColliderGroups.Add(colliderGroup); if (g != null && g.Colliders != null) { foreach (var c in g.Colliders) { var collider = colliders[c]; colliderGroup.Colliders.Add(collider); } } } } } // springs if (gltfVrmSpringBone.Springs != null) { // spring foreach (var gltfSpring in gltfVrmSpringBone.Springs) { if (gltfSpring.Joints == null || gltfSpring.Joints.Count == 0) { continue; } var spring = new Vrm10InstanceSpringBone.Spring(gltfSpring.Name); controller.SpringBone.Springs.Add(spring); if (gltfSpring.Center.HasValue) { spring.Center = Nodes[gltfSpring.Center.Value]; } if (gltfSpring.ColliderGroups != null) { spring.ColliderGroups = gltfSpring.ColliderGroups.Select(x => controller.SpringBone.ColliderGroups[x]).ToList(); } // joint foreach (var gltfJoint in gltfSpring.Joints) { if (gltfJoint.Node.HasValue) { var index = gltfJoint.Node.Value; if (index < 0 || index >= Nodes.Count) { throw new IndexOutOfRangeException($"{index} > {Nodes.Count}"); } // https://github.com/vrm-c/UniVRM/issues/1441 // // https://github.com/vrm-c/vrm-specification/blob/master/specification/VRMC_springBone-1.0-beta/schema/VRMC_springBone.joint.schema.json // に基づきデフォルト値を補う // node is required var go = Nodes[gltfJoint.Node.Value].gameObject; var joint = go.GetComponent(); if (joint != null) { // 仕様違反。マイグレーションで発生しうるのと、エクスポーターでの除外などがされていないので、 // エラーにせずに飛ばす Debug.LogWarning($"duplicated spring joint: {Data.TargetPath}"); continue; } joint = go.AddComponent(); joint.m_jointRadius = gltfJoint.HitRadius.GetValueOrDefault(0.0f); joint.m_dragForce = gltfJoint.DragForce.GetValueOrDefault(0.5f); joint.m_gravityDir = gltfJoint.GravityDir != null ? Vector3InvertX(gltfJoint.GravityDir) : Vector3.down; joint.m_gravityPower = gltfJoint.GravityPower.GetValueOrDefault(0.0f); joint.m_stiffnessForce = gltfJoint.Stiffness.GetValueOrDefault(1.0f); spring.Joints.Add(joint); } } } } } static AxisMask ConstraintAxes(bool[] flags) { var mask = default(AxisMask); if (flags != null && flags.Length == 3) { if (flags[0]) mask |= AxisMask.X; if (flags[1]) mask |= AxisMask.Y; if (flags[2]) mask |= AxisMask.Z; } return mask; } static Vector3 Vector3InvertX(float[] f) { var v = default(Vector3); if (f != null && f.Length == 3) { v.x = -f[0]; v.y = f[1]; v.z = f[2]; } return v; } /// /// https://github.com/vrm-c/vrm-specification/tree/master/specification/VRMC_node_constraint-1.0_beta /// /// * roll /// * aim /// * rotaton /// /// /// /// /// async Task LoadConstraintAsync(IAwaitCaller awaitCaller, Vrm10Instance controller) { for (int i = 0; i < Data.GLTF.nodes.Count; ++i) { var gltfNode = Data.GLTF.nodes[i]; if (UniGLTF.Extensions.VRMC_node_constraint.GltfDeserializer.TryGet(gltfNode.extensions, out UniGLTF.Extensions.VRMC_node_constraint.VRMC_node_constraint ext)) { var constraint = ext.Constraint; var node = Nodes[i]; if (constraint.Roll != null) { var roll = constraint.Roll; var component = node.gameObject.AddComponent(); component.Source = Nodes[roll.Source.Value]; // required component.Weight = roll.Weight.GetValueOrDefault(1.0f); component.RollAxis = roll.RollAxis; // required } else if (constraint.Aim != null) { var aim = constraint.Aim; var component = node.gameObject.AddComponent(); component.Source = Nodes[aim.Source.Value]; // required component.Weight = aim.Weight.GetValueOrDefault(1.0f); component.AimAxis = Vrm10ConstraintUtil.ReverseX(aim.AimAxis); // required } else if (constraint.Rotation != null) { var rotation = constraint.Rotation; var component = node.gameObject.AddComponent(); component.Source = Nodes[rotation.Source.Value]; // required component.Weight = rotation.Weight.GetValueOrDefault(1.0f); } else { throw new NotImplementedException(); } } } await awaitCaller.NextFrame(); } public static HumanBodyBones ToUnity(VrmLib.HumanoidBones bone) { switch (bone) { // https://github.com/vrm-c/vrm-specification/issues/380 case VrmLib.HumanoidBones.unknown: return HumanBodyBones.LastBone; case VrmLib.HumanoidBones.leftThumbMetacarpal: return HumanBodyBones.LeftThumbProximal; case VrmLib.HumanoidBones.leftThumbProximal: return HumanBodyBones.LeftThumbIntermediate; case VrmLib.HumanoidBones.rightThumbMetacarpal: return HumanBodyBones.RightThumbProximal; case VrmLib.HumanoidBones.rightThumbProximal: return HumanBodyBones.RightThumbIntermediate; } return CachedEnum.Parse(bone.ToString(), ignoreCase: true); } /// /// ヒエラルキーを再帰的に構築する /// public static void CreateNodes(VrmLib.Node node, GameObject parent, Dictionary nodes) { GameObject go = new GameObject(node.Name); nodes.Add(node, go); // world go.transform.SetPositionAndRotation(node.Translation, node.Rotation); if (parent != null) { go.transform.SetParent(parent.transform, true); } // local go.transform.localScale = node.LocalScaling; if (node.Children.Count > 0) { for (int n = 0; n < node.Children.Count; n++) { CreateNodes(node.Children[n], go, nodes); } } } /// /// MeshFilter + MeshRenderer もしくは SkinnedMeshRenderer を構築する /// public static Renderer CreateRenderer(VrmLib.Node node, GameObject go, ModelMap map, IReadOnlyList materialLoadInfos) { Renderer renderer = null; var hasBlendShape = node.MeshGroup.Meshes[0].MorphTargets.Any(); if (node.MeshGroup.Skin != null || hasBlendShape) { var skinnedMeshRenderer = go.AddComponent(); renderer = skinnedMeshRenderer; skinnedMeshRenderer.sharedMesh = map.Meshes[node.MeshGroup]; if (node.MeshGroup.Skin != null) { skinnedMeshRenderer.bones = node.MeshGroup.Skin.Joints.Select(x => map.Nodes[x].transform).ToArray(); if (node.MeshGroup.Skin.Root != null) { skinnedMeshRenderer.rootBone = map.Nodes[node.MeshGroup.Skin.Root].transform; } } } else { var meshFilter = go.AddComponent(); renderer = go.AddComponent(); meshFilter.sharedMesh = map.Meshes[node.MeshGroup]; } // hide by default renderer.enabled = false; if (node.MeshGroup.Meshes.Count == 0) { throw new NotImplementedException(); } else if (node.MeshGroup.Meshes.Count == 1) { var materials = node.MeshGroup.Meshes[0].Submeshes.Select(x => materialLoadInfos[x.Material].Asset).ToArray(); renderer.sharedMaterials = materials; } else { var materials = node.MeshGroup.Meshes.Select(x => materialLoadInfos[x.Submeshes[0].Material].Asset).ToArray(); renderer.sharedMaterials = materials; } return renderer; } public override void TransferOwnership(TakeResponsibilityForDestroyObjectFunc take) { // VRM 固有のリソース(ScriptableObject) take(SubAssetKey.Create(m_humanoid), m_humanoid); m_humanoid = null; if (m_vrmObject != null) { take(VRM10Object.SubAssetKey, m_vrmObject); m_vrmObject = null; } foreach (var (preset, x) in m_expressions) { take(new ExpressionKey(preset, x.name).SubAssetKey, x); // do nothing } m_expressions.Clear(); // GLTF のリソース base.TransferOwnership(take); } public override void Dispose() { // VRM specific if (m_humanoid != null) { UnityObjectDestroyer.DestroyRuntimeOrEditor(m_humanoid); m_humanoid = null; } if (m_vrmObject != null) { UnityObjectDestroyer.DestroyRuntimeOrEditor(m_vrmObject); m_vrmObject = null; } foreach (var (preset, clip) in m_expressions) { UnityObjectDestroyer.DestroyRuntimeOrEditor(clip); } m_expressions.Clear(); base.Dispose(); } public sealed class ModelMap { public readonly Dictionary Nodes = new Dictionary(); public readonly Dictionary Meshes = new Dictionary(); } } }