/// <summary> /// モデルの正規化を実行する /// </summary> /// <param name="go">対象モデルのルート</param> /// <param name="forceTPose">強制的にT-Pose化するか</param> /// <returns>正規化済みのモデル</returns> public static GameObject Execute(GameObject go, bool forceTPose) { // // T-Poseにする // if (forceTPose) { var hips = go.GetComponent <Animator>().GetBoneTransform(HumanBodyBones.Hips); var hipsPosition = hips.position; var hipsRotation = hips.rotation; try { EnforceTPose(go); } finally { hips.position = hipsPosition; // restore hipsPosition hips.rotation = hipsRotation; } } // // 正規化されたヒエラルキーを作る // var(normalized, bMap) = MeshUtility.BoneNormalizer.Execute(go, (_src, dst, boneMap) => { var src = _src.GetComponent <Animator>(); var srcHumanBones = Enum.GetValues(typeof(HumanBodyBones)) .Cast <HumanBodyBones>() .Where(x => x != HumanBodyBones.LastBone) .Select(x => new { Key = x, Value = src.GetBoneTransform(x) }) .Where(x => x.Value != null) ; var map = srcHumanBones .Where(x => boneMap.ContainsKey(x.Value)) .ToDictionary(x => x.Key, x => boneMap[x.Value]) ; if (dst.GetComponent <Animator>() == null) { var animator = dst.AddComponent <Animator>(); } var vrmHuman = go.GetComponent <VRMHumanoidDescription>(); var avatarDescription = AvatarDescription.Create(); if (vrmHuman != null && vrmHuman.Description != null) { avatarDescription.armStretch = vrmHuman.Description.armStretch; avatarDescription.legStretch = vrmHuman.Description.legStretch; avatarDescription.upperArmTwist = vrmHuman.Description.upperArmTwist; avatarDescription.lowerArmTwist = vrmHuman.Description.lowerArmTwist; avatarDescription.upperLegTwist = vrmHuman.Description.upperLegTwist; avatarDescription.lowerLegTwist = vrmHuman.Description.lowerLegTwist; avatarDescription.feetSpacing = vrmHuman.Description.feetSpacing; avatarDescription.hasTranslationDoF = vrmHuman.Description.hasTranslationDoF; } avatarDescription.SetHumanBones(map); var avatar = avatarDescription.CreateAvatar(dst.transform); return(avatar); }); CopyVRMComponents(go, normalized, bMap); return(normalized); }
public static GameObject Execute(GameObject go, Dictionary <Transform, Transform> boneMap, bool forceTPose) { // // T-Poseにする // if (forceTPose) { var animator = go.GetComponent <Animator>(); if (animator == null) { throw new ArgumentException("Animator with avatar is required"); } var avatar = animator.avatar; if (avatar == null) { throw new ArgumentException("avatar is required"); } if (!avatar.isValid) { throw new ArgumentException("invalid avatar"); } if (!avatar.isHuman) { throw new ArgumentException("avatar is not human"); } HumanPoseTransfer.SetTPose(avatar, go.transform); } // // 回転・スケールの無いヒエラルキーをコピーする // var normalized = new GameObject(go.name + "(normalized)"); normalized.transform.position = go.transform.position; CopyAndBuild(go.transform, normalized.transform, boneMap); // // 新しいヒエラルキーからAvatarを作る // { var src = go.GetComponent <Animator>(); var map = Enum.GetValues(typeof(HumanBodyBones)) .Cast <HumanBodyBones>() .Where(x => x != HumanBodyBones.LastBone) .Select(x => new { Key = x, Value = src.GetBoneTransform(x) }) .Where(x => x.Value != null) .ToDictionary(x => x.Key, x => boneMap[x.Value]) ; var animator = normalized.AddComponent <Animator>(); var vrmHuman = go.GetComponent <VRMHumanoidDescription>(); var avatarDescription = AvatarDescription.Create(); if (vrmHuman != null && vrmHuman.Description != null) { avatarDescription.armStretch = vrmHuman.Description.armStretch; avatarDescription.legStretch = vrmHuman.Description.legStretch; avatarDescription.upperArmTwist = vrmHuman.Description.upperArmTwist; avatarDescription.lowerArmTwist = vrmHuman.Description.lowerArmTwist; avatarDescription.upperLegTwist = vrmHuman.Description.upperLegTwist; avatarDescription.lowerLegTwist = vrmHuman.Description.lowerLegTwist; avatarDescription.feetSpacing = vrmHuman.Description.feetSpacing; avatarDescription.hasTranslationDoF = vrmHuman.Description.hasTranslationDoF; } avatarDescription.SetHumanBones(map); var avatar = avatarDescription.CreateAvatar(normalized.transform); avatar.name = go.name + ".normalized"; animator.avatar = avatar; var humanPoseTransfer = normalized.AddComponent <HumanPoseTransfer>(); humanPoseTransfer.Avatar = avatar; } // // 各メッシュから回転・スケールを取り除いてBinding行列を再計算する // foreach (var src in go.transform.Traverse()) { Transform dst; if (!boneMap.TryGetValue(src, out dst)) { continue; } { // // SkinnedMesh // var srcRenderer = src.GetComponent <SkinnedMeshRenderer>(); if (srcRenderer != null && srcRenderer.enabled && srcRenderer.sharedMesh != null && srcRenderer.sharedMesh.vertexCount > 0) { // clear blendShape var srcMesh = srcRenderer.sharedMesh; for (int i = 0; i < srcMesh.blendShapeCount; ++i) { srcRenderer.SetBlendShapeWeight(i, 0); } var mesh = new Mesh(); mesh.name = srcMesh.name + ".baked"; #if UNITY_2017_3_OR_NEWER mesh.indexFormat = srcMesh.indexFormat; #endif srcRenderer.BakeMesh(mesh); //var m = src.localToWorldMatrix; var m = default(Matrix4x4); m.SetTRS(Vector3.zero, src.rotation, Vector3.one); mesh.vertices = mesh.vertices.Select(x => m.MultiplyPoint(x)).ToArray(); mesh.normals = mesh.normals.Select(x => m.MultiplyVector(x).normalized).ToArray(); mesh.uv = srcMesh.uv; mesh.tangents = srcMesh.tangents; mesh.subMeshCount = srcMesh.subMeshCount; for (int i = 0; i < srcMesh.subMeshCount; ++i) { mesh.SetIndices(srcMesh.GetIndices(i), srcMesh.GetTopology(i), i); } // boneweights var bones = srcRenderer.bones.Select(x => boneMap[x]).ToArray(); if (bones.Length > 0) { mesh.boneWeights = srcMesh.boneWeights; } else { Debug.LogFormat("no weight: {0}", srcMesh.name); bones = new[] { boneMap[srcRenderer.transform] }; var bw = new BoneWeight { boneIndex0 = 0, boneIndex1 = 0, boneIndex2 = 0, boneIndex3 = 0, weight0 = 1.0f, weight1 = 0.0f, weight2 = 0.0f, weight3 = 0.0f, }; mesh.boneWeights = Enumerable.Range(0, srcMesh.vertexCount).Select(x => bw).ToArray(); } var meshVertices = mesh.vertices; var meshNormals = mesh.normals; var meshTangents = mesh.tangents.Select(x => (Vector3)x).ToArray(); var _meshVertices = new Vector3[meshVertices.Length]; var _meshNormals = new Vector3[meshVertices.Length]; var _meshTangents = new Vector3[meshVertices.Length]; var blendShapeMesh = new Mesh(); for (int i = 0; i < srcMesh.blendShapeCount; ++i) { // check blendShape srcRenderer.sharedMesh.GetBlendShapeFrameVertices(i, 0, _meshVertices, _meshNormals, _meshTangents); var hasVertices = !_meshVertices.All(x => x == Vector3.zero); var hasNormals = !_meshNormals.All(x => x == Vector3.zero); var hasTangents = !_meshTangents.All(x => x == Vector3.zero); srcRenderer.SetBlendShapeWeight(i, 100.0f); srcRenderer.BakeMesh(blendShapeMesh); if (blendShapeMesh.vertices.Length != mesh.vertices.Length) { throw new Exception("diffrent vertex count"); } srcRenderer.SetBlendShapeWeight(i, 0); Vector3[] vertices = null; if (hasVertices) { vertices = blendShapeMesh.vertices; // to delta for (int j = 0; j < vertices.Length; ++j) { vertices[j] = m.MultiplyPoint(vertices[j]) - meshVertices[j]; } } else { vertices = new Vector3[mesh.vertexCount]; } Vector3[] normals = null; if (hasNormals) { normals = blendShapeMesh.normals; // to delta for (int j = 0; j < normals.Length; ++j) { normals[j] = m.MultiplyVector(normals[j]) - meshNormals[j]; } } else { normals = new Vector3[mesh.vertexCount]; } Vector3[] tangents = null; if (hasTangents) { tangents = blendShapeMesh.tangents.Select(x => (Vector3)x).ToArray(); // to delta for (int j = 0; j < tangents.Length; ++j) { tangents[j] = m.MultiplyVector(tangents[j]) - meshTangents[j]; } } else { tangents = new Vector3[mesh.vertexCount]; } var name = srcMesh.GetBlendShapeName(i); if (string.IsNullOrEmpty(name)) { name = String.Format("{0}", i); } var weight = srcMesh.GetBlendShapeFrameWeight(i, 0); try { mesh.AddBlendShapeFrame(name, weight, vertices, normals, tangents ); } catch (Exception) { Debug.LogErrorFormat("fail to mesh.AddBlendShapeFrame {0}.{1}", mesh.name, srcMesh.GetBlendShapeName(i) ); throw; } } // recalc bindposes mesh.bindposes = bones.Select(x => x.worldToLocalMatrix * dst.transform.localToWorldMatrix).ToArray(); mesh.RecalculateBounds(); var dstRenderer = dst.gameObject.AddComponent <SkinnedMeshRenderer>(); dstRenderer.sharedMaterials = srcRenderer.sharedMaterials; dstRenderer.sharedMesh = mesh; dstRenderer.bones = bones; if (srcRenderer.rootBone != null) { dstRenderer.rootBone = boneMap[srcRenderer.rootBone]; } } } { // // not SkinnedMesh // var srcFilter = src.GetComponent <MeshFilter>(); if (srcFilter != null && srcFilter.sharedMesh != null && srcFilter.sharedMesh.vertexCount > 0) { var srcRenderer = src.GetComponent <MeshRenderer>(); if (srcRenderer != null && srcRenderer.enabled) { var dstFilter = dst.gameObject.AddComponent <MeshFilter>(); dstFilter.sharedMesh = TransformMesh(srcFilter.sharedMesh, src.localToWorldMatrix); var dstRenderer = dst.gameObject.AddComponent <MeshRenderer>(); dstRenderer.sharedMaterials = srcRenderer.sharedMaterials; } } } } return(normalized); }
public AdvancedAvatarRagdollSample(Microsoft.Xna.Framework.Game game) : base(game) { // This sample uses for a DebugRenderer to draw text and rigid bodies. _debugRenderer = new DebugRenderer(GraphicsService, SpriteFont) { DefaultColor = Color.Black, DefaultTextPosition = new Vector2F(10), }; // Add a custom game object which controls the camera. _cameraObject = new CameraObject(Services); _cameraObject.ResetPose(new Vector3F(0, 1, -3), ConstantsF.Pi, 0); GameObjectService.Objects.Add(_cameraObject); // Add some objects which allow the user to interact with the rigid bodies. _grabObject = new GrabObject(Services); _ballShooterObject = new BallShooterObject(Services) { Speed = 20 }; GameObjectService.Objects.Add(_grabObject); GameObjectService.Objects.Add(_ballShooterObject); // Add some default force effects. Simulation.ForceEffects.Add(new Gravity()); Simulation.ForceEffects.Add(new Damping()); // Create a random avatar. _avatarDescription = AvatarDescription.CreateRandom(); _avatarRenderer = new AvatarRenderer(_avatarDescription); // Use the "Wave" animation preset. var avatarAnimation = new AvatarAnimation(AvatarAnimationPreset.Wave); _expressionAnimation = new AnimationClip <AvatarExpression>(new WrappedAvatarExpressionAnimation(avatarAnimation)) { LoopBehavior = LoopBehavior.Cycle, Duration = TimeSpan.MaxValue, }; _skeletonAnimation = new AnimationClip <SkeletonPose>(new WrappedAvatarSkeletonAnimation(avatarAnimation)) { LoopBehavior = LoopBehavior.Cycle, Duration = TimeSpan.MaxValue, }; // Add a ground plane in the simulation. Simulation.RigidBodies.Add(new RigidBody(new PlaneShape(Vector3F.UnitY, 0)) { MotionType = MotionType.Static }); // Distribute a few dynamic spheres and boxes across the landscape. SphereShape sphereShape = new SphereShape(0.3f); for (int i = 0; i < 10; i++) { Vector3F position = RandomHelper.Random.NextVector3F(-10, 10); position.Y = 1; Simulation.RigidBodies.Add(new RigidBody(sphereShape) { Pose = new Pose(position) }); } BoxShape boxShape = new BoxShape(0.6f, 0.6f, 0.6f); for (int i = 0; i < 10; i++) { Vector3F position = RandomHelper.Random.NextVector3F(-10, 10); position.Y = 1; Simulation.RigidBodies.Add(new RigidBody(boxShape) { Pose = new Pose(position) }); } }
/// <summary> /// Load the avatar for a gamer /// </summary> private void LoadAvatar(Gamer gamer) { UnloadAvatar(); AvatarDescription.BeginGetFromGamer(gamer, LoadAvatarDescription, null); }
public static GameObject Execute(GameObject go, Dictionary <Transform, Transform> boneMap, bool forceTPose) { // // T-Poseにする // if (forceTPose) { var animator = go.GetComponent <Animator>(); if (animator == null) { throw new ArgumentException("Animator with avatar is required"); } var avatar = animator.avatar; if (avatar == null) { throw new ArgumentException("avatar is required"); } if (!avatar.isValid) { throw new ArgumentException("invalid avatar"); } if (!avatar.isHuman) { throw new ArgumentException("avatar is not human"); } HumanPoseTransfer.SetTPose(avatar, go.transform); } // // 回転・スケールの無いヒエラルキーをコピーする // var normalized = new GameObject(go.name + "(normalized)"); normalized.transform.position = go.transform.position; CopyAndBuild(go.transform, normalized.transform, boneMap); // // 新しいヒエラルキーからAvatarを作る // { var src = go.GetComponent <Animator>(); var map = Enum.GetValues(typeof(HumanBodyBones)) .Cast <HumanBodyBones>() .Where(x => x != HumanBodyBones.LastBone) .Select(x => new { Key = x, Value = src.GetBoneTransform(x) }) .Where(x => x.Value != null) .ToDictionary(x => x.Key, x => boneMap[x.Value]) ; var animator = normalized.AddComponent <Animator>(); var vrmHuman = go.GetComponent <VRMHumanoidDescription>(); var avatarDescription = AvatarDescription.Create(); if (vrmHuman != null && vrmHuman.Description != null) { avatarDescription.armStretch = vrmHuman.Description.armStretch; avatarDescription.legStretch = vrmHuman.Description.legStretch; avatarDescription.upperArmTwist = vrmHuman.Description.upperArmTwist; avatarDescription.lowerArmTwist = vrmHuman.Description.lowerArmTwist; avatarDescription.upperLegTwist = vrmHuman.Description.upperLegTwist; avatarDescription.lowerLegTwist = vrmHuman.Description.lowerLegTwist; avatarDescription.feetSpacing = vrmHuman.Description.feetSpacing; avatarDescription.hasTranslationDoF = vrmHuman.Description.hasTranslationDoF; } avatarDescription.SetHumanBones(map); var avatar = avatarDescription.CreateAvatar(normalized.transform); avatar.name = go.name + ".normalized"; animator.avatar = avatar; var humanPoseTransfer = normalized.AddComponent <HumanPoseTransfer>(); humanPoseTransfer.Avatar = avatar; } // // 各メッシュから回転・スケールを取り除いてBinding行列を再計算する // foreach (var src in go.transform.Traverse()) { var dst = boneMap[src]; { // // SkinnedMesh // var srcRenderer = src.GetComponent <SkinnedMeshRenderer>(); if (srcRenderer != null && srcRenderer.enabled) { // clear blendShape var srcMesh = srcRenderer.sharedMesh; for (int i = 0; i < srcMesh.blendShapeCount; ++i) { srcRenderer.SetBlendShapeWeight(i, 0); } var mesh = new Mesh(); mesh.name = srcMesh.name + ".baked"; srcRenderer.BakeMesh(mesh); //var m = src.localToWorldMatrix; var m = default(Matrix4x4); m.SetTRS(Vector3.zero, src.rotation, Vector3.one); mesh.vertices = mesh.vertices.Select(x => m.MultiplyPoint(x)).ToArray(); mesh.normals = mesh.normals.Select(x => m.MultiplyVector(x).normalized).ToArray(); mesh.uv = srcMesh.uv; mesh.tangents = srcMesh.tangents; mesh.subMeshCount = srcMesh.subMeshCount; for (int i = 0; i < srcMesh.subMeshCount; ++i) { mesh.SetIndices(srcMesh.GetIndices(i), srcMesh.GetTopology(i), i); } mesh.boneWeights = srcMesh.boneWeights; var meshVertices = mesh.vertices; var blendShapeMesh = new Mesh(); for (int i = 0; i < srcMesh.blendShapeCount; ++i) { srcRenderer.SetBlendShapeWeight(i, 100.0f); srcRenderer.BakeMesh(blendShapeMesh); srcRenderer.SetBlendShapeWeight(i, 0); if (blendShapeMesh.vertices.Length != mesh.vertices.Length) { throw new Exception("diffrent vertex count"); } var weight = srcMesh.GetBlendShapeFrameWeight(i, 0); var vertices = blendShapeMesh.vertices; var normals = blendShapeMesh.normals; var tangents = blendShapeMesh.tangents.Select(x => (Vector3)x).ToArray(); for (int j = 0; j < vertices.Length; ++j) { vertices[j] = m.MultiplyPoint(vertices[j]) - meshVertices[j]; } for (int j = 0; j < normals.Length; ++j) { normals[j] = m.MultiplyVector(normals[j]).normalized; } for (int j = 0; j < tangents.Length; ++j) { tangents[j] = m.MultiplyVector(tangents[j]).normalized; } var name = srcMesh.GetBlendShapeName(i); if (string.IsNullOrEmpty(name)) { name = String.Format("{0}", i); } try { mesh.AddBlendShapeFrame(name, weight, vertices.Length == meshVertices.Length ? vertices : null, normals.Length == meshVertices.Length ? normals : null, tangents.Length == meshVertices.Length ? tangents : null ); } catch (Exception) { Debug.LogWarningFormat("{0}.{1}", mesh.name, srcMesh.GetBlendShapeName(i)); throw; } } // recalc bindposes var bones = srcRenderer.bones.Select(x => boneMap[x]).ToArray(); mesh.bindposes = bones.Select(x => x.worldToLocalMatrix * dst.transform.localToWorldMatrix).ToArray(); mesh.RecalculateBounds(); var dstRenderer = dst.gameObject.AddComponent <SkinnedMeshRenderer>(); dstRenderer.sharedMaterials = srcRenderer.sharedMaterials; dstRenderer.sharedMesh = mesh; dstRenderer.bones = bones; if (srcRenderer.rootBone != null) { dstRenderer.rootBone = boneMap[srcRenderer.rootBone]; } } } { // // not SkinnedMesh // var srcFilter = src.GetComponent <MeshFilter>(); if (srcFilter != null) { var srcRenderer = src.GetComponent <MeshRenderer>(); if (srcRenderer != null && srcRenderer.enabled) { var dstFilter = dst.gameObject.AddComponent <MeshFilter>(); dstFilter.sharedMesh = srcFilter.sharedMesh; var dstRenderer = dst.gameObject.AddComponent <MeshRenderer>(); dstRenderer.sharedMaterials = srcRenderer.sharedMaterials; } } } } return(normalized); }
public static void Import(ImporterContext context) { // // parse // // context.Source = File.ReadAllText(context.Path, Encoding.UTF8); context.Bvh = Bvh.Parse(context.Source); Debug.LogFormat("parsed {0}", context.Bvh); // // build hierarchy // context.Root = new GameObject(Path.GetFileNameWithoutExtension(context.Path)); BuildHierarchy(context.Root.transform, context.Bvh.Root, 1.0f); var hips = context.Root.transform.GetChild(0); var estimater = new BvhSkeletonEstimator(); var skeleton = estimater.Detect(hips.transform); var description = AvatarDescription.Create(); //var values= ((HumanBodyBones[])Enum.GetValues(typeof(HumanBodyBones))); description.SetHumanBones(skeleton.ToDictionary(hips.Traverse().ToArray())); // // scaling. reposition // float scaling = 1.0f; { //var foot = animator.GetBoneTransform(HumanBodyBones.LeftFoot); var foot = hips.Traverse().Skip(skeleton.GetBoneIndex(HumanBodyBones.LeftFoot)).First(); var hipHeight = hips.position.y - foot.position.y; // hips height to a meter scaling = 1.0f / hipHeight; foreach (var x in context.Root.transform.Traverse()) { x.localPosition *= scaling; } var scaledHeight = hipHeight * scaling; hips.position = new Vector3(0, scaledHeight, 0); // foot to ground } // // avatar // context.Avatar = description.CreateAvatar(context.Root.transform); context.Avatar.name = "Avatar"; context.AvatarDescription = description; var animator = context.Root.AddComponent <Animator>(); animator.avatar = context.Avatar; // // create AnimationClip // context.Animation = BvhAnimation.CreateAnimationClip(context.Bvh, scaling); context.Animation.name = context.Root.name; context.Animation.legacy = true; context.Animation.wrapMode = WrapMode.Loop; var animation = context.Root.AddComponent <Animation>(); animation.AddClip(context.Animation, context.Animation.name); animation.clip = context.Animation; animation.Play(); var humanPoseTransfer = context.Root.AddComponent <HumanPoseTransfer>(); humanPoseTransfer.Avatar = context.Avatar; // create SkinnedMesh for bone visualize var renderer = SkeletonMeshUtility.CreateRenderer(animator); context.Material = new Material(Shader.Find("Standard")); renderer.sharedMaterial = context.Material; context.Mesh = renderer.sharedMesh; context.Mesh.name = "box-man"; context.Root.AddComponent <BoneMapping>(); }