Skip to content

Instantly share code, notes, and snippets.

@m2wasabi
Created April 12, 2019 09:30
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save m2wasabi/d9a380cfedd7daf4c4e3b39bab720b27 to your computer and use it in GitHub Desktop.
Save m2wasabi/d9a380cfedd7daf4c4e3b39bab720b27 to your computer and use it in GitHub Desktop.

VRM取り込みで揺れものにDynamicBoneを使う

using System.Collections.Generic;
using System.Linq;
using UnityEngine;
using UniGLTF;
namespace VRM.DynamicBoneExtension
{
public class DynamicBoneLoadUtility {
public static void LoadSecondary(Transform root, List<Transform> nodes,
glTF_VRM_SecondaryAnimation secondaryAnimation)
{
// clear components
var remove = root.Traverse()
.SelectMany(x => x.GetComponents<Component>())
.Where(x => x is VRMSpringBone || x is VRMSpringBoneColliderGroup
|| x is DynamicBone || x is DynamicBoneCollider || x is DynamicBonePlaneCollider)
.ToArray();
foreach (var x in remove)
{
if (Application.isPlaying)
{
GameObject.Destroy(x);
}
else
{
GameObject.DestroyImmediate(x);
}
}
//var secondaryAnimation = context.VRM.extensions.VRM.secondaryAnimation;
var colliders = new List<DynamicBoneCollider>();
foreach (var colliderGroup in secondaryAnimation.colliderGroups)
{
foreach (var collider in colliderGroup.colliders)
{
var dbCollider = nodes[colliderGroup.node].gameObject.AddComponent<DynamicBoneCollider>();
dbCollider.m_Radius = collider.radius;
dbCollider.m_Center = collider.offset;
colliders.Add(dbCollider);
}
}
if (secondaryAnimation.boneGroups.Count > 0)
{
foreach (var boneGroup in secondaryAnimation.boneGroups)
{
foreach (var boneindex in boneGroup.bones)
{
var db = nodes[boneindex].gameObject.AddComponent<DynamicBone>();
if (boneGroup.center != -1)
{
db.m_Root = nodes[boneGroup.center];
}
else
{
db.m_Root = db.gameObject.transform;
}
db.m_Damping = boneGroup.dragForce;
db.m_Stiffness = boneGroup.stiffiness * 0.25f;
db.m_Gravity = boneGroup.gravityDir * boneGroup.gravityPower;
db.m_Radius = boneGroup.hitRadius;
db.m_Colliders = new List<DynamicBoneColliderBase>();
if (boneGroup.colliderGroups != null && boneGroup.colliderGroups.Any())
{
foreach (var colliderIndex in boneGroup.colliderGroups)
{
db.m_Colliders.Add(colliders[colliderIndex]);
}
}
}
}
}
}
}
}
public class ModelLoaderUniRx : MonoBehaviour
{
void LoadVRM(byte[] bytes)
{
// GLB形式のperse
var context = new VRMDbImporterContext();
context.ParseGlb(bytes);
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using UniGLTF;
using UnityEngine;
using VRM;
namespace VRM.DynamicBoneExtension
{
public class VRMDbImporterContext : VRMImporterContext {
#region OnLoad
protected override void OnLoadModel()
{
Root.name = "VRM";
using (MeasureTime("VRM LoadMeta"))
{
LoadMeta();
}
using (MeasureTime("VRM LoadHumanoid"))
{
LoadHumanoid();
}
using (MeasureTime("VRM LoadBlendShapeMaster"))
{
LoadBlendShapeMaster();
}
using (MeasureTime("VRM LoadSecondary"))
{
DynamicBoneLoadUtility.LoadSecondary(Root.transform, Nodes,
GLTF.extensions.VRM.secondaryAnimation);
}
using (MeasureTime("VRM LoadFirstPerson"))
{
LoadFirstPerson();
}
}
void LoadMeta()
{
var meta = ReadMeta();
if (meta.Thumbnail == null)
{
/*
// 作る
var lookAt = Root.GetComponent<VRMLookAtHead>();
var thumbnail = lookAt.CreateThumbnail();
thumbnail.name = "thumbnail";
meta.Thumbnail = thumbnail;
Textures.Add(new TextureItem(thumbnail));
*/
}
var _meta = Root.AddComponent<VRMMeta>();
_meta.Meta = meta;
Meta = meta;
}
void LoadFirstPerson()
{
var firstPerson = Root.AddComponent<VRMFirstPerson>();
var gltfFirstPerson = GLTF.extensions.VRM.firstPerson;
if (gltfFirstPerson.firstPersonBone != -1)
{
firstPerson.FirstPersonBone = Nodes[gltfFirstPerson.firstPersonBone];
firstPerson.FirstPersonOffset = gltfFirstPerson.firstPersonBoneOffset;
}
else
{
// fallback
firstPerson.SetDefault();
}
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 = GLTF.extensions.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.StartsWith(prefix))
{
groupName = groupName.Substring(prefix.Length);
}
asset.name = "BlendShape." + groupName;
if (group != null)
{
asset.BlendShapeName = groupName;
asset.Preset = EnumUtil.TryParseOrDefault<BlendShapePreset>(group.presetName);
asset.IsBinary = group.isBinary;
if (asset.Preset == BlendShapePreset.Unknown)
{
// fallback
asset.Preset = EnumUtil.TryParseOrDefault<BlendShapePreset>(group.name);
}
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 = GetMaterials().FirstOrDefault(y => y.name == x.materialName);
var propertyName = x.propertyName;
if (x.propertyName.EndsWith("_ST_S")
|| x.propertyName.EndsWith("_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 = GLTF.extensions.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;
}
#endregion
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment