From 8a35345bb0f3b60fe23552e2f04b7f9d55f86f8f Mon Sep 17 00:00:00 2001 From: ousttrue Date: Fri, 28 Dec 2018 21:08:56 +0900 Subject: [PATCH] Squashed 'UniHumanoid/' content from commit 9af6c0d git-subtree-dir: UniHumanoid git-subtree-split: 9af6c0d3995ca8886c1ad87429dd9f28c7b4375d --- LICENSE.md | 21 + LICENSE.md.meta | 8 + PoseModifier.meta | 9 + PoseModifier/HandPose.cs | 175 +++ PoseModifier/HandPose.cs.meta | 12 + PoseModifier/HandRig.cs | 100 ++ PoseModifier/HandRig.cs.meta | 12 + PoseModifier/IPoseModifier.cs | 10 + PoseModifier/IPoseModifier.cs.meta | 12 + README.md | 77 + README.md.meta | 8 + Resources.meta | 10 + Resources/T-Pose.pose.asset | 111 ++ Resources/T-Pose.pose.asset.meta | 10 + Scenes.meta | 8 + Scenes/HumanBuilderTest.cs | 149 ++ Scenes/HumanBuilderTest.cs.meta | 11 + Scenes/PoseTransfer.unity | 691 +++++++++ Scenes/PoseTransfer.unity.meta | 7 + Scenes/RuntimeBvhLoader.cs | 84 + Scenes/RuntimeBvhLoader.cs.meta | 11 + Scenes/RuntimeBvhLoader.unity | 615 ++++++++ Scenes/RuntimeBvhLoader.unity.meta | 7 + Scenes/target.mat | 76 + Scenes/target.mat.meta | 8 + Scripts.meta | 9 + Scripts/AnimationClipUtility.cs | 134 ++ Scripts/AnimationClipUtility.cs.meta | 13 + Scripts/AvatarDescription.cs | 242 +++ Scripts/AvatarDescription.cs.meta | 12 + Scripts/BoneGizmoDrawer.cs | 28 + Scripts/BoneGizmoDrawer.cs.meta | 12 + Scripts/BoneMapping.cs | 103 ++ Scripts/BoneMapping.cs.meta | 13 + Scripts/BvhBone.cs | 80 + Scripts/BvhBone.cs.meta | 11 + Scripts/Editor.meta | 9 + Scripts/Editor/BoneMappingEditor.cs | 419 +++++ Scripts/Editor/BoneMappingEditor.cs.meta | 13 + Scripts/Editor/HumanPoseTransferEditor.cs | 151 ++ .../Editor/HumanPoseTransferEditor.cs.meta | 13 + Scripts/Editor/MuscleInspectorEditor.cs | 352 +++++ Scripts/Editor/MuscleInspectorEditor.cs.meta | 12 + Scripts/Editor/Tests.meta | 9 + Scripts/Editor/Tests/BvhLoaderTests.cs | 1360 +++++++++++++++++ Scripts/Editor/Tests/BvhLoaderTests.cs.meta | 12 + Scripts/Editor/bvhAssetPostprocessor.cs | 36 + Scripts/Editor/bvhAssetPostprocessor.cs.meta | 12 + Scripts/Format.meta | 9 + Scripts/Format/Bvh.cs | 444 ++++++ Scripts/Format/Bvh.cs.meta | 12 + Scripts/HumanPoseClip.cs | 33 + Scripts/HumanPoseClip.cs.meta | 13 + Scripts/HumanPoseTransfer.cs | 143 ++ Scripts/HumanPoseTransfer.cs.meta | 13 + Scripts/IBone.cs | 41 + Scripts/IBone.cs.meta | 11 + Scripts/IO.meta | 9 + Scripts/IO/BvhAnimationClip.cs | 131 ++ Scripts/IO/BvhAnimationClip.cs.meta | 12 + Scripts/IO/BvhImporter.cs | 15 + Scripts/IO/BvhImporter.cs.meta | 12 + Scripts/IO/BvhImporterContext.cs | 247 +++ Scripts/IO/BvhImporterContext.cs.meta | 12 + Scripts/IO/Extensions.meta | 9 + Scripts/IO/Extensions/BvhExtensions.cs | 45 + Scripts/IO/Extensions/BvhExtensions.cs.meta | 12 + Scripts/IO/Extensions/UnityExtensions.cs | 49 + Scripts/IO/Extensions/UnityExtensions.cs.meta | 13 + Scripts/MuscleDebug.cs | 105 ++ Scripts/MuscleDebug.cs.meta | 12 + Scripts/MuscleInspector.cs | 11 + Scripts/MuscleInspector.cs.meta | 12 + Scripts/Skeleton.cs | 92 ++ Scripts/Skeleton.cs.meta | 11 + Scripts/SkeletonEstimator.cs | 244 +++ Scripts/SkeletonEstimator.cs.meta | 12 + Scripts/SkeletonMeshUtility.cs | 232 +++ Scripts/SkeletonMeshUtility.cs.meta | 12 + doc.meta | 10 + doc/BoneMappingGizmo.png | Bin 0 -> 65391 bytes doc/BoneMappingGizmo.png.meta | 77 + doc/BoneMappingInspector.png | Bin 0 -> 29616 bytes doc/BoneMappingInspector.png.meta | 77 + doc/assets.png | Bin 0 -> 7783 bytes doc/assets.png.meta | 68 + doc/bvh_bonemapping.png | Bin 0 -> 37823 bytes doc/bvh_bonemapping.png.meta | 77 + doc/bvh_gameobject.png | Bin 0 -> 28511 bytes doc/bvh_gameobject.png.meta | 77 + doc/bvh_prefab.png | Bin 0 -> 3386 bytes doc/bvh_prefab.png.meta | 77 + doc/humanoid.gif | Bin 0 -> 712407 bytes doc/humanoid.gif.meta | 77 + doc/humanpose_transfer.png | Bin 0 -> 70353 bytes doc/humanpose_transfer.png.meta | 77 + doc/humanpose_transfer_inspector.png | Bin 0 -> 38993 bytes doc/humanpose_transfer_inspector.png.meta | 77 + doc/mesh.png | Bin 0 -> 91763 bytes doc/mesh.png.meta | 68 + 100 files changed, 8087 insertions(+) create mode 100644 LICENSE.md create mode 100644 LICENSE.md.meta create mode 100644 PoseModifier.meta create mode 100644 PoseModifier/HandPose.cs create mode 100644 PoseModifier/HandPose.cs.meta create mode 100644 PoseModifier/HandRig.cs create mode 100644 PoseModifier/HandRig.cs.meta create mode 100644 PoseModifier/IPoseModifier.cs create mode 100644 PoseModifier/IPoseModifier.cs.meta create mode 100644 README.md create mode 100644 README.md.meta create mode 100644 Resources.meta create mode 100644 Resources/T-Pose.pose.asset create mode 100644 Resources/T-Pose.pose.asset.meta create mode 100644 Scenes.meta create mode 100644 Scenes/HumanBuilderTest.cs create mode 100644 Scenes/HumanBuilderTest.cs.meta create mode 100644 Scenes/PoseTransfer.unity create mode 100644 Scenes/PoseTransfer.unity.meta create mode 100644 Scenes/RuntimeBvhLoader.cs create mode 100644 Scenes/RuntimeBvhLoader.cs.meta create mode 100644 Scenes/RuntimeBvhLoader.unity create mode 100644 Scenes/RuntimeBvhLoader.unity.meta create mode 100644 Scenes/target.mat create mode 100644 Scenes/target.mat.meta create mode 100644 Scripts.meta create mode 100644 Scripts/AnimationClipUtility.cs create mode 100644 Scripts/AnimationClipUtility.cs.meta create mode 100644 Scripts/AvatarDescription.cs create mode 100644 Scripts/AvatarDescription.cs.meta create mode 100644 Scripts/BoneGizmoDrawer.cs create mode 100644 Scripts/BoneGizmoDrawer.cs.meta create mode 100644 Scripts/BoneMapping.cs create mode 100644 Scripts/BoneMapping.cs.meta create mode 100644 Scripts/BvhBone.cs create mode 100644 Scripts/BvhBone.cs.meta create mode 100644 Scripts/Editor.meta create mode 100644 Scripts/Editor/BoneMappingEditor.cs create mode 100644 Scripts/Editor/BoneMappingEditor.cs.meta create mode 100644 Scripts/Editor/HumanPoseTransferEditor.cs create mode 100644 Scripts/Editor/HumanPoseTransferEditor.cs.meta create mode 100644 Scripts/Editor/MuscleInspectorEditor.cs create mode 100644 Scripts/Editor/MuscleInspectorEditor.cs.meta create mode 100644 Scripts/Editor/Tests.meta create mode 100644 Scripts/Editor/Tests/BvhLoaderTests.cs create mode 100644 Scripts/Editor/Tests/BvhLoaderTests.cs.meta create mode 100644 Scripts/Editor/bvhAssetPostprocessor.cs create mode 100644 Scripts/Editor/bvhAssetPostprocessor.cs.meta create mode 100644 Scripts/Format.meta create mode 100644 Scripts/Format/Bvh.cs create mode 100644 Scripts/Format/Bvh.cs.meta create mode 100644 Scripts/HumanPoseClip.cs create mode 100644 Scripts/HumanPoseClip.cs.meta create mode 100644 Scripts/HumanPoseTransfer.cs create mode 100644 Scripts/HumanPoseTransfer.cs.meta create mode 100644 Scripts/IBone.cs create mode 100644 Scripts/IBone.cs.meta create mode 100644 Scripts/IO.meta create mode 100644 Scripts/IO/BvhAnimationClip.cs create mode 100644 Scripts/IO/BvhAnimationClip.cs.meta create mode 100644 Scripts/IO/BvhImporter.cs create mode 100644 Scripts/IO/BvhImporter.cs.meta create mode 100644 Scripts/IO/BvhImporterContext.cs create mode 100644 Scripts/IO/BvhImporterContext.cs.meta create mode 100644 Scripts/IO/Extensions.meta create mode 100644 Scripts/IO/Extensions/BvhExtensions.cs create mode 100644 Scripts/IO/Extensions/BvhExtensions.cs.meta create mode 100644 Scripts/IO/Extensions/UnityExtensions.cs create mode 100644 Scripts/IO/Extensions/UnityExtensions.cs.meta create mode 100644 Scripts/MuscleDebug.cs create mode 100644 Scripts/MuscleDebug.cs.meta create mode 100644 Scripts/MuscleInspector.cs create mode 100644 Scripts/MuscleInspector.cs.meta create mode 100644 Scripts/Skeleton.cs create mode 100644 Scripts/Skeleton.cs.meta create mode 100644 Scripts/SkeletonEstimator.cs create mode 100644 Scripts/SkeletonEstimator.cs.meta create mode 100644 Scripts/SkeletonMeshUtility.cs create mode 100644 Scripts/SkeletonMeshUtility.cs.meta create mode 100644 doc.meta create mode 100644 doc/BoneMappingGizmo.png create mode 100644 doc/BoneMappingGizmo.png.meta create mode 100644 doc/BoneMappingInspector.png create mode 100644 doc/BoneMappingInspector.png.meta create mode 100644 doc/assets.png create mode 100644 doc/assets.png.meta create mode 100644 doc/bvh_bonemapping.png create mode 100644 doc/bvh_bonemapping.png.meta create mode 100644 doc/bvh_gameobject.png create mode 100644 doc/bvh_gameobject.png.meta create mode 100644 doc/bvh_prefab.png create mode 100644 doc/bvh_prefab.png.meta create mode 100644 doc/humanoid.gif create mode 100644 doc/humanoid.gif.meta create mode 100644 doc/humanpose_transfer.png create mode 100644 doc/humanpose_transfer.png.meta create mode 100644 doc/humanpose_transfer_inspector.png create mode 100644 doc/humanpose_transfer_inspector.png.meta create mode 100644 doc/mesh.png create mode 100644 doc/mesh.png.meta diff --git a/LICENSE.md b/LICENSE.md new file mode 100644 index 000000000..3299d454f --- /dev/null +++ b/LICENSE.md @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2018 ousttrue + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/LICENSE.md.meta b/LICENSE.md.meta new file mode 100644 index 000000000..24df7cd34 --- /dev/null +++ b/LICENSE.md.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 3ea85fd858590174f98cc2cd4c3e083f +timeCreated: 1522228320 +licenseType: Free +DefaultImporter: + userData: + assetBundleName: + assetBundleVariant: diff --git a/PoseModifier.meta b/PoseModifier.meta new file mode 100644 index 000000000..2ecc6b90c --- /dev/null +++ b/PoseModifier.meta @@ -0,0 +1,9 @@ +fileFormatVersion: 2 +guid: a90ca020c7788804ea91e172fa5efd1a +folderAsset: yes +timeCreated: 1524224672 +licenseType: Pro +DefaultImporter: + userData: + assetBundleName: + assetBundleVariant: diff --git a/PoseModifier/HandPose.cs b/PoseModifier/HandPose.cs new file mode 100644 index 000000000..8036ec9a3 --- /dev/null +++ b/PoseModifier/HandPose.cs @@ -0,0 +1,175 @@ +using System; +using UnityEngine; + + +namespace UniHumanoid +{ + public class HandPoseModifier : IPoseModifier + { + public class HandPose + { + public float ThumbStrech; + public float ThumbSpread; + + public float IndexStrech; + public float IndexSpread; + + public float MiddleStrech; + public float MiddleSpread; + + public float RingStrech; + public float RingSpread; + + public float LittleStrech; + public float LittleSpread; + } + public HandPose LeftHandPose + { + get; + set; + } + public HandPose RightHandPose + { + get; + set; + } + + int LeftThumb1Stretched; + int LeftThumb2Stretched; + int LeftThumb3Stretched; + int LeftIndex1Stretched; + int LeftIndex2Stretched; + int LeftIndex3Stretched; + int LeftMiddle1Stretched; + int LeftMiddle2Stretched; + int LeftMiddle3Stretched; + int LeftRing1Stretched; + int LeftRing2Stretched; + int LeftRing3Stretched; + int LeftLittle1Stretched; + int LeftLittle2Stretched; + int LeftLittle3Stretched; + int LeftThumbSpread; + int LeftIndexSpread; + int LeftMiddleSpread; + int LeftRingSpread; + int LeftLittleSpread; + + int RightThumb1Stretched; + int RightThumb2Stretched; + int RightThumb3Stretched; + int RightIndex1Stretched; + int RightIndex2Stretched; + int RightIndex3Stretched; + int RightMiddle1Stretched; + int RightMiddle2Stretched; + int RightMiddle3Stretched; + int RightRing1Stretched; + int RightRing2Stretched; + int RightRing3Stretched; + int RightLittle1Stretched; + int RightLittle2Stretched; + int RightLittle3Stretched; + int RightThumbSpread; + int RightIndexSpread; + int RightMiddleSpread; + int RightRingSpread; + int RightLittleSpread; + + public HandPoseModifier() + { + LeftThumb1Stretched = Array.IndexOf(HumanTrait.MuscleName, "Left Thumb 1 Stretched"); + LeftThumb2Stretched = Array.IndexOf(HumanTrait.MuscleName, "Left Thumb 2 Stretched"); + LeftThumb3Stretched = Array.IndexOf(HumanTrait.MuscleName, "Left Thumb 3 Stretched"); + LeftIndex1Stretched = Array.IndexOf(HumanTrait.MuscleName, "Left Index 1 Stretched"); + LeftIndex2Stretched = Array.IndexOf(HumanTrait.MuscleName, "Left Index 2 Stretched"); + LeftIndex3Stretched = Array.IndexOf(HumanTrait.MuscleName, "Left Index 3 Stretched"); + LeftMiddle1Stretched = Array.IndexOf(HumanTrait.MuscleName, "Left Middle 1 Stretched"); + LeftMiddle2Stretched = Array.IndexOf(HumanTrait.MuscleName, "Left Middle 2 Stretched"); + LeftMiddle3Stretched = Array.IndexOf(HumanTrait.MuscleName, "Left Middle 3 Stretched"); + LeftRing1Stretched = Array.IndexOf(HumanTrait.MuscleName, "Left Ring 1 Stretched"); + LeftRing2Stretched = Array.IndexOf(HumanTrait.MuscleName, "Left Ring 2 Stretched"); + LeftRing3Stretched = Array.IndexOf(HumanTrait.MuscleName, "Left Ring 3 Stretched"); + LeftLittle1Stretched = Array.IndexOf(HumanTrait.MuscleName, "Left Little 1 Stretched"); + LeftLittle2Stretched = Array.IndexOf(HumanTrait.MuscleName, "Left Little 2 Stretched"); + LeftLittle3Stretched = Array.IndexOf(HumanTrait.MuscleName, "Left Little 3 Stretched"); + LeftThumbSpread = Array.IndexOf(HumanTrait.MuscleName, "Left Thumb Spread"); + LeftIndexSpread = Array.IndexOf(HumanTrait.MuscleName, "Left Index Spread"); + LeftMiddleSpread = Array.IndexOf(HumanTrait.MuscleName, "Left Middle Spread"); + LeftRingSpread = Array.IndexOf(HumanTrait.MuscleName, "Left Ring Spread"); + LeftLittleSpread = Array.IndexOf(HumanTrait.MuscleName, "Left Little Spread"); + + RightThumb1Stretched = Array.IndexOf(HumanTrait.MuscleName, "Right Thumb 1 Stretched"); + RightThumb2Stretched = Array.IndexOf(HumanTrait.MuscleName, "Right Thumb 2 Stretched"); + RightThumb3Stretched = Array.IndexOf(HumanTrait.MuscleName, "Right Thumb 3 Stretched"); + RightIndex1Stretched = Array.IndexOf(HumanTrait.MuscleName, "Right Index 1 Stretched"); + RightIndex2Stretched = Array.IndexOf(HumanTrait.MuscleName, "Right Index 2 Stretched"); + RightIndex3Stretched = Array.IndexOf(HumanTrait.MuscleName, "Right Index 3 Stretched"); + RightMiddle1Stretched = Array.IndexOf(HumanTrait.MuscleName, "Right Middle 1 Stretched"); + RightMiddle2Stretched = Array.IndexOf(HumanTrait.MuscleName, "Right Middle 2 Stretched"); + RightMiddle3Stretched = Array.IndexOf(HumanTrait.MuscleName, "Right Middle 3 Stretched"); + RightRing1Stretched = Array.IndexOf(HumanTrait.MuscleName, "Right Ring 1 Stretched"); + RightRing2Stretched = Array.IndexOf(HumanTrait.MuscleName, "Right Ring 2 Stretched"); + RightRing3Stretched = Array.IndexOf(HumanTrait.MuscleName, "Right Ring 3 Stretched"); + RightLittle1Stretched = Array.IndexOf(HumanTrait.MuscleName, "Right Little 1 Stretched"); + RightLittle2Stretched = Array.IndexOf(HumanTrait.MuscleName, "Right Little 2 Stretched"); + RightLittle3Stretched = Array.IndexOf(HumanTrait.MuscleName, "Right Little 3 Stretched"); + RightThumbSpread = Array.IndexOf(HumanTrait.MuscleName, "Right Thumb Spread"); + RightIndexSpread = Array.IndexOf(HumanTrait.MuscleName, "Right Index Spread"); + RightMiddleSpread = Array.IndexOf(HumanTrait.MuscleName, "Right Middle Spread"); + RightRingSpread = Array.IndexOf(HumanTrait.MuscleName, "Right Ring Spread"); + RightLittleSpread = Array.IndexOf(HumanTrait.MuscleName, "Right Little Spread"); + } + + public void Modify(ref HumanPose pose) + { + if (LeftHandPose != null) + { + pose.muscles[this.LeftThumb1Stretched] = LeftHandPose.ThumbStrech; + pose.muscles[this.LeftThumb2Stretched] = LeftHandPose.ThumbStrech; + pose.muscles[this.LeftThumb3Stretched] = LeftHandPose.ThumbStrech; + pose.muscles[this.LeftIndex1Stretched] = LeftHandPose.IndexStrech; + pose.muscles[this.LeftIndex2Stretched] = LeftHandPose.IndexStrech; + pose.muscles[this.LeftIndex3Stretched] = LeftHandPose.IndexStrech; + pose.muscles[this.LeftMiddle1Stretched] = LeftHandPose.MiddleStrech; + pose.muscles[this.LeftMiddle2Stretched] = LeftHandPose.MiddleStrech; + pose.muscles[this.LeftMiddle3Stretched] = LeftHandPose.MiddleStrech; + pose.muscles[this.LeftRing1Stretched] = LeftHandPose.RingStrech; + pose.muscles[this.LeftRing2Stretched] = LeftHandPose.RingStrech; + pose.muscles[this.LeftRing3Stretched] = LeftHandPose.RingStrech; + pose.muscles[this.LeftLittle1Stretched] = LeftHandPose.LittleStrech; + pose.muscles[this.LeftLittle2Stretched] = LeftHandPose.LittleStrech; + pose.muscles[this.LeftLittle3Stretched] = LeftHandPose.LittleStrech; + pose.muscles[this.LeftThumbSpread] = LeftHandPose.ThumbSpread; + pose.muscles[this.LeftIndexSpread] = LeftHandPose.IndexSpread; + pose.muscles[this.LeftMiddleSpread] = LeftHandPose.MiddleSpread; + pose.muscles[this.LeftRingSpread] = LeftHandPose.RingSpread; + pose.muscles[this.LeftLittleSpread] = LeftHandPose.LittleSpread; + } + + if (RightHandPose != null) + { + pose.muscles[this.RightThumb1Stretched] = RightHandPose.ThumbStrech; + pose.muscles[this.RightThumb2Stretched] = RightHandPose.ThumbStrech; + pose.muscles[this.RightThumb3Stretched] = RightHandPose.ThumbStrech; + pose.muscles[this.RightIndex1Stretched] = RightHandPose.IndexStrech; + pose.muscles[this.RightIndex2Stretched] = RightHandPose.IndexStrech; + pose.muscles[this.RightIndex3Stretched] = RightHandPose.IndexStrech; + pose.muscles[this.RightMiddle1Stretched] = RightHandPose.MiddleStrech; + pose.muscles[this.RightMiddle2Stretched] = RightHandPose.MiddleStrech; + pose.muscles[this.RightMiddle3Stretched] = RightHandPose.MiddleStrech; + pose.muscles[this.RightRing1Stretched] = RightHandPose.RingStrech; + pose.muscles[this.RightRing2Stretched] = RightHandPose.RingStrech; + pose.muscles[this.RightRing3Stretched] = RightHandPose.RingStrech; + pose.muscles[this.RightLittle1Stretched] = RightHandPose.LittleStrech; + pose.muscles[this.RightLittle2Stretched] = RightHandPose.LittleStrech; + pose.muscles[this.RightLittle3Stretched] = RightHandPose.LittleStrech; + pose.muscles[this.RightThumbSpread] = RightHandPose.ThumbSpread; + pose.muscles[this.RightIndexSpread] = RightHandPose.IndexSpread; + pose.muscles[this.RightMiddleSpread] = RightHandPose.MiddleSpread; + pose.muscles[this.RightRingSpread] = RightHandPose.RingSpread; + pose.muscles[this.RightLittleSpread] = RightHandPose.LittleSpread; + } + } + } +} diff --git a/PoseModifier/HandPose.cs.meta b/PoseModifier/HandPose.cs.meta new file mode 100644 index 000000000..a6b02c997 --- /dev/null +++ b/PoseModifier/HandPose.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: 14d18bc5a296a894eb7154d3f5f0e18b +timeCreated: 1523606797 +licenseType: Free +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/PoseModifier/HandRig.cs b/PoseModifier/HandRig.cs new file mode 100644 index 000000000..13fa3d245 --- /dev/null +++ b/PoseModifier/HandRig.cs @@ -0,0 +1,100 @@ +using UnityEngine; + + +namespace UniHumanoid +{ + public class HandRig : MonoBehaviour + { + [SerializeField] + Animator m_animator; + public Animator Animator + { + get { return m_animator; } + } + + [SerializeField, Range(-1, 1)] + public float LeftStrech; + + [SerializeField, Range(-1, 1)] + public float LeftSpread; + + [SerializeField, Range(-1, 1)] + public float RightStrech; + + [SerializeField, Range(-1, 1)] + public float RightSpread; + + private void Reset() + { + m_animator = GetComponent(); + } + + HumanPoseHandler m_handler; + public static HumanPoseHandler GetHandler(Animator animator) + { + if (animator == null) + { + return null; + } + if (animator.avatar == null) + { + return null; + } + if (!animator.avatar.isValid + || !animator.avatar.isHuman) + { + return null; + } + return new HumanPoseHandler(animator.avatar, animator.transform); + } + + HandPoseModifier m_updater; + + private void Awake() + { + m_handler = GetHandler(m_animator); + if (m_handler == null) + { + enabled = false; + return; + } + m_updater = new HandPoseModifier(); + } + + HandPoseModifier.HandPose m_leftHand = new HandPoseModifier.HandPose(); + HandPoseModifier.HandPose m_rightHand = new HandPoseModifier.HandPose(); + HumanPose m_pose; + + private void Update() + { + m_leftHand.ThumbStrech = LeftStrech; + m_leftHand.ThumbSpread = LeftSpread; + m_leftHand.IndexStrech = LeftStrech; + m_leftHand.IndexSpread = LeftSpread; + m_leftHand.MiddleStrech = LeftStrech; + m_leftHand.MiddleSpread = LeftSpread; + m_leftHand.RingStrech = LeftStrech; + m_leftHand.RingSpread = LeftSpread; + m_leftHand.LittleStrech = LeftStrech; + m_leftHand.LittleSpread = LeftSpread; + + m_rightHand.ThumbStrech = RightStrech; + m_rightHand.ThumbSpread = RightSpread; + m_rightHand.IndexStrech = RightStrech; + m_rightHand.IndexSpread = RightSpread; + m_rightHand.MiddleStrech = RightStrech; + m_rightHand.MiddleSpread = RightSpread; + m_rightHand.RingStrech = RightStrech; + m_rightHand.RingSpread = RightSpread; + m_rightHand.LittleStrech = RightStrech; + m_rightHand.LittleSpread = RightSpread; + + m_updater.LeftHandPose = m_leftHand; + m_updater.RightHandPose = m_rightHand; + + m_handler.GetHumanPose(ref m_pose); + m_updater.Modify(ref m_pose); + m_handler.SetHumanPose(ref m_pose); + } + } +} diff --git a/PoseModifier/HandRig.cs.meta b/PoseModifier/HandRig.cs.meta new file mode 100644 index 000000000..2a5cb41f8 --- /dev/null +++ b/PoseModifier/HandRig.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: 45d55d80a64783c41ace70d5d81db454 +timeCreated: 1523602613 +licenseType: Free +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/PoseModifier/IPoseModifier.cs b/PoseModifier/IPoseModifier.cs new file mode 100644 index 000000000..e52c5468b --- /dev/null +++ b/PoseModifier/IPoseModifier.cs @@ -0,0 +1,10 @@ +using UnityEngine; + + +namespace UniHumanoid +{ + public interface IPoseModifier + { + void Modify(ref HumanPose pose); + } +} diff --git a/PoseModifier/IPoseModifier.cs.meta b/PoseModifier/IPoseModifier.cs.meta new file mode 100644 index 000000000..4e4b1d9d9 --- /dev/null +++ b/PoseModifier/IPoseModifier.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: 973f36da309a48d4ea3c5526a51e957f +timeCreated: 1523606728 +licenseType: Free +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/README.md b/README.md new file mode 100644 index 000000000..bf44dae09 --- /dev/null +++ b/README.md @@ -0,0 +1,77 @@ +# UniHumanoid + +Unity humanoid utility with bvh importer. + +# License + +* [MIT](./LICENSE.md) + +# BVH runtime loader + +```cs +var context = new BvhImporterContext(); +context.Parse(path); +context.Load(); // create Skeleton hierarchy and mesh for visualize +GameObject root = context.Root; +``` + +## RuntimeLoader +* Scenes/RuntimeBvhLoader.unity + +## RuntimeLoader and PoseTransfer +Load BVH and transfer pose to any model with humanoid avatar. + +* Scenes/PoseTransfer.unity + +![humanpose transfer target](doc/humanpose_transfer_inspector.png) + +![humanpose transfer](doc/humanpose_transfer.png) + +# Load bvh and create prefab with AnimationClip + +Drop bvh file to Assets folder. +Then, AssetPostprocessor import bvh file. + +* create a hierarchy prefab +* create a humanoid Avatar +* create a legacy mode AnimationClip +* create a skinned mesh for preview + +![bvh prefab](doc/assets.png) + +Instanciate prefab to scene. + +![bvh gameobject](doc/mesh.png) + +That object can play. + +# BoneMapping + +This script help create human avatar from exist GameObject hierarchy. +First, attach this script to root GameObject that has Animator. + +Next, setup below. + +* model position is origin +* model look at +z orientation +* model root node rotation is Quatenion.identity +* Set hips bone. + +press Guess bone mapping. +If fail to guess bone mapping, you can set bones manually. + +Optional, press Ensure T-Pose. +Create avatar. + +![bvh bone mapping](doc/bvh_bonemapping.png) + +These humanoids imported by [UniGLTF](https://github.com/ousttrue/UniGLTF) and created human avatar by BoneMapping. + +![humanoid](doc/humanoid.gif) + +# Download BVH files + +* https://sites.google.com/a/cgspeed.com/cgspeed/motion-capture +* http://mocapdata.com/ +* http://www.thetrailerspark.com/download/Mocap/Packed/EYES-JAPAN/BVH/ + diff --git a/README.md.meta b/README.md.meta new file mode 100644 index 000000000..832294e89 --- /dev/null +++ b/README.md.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: be513cfaa8530114d85f1e8e0f29708a +timeCreated: 1517370218 +licenseType: Free +DefaultImporter: + userData: + assetBundleName: + assetBundleVariant: diff --git a/Resources.meta b/Resources.meta new file mode 100644 index 000000000..2285cdc60 --- /dev/null +++ b/Resources.meta @@ -0,0 +1,10 @@ +fileFormatVersion: 2 +guid: c61106d290c827b49b7a6e3f6497bd3f +folderAsset: yes +timeCreated: 1519379142 +licenseType: Free +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Resources/T-Pose.pose.asset b/Resources/T-Pose.pose.asset new file mode 100644 index 000000000..d6a055f25 --- /dev/null +++ b/Resources/T-Pose.pose.asset @@ -0,0 +1,111 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!114 &11400000 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_PrefabParentObject: {fileID: 0} + m_PrefabInternal: {fileID: 0} + m_GameObject: {fileID: 0} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 4fdf4ef744a103a4ca01657c00d7fec2, type: 3} + m_Name: T-Pose.pose + m_EditorClassIdentifier: + bodyPosition: {x: 0.0024022944, y: 1.000028, z: 0.0019842784} + bodyRotation: {x: -0, y: 0.000000014901161, z: 0.000000014901161, w: 1} + muscles: + - -0.000000010672161 + - -6.3611094e-16 + - 6.3611094e-16 + - -0.00000055495286 + - 0.00000026851825 + - -0.0000000012914859 + - 0 + - 0 + - 0 + - 0.00000029369042 + - -0.0000005362765 + - 0.00000026918045 + - 0.00000052293626 + - 0.00000012148932 + - 0.0000003683441 + - 0 + - 0 + - 0 + - 0 + - 0 + - 0 + - 0.5957687 + - -0.018923696 + - 0.2105892 + - 1.0025908 + - -0.13775763 + - -0.002854546 + - -0.020644147 + - -0.0000060816024 + - 0.5957681 + - -0.01892539 + - 0.2106165 + - 1.0025909 + - -0.13777769 + - -0.0028554748 + - -0.020645387 + - -2.0029848e-12 + - 0.0000003415094 + - -0.00000011383648 + - 0.39829093 + - 0.3004907 + - -0.030618805 + - 0.999798 + - 0.03679788 + - -0.0025310651 + - 0.00030608202 + - 6.7851816e-15 + - -0.00000045534588 + - 0.39829168 + - 0.3004914 + - -0.030611247 + - 0.9997984 + - 0.03679079 + - -0.0025303008 + - 0.00030552692 + - -0.68517876 + - 0.4567073 + - 0.64590156 + - 0.6459016 + - 0.66896635 + - -0.40027583 + - 0.8113421 + - 0.81134295 + - 0.66770303 + - -0.62352574 + - 0.8111324 + - 0.811132 + - 0.6683899 + - -0.5698266 + - 0.8116428 + - 0.8116354 + - 0.6692385 + - -0.44004643 + - 0.8082721 + - 0.8082728 + - -0.68401676 + - 0.45769995 + - 0.6457741 + - 0.64577323 + - 0.6689646 + - -0.40025005 + - 0.81134063 + - 0.81134117 + - 0.6677078 + - -0.62352294 + - 0.81113243 + - 0.8111291 + - 0.6683884 + - -0.5698764 + - 0.81164306 + - 0.8116342 + - 0.66924673 + - -0.44011596 + - 0.808278 + - 0.8082771 diff --git a/Resources/T-Pose.pose.asset.meta b/Resources/T-Pose.pose.asset.meta new file mode 100644 index 000000000..bbcce4e8c --- /dev/null +++ b/Resources/T-Pose.pose.asset.meta @@ -0,0 +1,10 @@ +fileFormatVersion: 2 +guid: 879e332f84a378c4da3b87af13da3e85 +timeCreated: 1519376738 +licenseType: Free +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 11400000 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Scenes.meta b/Scenes.meta new file mode 100644 index 000000000..dbc08374f --- /dev/null +++ b/Scenes.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 9edc7cfd64210934e817c98db16bdaea +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Scenes/HumanBuilderTest.cs b/Scenes/HumanBuilderTest.cs new file mode 100644 index 000000000..6d165afb9 --- /dev/null +++ b/Scenes/HumanBuilderTest.cs @@ -0,0 +1,149 @@ +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using UnityEngine; + + +namespace UniHumanoid +{ + [RequireComponent(typeof(Animator))] + public class HumanBuilderTest : MonoBehaviour + { + [SerializeField] + Material m_material; + + class SkeletonBuilder + { + Dictionary m_skeleton = new Dictionary(); + public IDictionary Skeleton + { + get { return m_skeleton; } + } + + Dictionary m_boneTail = new Dictionary(); + Transform m_root; + + public SkeletonBuilder(Transform root) + { + m_root = root; + } + + void Add(HumanBodyBones key, Transform parent, Vector3 headPosition, Vector3 tailPosition) + { + var bone = new GameObject(key.ToString()).transform; + bone.SetParent(parent, false); + bone.localPosition = headPosition; + m_skeleton[key] = bone; + m_boneTail[key] = tailPosition; + } + + void Add(HumanBodyBones key, HumanBodyBones parentKey, Vector3 tailPosition) + { + Add(key, m_skeleton[parentKey], m_boneTail[parentKey], tailPosition); + } + + #region Spine + public void AddHips(float height, float len) + { + Add(HumanBodyBones.Hips, m_root, new Vector3(0, height, 0), new Vector3(0, len, 0)); + } + + public void AddSpine(float len) + { + Add(HumanBodyBones.Spine, HumanBodyBones.Hips, new Vector3(0, len, 0)); + } + + public void AddChest(float len) + { + Add(HumanBodyBones.Chest, HumanBodyBones.Spine, new Vector3(0, len, 0)); + } + + public void AddNeck(float len) + { + Add(HumanBodyBones.Neck, HumanBodyBones.Chest, new Vector3(0, len, 0)); + } + + public void AddHead(float len) + { + Add(HumanBodyBones.Head, HumanBodyBones.Neck, new Vector3(0, len, 0)); + } + #endregion + + public void AddArm(float shoulder, float upper, float lower, float hand) + { + Add(HumanBodyBones.LeftShoulder, HumanBodyBones.Chest, new Vector3(-shoulder, 0, 0)); + Add(HumanBodyBones.LeftUpperArm, HumanBodyBones.LeftShoulder, new Vector3(-upper, 0, 0)); + Add(HumanBodyBones.LeftLowerArm, HumanBodyBones.LeftUpperArm, new Vector3(-lower, 0, 0)); + Add(HumanBodyBones.LeftHand, HumanBodyBones.LeftLowerArm, new Vector3(-hand, 0, 0)); + + Add(HumanBodyBones.RightShoulder, HumanBodyBones.Chest, new Vector3(shoulder, 0, 0)); + Add(HumanBodyBones.RightUpperArm, HumanBodyBones.RightShoulder, new Vector3(upper, 0, 0)); + Add(HumanBodyBones.RightLowerArm, HumanBodyBones.RightUpperArm, new Vector3(lower, 0, 0)); + Add(HumanBodyBones.RightHand, HumanBodyBones.RightLowerArm, new Vector3(hand, 0, 0)); + } + + public void AddLeg(float distance, float upper, float lower, float foot, float toe) + { + Add(HumanBodyBones.LeftUpperLeg, m_skeleton[HumanBodyBones.Hips], new Vector3(-distance, 0, 0), new Vector3(0, -upper, 0)); + Add(HumanBodyBones.LeftLowerLeg, HumanBodyBones.LeftUpperLeg, new Vector3(0, -lower, 0)); + Add(HumanBodyBones.LeftFoot, HumanBodyBones.LeftLowerLeg, new Vector3(0, -foot, foot)); + Add(HumanBodyBones.LeftToes, HumanBodyBones.LeftFoot, new Vector3(0, 0, toe)); + + Add(HumanBodyBones.RightUpperLeg, m_skeleton[HumanBodyBones.Hips], new Vector3(distance, 0, 0), new Vector3(0, -upper, 0)); + Add(HumanBodyBones.RightLowerLeg, HumanBodyBones.RightUpperLeg, new Vector3(0, -lower, 0)); + Add(HumanBodyBones.RightFoot, HumanBodyBones.RightLowerLeg, new Vector3(0, -foot, foot)); + Add(HumanBodyBones.RightToes, HumanBodyBones.RightFoot, new Vector3(0, 0, toe)); + } + } + + void OnEnable() + { + BuildSkeleton(transform); + } + + private void BuildSkeleton(Transform root) + { + var position = root.position; + root.position = Vector3.zero; + + try + { + // hips -> spine -> chest + var builder = new SkeletonBuilder(root); + builder.AddHips(0.8f, 0.2f); + builder.AddSpine(0.1f); + builder.AddChest(0.2f); + builder.AddNeck(0.1f); + builder.AddHead(0.2f); + builder.AddArm(0.1f, 0.3f, 0.3f, 0.1f); + builder.AddLeg(0.1f, 0.3f, 0.4f, 0.1f, 0.1f); + + var description = AvatarDescription.Create(builder.Skeleton); + var animator = GetComponent(); + animator.avatar = description.CreateAvatar(root); + + // create SkinnedMesh for bone visualize + var renderer = SkeletonMeshUtility.CreateRenderer(animator); + + if (m_material == null) + { + m_material = new Material(Shader.Find("Standard")); + } + renderer.sharedMaterial = m_material; + //root.gameObject.AddComponent(); + + var transfer = GetComponent(); + if (transfer != null) + { + transfer.Avatar = animator.avatar; + transfer.Setup(); + } + } + finally + { + // restore position + root.position = position; + } + } + } +} diff --git a/Scenes/HumanBuilderTest.cs.meta b/Scenes/HumanBuilderTest.cs.meta new file mode 100644 index 000000000..420a63cd2 --- /dev/null +++ b/Scenes/HumanBuilderTest.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: dfdcf658d2da05649974b59b849de74c +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Scenes/PoseTransfer.unity b/Scenes/PoseTransfer.unity new file mode 100644 index 000000000..f9507a2b6 --- /dev/null +++ b/Scenes/PoseTransfer.unity @@ -0,0 +1,691 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!29 &1 +OcclusionCullingSettings: + m_ObjectHideFlags: 0 + serializedVersion: 2 + m_OcclusionBakeSettings: + smallestOccluder: 5 + smallestHole: 0.25 + backfaceThreshold: 100 + m_SceneGUID: 00000000000000000000000000000000 + m_OcclusionCullingData: {fileID: 0} +--- !u!104 &2 +RenderSettings: + m_ObjectHideFlags: 0 + serializedVersion: 9 + m_Fog: 0 + m_FogColor: {r: 0.5, g: 0.5, b: 0.5, a: 1} + m_FogMode: 3 + m_FogDensity: 0.01 + m_LinearFogStart: 0 + m_LinearFogEnd: 300 + m_AmbientSkyColor: {r: 0.212, g: 0.227, b: 0.259, a: 1} + m_AmbientEquatorColor: {r: 0.114, g: 0.125, b: 0.133, a: 1} + m_AmbientGroundColor: {r: 0.047, g: 0.043, b: 0.035, a: 1} + m_AmbientIntensity: 1 + m_AmbientMode: 0 + m_SubtractiveShadowColor: {r: 0.42, g: 0.478, b: 0.627, a: 1} + m_SkyboxMaterial: {fileID: 10304, guid: 0000000000000000f000000000000000, type: 0} + m_HaloStrength: 0.5 + m_FlareStrength: 1 + m_FlareFadeSpeed: 3 + m_HaloTexture: {fileID: 0} + m_SpotCookie: {fileID: 10001, guid: 0000000000000000e000000000000000, type: 0} + m_DefaultReflectionMode: 0 + m_DefaultReflectionResolution: 128 + m_ReflectionBounces: 1 + m_ReflectionIntensity: 1 + m_CustomReflection: {fileID: 0} + m_Sun: {fileID: 0} + m_IndirectSpecularColor: {r: 0.44657898, g: 0.4964133, b: 0.5748178, a: 1} + m_UseRadianceAmbientProbe: 0 +--- !u!157 &3 +LightmapSettings: + m_ObjectHideFlags: 0 + serializedVersion: 11 + m_GIWorkflowMode: 0 + m_GISettings: + serializedVersion: 2 + m_BounceScale: 1 + m_IndirectOutputScale: 1 + m_AlbedoBoost: 1 + m_TemporalCoherenceThreshold: 1 + m_EnvironmentLightingMode: 0 + m_EnableBakedLightmaps: 1 + m_EnableRealtimeLightmaps: 1 + m_LightmapEditorSettings: + serializedVersion: 10 + m_Resolution: 2 + m_BakeResolution: 40 + m_AtlasSize: 1024 + m_AO: 0 + m_AOMaxDistance: 1 + m_CompAOExponent: 1 + m_CompAOExponentDirect: 0 + m_Padding: 2 + m_LightmapParameters: {fileID: 0} + m_LightmapsBakeMode: 1 + m_TextureCompression: 1 + m_FinalGather: 0 + m_FinalGatherFiltering: 1 + m_FinalGatherRayCount: 256 + m_ReflectionCompression: 2 + m_MixedBakeMode: 2 + m_BakeBackend: 1 + m_PVRSampling: 1 + m_PVRDirectSampleCount: 32 + m_PVRSampleCount: 500 + m_PVRBounces: 2 + m_PVRFilterTypeDirect: 0 + m_PVRFilterTypeIndirect: 0 + m_PVRFilterTypeAO: 0 + m_PVRFilteringMode: 1 + m_PVRCulling: 1 + m_PVRFilteringGaussRadiusDirect: 1 + m_PVRFilteringGaussRadiusIndirect: 5 + m_PVRFilteringGaussRadiusAO: 2 + m_PVRFilteringAtrousPositionSigmaDirect: 0.5 + m_PVRFilteringAtrousPositionSigmaIndirect: 2 + m_PVRFilteringAtrousPositionSigmaAO: 1 + m_ShowResolutionOverlay: 1 + m_LightingDataAsset: {fileID: 0} + m_UseShadowmask: 1 +--- !u!196 &4 +NavMeshSettings: + serializedVersion: 2 + m_ObjectHideFlags: 0 + m_BuildSettings: + serializedVersion: 2 + agentTypeID: 0 + agentRadius: 0.5 + agentHeight: 2 + agentSlope: 45 + agentClimb: 0.4 + ledgeDropHeight: 0 + maxJumpAcrossDistance: 0 + minRegionArea: 2 + manualCellSize: 0 + cellSize: 0.16666667 + manualTileSize: 0 + tileSize: 256 + accuratePlacement: 0 + debug: + m_Flags: 0 + m_NavMeshData: {fileID: 0} +--- !u!1 &406922205 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInternal: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 406922208} + - component: {fileID: 406922207} + - component: {fileID: 406922206} + m_Layer: 0 + m_Name: Main Camera + m_TagString: MainCamera + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!81 &406922206 +AudioListener: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInternal: {fileID: 0} + m_GameObject: {fileID: 406922205} + m_Enabled: 1 +--- !u!20 &406922207 +Camera: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInternal: {fileID: 0} + m_GameObject: {fileID: 406922205} + m_Enabled: 1 + serializedVersion: 2 + m_ClearFlags: 1 + m_BackGroundColor: {r: 0.19215687, g: 0.3019608, b: 0.4745098, a: 0} + m_projectionMatrixMode: 1 + m_SensorSize: {x: 36, y: 24} + m_LensShift: {x: 0, y: 0} + m_FocalLength: 50 + m_NormalizedViewPortRect: + serializedVersion: 2 + x: 0 + y: 0 + width: 1 + height: 1 + near clip plane: 0.3 + far clip plane: 1000 + field of view: 60 + orthographic: 0 + orthographic size: 5 + m_Depth: -1 + m_CullingMask: + serializedVersion: 2 + m_Bits: 4294967295 + m_RenderingPath: -1 + m_TargetTexture: {fileID: 0} + m_TargetDisplay: 0 + m_TargetEye: 3 + m_HDR: 1 + m_AllowMSAA: 1 + m_AllowDynamicResolution: 0 + m_ForceIntoRT: 0 + m_OcclusionCulling: 1 + m_StereoConvergence: 10 + m_StereoSeparation: 0.022 +--- !u!4 &406922208 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInternal: {fileID: 0} + m_GameObject: {fileID: 406922205} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 1, z: -10} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: [] + m_Father: {fileID: 0} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!1 &540606465 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInternal: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 540606468} + - component: {fileID: 540606467} + - component: {fileID: 540606466} + m_Layer: 0 + m_Name: EventSystem + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!114 &540606466 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInternal: {fileID: 0} + m_GameObject: {fileID: 540606465} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 1077351063, guid: f70555f144d8491a825f0804e09c671c, type: 3} + m_Name: + m_EditorClassIdentifier: + m_HorizontalAxis: Horizontal + m_VerticalAxis: Vertical + m_SubmitButton: Submit + m_CancelButton: Cancel + m_InputActionsPerSecond: 10 + m_RepeatDelay: 0.5 + m_ForceModuleActive: 0 +--- !u!114 &540606467 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInternal: {fileID: 0} + m_GameObject: {fileID: 540606465} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: -619905303, guid: f70555f144d8491a825f0804e09c671c, type: 3} + m_Name: + m_EditorClassIdentifier: + m_FirstSelected: {fileID: 0} + m_sendNavigationEvents: 1 + m_DragThreshold: 10 +--- !u!4 &540606468 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInternal: {fileID: 0} + m_GameObject: {fileID: 540606465} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: [] + m_Father: {fileID: 0} + m_RootOrder: 3 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!1 &745406201 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInternal: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 745406206} + - component: {fileID: 745406205} + - component: {fileID: 745406204} + - component: {fileID: 745406203} + - component: {fileID: 745406202} + m_Layer: 5 + m_Name: Canvas + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!114 &745406202 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInternal: {fileID: 0} + m_GameObject: {fileID: 745406201} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 7b994e1476323bf4fbe1ae28bea164f2, type: 3} + m_Name: + m_EditorClassIdentifier: + m_openButton: {fileID: 860463875} + m_dst: {fileID: 948407063} +--- !u!114 &745406203 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInternal: {fileID: 0} + m_GameObject: {fileID: 745406201} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 1301386320, guid: f70555f144d8491a825f0804e09c671c, type: 3} + m_Name: + m_EditorClassIdentifier: + m_IgnoreReversedGraphics: 1 + m_BlockingObjects: 0 + m_BlockingMask: + serializedVersion: 2 + m_Bits: 4294967295 +--- !u!114 &745406204 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInternal: {fileID: 0} + m_GameObject: {fileID: 745406201} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 1980459831, guid: f70555f144d8491a825f0804e09c671c, type: 3} + m_Name: + m_EditorClassIdentifier: + m_UiScaleMode: 0 + m_ReferencePixelsPerUnit: 100 + m_ScaleFactor: 1 + m_ReferenceResolution: {x: 800, y: 600} + m_ScreenMatchMode: 0 + m_MatchWidthOrHeight: 0 + m_PhysicalUnit: 3 + m_FallbackScreenDPI: 96 + m_DefaultSpriteDPI: 96 + m_DynamicPixelsPerUnit: 1 +--- !u!223 &745406205 +Canvas: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInternal: {fileID: 0} + m_GameObject: {fileID: 745406201} + m_Enabled: 1 + serializedVersion: 3 + m_RenderMode: 0 + m_Camera: {fileID: 0} + m_PlaneDistance: 100 + m_PixelPerfect: 0 + m_ReceivesEvents: 1 + m_OverrideSorting: 0 + m_OverridePixelPerfect: 0 + m_SortingBucketNormalizedSize: 0 + m_AdditionalShaderChannelsFlag: 0 + m_SortingLayerID: 0 + m_SortingOrder: 0 + m_TargetDisplay: 0 +--- !u!224 &745406206 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInternal: {fileID: 0} + m_GameObject: {fileID: 745406201} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 0, y: 0, z: 0} + m_Children: + - {fileID: 860463874} + m_Father: {fileID: 0} + m_RootOrder: 2 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 0} + m_AnchorMax: {x: 0, y: 0} + m_AnchoredPosition: {x: 0, y: 0} + m_SizeDelta: {x: 0, y: 0} + m_Pivot: {x: 0, y: 0} +--- !u!1 &860463873 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInternal: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 860463874} + - component: {fileID: 860463877} + - component: {fileID: 860463876} + - component: {fileID: 860463875} + m_Layer: 5 + m_Name: Button + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &860463874 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInternal: {fileID: 0} + m_GameObject: {fileID: 860463873} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: + - {fileID: 2080820046} + m_Father: {fileID: 745406206} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 1} + m_AnchorMax: {x: 0, y: 1} + m_AnchoredPosition: {x: 0, y: 0} + m_SizeDelta: {x: 160, y: 30} + m_Pivot: {x: 0, y: 1} +--- !u!114 &860463875 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInternal: {fileID: 0} + m_GameObject: {fileID: 860463873} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 1392445389, guid: f70555f144d8491a825f0804e09c671c, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Navigation: + m_Mode: 3 + m_SelectOnUp: {fileID: 0} + m_SelectOnDown: {fileID: 0} + m_SelectOnLeft: {fileID: 0} + m_SelectOnRight: {fileID: 0} + m_Transition: 1 + m_Colors: + m_NormalColor: {r: 1, g: 1, b: 1, a: 1} + m_HighlightedColor: {r: 0.9607843, g: 0.9607843, b: 0.9607843, a: 1} + m_PressedColor: {r: 0.78431374, g: 0.78431374, b: 0.78431374, a: 1} + m_DisabledColor: {r: 0.78431374, g: 0.78431374, b: 0.78431374, a: 0.5019608} + m_ColorMultiplier: 1 + m_FadeDuration: 0.1 + m_SpriteState: + m_HighlightedSprite: {fileID: 0} + m_PressedSprite: {fileID: 0} + m_DisabledSprite: {fileID: 0} + m_AnimationTriggers: + m_NormalTrigger: Normal + m_HighlightedTrigger: Highlighted + m_PressedTrigger: Pressed + m_DisabledTrigger: Disabled + m_Interactable: 1 + m_TargetGraphic: {fileID: 860463876} + m_OnClick: + m_PersistentCalls: + m_Calls: [] + m_TypeName: UnityEngine.UI.Button+ButtonClickedEvent, UnityEngine.UI, Version=1.0.0.0, + Culture=neutral, PublicKeyToken=null +--- !u!114 &860463876 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInternal: {fileID: 0} + m_GameObject: {fileID: 860463873} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: -765806418, guid: f70555f144d8491a825f0804e09c671c, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 1, g: 1, b: 1, a: 1} + m_RaycastTarget: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_TypeName: UnityEngine.UI.MaskableGraphic+CullStateChangedEvent, UnityEngine.UI, + Version=1.0.0.0, Culture=neutral, PublicKeyToken=null + m_Sprite: {fileID: 10905, guid: 0000000000000000f000000000000000, type: 0} + m_Type: 1 + m_PreserveAspect: 0 + m_FillCenter: 1 + m_FillMethod: 4 + m_FillAmount: 1 + m_FillClockwise: 1 + m_FillOrigin: 0 +--- !u!222 &860463877 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInternal: {fileID: 0} + m_GameObject: {fileID: 860463873} + m_CullTransparentMesh: 0 +--- !u!1 &948407059 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInternal: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 948407062} + - component: {fileID: 948407061} + - component: {fileID: 948407060} + - component: {fileID: 948407063} + m_Layer: 0 + m_Name: Target + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!114 &948407060 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInternal: {fileID: 0} + m_GameObject: {fileID: 948407059} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: dfdcf658d2da05649974b59b849de74c, type: 3} + m_Name: + m_EditorClassIdentifier: + m_material: {fileID: 2100000, guid: 94b4b45712e88334c9e7c5ecc53c50e6, type: 2} +--- !u!95 &948407061 +Animator: + serializedVersion: 3 + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInternal: {fileID: 0} + m_GameObject: {fileID: 948407059} + m_Enabled: 1 + m_Avatar: {fileID: 0} + m_Controller: {fileID: 0} + m_CullingMode: 0 + m_UpdateMode: 0 + m_ApplyRootMotion: 0 + m_LinearVelocityBlending: 0 + m_WarningMessage: + m_HasTransformHierarchy: 1 + m_AllowConstantClipSamplingOptimization: 1 + m_KeepAnimatorControllerStateOnDisable: 0 +--- !u!4 &948407062 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInternal: {fileID: 0} + m_GameObject: {fileID: 948407059} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: -1, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: [] + m_Father: {fileID: 0} + m_RootOrder: 4 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!114 &948407063 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInternal: {fileID: 0} + m_GameObject: {fileID: 948407059} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: e5548e4785b76854cbbf3f6d9a8e9c1d, type: 3} + m_Name: + m_EditorClassIdentifier: + SourceType: 0 + Avatar: {fileID: 0} + Source: {fileID: 0} + PoseClip: {fileID: 0} +--- !u!1 &1536726113 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInternal: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1536726115} + - component: {fileID: 1536726114} + m_Layer: 0 + m_Name: Directional Light + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!108 &1536726114 +Light: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInternal: {fileID: 0} + m_GameObject: {fileID: 1536726113} + m_Enabled: 1 + serializedVersion: 8 + m_Type: 1 + m_Color: {r: 1, g: 0.95686275, b: 0.8392157, a: 1} + m_Intensity: 1 + m_Range: 10 + m_SpotAngle: 30 + m_CookieSize: 10 + m_Shadows: + m_Type: 2 + m_Resolution: -1 + m_CustomResolution: -1 + m_Strength: 1 + m_Bias: 0.05 + m_NormalBias: 0.4 + m_NearPlane: 0.2 + m_Cookie: {fileID: 0} + m_DrawHalo: 0 + m_Flare: {fileID: 0} + m_RenderMode: 0 + m_CullingMask: + serializedVersion: 2 + m_Bits: 4294967295 + m_Lightmapping: 4 + m_LightShadowCasterMode: 0 + m_AreaSize: {x: 1, y: 1} + m_BounceIntensity: 1 + m_ColorTemperature: 6570 + m_UseColorTemperature: 0 + m_ShadowRadius: 0 + m_ShadowAngle: 0 +--- !u!4 &1536726115 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInternal: {fileID: 0} + m_GameObject: {fileID: 1536726113} + m_LocalRotation: {x: 0.40821788, y: -0.23456968, z: 0.10938163, w: 0.8754261} + m_LocalPosition: {x: 0, y: 3, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: [] + m_Father: {fileID: 0} + m_RootOrder: 1 + m_LocalEulerAnglesHint: {x: 50, y: -30, z: 0} +--- !u!1 &2080820045 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInternal: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 2080820046} + - component: {fileID: 2080820048} + - component: {fileID: 2080820047} + m_Layer: 5 + m_Name: Text + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &2080820046 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInternal: {fileID: 0} + m_GameObject: {fileID: 2080820045} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: [] + m_Father: {fileID: 860463874} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 0} + m_AnchorMax: {x: 1, y: 1} + m_AnchoredPosition: {x: 0, y: 0} + m_SizeDelta: {x: 0, y: 0} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!114 &2080820047 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInternal: {fileID: 0} + m_GameObject: {fileID: 2080820045} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 708705254, guid: f70555f144d8491a825f0804e09c671c, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 0.19607843, g: 0.19607843, b: 0.19607843, a: 1} + m_RaycastTarget: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_TypeName: UnityEngine.UI.MaskableGraphic+CullStateChangedEvent, UnityEngine.UI, + Version=1.0.0.0, Culture=neutral, PublicKeyToken=null + m_FontData: + m_Font: {fileID: 10102, guid: 0000000000000000e000000000000000, type: 0} + m_FontSize: 14 + m_FontStyle: 0 + m_BestFit: 0 + m_MinSize: 10 + m_MaxSize: 40 + m_Alignment: 4 + m_AlignByGeometry: 0 + m_RichText: 1 + m_HorizontalOverflow: 0 + m_VerticalOverflow: 0 + m_LineSpacing: 1 + m_Text: Open +--- !u!222 &2080820048 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInternal: {fileID: 0} + m_GameObject: {fileID: 2080820045} + m_CullTransparentMesh: 0 diff --git a/Scenes/PoseTransfer.unity.meta b/Scenes/PoseTransfer.unity.meta new file mode 100644 index 000000000..04c014c8b --- /dev/null +++ b/Scenes/PoseTransfer.unity.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: ae847f673eb3e6f4a95f61b4658b806a +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Scenes/RuntimeBvhLoader.cs b/Scenes/RuntimeBvhLoader.cs new file mode 100644 index 000000000..5d8d1d15c --- /dev/null +++ b/Scenes/RuntimeBvhLoader.cs @@ -0,0 +1,84 @@ +using UnityEngine; +using UnityEngine.Events; +using UnityEngine.UI; +using UniHumanoid; +using System.IO; +using System; +#if UNITY_EDITOR +using UnityEditor; +#endif + + +namespace UniHumanoid +{ + public class RuntimeBvhLoader : MonoBehaviour + { + [SerializeField] + Button m_openButton; + + [SerializeField] + HumanPoseTransfer m_dst; + + UnityAction m_onClick; + + private void Awake() + { + m_onClick = new UnityEngine.Events.UnityAction(OnClick); + } + + private void OnEnable() + { + m_openButton.onClick.AddListener(m_onClick); + } + + private void OnDisable() + { + m_openButton.onClick.RemoveListener(m_onClick); + } + + static string m_lastDir; + + public void OnClick() + { +#if UNITY_EDITOR + var path = EditorUtility.OpenFilePanel("open bvh", m_lastDir, "bvh"); + if (String.IsNullOrEmpty(path)) + { + return; + } + m_lastDir = Path.GetDirectoryName(path); +#else + string path=null; + throw new NotImplementedException(); +#endif + +#pragma warning disable 4014 + Open(path); +#pragma warning restore 4014 + } + + BvhImporterContext m_context; + + void Open(string path) + { + Debug.LogFormat("Open: {0}", path); + if (m_context != null) + { + m_context.Destroy(true); + m_context = null; + } + + m_context = new BvhImporterContext(); + m_context.Parse(path); + m_context.Load(); + + var src = m_context.Root.AddComponent(); + + if (m_dst != null) + { + m_dst.SourceType = HumanPoseTransfer.HumanPoseTransferSourceType.HumanPoseTransfer; + m_dst.Source = src; + } + } + } +} diff --git a/Scenes/RuntimeBvhLoader.cs.meta b/Scenes/RuntimeBvhLoader.cs.meta new file mode 100644 index 000000000..0fae39bc4 --- /dev/null +++ b/Scenes/RuntimeBvhLoader.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 7b994e1476323bf4fbe1ae28bea164f2 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Scenes/RuntimeBvhLoader.unity b/Scenes/RuntimeBvhLoader.unity new file mode 100644 index 000000000..c1e610d83 --- /dev/null +++ b/Scenes/RuntimeBvhLoader.unity @@ -0,0 +1,615 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!29 &1 +OcclusionCullingSettings: + m_ObjectHideFlags: 0 + serializedVersion: 2 + m_OcclusionBakeSettings: + smallestOccluder: 5 + smallestHole: 0.25 + backfaceThreshold: 100 + m_SceneGUID: 00000000000000000000000000000000 + m_OcclusionCullingData: {fileID: 0} +--- !u!104 &2 +RenderSettings: + m_ObjectHideFlags: 0 + serializedVersion: 9 + m_Fog: 0 + m_FogColor: {r: 0.5, g: 0.5, b: 0.5, a: 1} + m_FogMode: 3 + m_FogDensity: 0.01 + m_LinearFogStart: 0 + m_LinearFogEnd: 300 + m_AmbientSkyColor: {r: 0.212, g: 0.227, b: 0.259, a: 1} + m_AmbientEquatorColor: {r: 0.114, g: 0.125, b: 0.133, a: 1} + m_AmbientGroundColor: {r: 0.047, g: 0.043, b: 0.035, a: 1} + m_AmbientIntensity: 1 + m_AmbientMode: 0 + m_SubtractiveShadowColor: {r: 0.42, g: 0.478, b: 0.627, a: 1} + m_SkyboxMaterial: {fileID: 10304, guid: 0000000000000000f000000000000000, type: 0} + m_HaloStrength: 0.5 + m_FlareStrength: 1 + m_FlareFadeSpeed: 3 + m_HaloTexture: {fileID: 0} + m_SpotCookie: {fileID: 10001, guid: 0000000000000000e000000000000000, type: 0} + m_DefaultReflectionMode: 0 + m_DefaultReflectionResolution: 128 + m_ReflectionBounces: 1 + m_ReflectionIntensity: 1 + m_CustomReflection: {fileID: 0} + m_Sun: {fileID: 0} + m_IndirectSpecularColor: {r: 0.44657898, g: 0.4964133, b: 0.5748178, a: 1} + m_UseRadianceAmbientProbe: 0 +--- !u!157 &3 +LightmapSettings: + m_ObjectHideFlags: 0 + serializedVersion: 11 + m_GIWorkflowMode: 0 + m_GISettings: + serializedVersion: 2 + m_BounceScale: 1 + m_IndirectOutputScale: 1 + m_AlbedoBoost: 1 + m_TemporalCoherenceThreshold: 1 + m_EnvironmentLightingMode: 0 + m_EnableBakedLightmaps: 1 + m_EnableRealtimeLightmaps: 1 + m_LightmapEditorSettings: + serializedVersion: 10 + m_Resolution: 2 + m_BakeResolution: 40 + m_AtlasSize: 1024 + m_AO: 0 + m_AOMaxDistance: 1 + m_CompAOExponent: 1 + m_CompAOExponentDirect: 0 + m_Padding: 2 + m_LightmapParameters: {fileID: 0} + m_LightmapsBakeMode: 1 + m_TextureCompression: 1 + m_FinalGather: 0 + m_FinalGatherFiltering: 1 + m_FinalGatherRayCount: 256 + m_ReflectionCompression: 2 + m_MixedBakeMode: 2 + m_BakeBackend: 1 + m_PVRSampling: 1 + m_PVRDirectSampleCount: 32 + m_PVRSampleCount: 500 + m_PVRBounces: 2 + m_PVRFilterTypeDirect: 0 + m_PVRFilterTypeIndirect: 0 + m_PVRFilterTypeAO: 0 + m_PVRFilteringMode: 1 + m_PVRCulling: 1 + m_PVRFilteringGaussRadiusDirect: 1 + m_PVRFilteringGaussRadiusIndirect: 5 + m_PVRFilteringGaussRadiusAO: 2 + m_PVRFilteringAtrousPositionSigmaDirect: 0.5 + m_PVRFilteringAtrousPositionSigmaIndirect: 2 + m_PVRFilteringAtrousPositionSigmaAO: 1 + m_ShowResolutionOverlay: 1 + m_LightingDataAsset: {fileID: 0} + m_UseShadowmask: 1 +--- !u!196 &4 +NavMeshSettings: + serializedVersion: 2 + m_ObjectHideFlags: 0 + m_BuildSettings: + serializedVersion: 2 + agentTypeID: 0 + agentRadius: 0.5 + agentHeight: 2 + agentSlope: 45 + agentClimb: 0.4 + ledgeDropHeight: 0 + maxJumpAcrossDistance: 0 + minRegionArea: 2 + manualCellSize: 0 + cellSize: 0.16666667 + manualTileSize: 0 + tileSize: 256 + accuratePlacement: 0 + debug: + m_Flags: 0 + m_NavMeshData: {fileID: 0} +--- !u!1 &406922205 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInternal: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 406922208} + - component: {fileID: 406922207} + - component: {fileID: 406922206} + m_Layer: 0 + m_Name: Main Camera + m_TagString: MainCamera + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!81 &406922206 +AudioListener: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInternal: {fileID: 0} + m_GameObject: {fileID: 406922205} + m_Enabled: 1 +--- !u!20 &406922207 +Camera: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInternal: {fileID: 0} + m_GameObject: {fileID: 406922205} + m_Enabled: 1 + serializedVersion: 2 + m_ClearFlags: 1 + m_BackGroundColor: {r: 0.19215687, g: 0.3019608, b: 0.4745098, a: 0} + m_projectionMatrixMode: 1 + m_SensorSize: {x: 36, y: 24} + m_LensShift: {x: 0, y: 0} + m_FocalLength: 50 + m_NormalizedViewPortRect: + serializedVersion: 2 + x: 0 + y: 0 + width: 1 + height: 1 + near clip plane: 0.3 + far clip plane: 1000 + field of view: 60 + orthographic: 0 + orthographic size: 5 + m_Depth: -1 + m_CullingMask: + serializedVersion: 2 + m_Bits: 4294967295 + m_RenderingPath: -1 + m_TargetTexture: {fileID: 0} + m_TargetDisplay: 0 + m_TargetEye: 3 + m_HDR: 1 + m_AllowMSAA: 1 + m_AllowDynamicResolution: 0 + m_ForceIntoRT: 0 + m_OcclusionCulling: 1 + m_StereoConvergence: 10 + m_StereoSeparation: 0.022 +--- !u!4 &406922208 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInternal: {fileID: 0} + m_GameObject: {fileID: 406922205} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 1, z: -10} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: [] + m_Father: {fileID: 0} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!1 &540606465 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInternal: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 540606468} + - component: {fileID: 540606467} + - component: {fileID: 540606466} + m_Layer: 0 + m_Name: EventSystem + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!114 &540606466 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInternal: {fileID: 0} + m_GameObject: {fileID: 540606465} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 1077351063, guid: f70555f144d8491a825f0804e09c671c, type: 3} + m_Name: + m_EditorClassIdentifier: + m_HorizontalAxis: Horizontal + m_VerticalAxis: Vertical + m_SubmitButton: Submit + m_CancelButton: Cancel + m_InputActionsPerSecond: 10 + m_RepeatDelay: 0.5 + m_ForceModuleActive: 0 +--- !u!114 &540606467 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInternal: {fileID: 0} + m_GameObject: {fileID: 540606465} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: -619905303, guid: f70555f144d8491a825f0804e09c671c, type: 3} + m_Name: + m_EditorClassIdentifier: + m_FirstSelected: {fileID: 0} + m_sendNavigationEvents: 1 + m_DragThreshold: 10 +--- !u!4 &540606468 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInternal: {fileID: 0} + m_GameObject: {fileID: 540606465} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: [] + m_Father: {fileID: 0} + m_RootOrder: 3 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!1 &745406201 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInternal: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 745406206} + - component: {fileID: 745406205} + - component: {fileID: 745406204} + - component: {fileID: 745406203} + - component: {fileID: 745406202} + m_Layer: 5 + m_Name: Canvas + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!114 &745406202 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInternal: {fileID: 0} + m_GameObject: {fileID: 745406201} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 7b994e1476323bf4fbe1ae28bea164f2, type: 3} + m_Name: + m_EditorClassIdentifier: + m_openButton: {fileID: 860463875} + m_dst: {fileID: 0} +--- !u!114 &745406203 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInternal: {fileID: 0} + m_GameObject: {fileID: 745406201} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 1301386320, guid: f70555f144d8491a825f0804e09c671c, type: 3} + m_Name: + m_EditorClassIdentifier: + m_IgnoreReversedGraphics: 1 + m_BlockingObjects: 0 + m_BlockingMask: + serializedVersion: 2 + m_Bits: 4294967295 +--- !u!114 &745406204 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInternal: {fileID: 0} + m_GameObject: {fileID: 745406201} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 1980459831, guid: f70555f144d8491a825f0804e09c671c, type: 3} + m_Name: + m_EditorClassIdentifier: + m_UiScaleMode: 0 + m_ReferencePixelsPerUnit: 100 + m_ScaleFactor: 1 + m_ReferenceResolution: {x: 800, y: 600} + m_ScreenMatchMode: 0 + m_MatchWidthOrHeight: 0 + m_PhysicalUnit: 3 + m_FallbackScreenDPI: 96 + m_DefaultSpriteDPI: 96 + m_DynamicPixelsPerUnit: 1 +--- !u!223 &745406205 +Canvas: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInternal: {fileID: 0} + m_GameObject: {fileID: 745406201} + m_Enabled: 1 + serializedVersion: 3 + m_RenderMode: 0 + m_Camera: {fileID: 0} + m_PlaneDistance: 100 + m_PixelPerfect: 0 + m_ReceivesEvents: 1 + m_OverrideSorting: 0 + m_OverridePixelPerfect: 0 + m_SortingBucketNormalizedSize: 0 + m_AdditionalShaderChannelsFlag: 0 + m_SortingLayerID: 0 + m_SortingOrder: 0 + m_TargetDisplay: 0 +--- !u!224 &745406206 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInternal: {fileID: 0} + m_GameObject: {fileID: 745406201} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 0, y: 0, z: 0} + m_Children: + - {fileID: 860463874} + m_Father: {fileID: 0} + m_RootOrder: 2 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 0} + m_AnchorMax: {x: 0, y: 0} + m_AnchoredPosition: {x: 0, y: 0} + m_SizeDelta: {x: 0, y: 0} + m_Pivot: {x: 0, y: 0} +--- !u!1 &860463873 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInternal: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 860463874} + - component: {fileID: 860463877} + - component: {fileID: 860463876} + - component: {fileID: 860463875} + m_Layer: 5 + m_Name: Button + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &860463874 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInternal: {fileID: 0} + m_GameObject: {fileID: 860463873} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: + - {fileID: 2080820046} + m_Father: {fileID: 745406206} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 1} + m_AnchorMax: {x: 0, y: 1} + m_AnchoredPosition: {x: 0, y: 0} + m_SizeDelta: {x: 160, y: 30} + m_Pivot: {x: 0, y: 1} +--- !u!114 &860463875 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInternal: {fileID: 0} + m_GameObject: {fileID: 860463873} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 1392445389, guid: f70555f144d8491a825f0804e09c671c, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Navigation: + m_Mode: 3 + m_SelectOnUp: {fileID: 0} + m_SelectOnDown: {fileID: 0} + m_SelectOnLeft: {fileID: 0} + m_SelectOnRight: {fileID: 0} + m_Transition: 1 + m_Colors: + m_NormalColor: {r: 1, g: 1, b: 1, a: 1} + m_HighlightedColor: {r: 0.9607843, g: 0.9607843, b: 0.9607843, a: 1} + m_PressedColor: {r: 0.78431374, g: 0.78431374, b: 0.78431374, a: 1} + m_DisabledColor: {r: 0.78431374, g: 0.78431374, b: 0.78431374, a: 0.5019608} + m_ColorMultiplier: 1 + m_FadeDuration: 0.1 + m_SpriteState: + m_HighlightedSprite: {fileID: 0} + m_PressedSprite: {fileID: 0} + m_DisabledSprite: {fileID: 0} + m_AnimationTriggers: + m_NormalTrigger: Normal + m_HighlightedTrigger: Highlighted + m_PressedTrigger: Pressed + m_DisabledTrigger: Disabled + m_Interactable: 1 + m_TargetGraphic: {fileID: 860463876} + m_OnClick: + m_PersistentCalls: + m_Calls: [] + m_TypeName: UnityEngine.UI.Button+ButtonClickedEvent, UnityEngine.UI, Version=1.0.0.0, + Culture=neutral, PublicKeyToken=null +--- !u!114 &860463876 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInternal: {fileID: 0} + m_GameObject: {fileID: 860463873} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: -765806418, guid: f70555f144d8491a825f0804e09c671c, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 1, g: 1, b: 1, a: 1} + m_RaycastTarget: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_TypeName: UnityEngine.UI.MaskableGraphic+CullStateChangedEvent, UnityEngine.UI, + Version=1.0.0.0, Culture=neutral, PublicKeyToken=null + m_Sprite: {fileID: 10905, guid: 0000000000000000f000000000000000, type: 0} + m_Type: 1 + m_PreserveAspect: 0 + m_FillCenter: 1 + m_FillMethod: 4 + m_FillAmount: 1 + m_FillClockwise: 1 + m_FillOrigin: 0 +--- !u!222 &860463877 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInternal: {fileID: 0} + m_GameObject: {fileID: 860463873} + m_CullTransparentMesh: 0 +--- !u!1 &1536726113 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInternal: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1536726115} + - component: {fileID: 1536726114} + m_Layer: 0 + m_Name: Directional Light + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!108 &1536726114 +Light: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInternal: {fileID: 0} + m_GameObject: {fileID: 1536726113} + m_Enabled: 1 + serializedVersion: 8 + m_Type: 1 + m_Color: {r: 1, g: 0.95686275, b: 0.8392157, a: 1} + m_Intensity: 1 + m_Range: 10 + m_SpotAngle: 30 + m_CookieSize: 10 + m_Shadows: + m_Type: 2 + m_Resolution: -1 + m_CustomResolution: -1 + m_Strength: 1 + m_Bias: 0.05 + m_NormalBias: 0.4 + m_NearPlane: 0.2 + m_Cookie: {fileID: 0} + m_DrawHalo: 0 + m_Flare: {fileID: 0} + m_RenderMode: 0 + m_CullingMask: + serializedVersion: 2 + m_Bits: 4294967295 + m_Lightmapping: 4 + m_LightShadowCasterMode: 0 + m_AreaSize: {x: 1, y: 1} + m_BounceIntensity: 1 + m_ColorTemperature: 6570 + m_UseColorTemperature: 0 + m_ShadowRadius: 0 + m_ShadowAngle: 0 +--- !u!4 &1536726115 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInternal: {fileID: 0} + m_GameObject: {fileID: 1536726113} + m_LocalRotation: {x: 0.40821788, y: -0.23456968, z: 0.10938163, w: 0.8754261} + m_LocalPosition: {x: 0, y: 3, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: [] + m_Father: {fileID: 0} + m_RootOrder: 1 + m_LocalEulerAnglesHint: {x: 50, y: -30, z: 0} +--- !u!1 &2080820045 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInternal: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 2080820046} + - component: {fileID: 2080820048} + - component: {fileID: 2080820047} + m_Layer: 5 + m_Name: Text + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &2080820046 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInternal: {fileID: 0} + m_GameObject: {fileID: 2080820045} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: [] + m_Father: {fileID: 860463874} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 0} + m_AnchorMax: {x: 1, y: 1} + m_AnchoredPosition: {x: 0, y: 0} + m_SizeDelta: {x: 0, y: 0} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!114 &2080820047 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInternal: {fileID: 0} + m_GameObject: {fileID: 2080820045} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 708705254, guid: f70555f144d8491a825f0804e09c671c, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 0.19607843, g: 0.19607843, b: 0.19607843, a: 1} + m_RaycastTarget: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_TypeName: UnityEngine.UI.MaskableGraphic+CullStateChangedEvent, UnityEngine.UI, + Version=1.0.0.0, Culture=neutral, PublicKeyToken=null + m_FontData: + m_Font: {fileID: 10102, guid: 0000000000000000e000000000000000, type: 0} + m_FontSize: 14 + m_FontStyle: 0 + m_BestFit: 0 + m_MinSize: 10 + m_MaxSize: 40 + m_Alignment: 4 + m_AlignByGeometry: 0 + m_RichText: 1 + m_HorizontalOverflow: 0 + m_VerticalOverflow: 0 + m_LineSpacing: 1 + m_Text: Open +--- !u!222 &2080820048 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInternal: {fileID: 0} + m_GameObject: {fileID: 2080820045} + m_CullTransparentMesh: 0 diff --git a/Scenes/RuntimeBvhLoader.unity.meta b/Scenes/RuntimeBvhLoader.unity.meta new file mode 100644 index 000000000..2a3f1964d --- /dev/null +++ b/Scenes/RuntimeBvhLoader.unity.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 99665560be9c32e4d9f7813b076c92ee +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Scenes/target.mat b/Scenes/target.mat new file mode 100644 index 000000000..1136c07c9 --- /dev/null +++ b/Scenes/target.mat @@ -0,0 +1,76 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!21 &2100000 +Material: + serializedVersion: 6 + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInternal: {fileID: 0} + m_Name: target + m_Shader: {fileID: 46, guid: 0000000000000000f000000000000000, type: 0} + m_ShaderKeywords: + m_LightmapFlags: 4 + m_EnableInstancingVariants: 0 + m_DoubleSidedGI: 0 + m_CustomRenderQueue: -1 + stringTagMap: {} + disabledShaderPasses: [] + m_SavedProperties: + serializedVersion: 3 + m_TexEnvs: + - _BumpMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _DetailAlbedoMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _DetailMask: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _DetailNormalMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _EmissionMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _MainTex: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _MetallicGlossMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _OcclusionMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _ParallaxMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + m_Floats: + - _BumpScale: 1 + - _Cutoff: 0.5 + - _DetailNormalMapScale: 1 + - _DstBlend: 0 + - _GlossMapScale: 1 + - _Glossiness: 0.5 + - _GlossyReflections: 1 + - _Metallic: 0 + - _Mode: 0 + - _OcclusionStrength: 1 + - _Parallax: 0.02 + - _SmoothnessTextureChannel: 0 + - _SpecularHighlights: 1 + - _SrcBlend: 1 + - _UVSec: 0 + - _ZWrite: 1 + m_Colors: + - _Color: {r: 0.60773194, g: 0.8584906, b: 0.14983091, a: 1} + - _EmissionColor: {r: 0, g: 0, b: 0, a: 1} diff --git a/Scenes/target.mat.meta b/Scenes/target.mat.meta new file mode 100644 index 000000000..133fed931 --- /dev/null +++ b/Scenes/target.mat.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 94b4b45712e88334c9e7c5ecc53c50e6 +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 2100000 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Scripts.meta b/Scripts.meta new file mode 100644 index 000000000..a7876e5ea --- /dev/null +++ b/Scripts.meta @@ -0,0 +1,9 @@ +fileFormatVersion: 2 +guid: 64819743741f38e43b64a7d6add8cea9 +folderAsset: yes +timeCreated: 1517166088 +licenseType: Free +DefaultImporter: + userData: + assetBundleName: + assetBundleVariant: diff --git a/Scripts/AnimationClipUtility.cs b/Scripts/AnimationClipUtility.cs new file mode 100644 index 000000000..905ed6ff1 --- /dev/null +++ b/Scripts/AnimationClipUtility.cs @@ -0,0 +1,134 @@ +using System.Collections.Generic; +using UnityEngine; + + +namespace UniHumanoid +{ + public static class AnimationClipUtility + { + static Dictionary TraitPropMap = new Dictionary +{ +{"Left Thumb 1 Stretched", "LeftHand.Thumb.1 Stretched"}, +{"Left Thumb Spread", "LeftHand.Thumb Spread"}, +{"Left Thumb 2 Stretched", "LeftHand.Thumb.2 Stretched"}, +{"Left Thumb 3 Stretched", "LeftHand.Thumb.3 Stretched"}, +{"Left Index 1 Stretched", "LeftHand.Index.1 Stretched"}, +{"Left Index Spread", "LeftHand.Index Spread"}, +{"Left Index 2 Stretched", "LeftHand.Index.2 Stretched"}, +{"Left Index 3 Stretched", "LeftHand.Index.3 Stretched"}, +{"Left Middle 1 Stretched", "LeftHand.Middle.1 Stretched"}, +{"Left Middle Spread", "LeftHand.Middle Spread"}, +{"Left Middle 2 Stretched", "LeftHand.Middle.2 Stretched"}, +{"Left Middle 3 Stretched", "LeftHand.Middle.3 Stretched"}, +{"Left Ring 1 Stretched", "LeftHand.Ring.1 Stretched"}, +{"Left Ring Spread", "LeftHand.Ring Spread"}, +{"Left Ring 2 Stretched", "LeftHand.Ring.2 Stretched"}, +{"Left Ring 3 Stretched", "LeftHand.Ring.3 Stretched"}, +{"Left Little 1 Stretched", "LeftHand.Little.1 Stretched"}, +{"Left Little Spread", "LeftHand.Little Spread"}, +{"Left Little 2 Stretched", "LeftHand.Little.2 Stretched"}, +{"Left Little 3 Stretched", "LeftHand.Little.3 Stretched"}, +{"Right Thumb 1 Stretched", "RightHand.Thumb.1 Stretched"}, +{"Right Thumb Spread", "RightHand.Thumb Spread"}, +{"Right Thumb 2 Stretched", "RightHand.Thumb.2 Stretched"}, +{"Right Thumb 3 Stretched", "RightHand.Thumb.3 Stretched"}, +{"Right Index 1 Stretched", "RightHand.Index.1 Stretched"}, +{"Right Index Spread", "RightHand.Index Spread"}, +{"Right Index 2 Stretched", "RightHand.Index.2 Stretched"}, +{"Right Index 3 Stretched", "RightHand.Index.3 Stretched"}, +{"Right Middle 1 Stretched", "RightHand.Middle.1 Stretched"}, +{"Right Middle Spread", "RightHand.Middle Spread"}, +{"Right Middle 2 Stretched", "RightHand.Middle.2 Stretched"}, +{"Right Middle 3 Stretched", "RightHand.Middle.3 Stretched"}, +{"Right Ring 1 Stretched", "RightHand.Ring.1 Stretched"}, +{"Right Ring Spread", "RightHand.Ring Spread"}, +{"Right Ring 2 Stretched", "RightHand.Ring.2 Stretched"}, +{"Right Ring 3 Stretched", "RightHand.Ring.3 Stretched"}, +{"Right Little 1 Stretched", "RightHand.Little.1 Stretched"}, +{"Right Little Spread", "RightHand.Little Spread"}, +{"Right Little 2 Stretched", "RightHand.Little.2 Stretched"}, +{"Right Little 3 Stretched", "RightHand.Little.3 Stretched"}, +}; + + public static AnimationClip CreateAnimationClipFromHumanPose(HumanPose pose) + { + var clip = new AnimationClip(); + + // pos + { + var curve = new AnimationCurve(new Keyframe[] + { + new Keyframe(0, pose.bodyPosition.x), + }); + var muscle = "RootT.x"; + clip.SetCurve(null, typeof(Animator), muscle, curve); + } + { + var curve = new AnimationCurve(new Keyframe[] + { + new Keyframe(0, pose.bodyPosition.y), + }); + var muscle = "RootT.y"; + clip.SetCurve(null, typeof(Animator), muscle, curve); + } + { + var curve = new AnimationCurve(new Keyframe[] + { + new Keyframe(0, pose.bodyPosition.z), + }); + var muscle = "RootT.z"; + clip.SetCurve(null, typeof(Animator), muscle, curve); + } + + // rot + { + var curve = new AnimationCurve(new Keyframe[] + { + new Keyframe(0, pose.bodyRotation.x), + }); + var muscle = "RootQ.x"; + clip.SetCurve(null, typeof(Animator), muscle, curve); + } + { + var curve = new AnimationCurve(new Keyframe[] + { + new Keyframe(0, pose.bodyRotation.y), + }); + var muscle = "RootQ.y"; + clip.SetCurve(null, typeof(Animator), muscle, curve); + } + { + var curve = new AnimationCurve(new Keyframe[] + { + new Keyframe(0, pose.bodyRotation.z), + }); + var muscle = "RootQ.z"; + clip.SetCurve(null, typeof(Animator), muscle, curve); + } + { + var curve = new AnimationCurve(new Keyframe[] + { + new Keyframe(0, pose.bodyRotation.w), + }); + var muscle = "RootQ.w"; + clip.SetCurve(null, typeof(Animator), muscle, curve); + } + + // muscles + for (int i = 0; i < HumanTrait.MuscleCount; ++i) + { + var curve = new AnimationCurve(new Keyframe[] + { + new Keyframe(0, pose.muscles[i]), + }); + var muscle = HumanTrait.MuscleName[i]; + if (TraitPropMap.ContainsKey(muscle)) + { + muscle = TraitPropMap[muscle]; + } + clip.SetCurve(null, typeof(Animator), muscle, curve); + } + return clip; + } + } +} diff --git a/Scripts/AnimationClipUtility.cs.meta b/Scripts/AnimationClipUtility.cs.meta new file mode 100644 index 000000000..54fb7ef28 --- /dev/null +++ b/Scripts/AnimationClipUtility.cs.meta @@ -0,0 +1,13 @@ +fileFormatVersion: 2 +guid: e13984a5449ddb843b03e21c3409df67 +timeCreated: 1516590895 +licenseType: Free +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Scripts/AvatarDescription.cs b/Scripts/AvatarDescription.cs new file mode 100644 index 000000000..01ab1bca2 --- /dev/null +++ b/Scripts/AvatarDescription.cs @@ -0,0 +1,242 @@ +#if UNITY_EDITOR +using UnityEditor; +#endif +using UnityEngine; +using System; +using System.Linq; +using System.Collections.Generic; + + +namespace UniHumanoid +{ + [Serializable] + public struct BoneLimit + { + public HumanBodyBones humanBone; + public string boneName; + public bool useDefaultValues; + public Vector3 min; + public Vector3 max; + public Vector3 center; + public float axisLength; + + public static BoneLimit From(HumanBone bone) + { + return new BoneLimit + { + humanBone = (HumanBodyBones)Enum.Parse(typeof(HumanBodyBones), bone.humanName.Replace(" ", ""), true), + boneName = bone.boneName, + useDefaultValues = bone.limit.useDefaultValues, + min = bone.limit.min, + max = bone.limit.max, + center = bone.limit.center, + axisLength = bone.limit.axisLength, + }; + } + + public static String ToHumanBoneName(HumanBodyBones b) + { + foreach (var x in HumanTrait.BoneName) + { + if (x.Replace(" ", "") == b.ToString()) + { + return x; + } + } + + throw new KeyNotFoundException(); + } + + public HumanBone ToHumanBone() + { + return new HumanBone + { + boneName = boneName, + humanName = ToHumanBoneName(humanBone), + limit = new HumanLimit + { + useDefaultValues = useDefaultValues, + axisLength = axisLength, + center = center, + max = max, + min = min + }, + }; + } + } + + [Serializable] + public class AvatarDescription : ScriptableObject + { + public float armStretch = 0.05f; + public float legStretch = 0.05f; + public float upperArmTwist = 0.5f; + public float lowerArmTwist = 0.5f; + public float upperLegTwist = 0.5f; + public float lowerLegTwist = 0.5f; + public float feetSpacing = 0; + public bool hasTranslationDoF; + public BoneLimit[] human; + + public HumanDescription ToHumanDescription(Transform root) + { + return new HumanDescription + { + skeleton = root.Traverse().Select(x => x.ToSkeletonBone()).ToArray(), + human = human.Select(x => x.ToHumanBone()).ToArray(), + armStretch = armStretch, + legStretch = legStretch, + upperArmTwist = upperArmTwist, + lowerArmTwist = lowerArmTwist, + upperLegTwist = upperLegTwist, + lowerLegTwist = lowerLegTwist, + feetSpacing = feetSpacing, + hasTranslationDoF = hasTranslationDoF, + }; + } + + public Avatar CreateAvatar(Transform root) + { + return AvatarBuilder.BuildHumanAvatar(root.gameObject, ToHumanDescription(root)); + } + + public Avatar CreateAvatarAndSetup(Transform root) + { + var avatar = CreateAvatar(root); + avatar.name = name; + + var animator = root.GetComponent(); + if (animator != null) + { + var positionMap = root.Traverse().ToDictionary(x => x, x => x.position); + animator.avatar = avatar; + foreach (var x in root.Traverse()) + { + x.position = positionMap[x]; + } + } + + var transfer = root.GetComponent(); + if (transfer != null) + { + transfer.Avatar = avatar; + } + + return avatar; + } + +#if UNITY_EDITOR + public static AvatarDescription CreateFrom(Avatar avatar) + { + var description = default(HumanDescription); + if (!GetHumanDescription(avatar, ref description)) + { + return null; + } + + return CreateFrom(description); + } +#endif + + public static AvatarDescription CreateFrom(HumanDescription description) + { + var avatarDescription = ScriptableObject.CreateInstance(); + avatarDescription.name = "AvatarDescription"; + avatarDescription.armStretch = description.armStretch; + avatarDescription.legStretch = description.legStretch; + avatarDescription.feetSpacing = description.feetSpacing; + avatarDescription.hasTranslationDoF = description.hasTranslationDoF; + avatarDescription.lowerArmTwist = description.lowerArmTwist; + avatarDescription.lowerLegTwist = description.lowerLegTwist; + avatarDescription.upperArmTwist = description.upperArmTwist; + avatarDescription.upperLegTwist = description.upperLegTwist; + avatarDescription.human = description.human.Select(BoneLimit.From).ToArray(); + return avatarDescription; + } + + public static AvatarDescription Create(AvatarDescription src=null) + { + var avatarDescription = ScriptableObject.CreateInstance(); + avatarDescription.name = "AvatarDescription"; + if (src != null) + { + avatarDescription.armStretch = src.armStretch; + avatarDescription.legStretch = src.legStretch; + avatarDescription.feetSpacing = src.feetSpacing; + avatarDescription.upperArmTwist = src.upperArmTwist; + avatarDescription.lowerArmTwist = src.lowerArmTwist; + avatarDescription.upperLegTwist = src.upperLegTwist; + avatarDescription.lowerLegTwist = src.lowerLegTwist; + } + else + { + avatarDescription.armStretch = 0.05f; + avatarDescription.legStretch = 0.05f; + avatarDescription.feetSpacing = 0.0f; + avatarDescription.lowerArmTwist = 0.5f; + avatarDescription.upperArmTwist = 0.5f; + avatarDescription.upperLegTwist = 0.5f; + avatarDescription.lowerLegTwist = 0.5f; + } + return avatarDescription; + } + + public static AvatarDescription Create(Transform[] boneTransforms, Skeleton skeleton) + { + return Create(skeleton.Bones.Select( + x => new KeyValuePair(x.Key, boneTransforms[x.Value]))); + } + + public static AvatarDescription Create(IEnumerable> skeleton) + { + var description = Create(); + description.SetHumanBones(skeleton); + return description; + } + + public void SetHumanBones(IEnumerable> skeleton) + { + human = skeleton.Select(x => + { + return new BoneLimit + { + humanBone = x.Key, + boneName = x.Value.name, + useDefaultValues = true, + }; + }).ToArray(); + } + +#if UNITY_EDITOR + /// + /// * https://answers.unity.com/questions/612177/how-can-i-access-human-avatar-bone-and-muscle-valu.html + /// + /// + /// + /// + public static bool GetHumanDescription(UnityEngine.Object target, ref HumanDescription des) + { + if (target != null) + { + var importer = AssetImporter.GetAtPath(AssetDatabase.GetAssetPath(target)); + if (importer != null) + { + Debug.Log("AssetImporter Type: " + importer.GetType()); + ModelImporter modelImporter = importer as ModelImporter; + if (modelImporter != null) + { + des = modelImporter.humanDescription; + Debug.Log("## Cool stuff data by ModelImporter ##"); + return true; + } + else + { + Debug.LogWarning("## Please Select Imported Model in Project View not prefab or other things ##"); + } + } + } + return false; + } +#endif + } +} diff --git a/Scripts/AvatarDescription.cs.meta b/Scripts/AvatarDescription.cs.meta new file mode 100644 index 000000000..3da5dea95 --- /dev/null +++ b/Scripts/AvatarDescription.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: 976e99d37c093ce4c9b249c81c2cbdd5 +timeCreated: 1520401720 +licenseType: Free +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Scripts/BoneGizmoDrawer.cs b/Scripts/BoneGizmoDrawer.cs new file mode 100644 index 000000000..8ee8e8589 --- /dev/null +++ b/Scripts/BoneGizmoDrawer.cs @@ -0,0 +1,28 @@ +using UnityEngine; + + +namespace UniHumanoid +{ + public class BoneGizmoDrawer : MonoBehaviour + { + const float size = 0.03f; + readonly Vector3 SIZE = new Vector3(size, size, size); + + [SerializeField] + public bool Draw = true; + + void OnDrawGizmos() + { +#if UNITY_EDITOR + if (Draw && transform.parent != null) + { + Gizmos.color = Color.yellow; + Gizmos.DrawCube(transform.position, SIZE); + Gizmos.DrawLine(transform.parent.position, transform.position); + + UnityEditor.Handles.Label(transform.position, name); + } +#endif + } + } +} diff --git a/Scripts/BoneGizmoDrawer.cs.meta b/Scripts/BoneGizmoDrawer.cs.meta new file mode 100644 index 000000000..12ebe910d --- /dev/null +++ b/Scripts/BoneGizmoDrawer.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: 677492d2843780e40aa2a6c7a6ae8fe6 +timeCreated: 1517332124 +licenseType: Free +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Scripts/BoneMapping.cs b/Scripts/BoneMapping.cs new file mode 100644 index 000000000..cb365636c --- /dev/null +++ b/Scripts/BoneMapping.cs @@ -0,0 +1,103 @@ +using UnityEngine; +using System.Linq; +using System; + +namespace UniHumanoid +{ + public class BoneMapping : MonoBehaviour + { + [SerializeField] + public GameObject[] Bones = new GameObject[(int)HumanBodyBones.LastBone]; + + [SerializeField] + public AvatarDescription Description; + + private void Reset() + { + GetBones(); + } + + private void GetBones() + { + Bones = new GameObject[(int)HumanBodyBones.LastBone]; + + var animator = GetComponent(); + if (animator != null) + { + if (animator.avatar != null) + { + foreach (HumanBodyBones key in Enum.GetValues(typeof(HumanBodyBones))) + { + if (key == HumanBodyBones.LastBone) + { + break; + } + var transform = animator.GetBoneTransform(key); + if (transform != null) + { + Bones[(int)key] = transform.gameObject; + } + } + } + } + } + + public void GuessBoneMapping() + { + var hips = Bones[(int)HumanBodyBones.Hips]; + if (hips == null) + { + Debug.LogWarning("require hips"); + return; + } + + var estimater = new BvhSkeletonEstimator(); + var skeleton = estimater.Detect(hips.transform); + var bones = hips.transform.Traverse().ToArray(); + for (int i = 0; i < (int)HumanBodyBones.LastBone; ++i) + { + var index = skeleton.GetBoneIndex((HumanBodyBones)i); + if (index >= 0) + { + Bones[i] = bones[index].gameObject; + } + } + } + + public void EnsureTPose() + { + var map = Bones + .Select((x, i) => new { i, x }) + .Where(x => x.x != null) + .ToDictionary(x => (HumanBodyBones)x.i, x => x.x.transform) + ; + { + var left = (map[HumanBodyBones.LeftLowerArm].position - map[HumanBodyBones.LeftUpperArm].position).normalized; + map[HumanBodyBones.LeftUpperArm].rotation = Quaternion.FromToRotation(left, Vector3.left) * map[HumanBodyBones.LeftUpperArm].rotation; + } + { + var right = (map[HumanBodyBones.RightLowerArm].position - map[HumanBodyBones.RightUpperArm].position).normalized; + map[HumanBodyBones.RightUpperArm].rotation = Quaternion.FromToRotation(right, Vector3.right) * map[HumanBodyBones.RightUpperArm].rotation; + } + } + + public static void SetBonesToDescription(BoneMapping mapping, AvatarDescription description) + { + var map = mapping.Bones + .Select((x, i) => new { i, x }) + .Where(x => x.x != null) + .ToDictionary(x => (HumanBodyBones)x.i, x => x.x.transform) + ; + description.SetHumanBones(map); + } + + private void Awake() + { + if (Bones == null + || Bones.All(x => x==null)) + { + GetBones(); + } + } + } +} diff --git a/Scripts/BoneMapping.cs.meta b/Scripts/BoneMapping.cs.meta new file mode 100644 index 000000000..d7f977116 --- /dev/null +++ b/Scripts/BoneMapping.cs.meta @@ -0,0 +1,13 @@ +fileFormatVersion: 2 +guid: 44f5895a2095a2848ba2c9627a5c1ad9 +timeCreated: 1516520420 +licenseType: Free +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Scripts/BvhBone.cs b/Scripts/BvhBone.cs new file mode 100644 index 000000000..18f1e7ee9 --- /dev/null +++ b/Scripts/BvhBone.cs @@ -0,0 +1,80 @@ +using System.Collections.Generic; +using UnityEngine; + + +namespace UniHumanoid +{ + public class BvhBone : IBone + { + public string Name + { + private set; + get; + } + + public Vector3 SkeletonLocalPosition + { + private set; + get; + } + + public BvhBone(string name, Vector3 position) + { + Name = name; + SkeletonLocalPosition = position; + } + + public override string ToString() + { + return string.Format("", Name); + } + + public IBone Parent + { + private set; + get; + } + + List _children = new List(); + public IList Children + { + get { return _children; } + } + + public void Build(Transform t) + { + foreach (Transform child in t) + { + var childBone = new BvhBone(child.name, SkeletonLocalPosition + child.localPosition); + childBone.Parent = this; + _children.Add(childBone); + + childBone.Build(child); + } + } + + public void Build(BvhNode node) + { + foreach (var child in node.Children) + { + var childBone = new BvhBone(child.Name, SkeletonLocalPosition + child.Offset.ToXReversedVector3()); + childBone.Parent = this; + _children.Add(childBone); + + childBone.Build(child); + } + } + + public IEnumerable Traverse() + { + yield return this; + foreach (var child in Children) + { + foreach (var x in child.Traverse()) + { + yield return (BvhBone)x; + } + } + } + } +} \ No newline at end of file diff --git a/Scripts/BvhBone.cs.meta b/Scripts/BvhBone.cs.meta new file mode 100644 index 000000000..a3578e4aa --- /dev/null +++ b/Scripts/BvhBone.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: c1a88b26726f137419428078debd253f +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Scripts/Editor.meta b/Scripts/Editor.meta new file mode 100644 index 000000000..700ef71e6 --- /dev/null +++ b/Scripts/Editor.meta @@ -0,0 +1,9 @@ +fileFormatVersion: 2 +guid: 6dfa46c173ff021418df4bada32ddfe9 +folderAsset: yes +timeCreated: 1517655448 +licenseType: Free +DefaultImporter: + userData: + assetBundleName: + assetBundleVariant: diff --git a/Scripts/Editor/BoneMappingEditor.cs b/Scripts/Editor/BoneMappingEditor.cs new file mode 100644 index 000000000..b14a8f658 --- /dev/null +++ b/Scripts/Editor/BoneMappingEditor.cs @@ -0,0 +1,419 @@ +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using System.IO; +using UnityEditor; +using UnityEngine; + + +namespace UniHumanoid +{ + [CustomEditor(typeof(BoneMapping))] + public class BoneMappingEditor : Editor + { + BoneMapping m_target; + + void OnEnable() + { + m_target = (BoneMapping)target; + + var animator = m_target.GetComponent(); + if (animator != null) + { + m_bones = EachBoneDefs.Select(x => new Bone( +animator.GetBoneTransform(x.Head), animator.GetBoneTransform(x.Tail))) +.Where(x => x.Head != null && x.Tail != null) +.ToArray(); + } + } + + static GameObject ObjectField(GameObject obj) + { + return (GameObject)EditorGUILayout.ObjectField(obj, typeof(GameObject), true); + } + + static GameObject ObjectField(string label, GameObject obj) + { + return (GameObject)EditorGUILayout.ObjectField(label, obj, typeof(GameObject), true); + } + + const int LABEL_WIDTH = 100; + + static void BoneField(HumanBodyBones bone, GameObject[] bones) + { + EditorGUILayout.BeginHorizontal(); + EditorGUILayout.LabelField(bone.ToString(), GUILayout.Width(LABEL_WIDTH)); + bones[(int)bone] = ObjectField(bones[(int)bone]); + EditorGUILayout.EndHorizontal(); + } + + static void BoneField(HumanBodyBones left, HumanBodyBones right, GameObject[] bones) + { + EditorGUILayout.BeginHorizontal(); + EditorGUILayout.LabelField(left.ToString().Substring(4), GUILayout.Width(LABEL_WIDTH)); // skip left + bones[(int)left] = ObjectField(bones[(int)left]); + bones[(int)right] = ObjectField(bones[(int)right]); + EditorGUILayout.EndHorizontal(); + } + + bool m_handFoldout; + bool m_settingsFoldout; + + public override void OnInspectorGUI() + { + var bones = m_target.Bones; + if (bones == null) + { + return; + } + + BoneField(HumanBodyBones.Hips, bones); + + if (bones[(int)HumanBodyBones.Hips] == null) + { + EditorGUILayout.HelpBox(@"First, you set hips", MessageType.Warning); + } + else + { + if (GUILayout.Button("Guess bone mapping")) + { + m_target.GuessBoneMapping(); + } + EditorGUILayout.HelpBox(@"Guess bones from hips", MessageType.Info); + + if (GUILayout.Button("Ensure T-Pose")) + { + m_target.EnsureTPose(); + } + EditorGUILayout.HelpBox(@"Arms to Horizontal", MessageType.Info); + + if (GUILayout.Button("Create avatar")) + { + var description = AvatarDescription.Create(m_target.Description); + BoneMapping.SetBonesToDescription(m_target, description); + var avatar = description.CreateAvatarAndSetup(m_target.transform); + if (avatar != null) + { + avatar.name = "avatar"; +#if UNITY_2018_2_OR_NEWER + var prefabRoot = PrefabUtility.GetCorrespondingObjectFromSource(m_target.gameObject); +#else + var prefabRoot = PrefabUtility.GetPrefabParent(m_target.gameObject); +#endif + var prefabPath = AssetDatabase.GetAssetPath(prefabRoot); + + var path = (string.IsNullOrEmpty(prefabPath)) + ? string.Format("Assets/{0}.asset", avatar.name) + : string.Format("{0}/{1}.asset", Path.GetDirectoryName(prefabPath), Path.GetFileNameWithoutExtension(prefabPath)) + ; + path = EditorUtility.SaveFilePanel( + "Save avatar", + Path.GetDirectoryName(path), + string.Format("{0}.avatar.asset", serializedObject.targetObject.name), + "asset"); + var assetPath = HumanPoseTransferEditor.ToAssetPath(path); + if (!string.IsNullOrEmpty(assetPath)) + { + AssetDatabase.CreateAsset(description, assetPath); // overwrite + AssetDatabase.AddObjectToAsset(avatar, assetPath); + + Debug.LogFormat("Create avatar {0}", path); + AssetDatabase.ImportAsset(assetPath); + Selection.activeObject = avatar; + } + else + { + Debug.LogWarning("fail to CreateAvatar"); + } + } + } + EditorGUILayout.HelpBox(@"before create, + +1. Model root transform should reset(origin without rotation) +2. Model forward to Z+(rotate child of model root) +3. Required bones filled +", MessageType.Info); + } + + /* + m_settingsFoldout = EditorGUILayout.Foldout(m_settingsFoldout, "AvatarSettings"); + if (m_settingsFoldout) + { + EditorGUILayout.FloatField("armStretch", m_target.armStretch); + EditorGUILayout.FloatField("legStretch", m_target.legStretch); + EditorGUILayout.FloatField("upperArmTwist", m_target.upperArmTwist); + EditorGUILayout.FloatField("lowerArmTwist", m_target.lowerArmTwist); + EditorGUILayout.FloatField("upperLegTwist", m_target.upperLegTwist); + EditorGUILayout.FloatField("lowerLegTwist", m_target.lowerLegTwist); + EditorGUILayout.FloatField("feetSpacing", m_target.feetSpacing); + EditorGUILayout.Toggle("hasTranslationDoF", m_target.hasTranslationDoF); + //public BoneLimit[] human; + } + */ + + EditorGUILayout.BeginHorizontal(); + EditorGUILayout.LabelField("Arm", EditorStyles.boldLabel, GUILayout.Width(LABEL_WIDTH)); + EditorGUILayout.LabelField("Left", EditorStyles.boldLabel, GUILayout.Width(40)); + EditorGUILayout.Space(); + EditorGUILayout.LabelField("Right", EditorStyles.boldLabel, GUILayout.Width(40)); + EditorGUILayout.EndHorizontal(); + BoneField(HumanBodyBones.LeftShoulder, HumanBodyBones.RightShoulder, bones); + BoneField(HumanBodyBones.LeftUpperArm, HumanBodyBones.RightUpperArm, bones); + BoneField(HumanBodyBones.LeftLowerArm, HumanBodyBones.RightLowerArm, bones); + BoneField(HumanBodyBones.LeftHand, HumanBodyBones.RightHand, bones); + + EditorGUILayout.LabelField("Body and Head", EditorStyles.boldLabel); + BoneField(HumanBodyBones.Spine, bones); + BoneField(HumanBodyBones.Chest, bones); +#if UNITY_5_6_OR_NEWER + BoneField(HumanBodyBones.UpperChest, bones); +#endif + BoneField(HumanBodyBones.Neck, bones); + BoneField(HumanBodyBones.Head, bones); + BoneField(HumanBodyBones.Jaw, bones); + BoneField(HumanBodyBones.LeftEye, HumanBodyBones.RightEye, bones); + + EditorGUILayout.BeginHorizontal(); + EditorGUILayout.LabelField("Leg", EditorStyles.boldLabel, GUILayout.Width(LABEL_WIDTH)); + EditorGUILayout.LabelField("Left", EditorStyles.boldLabel, GUILayout.Width(40)); + EditorGUILayout.Space(); + EditorGUILayout.LabelField("Right", EditorStyles.boldLabel, GUILayout.Width(40)); + EditorGUILayout.EndHorizontal(); + BoneField(HumanBodyBones.LeftUpperLeg, HumanBodyBones.RightUpperLeg, bones); + BoneField(HumanBodyBones.LeftLowerLeg, HumanBodyBones.RightLowerLeg, bones); + BoneField(HumanBodyBones.LeftFoot, HumanBodyBones.RightFoot, bones); + BoneField(HumanBodyBones.LeftToes, HumanBodyBones.RightToes, bones); + + m_handFoldout = EditorGUILayout.Foldout(m_handFoldout, "Hand"); + if (m_handFoldout) + { + EditorGUILayout.BeginHorizontal(); + EditorGUILayout.LabelField("Thumb", EditorStyles.boldLabel, GUILayout.Width(LABEL_WIDTH)); + EditorGUILayout.LabelField("Left", EditorStyles.boldLabel, GUILayout.Width(40)); + EditorGUILayout.Space(); + EditorGUILayout.LabelField("Right", EditorStyles.boldLabel, GUILayout.Width(40)); + EditorGUILayout.EndHorizontal(); + BoneField(HumanBodyBones.LeftThumbProximal, HumanBodyBones.RightThumbProximal, bones); + BoneField(HumanBodyBones.LeftThumbIntermediate, HumanBodyBones.RightThumbIntermediate, bones); + BoneField(HumanBodyBones.LeftThumbDistal, HumanBodyBones.RightThumbDistal, bones); + + EditorGUILayout.BeginHorizontal(); + EditorGUILayout.LabelField("Index", EditorStyles.boldLabel, GUILayout.Width(LABEL_WIDTH)); + EditorGUILayout.LabelField("Left", EditorStyles.boldLabel, GUILayout.Width(40)); + EditorGUILayout.Space(); + EditorGUILayout.LabelField("Right", EditorStyles.boldLabel, GUILayout.Width(40)); + EditorGUILayout.EndHorizontal(); + BoneField(HumanBodyBones.LeftIndexProximal, HumanBodyBones.RightIndexProximal, bones); + BoneField(HumanBodyBones.LeftIndexIntermediate, HumanBodyBones.RightIndexIntermediate, bones); + BoneField(HumanBodyBones.LeftIndexDistal, HumanBodyBones.RightIndexDistal, bones); + + EditorGUILayout.BeginHorizontal(); + EditorGUILayout.LabelField("Middle", EditorStyles.boldLabel, GUILayout.Width(LABEL_WIDTH)); + EditorGUILayout.LabelField("Left", EditorStyles.boldLabel, GUILayout.Width(40)); + EditorGUILayout.Space(); + EditorGUILayout.LabelField("Right", EditorStyles.boldLabel, GUILayout.Width(40)); + EditorGUILayout.EndHorizontal(); + BoneField(HumanBodyBones.LeftMiddleProximal, HumanBodyBones.RightMiddleProximal, bones); + BoneField(HumanBodyBones.LeftMiddleIntermediate, HumanBodyBones.RightMiddleIntermediate, bones); + BoneField(HumanBodyBones.LeftMiddleDistal, HumanBodyBones.RightMiddleDistal, bones); + + EditorGUILayout.BeginHorizontal(); + EditorGUILayout.LabelField("Ring", EditorStyles.boldLabel, GUILayout.Width(LABEL_WIDTH)); + EditorGUILayout.LabelField("Left", EditorStyles.boldLabel, GUILayout.Width(40)); + EditorGUILayout.Space(); + EditorGUILayout.LabelField("Right", EditorStyles.boldLabel, GUILayout.Width(40)); + EditorGUILayout.EndHorizontal(); + BoneField(HumanBodyBones.LeftRingProximal, HumanBodyBones.RightRingProximal, bones); + BoneField(HumanBodyBones.LeftRingIntermediate, HumanBodyBones.RightRingIntermediate, bones); + BoneField(HumanBodyBones.LeftRingDistal, HumanBodyBones.RightRingDistal, bones); + + EditorGUILayout.BeginHorizontal(); + EditorGUILayout.LabelField("Little", EditorStyles.boldLabel, GUILayout.Width(LABEL_WIDTH)); + EditorGUILayout.LabelField("Left", EditorStyles.boldLabel, GUILayout.Width(40)); + EditorGUILayout.Space(); + EditorGUILayout.LabelField("Right", EditorStyles.boldLabel, GUILayout.Width(40)); + EditorGUILayout.EndHorizontal(); + BoneField(HumanBodyBones.LeftLittleProximal, HumanBodyBones.RightLittleProximal, bones); + BoneField(HumanBodyBones.LeftLittleIntermediate, HumanBodyBones.RightLittleIntermediate, bones); + BoneField(HumanBodyBones.LeftLittleDistal, HumanBodyBones.RightLittleDistal, bones); + } + + EditorUtility.SetDirty(m_target); + } + + struct Bone + { + public Transform Head; + public Transform Tail; + + public Bone(Transform head, Transform tail) + { + Head = head; + Tail = tail; + } + + public void Draw() + { + Handles.DrawLine(Head.transform.position, Tail.transform.position); + } + } + + Bone[] m_bones; + + struct BoneDef + { + public HumanBodyBones Head; + public HumanBodyBones Tail; + + public BoneDef(HumanBodyBones head, HumanBodyBones tail) + { + Head = head; + Tail = tail; + } + } + static readonly HumanBodyBones[][] BoneDefs = + { + new HumanBodyBones[] + { + HumanBodyBones.Hips, + HumanBodyBones.Spine, + HumanBodyBones.Chest, + HumanBodyBones.Neck, + HumanBodyBones.Head, + }, + new HumanBodyBones[] + { + HumanBodyBones.Chest, + HumanBodyBones.LeftShoulder, + HumanBodyBones.LeftUpperArm, + HumanBodyBones.LeftLowerArm, + HumanBodyBones.LeftHand, + }, + new HumanBodyBones[] + { + HumanBodyBones.Chest, + HumanBodyBones.RightShoulder, + HumanBodyBones.RightUpperArm, + HumanBodyBones.RightLowerArm, + HumanBodyBones.RightHand, + }, + + new HumanBodyBones[] + { + HumanBodyBones.LeftThumbProximal, + HumanBodyBones.LeftThumbIntermediate, + HumanBodyBones.LeftThumbDistal, + }, + new HumanBodyBones[] + { + HumanBodyBones.LeftIndexProximal, + HumanBodyBones.LeftIndexIntermediate, + HumanBodyBones.LeftIndexDistal, + }, + new HumanBodyBones[] + { + HumanBodyBones.LeftMiddleProximal, + HumanBodyBones.LeftMiddleIntermediate, + HumanBodyBones.LeftMiddleDistal, + }, + new HumanBodyBones[] + { + HumanBodyBones.LeftRingProximal, + HumanBodyBones.LeftRingIntermediate, + HumanBodyBones.LeftRingDistal, + }, + new HumanBodyBones[] + { + HumanBodyBones.LeftLittleProximal, + HumanBodyBones.LeftLittleIntermediate, + HumanBodyBones.LeftLittleDistal, + }, + + new HumanBodyBones[] + { + HumanBodyBones.RightThumbProximal, + HumanBodyBones.RightThumbIntermediate, + HumanBodyBones.RightThumbDistal, + }, + new HumanBodyBones[] + { + HumanBodyBones.RightIndexProximal, + HumanBodyBones.RightIndexIntermediate, + HumanBodyBones.RightIndexDistal, + }, + new HumanBodyBones[] + { + HumanBodyBones.RightMiddleProximal, + HumanBodyBones.RightMiddleIntermediate, + HumanBodyBones.RightMiddleDistal, + }, + new HumanBodyBones[] + { + HumanBodyBones.RightRingProximal, + HumanBodyBones.RightRingIntermediate, + HumanBodyBones.RightRingDistal, + }, + new HumanBodyBones[] + { + HumanBodyBones.RightLittleProximal, + HumanBodyBones.RightLittleIntermediate, + HumanBodyBones.RightLittleDistal, + }, + + new HumanBodyBones[] + { + HumanBodyBones.LeftUpperLeg, + HumanBodyBones.LeftLowerLeg, + HumanBodyBones.LeftFoot, + }, + + new HumanBodyBones[] + { + HumanBodyBones.RightUpperLeg, + HumanBodyBones.RightLowerLeg, + HumanBodyBones.RightFoot, + }, + }; + static IEnumerable EachBoneDefs + { + get + { + foreach (var x in BoneDefs) + { + var count = x.Length - 1; + for (int i = 0; i < count; ++i) + { + yield return new BoneDef(x[i], x[i + 1]); + } + } + } + } + + void DrawBone(HumanBodyBones bone, GameObject go) + { + if (go == null) + { + return; + } + + Handles.Label(go.transform.position, + go.name + "\n(" + bone.ToString() + ")"); + } + + private void OnSceneGUI() + { + var bones = m_target.Bones; + if (bones != null) + { + for (int i = 0; i < bones.Length; ++i) + { + DrawBone((HumanBodyBones)i, bones[i]); + } + foreach(var x in m_bones) + { + x.Draw(); + } + } + } + } +} diff --git a/Scripts/Editor/BoneMappingEditor.cs.meta b/Scripts/Editor/BoneMappingEditor.cs.meta new file mode 100644 index 000000000..d6a1c6db9 --- /dev/null +++ b/Scripts/Editor/BoneMappingEditor.cs.meta @@ -0,0 +1,13 @@ +fileFormatVersion: 2 +guid: 06b3418a97f8c204da34a849fa86a7c0 +timeCreated: 1516520481 +licenseType: Free +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Scripts/Editor/HumanPoseTransferEditor.cs b/Scripts/Editor/HumanPoseTransferEditor.cs new file mode 100644 index 000000000..eb865138d --- /dev/null +++ b/Scripts/Editor/HumanPoseTransferEditor.cs @@ -0,0 +1,151 @@ +using System; +using System.IO; +using System.Linq; +using UnityEditor; +using UnityEngine; + + +namespace UniHumanoid +{ + [CustomEditor(typeof(HumanPoseTransfer))] + public class HumanPoseTransferEditor : Editor + { + //HumanPoseTransfer m_target; + SerializedProperty m_avatarProp; + SerializedProperty m_typeProp; + SerializedProperty m_clipProp; + SerializedProperty m_transferProp; + + static string[] SOURCE_TYPES = ((HumanPoseTransfer.HumanPoseTransferSourceType[]) + Enum.GetValues(typeof(HumanPoseTransfer.HumanPoseTransferSourceType))) + .Select(x => x.ToString()) + .ToArray(); + + private void OnEnable() + { + //m_target = (HumanPoseTransfer)target; + m_typeProp = serializedObject.FindProperty("SourceType"); + m_clipProp = serializedObject.FindProperty("PoseClip"); + m_avatarProp = serializedObject.FindProperty("Avatar"); + m_transferProp = serializedObject.FindProperty("Source"); + } + + public override void OnInspectorGUI() + { + //base.OnInspectorGUI(); + serializedObject.Update(); + + EditorGUILayout.PropertyField(m_avatarProp); + + /* + m_typeProp.intValue = + GUILayout.Toolbar(m_typeProp.intValue, SOURCE_TYPES); + */ + m_typeProp.intValue = + EditorGUILayout.Popup("SourceType", m_typeProp.intValue, SOURCE_TYPES); + + switch ((HumanPoseTransfer.HumanPoseTransferSourceType)m_typeProp.intValue) + { + case HumanPoseTransfer.HumanPoseTransferSourceType.None: + serializedObject.ApplyModifiedProperties(); + break; + + case HumanPoseTransfer.HumanPoseTransferSourceType.HumanPoseClip: + PoseClipInspector(); + break; + + case HumanPoseTransfer.HumanPoseTransferSourceType.HumanPoseTransfer: + PoseHandler(); + break; + } + GUILayout.Space(20); + + // CreatePose + if (GUILayout.Button("Pose to HumanPoseClip")) + { + var path = EditorUtility.SaveFilePanel( + "Save humanpose", + Application.dataPath, + string.Format("{0}.pose.asset", serializedObject.targetObject.name), + "asset"); + var assetPath = ToAssetPath(path); + if (!string.IsNullOrEmpty(path)) + { + var pose = ((HumanPoseTransfer)serializedObject.targetObject).CreatePose(); + + var clip = ScriptableObject.CreateInstance(); + clip.ApplyPose(ref pose); + + AssetDatabase.CreateAsset(clip, assetPath); + Selection.activeObject = clip; + } + } + + // CreatePose + if (GUILayout.Button("Pose to AnimationClip")) + { + var path = EditorUtility.SaveFilePanel( + "Save animnationClip", + Application.dataPath, + string.Format("{0}.pose.anim", serializedObject.targetObject.name), + "anim"); + var assetPath = ToAssetPath(path); + if (!string.IsNullOrEmpty(path)) + { + var pose = ((HumanPoseTransfer)serializedObject.targetObject).CreatePose(); + var clip = AnimationClipUtility.CreateAnimationClipFromHumanPose(pose); + AssetDatabase.CreateAsset(clip, assetPath); + Selection.activeObject = clip; + } + } + } + + public static string ToAssetPath(string src) + { + src = src.Replace("\\", "/"); + var basePath = Path.GetFullPath(Application.dataPath + "/..").Replace("\\", "/"); + if (!src.StartsWith(basePath)) + { + return null; + } + return src.Substring(basePath.Length + 1); + } + + void PoseClipInspector() + { + var old = (HumanPoseClip)m_clipProp.objectReferenceValue; + EditorGUILayout.PropertyField(m_clipProp); + serializedObject.ApplyModifiedProperties(); + + var _target = (HumanPoseTransfer)target; + if (_target.PoseClip != old) + { + //Debug.Log("clip != old"); + if (_target.PoseClip != null) + { + var pose = _target.PoseClip.GetPose(); + _target.SetPose(pose); + } + } + +#if false + if (_target.PoseClip != null) + { + if (GUILayout.Button("Apply PoseClip")) + { + Debug.Log("apply"); + var pose = default(HumanPose); + _target.PoseClip.GetPose(out pose); + _target.SetPose(pose); + } + } +#endif + } + + void PoseHandler() + { + EditorGUILayout.PropertyField(m_transferProp); + serializedObject.ApplyModifiedProperties(); + } + } +} diff --git a/Scripts/Editor/HumanPoseTransferEditor.cs.meta b/Scripts/Editor/HumanPoseTransferEditor.cs.meta new file mode 100644 index 000000000..0e206056d --- /dev/null +++ b/Scripts/Editor/HumanPoseTransferEditor.cs.meta @@ -0,0 +1,13 @@ +fileFormatVersion: 2 +guid: d51cc8ca343beb647bc6d8e23600dd66 +timeCreated: 1519366120 +licenseType: Free +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Scripts/Editor/MuscleInspectorEditor.cs b/Scripts/Editor/MuscleInspectorEditor.cs new file mode 100644 index 000000000..c48010c6b --- /dev/null +++ b/Scripts/Editor/MuscleInspectorEditor.cs @@ -0,0 +1,352 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using UnityEditor; +using UnityEditor.IMGUI.Controls; +using UnityEngine; + + +namespace UniHumanoid +{ + class BoneNode : IEnumerable + { + public HumanBodyBones Bone { get; private set; } + + public List Children = new List(); + + public int[] Muscles; + + public BoneNode(HumanBodyBones bone, params int[] muscles) + { + Bone = bone; + Muscles = muscles; + } + + public IEnumerator GetEnumerator() + { + throw new NotImplementedException(); + } + + IEnumerator IEnumerable.GetEnumerator() + { + throw new NotImplementedException(); + } + + public void Add(BoneNode child) + { + Children.Add(child); + } + } + + class BoneTreeViewItem : TreeViewItem + { + //HumanBodyBones m_bone; + + public BoneTreeViewItem(int id, int depth, HumanBodyBones bone) : base(id, depth, bone.ToString()) + { + //m_bone = bone; + } + } + + class MuscleTreeViewItem : TreeViewItem + { + public int Muscle + { + get; + private set; + } + + public MuscleTreeViewItem(int id, int depth, int muscle) : base(id, depth, HumanTrait.MuscleName[muscle]) + { + Muscle = muscle; + } + } + + class BoneTreeView : TreeView + { + static BoneNode Skeleton = new BoneNode(HumanBodyBones.Hips) + { + new BoneNode(HumanBodyBones.Spine, 0, 1, 2){ + new BoneNode(HumanBodyBones.Chest, 3, 4, 5){ + new BoneNode(HumanBodyBones.UpperChest, 6, 7, 8){ + new BoneNode(HumanBodyBones.Neck, 9, 10, 11){ + new BoneNode(HumanBodyBones.Head, 12, 13, 14){ + new BoneNode(HumanBodyBones.LeftEye, 15, 16), + new BoneNode(HumanBodyBones.RightEye, 17, 18) + } + }, + new BoneNode(HumanBodyBones.LeftShoulder, 37, 38){ + new BoneNode(HumanBodyBones.LeftUpperArm, 39, 40, 41){ + new BoneNode(HumanBodyBones.LeftLowerArm, 42, 43){ + new BoneNode(HumanBodyBones.LeftHand, 44, 45) + } + } + }, + new BoneNode(HumanBodyBones.RightShoulder, 46, 47){ + new BoneNode(HumanBodyBones.RightUpperArm, 48, 49, 50){ + new BoneNode(HumanBodyBones.RightLowerArm, 51, 52){ + new BoneNode(HumanBodyBones.RightHand, 53, 54) + } + } + } + } + } + }, + new BoneNode(HumanBodyBones.LeftUpperLeg, 21, 22, 23){ + new BoneNode(HumanBodyBones.LeftLowerLeg, 24, 25){ + new BoneNode(HumanBodyBones.LeftFoot, 26, 27){ + new BoneNode(HumanBodyBones.LeftToes, 28) + } + } + }, + new BoneNode(HumanBodyBones.RightUpperLeg, 29, 30, 31){ + new BoneNode(HumanBodyBones.RightLowerLeg, 32, 33){ + new BoneNode(HumanBodyBones.RightFoot, 34, 35){ + new BoneNode(HumanBodyBones.RightToes, 36) + } + } + } + }; + + //Animator m_animator; + HumanPoseHandler m_handler; + HumanPose m_pose; + + bool m_updated; + + public void Begin() + { + m_handler.GetHumanPose(ref m_pose); + } + + public void End() + { + if (m_updated) + { + m_handler.SetHumanPose(ref m_pose); + } + m_updated = false; + } + + public BoneTreeView(TreeViewState treeViewState, MultiColumnHeader header, HumanPoseHandler handler) + : base(treeViewState, header) + { + m_handler = handler; + Reload(); + } + + protected override TreeViewItem BuildRoot() + { + return new TreeViewItem { id = 0, depth = -1 }; + } + + protected override IList BuildRows(TreeViewItem root) + { + var rows = GetRows() ?? new List(200); + + // We use the GameObject instanceIDs as ids for items as we want to + // select the game objects and not the transform components. + rows.Clear(); + + var item = CreateTreeViewItemForBone(HumanBodyBones.Hips); + root.AddChild(item); + rows.Add(item); + + if (IsExpanded(item.id)) + { + AddChildrenRecursive(Skeleton, item, rows); + } + else + { + item.children = CreateChildListForCollapsedParent(); + } + + SetupDepthsFromParentsAndChildren(root); + + return rows; + } + + void AddChildrenRecursive(BoneNode bone, TreeViewItem item, IList rows) + { + int childCount = bone.Children.Count; + item.children = new List(childCount); + if (bone.Muscles != null) + { + foreach (var muscle in bone.Muscles) + { + var childItem = new MuscleTreeViewItem(muscle + 20000, -1, muscle); + item.AddChild(childItem); + rows.Add(childItem); + } + } + + foreach (var child in bone.Children) + { + var childItem = CreateTreeViewItemForBone(child.Bone); + item.AddChild(childItem); + rows.Add(childItem); + + //if (child.Children.Count > 0) + { + if (IsExpanded(childItem.id)) + { + AddChildrenRecursive(child, childItem, rows); + } + else + { + childItem.children = CreateChildListForCollapsedParent(); + } + } + } + } + + static TreeViewItem CreateTreeViewItemForBone(HumanBodyBones bone) + { + return new TreeViewItem((int)bone, -1, Enum.GetName(typeof(HumanBodyBones), bone)); + } + + protected override void RowGUI(RowGUIArgs args) + { + for (int i = 0; i < args.GetNumVisibleColumns(); ++i) + { + CellGUI(args.GetCellRect(i), args.GetColumn(i), ref args); + } + } + + void CellGUI(Rect cellRect, int index, ref RowGUIArgs args) + { + // Center cell rect vertically (makes it easier to place controls, icons etc in the cells) + CenterRectUsingSingleLineHeight(ref cellRect); + + switch (index) + { + case 0: + { + // Default icon and label + args.rowRect = cellRect; + base.RowGUI(args); + } + break; + + case 1: + { + var muscleItem = args.item as MuscleTreeViewItem; + if (muscleItem != null) + { + var muscleIndex = muscleItem.Muscle; + var muscles = m_pose.muscles; + var value = EditorGUI.Slider(cellRect, GUIContent.none, muscles[muscleIndex], -1f, 1f); + if (value != muscles[muscleIndex]) + { + muscles[muscleIndex] = value; + m_updated = true; + } + } + else + { + + } + } + break; + } + } + + public static MultiColumnHeaderState CreateDefaultMultiColumnHeaderState() + { + var columns = new[] + { + new MultiColumnHeaderState.Column + { + headerContent = new GUIContent("Name"), + headerTextAlignment = TextAlignment.Left, + width = 250, + minWidth = 60, + autoResize = false, + allowToggleVisibility = false + }, + new MultiColumnHeaderState.Column + { + headerContent = new GUIContent("Muscle value"), + headerTextAlignment = TextAlignment.Left, + width = 110, + minWidth = 60, + autoResize = true + }, + }; + return new MultiColumnHeaderState(columns); + } + } + + + [CustomEditor(typeof(MuscleInspector))] + public class MuscleInspectorEditor : Editor + { + [NonSerialized] bool m_Initialized; + [SerializeField] TreeViewState m_TreeViewState; // Serialized in the window layout file so it survives assembly reloading + //[SerializeField] MultiColumnHeaderState m_MultiColumnHeaderState; + SearchField m_SearchField; + BoneTreeView m_TreeView; + + MuscleInspector m_target; + HumanPoseHandler m_handler; + + + MultiColumnHeader GetHeaderState() + { + //bool firstInit = m_MultiColumnHeaderState == null; + + var headerState = BoneTreeView.CreateDefaultMultiColumnHeaderState(); + /* + if (MultiColumnHeaderState.CanOverwriteSerializedFields(m_MultiColumnHeaderState, headerState)) + { + MultiColumnHeaderState.OverwriteSerializedFields(m_MultiColumnHeaderState, headerState); + } + m_MultiColumnHeaderState = headerState; + */ + var multiColumnHeader = new MultiColumnHeader(headerState); + multiColumnHeader.ResizeToFit(); + return multiColumnHeader; + } + + void OnEnable() + { + var mi = this.target as MuscleInspector; + var animator = mi.GetComponent(); + if (animator != null + && animator.avatar != null + && animator.avatar.isValid + && animator.avatar.isHuman + ) + { + Debug.LogFormat("MuscleInspectorEditor.OnEnable"); + m_handler = new HumanPoseHandler(animator.avatar, animator.transform); + + m_TreeView = new BoneTreeView(new TreeViewState(), GetHeaderState(), m_handler); + } + } + + void OnDisable() + { + if (m_handler != null) + { + m_handler.Dispose(); + m_handler = null; + } + } + + public override void OnInspectorGUI() + { + if (m_TreeView == null) + { + EditorGUILayout.HelpBox("Animator required", MessageType.Error); + return; + } + + var rect = GUILayoutUtility.GetRect(0, 10000, 0, m_TreeView.totalHeight); + m_TreeView.Begin(); + m_TreeView.OnGUI(rect); + m_TreeView.End(); + } + } +} diff --git a/Scripts/Editor/MuscleInspectorEditor.cs.meta b/Scripts/Editor/MuscleInspectorEditor.cs.meta new file mode 100644 index 000000000..101761bad --- /dev/null +++ b/Scripts/Editor/MuscleInspectorEditor.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: 0d2c3401d1adf41488c19ec4d47386a0 +timeCreated: 1541840728 +licenseType: Free +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Scripts/Editor/Tests.meta b/Scripts/Editor/Tests.meta new file mode 100644 index 000000000..3b8730642 --- /dev/null +++ b/Scripts/Editor/Tests.meta @@ -0,0 +1,9 @@ +fileFormatVersion: 2 +guid: fcdf450ca966f85428c238046e51c2ca +folderAsset: yes +timeCreated: 1534750149 +licenseType: Free +DefaultImporter: + userData: + assetBundleName: + assetBundleVariant: diff --git a/Scripts/Editor/Tests/BvhLoaderTests.cs b/Scripts/Editor/Tests/BvhLoaderTests.cs new file mode 100644 index 000000000..4a5722d26 --- /dev/null +++ b/Scripts/Editor/Tests/BvhLoaderTests.cs @@ -0,0 +1,1360 @@ +using NUnit.Framework; +using UnityEngine; + + +namespace UniHumanoid +{ +#if UNITY_5_6_OR_NEWER + public class BvhLoaderTests + { + #region LOUICE + /// + /// https://github.com/wspr/bvh-matlab/blob/master/louise.bvh + /// + const string bvh_louise = @"HIERARCHY +ROOT Hips +{ + OFFSET 0.000000 0.000000 0.000000 + CHANNELS 6 Xposition Yposition Zposition Zrotation Xrotation Yrotation + JOINT Chest + { + OFFSET -0.000000 30.833075 -0.000000 + CHANNELS 3 Zrotation Xrotation Yrotation + JOINT Neck + { + OFFSET -0.000000 23.115997 0.000000 + CHANNELS 3 Zrotation Xrotation Yrotation + JOINT Head + { + OFFSET -0.000000 10.266666 0.000000 + CHANNELS 3 Zrotation Xrotation Yrotation + End Site + { + OFFSET -0.000000 15.866669 0.000000 + } + } + } + JOINT LeftCollar + { + OFFSET -0.000000 23.115997 0.000000 + CHANNELS 3 Zrotation Xrotation Yrotation + JOINT LeftShoulder + { + OFFSET 18.666668 -0.000000 0.000000 + CHANNELS 3 Zrotation Xrotation Yrotation + JOINT LeftElbow + { + OFFSET 25.298601 0.000000 0.000000 + CHANNELS 3 Zrotation Xrotation Yrotation + JOINT LeftWrist + { + OFFSET 27.056377 0.000000 0.000000 + CHANNELS 3 Zrotation Xrotation Yrotation + End Site + { + OFFSET 0.000000 -14.000002 0.000000 + } + } + } + } + } + JOINT RightCollar + { + OFFSET -0.000000 23.115997 0.000000 + CHANNELS 3 Zrotation Xrotation Yrotation + JOINT RightShoulder + { + OFFSET -18.666668 0.000000 0.000000 + CHANNELS 3 Zrotation Xrotation Yrotation + JOINT RightElbow + { + OFFSET -25.298601 0.000000 0.000000 + CHANNELS 3 Zrotation Xrotation Yrotation + JOINT RightWrist + { + OFFSET -27.056377 0.000000 0.000000 + CHANNELS 3 Zrotation Xrotation Yrotation + End Site + { + OFFSET -0.000000 -14.000002 0.000000 + } + } + } + } + } + } + JOINT LeftHip + { + OFFSET 11.200000 0.000000 0.000000 + CHANNELS 3 Zrotation Xrotation Yrotation + JOINT LeftKnee + { + OFFSET -0.000000 -43.871983 0.000000 + CHANNELS 3 Zrotation Xrotation Yrotation + JOINT LeftAnkle + { + OFFSET -0.000000 -44.488350 0.000000 + CHANNELS 3 Zrotation Xrotation Yrotation + End Site + { + OFFSET -0.000000 -4.666667 15.866669 + } + } + } + } + JOINT RightHip + { + OFFSET -11.200000 0.000000 0.000000 + CHANNELS 3 Zrotation Xrotation Yrotation + JOINT RightKnee + { + OFFSET -0.000000 -43.871983 0.000000 + CHANNELS 3 Zrotation Xrotation Yrotation + JOINT RightAnkle + { + OFFSET -0.000000 -44.488350 0.000000 + CHANNELS 3 Zrotation Xrotation Yrotation + End Site + { + OFFSET -0.000000 -4.666667 15.866669 + } + } + } + } +} +"; + + [Test] + public void GuessBoneMapping_louise() + { + var bvh = Bvh.Parse(bvh_louise); + var detector = new BvhSkeletonEstimator(); + var skeleton = detector.Detect(bvh); + + Assert.AreEqual(0, skeleton.GetBoneIndex(HumanBodyBones.Hips)); + + Assert.AreEqual("Hips", skeleton.GetBoneName(HumanBodyBones.Hips)); + Assert.AreEqual("Chest", skeleton.GetBoneName(HumanBodyBones.Spine)); + Assert.IsNull(skeleton.GetBoneName(HumanBodyBones.Chest)); + Assert.AreEqual("Neck", skeleton.GetBoneName(HumanBodyBones.Neck)); + Assert.AreEqual("Head", skeleton.GetBoneName(HumanBodyBones.Head)); + + Assert.AreEqual("LeftCollar", skeleton.GetBoneName(HumanBodyBones.LeftShoulder)); + Assert.AreEqual("LeftShoulder", skeleton.GetBoneName(HumanBodyBones.LeftUpperArm)); + Assert.AreEqual("LeftElbow", skeleton.GetBoneName(HumanBodyBones.LeftLowerArm)); + Assert.AreEqual("LeftWrist", skeleton.GetBoneName(HumanBodyBones.LeftHand)); + + Assert.AreEqual("RightCollar", skeleton.GetBoneName(HumanBodyBones.RightShoulder)); + Assert.AreEqual("RightShoulder", skeleton.GetBoneName(HumanBodyBones.RightUpperArm)); + Assert.AreEqual("RightElbow", skeleton.GetBoneName(HumanBodyBones.RightLowerArm)); + Assert.AreEqual("RightWrist", skeleton.GetBoneName(HumanBodyBones.RightHand)); + + Assert.AreEqual("LeftHip", skeleton.GetBoneName(HumanBodyBones.LeftUpperLeg)); + Assert.AreEqual("LeftKnee", skeleton.GetBoneName(HumanBodyBones.LeftLowerLeg)); + Assert.AreEqual("LeftAnkle", skeleton.GetBoneName(HumanBodyBones.LeftFoot)); + Assert.IsNull(skeleton.GetBoneName(HumanBodyBones.LeftToes)); + + Assert.AreEqual("RightHip", skeleton.GetBoneName(HumanBodyBones.RightUpperLeg)); + Assert.AreEqual("RightKnee", skeleton.GetBoneName(HumanBodyBones.RightLowerLeg)); + Assert.AreEqual("RightAnkle", skeleton.GetBoneName(HumanBodyBones.RightFoot)); + Assert.IsNull(skeleton.GetBoneName(HumanBodyBones.RightToes)); + } + #endregion + + #region cgspeed + /// + /// https://sites.google.com/a/cgspeed.com/cgspeed/motion-capture + /// + const string bvh_cgspeed = @"HIERARCHY +ROOT Hips +{ + OFFSET 0.00000 0.00000 0.00000 + CHANNELS 6 Xposition Yposition Zposition Zrotation Yrotation Xrotation + JOINT LHipJoint + { + OFFSET 0 0 0 + CHANNELS 3 Zrotation Yrotation Xrotation + JOINT LeftUpLeg + { + OFFSET 1.64549 -1.70879 0.84566 + CHANNELS 3 Zrotation Yrotation Xrotation + JOINT LeftLeg + { + OFFSET 2.24963 -6.18082 0.00000 + CHANNELS 3 Zrotation Yrotation Xrotation + JOINT LeftFoot + { + OFFSET 2.71775 -7.46697 0.00000 + CHANNELS 3 Zrotation Yrotation Xrotation + JOINT LeftToeBase + { + OFFSET 0.18768 -0.51564 2.24737 + CHANNELS 3 Zrotation Yrotation Xrotation + End Site + { + OFFSET 0.00000 -0.00000 1.15935 + } + } + } + } + } + } + JOINT RHipJoint + { + OFFSET 0 0 0 + CHANNELS 3 Zrotation Yrotation Xrotation + JOINT RightUpLeg + { + OFFSET -1.58830 -1.70879 0.84566 + CHANNELS 3 Zrotation Yrotation Xrotation + JOINT RightLeg + { + OFFSET -2.25006 -6.18201 0.00000 + CHANNELS 3 Zrotation Yrotation Xrotation + JOINT RightFoot + { + OFFSET -2.72829 -7.49593 0.00000 + CHANNELS 3 Zrotation Yrotation Xrotation + JOINT RightToeBase + { + OFFSET -0.21541 -0.59185 2.10643 + CHANNELS 3 Zrotation Yrotation Xrotation + End Site + { + OFFSET -0.00000 -0.00000 1.09838 + } + } + } + } + } + } + JOINT LowerBack + { + OFFSET 0 0 0 + CHANNELS 3 Zrotation Yrotation Xrotation + JOINT Spine + { + OFFSET 0.03142 2.10496 -0.11038 + CHANNELS 3 Zrotation Yrotation Xrotation + JOINT Spine1 + { + OFFSET -0.01863 2.10897 -0.06956 + CHANNELS 3 Zrotation Yrotation Xrotation + JOINT Neck + { + OFFSET 0 0 0 + CHANNELS 3 Zrotation Yrotation Xrotation + JOINT Neck1 + { + OFFSET -0.02267 1.73238 0.00451 + CHANNELS 3 Zrotation Yrotation Xrotation + JOINT Head + { + OFFSET -0.05808 1.54724 -0.61749 + CHANNELS 3 Zrotation Yrotation Xrotation + End Site + { + OFFSET -0.01396 1.71468 -0.21082 + } + } + } + } + JOINT LeftShoulder + { + OFFSET 0 0 0 + CHANNELS 3 Zrotation Yrotation Xrotation + JOINT LeftArm + { + OFFSET 3.44898 0.50298 0.21920 + CHANNELS 3 Zrotation Yrotation Xrotation + JOINT LeftForeArm + { + OFFSET 5.41917 -0.00000 -0.00000 + CHANNELS 3 Zrotation Yrotation Xrotation + JOINT LeftHand + { + OFFSET 2.44373 -0.00000 0.00000 + CHANNELS 3 Zrotation Yrotation Xrotation + JOINT LeftFingerBase + { + OFFSET 0 0 0 + CHANNELS 3 Zrotation Yrotation Xrotation + JOINT LeftHandIndex1 + { + OFFSET 0.72750 -0.00000 0.00000 + CHANNELS 3 Zrotation Yrotation Xrotation + End Site + { + OFFSET 0.58653 -0.00000 0.00000 + } + } + } + JOINT LThumb + { + OFFSET 0 0 0 + CHANNELS 3 Zrotation Yrotation Xrotation + End Site + { + OFFSET 0.59549 -0.00000 0.59549 + } + } + } + } + } + } + JOINT RightShoulder + { + OFFSET 0 0 0 + CHANNELS 3 Zrotation Yrotation Xrotation + JOINT RightArm + { + OFFSET -3.23015 0.55830 0.31051 + CHANNELS 3 Zrotation Yrotation Xrotation + JOINT RightForeArm + { + OFFSET -5.58976 -0.00010 0.00014 + CHANNELS 3 Zrotation Yrotation Xrotation + JOINT RightHand + { + OFFSET -2.48060 -0.00000 0.00000 + CHANNELS 3 Zrotation Yrotation Xrotation + JOINT RightFingerBase + { + OFFSET 0 0 0 + CHANNELS 3 Zrotation Yrotation Xrotation + JOINT RightHandIndex1 + { + OFFSET -0.81601 -0.00000 0.00000 + CHANNELS 3 Zrotation Yrotation Xrotation + End Site + { + OFFSET -0.65789 -0.00000 0.00000 + } + } + } + JOINT RThumb + { + OFFSET 0 0 0 + CHANNELS 3 Zrotation Yrotation Xrotation + End Site + { + OFFSET -0.66793 -0.00000 0.66793 + } + } + } + } + } + } + } + } + } +}" +; + [Test] + public void GuessBoneMapping_cgspeed() + { + var bvh = Bvh.Parse(bvh_cgspeed); + var detector = new BvhSkeletonEstimator(); + var skeleton = detector.Detect(bvh); + + Assert.AreEqual(0, skeleton.GetBoneIndex(HumanBodyBones.Hips)); + + Assert.AreEqual("Hips", skeleton.GetBoneName(HumanBodyBones.Hips)); + Assert.AreEqual("LowerBack", skeleton.GetBoneName(HumanBodyBones.Spine)); + Assert.AreEqual("Spine", skeleton.GetBoneName(HumanBodyBones.Chest)); +#if UNITY_5_6_OR_NEWER + Assert.AreEqual("Spine1", skeleton.GetBoneName(HumanBodyBones.UpperChest)); +#endif + Assert.AreEqual("Neck", skeleton.GetBoneName(HumanBodyBones.Neck)); + Assert.AreEqual("Head", skeleton.GetBoneName(HumanBodyBones.Head)); + + Assert.AreEqual("LeftShoulder", skeleton.GetBoneName(HumanBodyBones.LeftShoulder)); + Assert.AreEqual("LeftArm", skeleton.GetBoneName(HumanBodyBones.LeftUpperArm)); + Assert.AreEqual("LeftForeArm", skeleton.GetBoneName(HumanBodyBones.LeftLowerArm)); + Assert.AreEqual("LeftHand", skeleton.GetBoneName(HumanBodyBones.LeftHand)); + + Assert.AreEqual("RightShoulder", skeleton.GetBoneName(HumanBodyBones.RightShoulder)); + Assert.AreEqual("RightArm", skeleton.GetBoneName(HumanBodyBones.RightUpperArm)); + Assert.AreEqual("RightForeArm", skeleton.GetBoneName(HumanBodyBones.RightLowerArm)); + Assert.AreEqual("RightHand", skeleton.GetBoneName(HumanBodyBones.RightHand)); + + Assert.AreEqual("LeftUpLeg", skeleton.GetBoneName(HumanBodyBones.LeftUpperLeg)); + Assert.AreEqual("LeftLeg", skeleton.GetBoneName(HumanBodyBones.LeftLowerLeg)); + Assert.AreEqual("LeftFoot", skeleton.GetBoneName(HumanBodyBones.LeftFoot)); + Assert.AreEqual("LeftToeBase", skeleton.GetBoneName(HumanBodyBones.LeftToes)); + + Assert.AreEqual("RightUpLeg", skeleton.GetBoneName(HumanBodyBones.RightUpperLeg)); + Assert.AreEqual("RightLeg", skeleton.GetBoneName(HumanBodyBones.RightLowerLeg)); + Assert.AreEqual("RightFoot", skeleton.GetBoneName(HumanBodyBones.RightFoot)); + Assert.AreEqual("RightToeBase", skeleton.GetBoneName(HumanBodyBones.RightToes)); + } + #endregion + + #region mocap + const string bvh_mocap = @"HIERARCHY +ROOT Hips +{ + OFFSET 0.000000 0.000000 0.000000 + CHANNELS 6 Xposition Yposition Zposition Zrotation Xrotation Yrotation + JOINT Spine + { + OFFSET -0.000000 7.509519 0.000000 + CHANNELS 3 Zrotation Xrotation Yrotation + JOINT Spine1 + { + OFFSET -0.000000 18.393364 0.000000 + CHANNELS 3 Zrotation Xrotation Yrotation + JOINT Neck + { + OFFSET -0.000000 20.224955 0.000000 + CHANNELS 3 Zrotation Xrotation Yrotation + JOINT Head + { + OFFSET -0.000000 14.194822 1.831590 + CHANNELS 3 Zrotation Xrotation Yrotation + End Site + { + OFFSET -0.000000 18.315899 0.000000 + } + } + } + JOINT LeftShoulder + { + OFFSET 3.663486 15.569419 -0.490481 + CHANNELS 3 Zrotation Xrotation Yrotation + JOINT LeftArm + { + OFFSET 14.246625 0.000000 0.000000 + CHANNELS 3 Zrotation Xrotation Yrotation + JOINT LeftForeArm + { + OFFSET 25.567986 0.000000 0.000000 + CHANNELS 3 Zrotation Xrotation Yrotation + JOINT LeftHand + { + OFFSET 29.965693 0.000000 0.000000 + CHANNELS 3 Zrotation Xrotation Yrotation + End Site + { + OFFSET 13.736924 0.000000 0.000000 + } + } + } + } + } + JOINT RightShoulder + { + OFFSET -3.661042 15.569419 -0.490481 + CHANNELS 3 Zrotation Xrotation Yrotation + JOINT RightArm + { + OFFSET -14.246625 0.000000 0.000000 + CHANNELS 3 Zrotation Xrotation Yrotation + JOINT RightForeArm + { + OFFSET -25.567986 0.000000 0.000000 + CHANNELS 3 Zrotation Xrotation Yrotation + JOINT RightHand + { + OFFSET -29.965693 0.000000 0.000000 + CHANNELS 3 Zrotation Xrotation Yrotation + End Site + { + OFFSET -13.736924 0.000000 0.000000 + } + } + } + } + } + } + } + JOINT LeftUpLeg + { + OFFSET 9.157949 0.000000 0.000000 + CHANNELS 3 Zrotation Xrotation Yrotation + JOINT LeftLeg + { + OFFSET -0.000000 -40.189121 0.000000 + CHANNELS 3 Zrotation Xrotation Yrotation + JOINT LeftFoot + { + OFFSET -0.000000 -39.816978 0.000000 + CHANNELS 3 Zrotation Xrotation Yrotation + JOINT LeftToeBase + { + OFFSET -0.000000 -5.952667 13.736924 + CHANNELS 3 Zrotation Xrotation Yrotation + End Site + { + OFFSET -0.000000 0.000000 3.663180 + } + } + } + } + } + JOINT RightUpLeg + { + OFFSET -9.157949 0.000000 0.000000 + CHANNELS 3 Zrotation Xrotation Yrotation + JOINT RightLeg + { + OFFSET -0.000000 -40.189121 0.000000 + CHANNELS 3 Zrotation Xrotation Yrotation + JOINT RightFoot + { + OFFSET -0.000000 -39.816978 0.000000 + CHANNELS 3 Zrotation Xrotation Yrotation + JOINT RightToeBase + { + OFFSET -0.000000 -5.952667 13.736924 + CHANNELS 3 Zrotation Xrotation Yrotation + End Site + { + OFFSET -0.000000 0.000000 3.663180 + } + } + } + } + } +} +"; + + [Test] + public void GuessBoneMapping_mocap() + { + var bvh = Bvh.Parse(bvh_mocap); + var detector = new BvhSkeletonEstimator(); + var skeleton = detector.Detect(bvh); + + Assert.AreEqual(0, skeleton.GetBoneIndex(HumanBodyBones.Hips)); + + Assert.AreEqual("Hips", skeleton.GetBoneName(HumanBodyBones.Hips)); + Assert.AreEqual("Spine", skeleton.GetBoneName(HumanBodyBones.Spine)); + Assert.AreEqual("Spine1", skeleton.GetBoneName(HumanBodyBones.Chest)); + + Assert.AreEqual(null, skeleton.GetBoneName(HumanBodyBones.UpperChest)); + + Assert.AreEqual("Neck", skeleton.GetBoneName(HumanBodyBones.Neck)); + Assert.AreEqual("Head", skeleton.GetBoneName(HumanBodyBones.Head)); + + Assert.AreEqual("LeftShoulder", skeleton.GetBoneName(HumanBodyBones.LeftShoulder)); + Assert.AreEqual("LeftArm", skeleton.GetBoneName(HumanBodyBones.LeftUpperArm)); + Assert.AreEqual("LeftForeArm", skeleton.GetBoneName(HumanBodyBones.LeftLowerArm)); + Assert.AreEqual("LeftHand", skeleton.GetBoneName(HumanBodyBones.LeftHand)); + + Assert.AreEqual("RightShoulder", skeleton.GetBoneName(HumanBodyBones.RightShoulder)); + Assert.AreEqual("RightArm", skeleton.GetBoneName(HumanBodyBones.RightUpperArm)); + Assert.AreEqual("RightForeArm", skeleton.GetBoneName(HumanBodyBones.RightLowerArm)); + Assert.AreEqual("RightHand", skeleton.GetBoneName(HumanBodyBones.RightHand)); + + Assert.AreEqual("LeftUpLeg", skeleton.GetBoneName(HumanBodyBones.LeftUpperLeg)); + Assert.AreEqual("LeftLeg", skeleton.GetBoneName(HumanBodyBones.LeftLowerLeg)); + Assert.AreEqual("LeftFoot", skeleton.GetBoneName(HumanBodyBones.LeftFoot)); + Assert.AreEqual("LeftToeBase", skeleton.GetBoneName(HumanBodyBones.LeftToes)); + + Assert.AreEqual("RightUpLeg", skeleton.GetBoneName(HumanBodyBones.RightUpperLeg)); + Assert.AreEqual("RightLeg", skeleton.GetBoneName(HumanBodyBones.RightLowerLeg)); + Assert.AreEqual("RightFoot", skeleton.GetBoneName(HumanBodyBones.RightFoot)); + Assert.AreEqual("RightToeBase", skeleton.GetBoneName(HumanBodyBones.RightToes)); + } + #endregion + + #region mocap2 + const string bvh_mocap2 = @"HIERARCHY +ROOT Hips +{ + OFFSET 0.000000 0.000000 0.000000 + CHANNELS 6 Xposition Yposition Zposition Yrotation Xrotation Zrotation + JOINT Chest + { + OFFSET 0.000000 10.678932 0.006280 + CHANNELS 3 Yrotation Xrotation Zrotation + JOINT Chest2 + { + OFFSET 0.000000 10.491159 -0.011408 + CHANNELS 3 Yrotation Xrotation Zrotation + JOINT Chest3 + { + OFFSET 0.000000 9.479342 0.000000 + CHANNELS 3 Yrotation Xrotation Zrotation + JOINT Chest4 + { + OFFSET 0.000000 9.479342 0.000000 + CHANNELS 3 Yrotation Xrotation Zrotation + JOINT Neck + { + OFFSET 0.000000 13.535332 0.000000 + CHANNELS 3 Yrotation Xrotation Zrotation + JOINT Head + { + OFFSET 0.000000 8.819083 -0.027129 + CHANNELS 3 Yrotation Xrotation Zrotation + End Site + { + OFFSET 0.000000 16.966594 -0.014170 + } + } + } + JOINT RightCollar + { + OFFSET -3.012546 7.545150 0.000000 + CHANNELS 3 Yrotation Xrotation Zrotation + JOINT RightShoulder + { + OFFSET -13.683099 0.000000 0.000000 + CHANNELS 3 Yrotation Xrotation Zrotation + JOINT RightElbow + { + OFFSET -26.359998 0.000000 0.000000 + CHANNELS 3 Yrotation Xrotation Zrotation + JOINT RightWrist + { + OFFSET -21.746691 0.000000 0.008601 + CHANNELS 3 Yrotation Xrotation Zrotation + End Site + { + OFFSET -16.348058 0.000000 0.000000 + } + } + } + } + } + JOINT LeftCollar + { + OFFSET 3.012546 7.545150 0.000000 + CHANNELS 3 Yrotation Xrotation Zrotation + JOINT LeftShoulder + { + OFFSET 13.683099 0.000000 0.000000 + CHANNELS 3 Yrotation Xrotation Zrotation + JOINT LeftElbow + { + OFFSET 26.359998 0.000000 0.000000 + CHANNELS 3 Yrotation Xrotation Zrotation + JOINT LeftWrist + { + OFFSET 21.746691 0.000000 0.008601 + CHANNELS 3 Yrotation Xrotation Zrotation + End Site + { + OFFSET 16.348058 0.000000 0.000000 + } + } + } + } + } + } + } + } + } + JOINT RightHip + { + OFFSET -8.622479 -0.030774 -0.003140 + CHANNELS 3 Yrotation Xrotation Zrotation + JOINT RightKnee + { + OFFSET 0.000000 -37.209160 -0.002630 + CHANNELS 3 Yrotation Xrotation Zrotation + JOINT RightAnkle + { + OFFSET 0.000000 -37.343279 -0.058479 + CHANNELS 3 Yrotation Xrotation Zrotation + JOINT RightToe + { + OFFSET 0.000000 -8.903465 15.088070 + CHANNELS 3 Yrotation Xrotation Zrotation + End Site + { + OFFSET 0.000000 -1.471739 6.884388 + } + } + } + } + } + JOINT LeftHip + { + OFFSET 8.622479 -0.030774 -0.003140 + CHANNELS 3 Yrotation Xrotation Zrotation + JOINT LeftKnee + { + OFFSET 0.000000 -37.209160 -0.002630 + CHANNELS 3 Yrotation Xrotation Zrotation + JOINT LeftAnkle + { + OFFSET 0.000000 -37.343279 -0.058479 + CHANNELS 3 Yrotation Xrotation Zrotation + JOINT LeftToe + { + OFFSET 0.000000 -8.903465 15.088070 + CHANNELS 3 Yrotation Xrotation Zrotation + End Site + { + OFFSET 0.000000 -1.471739 6.884388 + } + } + } + } + } +} +"; + + [Test] + public void GuessBoneMapping_mocap2() + { + var bvh = Bvh.Parse(bvh_mocap2); + var detector = new BvhSkeletonEstimator(); + var skeleton = detector.Detect(bvh); + + Assert.AreEqual(0, skeleton.GetBoneIndex(HumanBodyBones.Hips)); + + Assert.AreEqual("Hips", skeleton.GetBoneName(HumanBodyBones.Hips)); + + Assert.AreEqual("Chest", skeleton.GetBoneName(HumanBodyBones.Spine)); + Assert.AreEqual("Chest2", skeleton.GetBoneName(HumanBodyBones.Chest)); + Assert.AreEqual("Chest4", skeleton.GetBoneName(HumanBodyBones.UpperChest)); + + Assert.AreEqual("Neck", skeleton.GetBoneName(HumanBodyBones.Neck)); + Assert.AreEqual("Head", skeleton.GetBoneName(HumanBodyBones.Head)); + + Assert.AreEqual("LeftCollar", skeleton.GetBoneName(HumanBodyBones.LeftShoulder)); + Assert.AreEqual("LeftShoulder", skeleton.GetBoneName(HumanBodyBones.LeftUpperArm)); + Assert.AreEqual("LeftElbow", skeleton.GetBoneName(HumanBodyBones.LeftLowerArm)); + Assert.AreEqual("LeftWrist", skeleton.GetBoneName(HumanBodyBones.LeftHand)); + + Assert.AreEqual("RightCollar", skeleton.GetBoneName(HumanBodyBones.RightShoulder)); + Assert.AreEqual("RightShoulder", skeleton.GetBoneName(HumanBodyBones.RightUpperArm)); + Assert.AreEqual("RightElbow", skeleton.GetBoneName(HumanBodyBones.RightLowerArm)); + Assert.AreEqual("RightWrist", skeleton.GetBoneName(HumanBodyBones.RightHand)); + + Assert.AreEqual("LeftHip", skeleton.GetBoneName(HumanBodyBones.LeftUpperLeg)); + Assert.AreEqual("LeftKnee", skeleton.GetBoneName(HumanBodyBones.LeftLowerLeg)); + Assert.AreEqual("LeftAnkle", skeleton.GetBoneName(HumanBodyBones.LeftFoot)); + Assert.AreEqual("LeftToe", skeleton.GetBoneName(HumanBodyBones.LeftToes)); + + Assert.AreEqual("RightHip", skeleton.GetBoneName(HumanBodyBones.RightUpperLeg)); + Assert.AreEqual("RightKnee", skeleton.GetBoneName(HumanBodyBones.RightLowerLeg)); + Assert.AreEqual("RightAnkle", skeleton.GetBoneName(HumanBodyBones.RightFoot)); + Assert.AreEqual("RightToe", skeleton.GetBoneName(HumanBodyBones.RightToes)); + } + #endregion + + #region mocapdata.com + const string mocapdata_com_hierarchy = @"HIERARCHY +ROOT Hips +{ + OFFSET 17.1116 39.7036 -3.684 + CHANNELS 6 Xposition Yposition Zposition Zrotation Xrotation Yrotation + JOINT LeftHip + { + OFFSET 3.43 2.84217e-014 -2.22045e-015 + CHANNELS 3 Zrotation Xrotation Yrotation + JOINT LeftKnee + { + OFFSET 6.75016e-014 -18.47 4.4853e-014 + CHANNELS 3 Zrotation Xrotation Yrotation + JOINT LeftAnkle + { + OFFSET 1.52767e-013 -17.95 6.98996e-013 + CHANNELS 3 Zrotation Xrotation Yrotation + End Site + { + OFFSET 0 -3.12 0 + } + } + } + } + JOINT RightHip + { + OFFSET -3.43 7.10543e-015 -1.11022e-015 + CHANNELS 3 Zrotation Xrotation Yrotation + JOINT RightKnee + { + OFFSET -1.35003e-013 -18.47 -7.10543e-015 + CHANNELS 3 Zrotation Xrotation Yrotation + JOINT RightAnkle + { + OFFSET -2.92122e-012 -17.95 -3.606e-013 + CHANNELS 3 Zrotation Xrotation Yrotation + End Site + { + OFFSET 0 -3.12 0 + } + } + } + } + JOINT Chest + { + OFFSET 7.10543e-015 4.57 -9.32587e-015 + CHANNELS 3 Zrotation Xrotation Yrotation + JOINT Chest2 + { + OFFSET 3.55271e-015 6.57 0 + CHANNELS 3 Zrotation Xrotation Yrotation + JOINT LeftCollar + { + OFFSET 1.06 4.19 1.76 + CHANNELS 3 Zrotation Xrotation Yrotation + JOINT LeftShoulder + { + OFFSET 5.81 -2.84217e-014 -1.17684e-014 + CHANNELS 3 Zrotation Xrotation Yrotation + JOINT LeftElbow + { + OFFSET 1.7053e-013 -12.08 2.13163e-014 + CHANNELS 3 Zrotation Xrotation Yrotation + JOINT LeftWrist + { + OFFSET 5.96856e-013 -9.82 -6.39488e-014 + CHANNELS 3 Zrotation Xrotation Yrotation + End Site + { + OFFSET 0 -7.37 0 + } + } + } + } + } + JOINT RightCollar + { + OFFSET -1.06 4.19 1.76 + CHANNELS 3 Zrotation Xrotation Yrotation + JOINT RightShoulder + { + OFFSET -6.06 -2.13163e-014 -5.32907e-015 + CHANNELS 3 Zrotation Xrotation Yrotation + JOINT RightElbow + { + OFFSET -1.42109e-013 -11.08 1.59872e-014 + CHANNELS 3 Zrotation Xrotation Yrotation + JOINT RightWrist + { + OFFSET -5.32907e-013 -9.82 -1.28342e-013 + CHANNELS 3 Zrotation Xrotation Yrotation + End Site + { + OFFSET 0 -7.14001 0 + } + } + } + } + } + JOINT Neck + { + OFFSET 0 4.05 -7.54952e-015 + CHANNELS 3 Zrotation Xrotation Yrotation + JOINT Head + { + OFFSET -3.55271e-015 5.19 -1.95399e-014 + CHANNELS 3 Zrotation Xrotation Yrotation + End Site + { + OFFSET 0 4.14001 0 + } + } + } + } + } +} +"; + [Test] + public void GuessBoneMapping_mocapdatacom() + { + var bvh = Bvh.Parse(mocapdata_com_hierarchy); + var detector = new BvhSkeletonEstimator(); + var skeleton = detector.Detect(bvh); + + Assert.AreEqual(0, skeleton.GetBoneIndex(HumanBodyBones.Hips)); + + Assert.AreEqual("Hips", skeleton.GetBoneName(HumanBodyBones.Hips)); + Assert.AreEqual("Chest", skeleton.GetBoneName(HumanBodyBones.Spine)); + Assert.AreEqual("Chest2", skeleton.GetBoneName(HumanBodyBones.Chest)); + + Assert.AreEqual("Neck", skeleton.GetBoneName(HumanBodyBones.Neck)); + Assert.AreEqual("Head", skeleton.GetBoneName(HumanBodyBones.Head)); + + Assert.AreEqual("LeftCollar", skeleton.GetBoneName(HumanBodyBones.LeftShoulder)); + Assert.AreEqual("LeftShoulder", skeleton.GetBoneName(HumanBodyBones.LeftUpperArm)); + Assert.AreEqual("LeftElbow", skeleton.GetBoneName(HumanBodyBones.LeftLowerArm)); + Assert.AreEqual("LeftWrist", skeleton.GetBoneName(HumanBodyBones.LeftHand)); + + Assert.AreEqual("RightCollar", skeleton.GetBoneName(HumanBodyBones.RightShoulder)); + Assert.AreEqual("RightShoulder", skeleton.GetBoneName(HumanBodyBones.RightUpperArm)); + Assert.AreEqual("RightElbow", skeleton.GetBoneName(HumanBodyBones.RightLowerArm)); + Assert.AreEqual("RightWrist", skeleton.GetBoneName(HumanBodyBones.RightHand)); + + Assert.AreEqual("LeftHip", skeleton.GetBoneName(HumanBodyBones.LeftUpperLeg)); + Assert.AreEqual("LeftKnee", skeleton.GetBoneName(HumanBodyBones.LeftLowerLeg)); + Assert.AreEqual("LeftAnkle", skeleton.GetBoneName(HumanBodyBones.LeftFoot)); + + Assert.AreEqual("RightHip", skeleton.GetBoneName(HumanBodyBones.RightUpperLeg)); + Assert.AreEqual("RightKnee", skeleton.GetBoneName(HumanBodyBones.RightLowerLeg)); + Assert.AreEqual("RightAnkle", skeleton.GetBoneName(HumanBodyBones.RightFoot)); + } + + const string mocapdatacom_hierarchy_2 = @"HIERARCHY +ROOT reference +{ + OFFSET 0 0 0 + CHANNELS 6 Xposition Yposition Zposition Zrotation Xrotation Yrotation + JOINT Hips + { + OFFSET 18.0689 39.8301 -3.56659 + CHANNELS 3 Zrotation Xrotation Yrotation + JOINT LeftHip + { + OFFSET 3.43 0 -2.22045e-016 + CHANNELS 3 Zrotation Xrotation Yrotation + JOINT LeftKnee + { + OFFSET 6.03961e-014 -18.47 -2.24487e-013 + CHANNELS 3 Zrotation Xrotation Yrotation + JOINT LeftAnkle + { + OFFSET 3.90443e-012 -17.95 -2.54197e-012 + CHANNELS 3 Zrotation Xrotation Yrotation + End Site + { + OFFSET 0 -3.12 0 + } + } + } + } + JOINT RightHip + { + OFFSET -3.43 -2.84217e-014 -1.11022e-015 + CHANNELS 3 Zrotation Xrotation Yrotation + JOINT RightKnee + { + OFFSET 2.16716e-013 -18.47 2.24709e-013 + CHANNELS 3 Zrotation Xrotation Yrotation + JOINT RightAnkle + { + OFFSET 5.25446e-012 -17.95 3.2685e-012 + CHANNELS 3 Zrotation Xrotation Yrotation + End Site + { + OFFSET 0 -3.12 0 + } + } + } + } + JOINT Chest + { + OFFSET 0 4.57 -2.22045e-016 + CHANNELS 3 Zrotation Xrotation Yrotation + JOINT Chest2 + { + OFFSET -7.10543e-015 6.57 0 + CHANNELS 3 Zrotation Xrotation Yrotation + JOINT LeftCollar + { + OFFSET 1.06 4.19 1.76 + CHANNELS 3 Zrotation Xrotation Yrotation + JOINT LeftShoulder + { + OFFSET 5.81 2.13163e-014 7.54952e-015 + CHANNELS 3 Zrotation Xrotation Yrotation + JOINT LeftElbow + { + OFFSET 2.13163e-014 -12.08 8.34888e-014 + CHANNELS 3 Zrotation Xrotation Yrotation + JOINT LeftWrist + { + OFFSET 2.98428e-013 -9.82 1.61648e-013 + CHANNELS 3 Zrotation Xrotation Yrotation + End Site + { + OFFSET 0 -7.37 0 + } + } + } + } + } + JOINT RightCollar + { + OFFSET -1.06 4.19 1.76 + CHANNELS 3 Zrotation Xrotation Yrotation + JOINT RightShoulder + { + OFFSET -6.06 2.13163e-014 5.77316e-015 + CHANNELS 3 Zrotation Xrotation Yrotation + JOINT RightElbow + { + OFFSET 1.42109e-013 -11.08 -9.05942e-014 + CHANNELS 3 Zrotation Xrotation Yrotation + JOINT RightWrist + { + OFFSET 5.7554e-013 -9.82 -1.98952e-013 + CHANNELS 3 Zrotation Xrotation Yrotation + End Site + { + OFFSET 0 -7.14001 0 + } + } + } + } + } + JOINT Neck + { + OFFSET 0 4.05 8.88178e-016 + CHANNELS 3 Zrotation Xrotation Yrotation + JOINT Head + { + OFFSET -3.55271e-015 5.19 1.06581e-014 + CHANNELS 3 Zrotation Xrotation Yrotation + End Site + { + OFFSET 0 4.14001 0 + } + } + } + } + } + } +} +"; + + [Test] + public void GuessBoneMapping_mocapdatacom_2() + { + var bvh = Bvh.Parse(mocapdatacom_hierarchy_2); + var detector = new BvhSkeletonEstimator(); + var skeleton = detector.Detect(bvh); + + Assert.AreEqual("Hips", skeleton.GetBoneName(HumanBodyBones.Hips)); + Assert.AreEqual("Chest", skeleton.GetBoneName(HumanBodyBones.Spine)); + Assert.AreEqual("Chest2", skeleton.GetBoneName(HumanBodyBones.Chest)); + + Assert.AreEqual("Neck", skeleton.GetBoneName(HumanBodyBones.Neck)); + Assert.AreEqual("Head", skeleton.GetBoneName(HumanBodyBones.Head)); + + Assert.AreEqual("LeftCollar", skeleton.GetBoneName(HumanBodyBones.LeftShoulder)); + Assert.AreEqual("LeftShoulder", skeleton.GetBoneName(HumanBodyBones.LeftUpperArm)); + Assert.AreEqual("LeftElbow", skeleton.GetBoneName(HumanBodyBones.LeftLowerArm)); + Assert.AreEqual("LeftWrist", skeleton.GetBoneName(HumanBodyBones.LeftHand)); + + Assert.AreEqual("RightCollar", skeleton.GetBoneName(HumanBodyBones.RightShoulder)); + Assert.AreEqual("RightShoulder", skeleton.GetBoneName(HumanBodyBones.RightUpperArm)); + Assert.AreEqual("RightElbow", skeleton.GetBoneName(HumanBodyBones.RightLowerArm)); + Assert.AreEqual("RightWrist", skeleton.GetBoneName(HumanBodyBones.RightHand)); + + Assert.AreEqual("LeftHip", skeleton.GetBoneName(HumanBodyBones.LeftUpperLeg)); + Assert.AreEqual("LeftKnee", skeleton.GetBoneName(HumanBodyBones.LeftLowerLeg)); + Assert.AreEqual("LeftAnkle", skeleton.GetBoneName(HumanBodyBones.LeftFoot)); + + Assert.AreEqual("RightHip", skeleton.GetBoneName(HumanBodyBones.RightUpperLeg)); + Assert.AreEqual("RightKnee", skeleton.GetBoneName(HumanBodyBones.RightLowerLeg)); + Assert.AreEqual("RightAnkle", skeleton.GetBoneName(HumanBodyBones.RightFoot)); + } + #endregion + + #region daz_friendry + const string daz_friendry_herarchy = @"HIERARCHY +ROOT hip +{ + OFFSET 0 0 0 + CHANNELS 6 Xposition Yposition Zposition Zrotation Yrotation Xrotation + JOINT abdomen + { + OFFSET 0 20.6881 -0.73152 + CHANNELS 3 Zrotation Xrotation Yrotation + JOINT chest + { + OFFSET 0 11.7043 -0.48768 + CHANNELS 3 Zrotation Xrotation Yrotation + JOINT neck + { + OFFSET 0 22.1894 -2.19456 + CHANNELS 3 Zrotation Xrotation Yrotation + JOINT head + { + OFFSET -0.24384 7.07133 1.2192 + CHANNELS 3 Zrotation Xrotation Yrotation + JOINT leftEye + { + OFFSET 4.14528 8.04674 8.04672 + CHANNELS 3 Zrotation Xrotation Yrotation + End Site + { + OFFSET 1 0 0 + } + } + JOINT rightEye + { + OFFSET -3.6576 8.04674 8.04672 + CHANNELS 3 Zrotation Xrotation Yrotation + End Site + { + OFFSET 1 0 0 + } + } + } + } + JOINT rCollar + { + OFFSET -2.68224 19.2634 -4.8768 + CHANNELS 3 Zrotation Xrotation Yrotation + JOINT rShldr + { + OFFSET -8.77824 -1.95073 1.46304 + CHANNELS 3 Zrotation Xrotation Yrotation + JOINT rForeArm + { + OFFSET -28.1742 -1.7115 0.48768 + CHANNELS 3 Zrotation Xrotation Yrotation + JOINT rHand + { + OFFSET -22.5879 0.773209 7.07136 + CHANNELS 3 Zrotation Xrotation Yrotation + JOINT rThumb1 + { + OFFSET -1.2192 -0.487915 3.41376 + CHANNELS 3 Zrotation Xrotation Yrotation + JOINT rThumb2 + { + OFFSET -3.37035 -0.52449 3.41376 + CHANNELS 3 Zrotation Xrotation Yrotation + End Site + { + OFFSET -1.78271 -1.18214 1.43049 + } + } + } + JOINT rIndex1 + { + OFFSET -7.75947 0.938293 5.60832 + CHANNELS 3 Zrotation Xrotation Yrotation + JOINT rIndex2 + { + OFFSET -2.54057 -0.884171 1.56538 + CHANNELS 3 Zrotation Xrotation Yrotation + End Site + { + OFFSET -1.62519 -0.234802 1.16502 + } + } + } + JOINT rMid1 + { + OFFSET -8.24714 1.18213 3.41376 + CHANNELS 3 Zrotation Xrotation Yrotation + JOINT rMid2 + { + OFFSET -3.10165 -0.590103 1.0647 + CHANNELS 3 Zrotation Xrotation Yrotation + End Site + { + OFFSET -2.48547 -0.328903 0.83742 + } + } + } + JOINT rRing1 + { + OFFSET -8.82822 0.546677 1.51678 + CHANNELS 3 Zrotation Xrotation Yrotation + JOINT rRing2 + { + OFFSET -2.60934 -0.819778 -0.0198488 + CHANNELS 3 Zrotation Xrotation Yrotation + End Site + { + OFFSET -2.33842 -0.294052 0.168128 + } + } + } + JOINT rPinky1 + { + OFFSET -8.27202 -0.0477905 -0.4584 + CHANNELS 3 Zrotation Xrotation Yrotation + JOINT rPinky2 + { + OFFSET -1.82734 -0.647385 -0.700984 + CHANNELS 3 Zrotation Xrotation Yrotation + End Site + { + OFFSET -1.69225 -0.51767 -0.607171 + } + } + } + } + } + } + } + JOINT lCollar + { + OFFSET 2.68224 19.2634 -4.8768 + CHANNELS 3 Zrotation Xrotation Yrotation + JOINT lShldr + { + OFFSET 8.77824 -1.95073 1.46304 + CHANNELS 3 Zrotation Xrotation Yrotation + JOINT lForeArm + { + OFFSET 28.1742 -1.7115 0.48768 + CHANNELS 3 Zrotation Xrotation Yrotation + JOINT lHand + { + OFFSET 22.5879 0.773209 7.07136 + CHANNELS 3 Zrotation Xrotation Yrotation + JOINT lThumb1 + { + OFFSET 1.2192 -0.487915 3.41376 + CHANNELS 3 Zrotation Xrotation Yrotation + JOINT lThumb2 + { + OFFSET 3.37035 -0.52449 3.41376 + CHANNELS 3 Zrotation Xrotation Yrotation + End Site + { + OFFSET 1.78271 -1.18214 1.43049 + } + } + } + JOINT lIndex1 + { + OFFSET 7.75947 0.938293 5.60832 + CHANNELS 3 Zrotation Xrotation Yrotation + JOINT lIndex2 + { + OFFSET 2.54057 -0.884171 1.56538 + CHANNELS 3 Zrotation Xrotation Yrotation + End Site + { + OFFSET 1.62519 -0.234802 1.16502 + } + } + } + JOINT lMid1 + { + OFFSET 8.24714 1.18213 3.41376 + CHANNELS 3 Zrotation Xrotation Yrotation + JOINT lMid2 + { + OFFSET 3.10165 -0.590103 1.0647 + CHANNELS 3 Zrotation Xrotation Yrotation + End Site + { + OFFSET 2.48547 -0.328903 0.83742 + } + } + } + JOINT lRing1 + { + OFFSET 8.82822 0.546677 1.51678 + CHANNELS 3 Zrotation Xrotation Yrotation + JOINT lRing2 + { + OFFSET 2.60934 -0.819778 -0.0198488 + CHANNELS 3 Zrotation Xrotation Yrotation + End Site + { + OFFSET 2.33842 -0.294052 0.168128 + } + } + } + JOINT lPinky1 + { + OFFSET 8.27202 -0.0477905 -0.4584 + CHANNELS 3 Zrotation Xrotation Yrotation + JOINT lPinky2 + { + OFFSET 1.82734 -0.647385 -0.700984 + CHANNELS 3 Zrotation Xrotation Yrotation + End Site + { + OFFSET 1.69225 -0.51767 -0.607171 + } + } + } + } + } + } + } + } + } + JOINT rButtock + { + OFFSET -8.77824 4.35084 1.2192 + CHANNELS 3 Zrotation Xrotation Yrotation + JOINT rThigh + { + OFFSET 0 -1.70687 -2.19456 + CHANNELS 3 Zrotation Xrotation Yrotation + JOINT rShin + { + OFFSET 0 -36.8199 0.73152 + CHANNELS 3 Zrotation Xrotation Yrotation + JOINT rFoot + { + OFFSET 0.73152 -45.1104 -5.12064 + CHANNELS 3 Zrotation Xrotation Yrotation + End Site + { + OFFSET -1.1221 -3.69964 12.103 + } + } + } + } + } + JOINT lButtock + { + OFFSET 8.77824 4.35084 1.2192 + CHANNELS 3 Zrotation Xrotation Yrotation + JOINT lThigh + { + OFFSET 0 -1.70687 -2.19456 + CHANNELS 3 Zrotation Xrotation Yrotation + JOINT lShin + { + OFFSET 0 -36.8199 0.73152 + CHANNELS 3 Zrotation Xrotation Yrotation + JOINT lFoot + { + OFFSET -0.73152 -45.1104 -5.12064 + CHANNELS 3 Zrotation Xrotation Yrotation + End Site + { + OFFSET 1.1221 -3.69964 12.103 + } + } + } + } + } +}"; + + [Test] + public void GuessBoneMapping_daz_friendry() + { + var bvh = Bvh.Parse(daz_friendry_herarchy); + var detector = new BvhSkeletonEstimator(); + var skeleton = detector.Detect(bvh); + + Assert.AreEqual("hip", skeleton.GetBoneName(HumanBodyBones.Hips)); + Assert.AreEqual("abdomen", skeleton.GetBoneName(HumanBodyBones.Spine)); + Assert.AreEqual("chest", skeleton.GetBoneName(HumanBodyBones.Chest)); + + Assert.AreEqual("neck", skeleton.GetBoneName(HumanBodyBones.Neck)); + Assert.AreEqual("head", skeleton.GetBoneName(HumanBodyBones.Head)); + + Assert.AreEqual("lCollar", skeleton.GetBoneName(HumanBodyBones.LeftShoulder)); + Assert.AreEqual("lShldr", skeleton.GetBoneName(HumanBodyBones.LeftUpperArm)); + Assert.AreEqual("lForeArm", skeleton.GetBoneName(HumanBodyBones.LeftLowerArm)); + Assert.AreEqual("lHand", skeleton.GetBoneName(HumanBodyBones.LeftHand)); + + Assert.AreEqual("rCollar", skeleton.GetBoneName(HumanBodyBones.RightShoulder)); + Assert.AreEqual("rShldr", skeleton.GetBoneName(HumanBodyBones.RightUpperArm)); + Assert.AreEqual("rForeArm", skeleton.GetBoneName(HumanBodyBones.RightLowerArm)); + Assert.AreEqual("rHand", skeleton.GetBoneName(HumanBodyBones.RightHand)); + + Assert.AreEqual("lThigh", skeleton.GetBoneName(HumanBodyBones.LeftUpperLeg)); + Assert.AreEqual("lShin", skeleton.GetBoneName(HumanBodyBones.LeftLowerLeg)); + Assert.AreEqual("lFoot", skeleton.GetBoneName(HumanBodyBones.LeftFoot)); + + Assert.AreEqual("rThigh", skeleton.GetBoneName(HumanBodyBones.RightUpperLeg)); + Assert.AreEqual("rShin", skeleton.GetBoneName(HumanBodyBones.RightLowerLeg)); + Assert.AreEqual("rFoot", skeleton.GetBoneName(HumanBodyBones.RightFoot)); + } + #endregion + } +#endif +} diff --git a/Scripts/Editor/Tests/BvhLoaderTests.cs.meta b/Scripts/Editor/Tests/BvhLoaderTests.cs.meta new file mode 100644 index 000000000..31c5676b7 --- /dev/null +++ b/Scripts/Editor/Tests/BvhLoaderTests.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: 568764cb60017fc44ad4361d5a18b478 +timeCreated: 1534750164 +licenseType: Free +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Scripts/Editor/bvhAssetPostprocessor.cs b/Scripts/Editor/bvhAssetPostprocessor.cs new file mode 100644 index 000000000..009ea24fe --- /dev/null +++ b/Scripts/Editor/bvhAssetPostprocessor.cs @@ -0,0 +1,36 @@ +using System; +using System.IO; +using UnityEditor; +using UnityEngine; + + +namespace UniHumanoid +{ + public class bvhAssetPostprocessor : AssetPostprocessor + { + static void OnPostprocessAllAssets(string[] importedAssets, string[] deletedAssets, string[] movedAssets, string[] movedFromAssetPaths) + { + foreach (string path in importedAssets) + { + var ext = Path.GetExtension(path).ToLower(); + if (ext == ".bvh") + { + Debug.LogFormat("ImportBvh: {0}", path); + var context = new BvhImporterContext(); + try + { + context.Parse(path); + context.Load(); + context.SaveAsAsset(); + context.Destroy(false); + } + catch(Exception ex) + { + Debug.LogError(ex); + context.Destroy(true); + } + } + } + } + } +} diff --git a/Scripts/Editor/bvhAssetPostprocessor.cs.meta b/Scripts/Editor/bvhAssetPostprocessor.cs.meta new file mode 100644 index 000000000..3a58175b9 --- /dev/null +++ b/Scripts/Editor/bvhAssetPostprocessor.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: 4283558adf39c194cb6c5d66d5a348de +timeCreated: 1517166107 +licenseType: Free +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Scripts/Format.meta b/Scripts/Format.meta new file mode 100644 index 000000000..3ee3d5dba --- /dev/null +++ b/Scripts/Format.meta @@ -0,0 +1,9 @@ +fileFormatVersion: 2 +guid: fc31477377367fb48bc85f0864fe33eb +folderAsset: yes +timeCreated: 1517655048 +licenseType: Free +DefaultImporter: + userData: + assetBundleName: + assetBundleVariant: diff --git a/Scripts/Format/Bvh.cs b/Scripts/Format/Bvh.cs new file mode 100644 index 000000000..581eff6e5 --- /dev/null +++ b/Scripts/Format/Bvh.cs @@ -0,0 +1,444 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; + + +namespace UniHumanoid +{ + public class BvhException : Exception + { + public BvhException(string msg) : base(msg) { } + } + + public enum Channel + { + Xposition, + Yposition, + Zposition, + Xrotation, + Yrotation, + Zrotation, + } + public static class ChannelExtensions + { + public static string ToProperty(this Channel ch) + { + switch (ch) + { + case Channel.Xposition: return "localPosition.x"; + case Channel.Yposition: return "localPosition.y"; + case Channel.Zposition: return "localPosition.z"; + case Channel.Xrotation: return "localEulerAnglesBaked.x"; + case Channel.Yrotation: return "localEulerAnglesBaked.y"; + case Channel.Zrotation: return "localEulerAnglesBaked.z"; + } + + throw new BvhException("no property for " + ch); + } + + public static bool IsLocation(this Channel ch) + { + switch (ch) + { + case Channel.Xposition: + case Channel.Yposition: + case Channel.Zposition: return true; + case Channel.Xrotation: + case Channel.Yrotation: + case Channel.Zrotation: return false; + } + + throw new BvhException("no property for " + ch); + } + } + + public struct Single3 + { + public Single x; + public Single y; + public Single z; + + public Single3(Single _x, Single _y, Single _z) + { + x = _x; + y = _y; + z = _z; + } + } + + public class BvhNode + { + public String Name + { + get; + private set; + } + + public Single3 Offset + { + get; + private set; + } + + public Channel[] Channels + { + get; + private set; + } + + public List Children + { + get; + private set; + } + + public BvhNode(string name) + { + Name = name; + Children = new List(); + } + + public virtual void Parse(StringReader r) + { + Offset = ParseOffset(r.ReadLine()); + + Channels = ParseChannel(r.ReadLine()); + } + + static Single3 ParseOffset(string line) + { + var splited = line.Trim().Split(); + if (splited[0] != "OFFSET") + { + throw new BvhException("OFFSET is not found"); + } + + var offset = splited.Skip(1).Where(x => !string.IsNullOrEmpty(x)).Select(x => float.Parse(x)).ToArray(); + return new Single3(offset[0], offset[1], offset[2]); + } + + static Channel[] ParseChannel(string line) + { + var splited = line.Trim().Split(); + if (splited[0] != "CHANNELS") + { + throw new BvhException("CHANNELS is not found"); + } + var count = int.Parse(splited[1]); + if (count + 2 != splited.Length) + { + throw new BvhException("channel count is not match with splited count"); + } + return splited.Skip(2).Select(x => (Channel)Enum.Parse(typeof(Channel), x)).ToArray(); + } + + public IEnumerable Traverse() + { + yield return this; + + foreach (var child in Children) + { + foreach (var descentant in child.Traverse()) + { + yield return descentant; + } + } + } + } + + public class EndSite : BvhNode + { + public EndSite(): base("") + { + } + + public override void Parse(StringReader r) + { + r.ReadLine(); // offset + } + } + + public class ChannelCurve + { + public float[] Keys + { + get; + private set; + } + + public ChannelCurve(int frameCount) + { + Keys = new float[frameCount]; + } + + public void SetKey(int frame, float value) + { + Keys[frame] = value; + } + } + + public class Bvh + { + public BvhNode Root + { + get; + private set; + } + + public TimeSpan FrameTime + { + get; + private set; + } + + public ChannelCurve[] Channels + { + get; + private set; + } + + int m_frames; + public int FrameCount + { + get { return m_frames; } + } + + public struct PathWithProperty + { + public string Path; + public string Property; + public bool IsLocation; + } + + public bool TryGetPathWithPropertyFromChannel(ChannelCurve channel, out PathWithProperty pathWithProp) + { + var index = Channels.ToList().IndexOf(channel); + if (index == -1) + { + pathWithProp = default(PathWithProperty); + return false; + } + + foreach(var node in Root.Traverse()) + { + for(int i=0; i() { node.Name }; + + var current = node; + while (current!=null) + { + current = GetParent(current); + if (current != null) + { + list.Insert(0, current.Name); + } + } + + return String.Join("/", list.ToArray()); + } + + BvhNode GetParent(BvhNode node) + { + foreach(var x in Root.Traverse()) + { + if (x.Children.Contains(node)) + { + return x; + } + } + + return null; + } + + public ChannelCurve GetChannel(BvhNode target, Channel channel) + { + var index = 0; + foreach (var node in Root.Traverse()) + { + for (int i = 0; i < node.Channels.Length; ++i, ++index) + { + if(node==target && node.Channels[i] == channel) + { + return Channels[index]; + } + } + } + + throw new BvhException("channel is not found"); + } + + public override string ToString() + { + return string.Format("{0}nodes, {1}channels, {2}frames, {3:0.00}seconds" + , Root.Traverse().Count() + , Channels.Length + , m_frames + , m_frames * FrameTime.TotalSeconds); + } + + public Bvh(BvhNode root, int frames, float seconds) + { + Root = root; + FrameTime = TimeSpan.FromSeconds(seconds); + m_frames = frames; + var channelCount = Root.Traverse() + .Where(x => x.Channels!=null) + .Select(x => x.Channels.Length) + .Sum(); + Channels = Enumerable.Range(0, channelCount) + .Select(x => new ChannelCurve(frames)) + .ToArray() + ; + } + + public void ParseFrame(int frame, string line) + { + var splited = line.Trim().Split().Where(x => !string.IsNullOrEmpty(x)).ToArray(); + if (splited.Length != Channels.Length) + { + throw new BvhException("frame key count is not match channel count"); + } + for(int i=0; i(HumanPoseClip.TPoseResourcePath); + var pose = humanPoseClip.GetPose(); + HumanPoseTransfer.SetPose(avatar, transform, pose); + } + #endregion + + private void Reset() + { + var animator = GetComponent(); + if (animator != null) + { + Avatar = animator.avatar; + } + } + + [SerializeField] + public HumanPoseTransfer Source; + + [SerializeField] + public HumanPoseClip PoseClip; + + [ContextMenu("Set T-Pose")] + void SetTPose() + { + if (Avatar == null) return; + SetTPose(Avatar, transform); + } + + HumanPoseHandler m_handler; + public void OnEnable() + { + var animator = GetComponent(); + if (animator != null) + { + Avatar = animator.avatar; + } + + Setup(); + } + + public void Setup() + { + if (Avatar == null) + { + return; + } + m_handler = new HumanPoseHandler(Avatar, transform); + } + + HumanPose m_pose; + + int m_lastFrameCount = -1; + + public bool GetPose(int frameCount, ref HumanPose pose) + { + if (PoseClip != null) + { + pose = PoseClip.GetPose(); + return true; + } + + if (m_handler == null) + { + pose = m_pose; + return false; + } + + if (frameCount != m_lastFrameCount) + { + m_handler.GetHumanPose(ref m_pose); + m_lastFrameCount = frameCount; + } + pose = m_pose; + return true; + } + + private void Update() + { + switch (SourceType) + { + case HumanPoseTransferSourceType.None: + break; + + case HumanPoseTransferSourceType.HumanPoseTransfer: + if (Source != null && m_handler != null) + { + if (Source.GetPose(Time.frameCount, ref m_pose)) + { + m_handler.SetHumanPose(ref m_pose); + } + } + break; + + case HumanPoseTransferSourceType.HumanPoseClip: + if (PoseClip != null) + { + var pose = PoseClip.GetPose(); + m_handler.SetHumanPose(ref pose); + } + break; + } + } + } +} diff --git a/Scripts/HumanPoseTransfer.cs.meta b/Scripts/HumanPoseTransfer.cs.meta new file mode 100644 index 000000000..d1dbf4241 --- /dev/null +++ b/Scripts/HumanPoseTransfer.cs.meta @@ -0,0 +1,13 @@ +fileFormatVersion: 2 +guid: e5548e4785b76854cbbf3f6d9a8e9c1d +timeCreated: 1517666226 +licenseType: Free +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Scripts/IBone.cs b/Scripts/IBone.cs new file mode 100644 index 000000000..892b606e4 --- /dev/null +++ b/Scripts/IBone.cs @@ -0,0 +1,41 @@ +using System.Collections.Generic; +using UnityEngine; + + +namespace UniHumanoid +{ + public interface IBone + { + string Name { get; } + Vector3 SkeletonLocalPosition { get; } + IBone Parent { get; } + IList Children { get; } + } + + public static class IBoneExtensions + { + public static IEnumerable Traverse(this IBone self) + { + yield return self; + foreach (var child in self.Children) + { + foreach (var x in child.Traverse()) + { + yield return x; + } + } + } + + public static Vector3 CenterOfDescendant(this IBone self) + { + var sum = Vector3.zero; + int i = 0; + foreach (var x in self.Traverse()) + { + sum += x.SkeletonLocalPosition; + ++i; + } + return sum / i; + } + } +} diff --git a/Scripts/IBone.cs.meta b/Scripts/IBone.cs.meta new file mode 100644 index 000000000..4a8df933e --- /dev/null +++ b/Scripts/IBone.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: c9c2e3ee9fe29a84cb902e54a90c5dec +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Scripts/IO.meta b/Scripts/IO.meta new file mode 100644 index 000000000..9a8d776ba --- /dev/null +++ b/Scripts/IO.meta @@ -0,0 +1,9 @@ +fileFormatVersion: 2 +guid: e07bcccfa1de56943ab8e18f8791740d +folderAsset: yes +timeCreated: 1517655053 +licenseType: Free +DefaultImporter: + userData: + assetBundleName: + assetBundleVariant: diff --git a/Scripts/IO/BvhAnimationClip.cs b/Scripts/IO/BvhAnimationClip.cs new file mode 100644 index 000000000..f919ab7c6 --- /dev/null +++ b/Scripts/IO/BvhAnimationClip.cs @@ -0,0 +1,131 @@ +using System; +using System.Collections.Generic; +using UnityEngine; + + +namespace UniHumanoid +{ + public static class BvhAnimation + { + class CurveSet + { + BvhNode Node; + Func EulerToRotation; + public CurveSet(BvhNode node) + { + Node = node; + } + + public ChannelCurve PositionX; + public ChannelCurve PositionY; + public ChannelCurve PositionZ; + public Vector3 GetPosition(int i) + { + return new Vector3( + PositionX.Keys[i], + PositionY.Keys[i], + PositionZ.Keys[i]); + } + + public ChannelCurve RotationX; + public ChannelCurve RotationY; + public ChannelCurve RotationZ; + public Quaternion GetRotation(int i) + { + if (EulerToRotation == null) + { + EulerToRotation = Node.GetEulerToRotation(); + } + return EulerToRotation( + RotationX.Keys[i], + RotationY.Keys[i], + RotationZ.Keys[i] + ); + } + + static void AddCurve(Bvh bvh, AnimationClip clip, ChannelCurve ch, float scaling) + { + if (ch == null) return; + var pathWithProp = default(Bvh.PathWithProperty); + bvh.TryGetPathWithPropertyFromChannel(ch, out pathWithProp); + var curve = new AnimationCurve(); + for (int i = 0; i < bvh.FrameCount; ++i) + { + var time = (float)(i * bvh.FrameTime.TotalSeconds); + var value = ch.Keys[i] * scaling; + curve.AddKey(time, value); + } + clip.SetCurve(pathWithProp.Path, typeof(Transform), pathWithProp.Property, curve); + } + + public void AddCurves(Bvh bvh, AnimationClip clip, float scaling) + { + AddCurve(bvh, clip, PositionX, -scaling); + AddCurve(bvh, clip, PositionY, scaling); + AddCurve(bvh, clip, PositionZ, scaling); + + var pathWithProp = default(Bvh.PathWithProperty); + bvh.TryGetPathWithPropertyFromChannel(RotationX, out pathWithProp); + + // rotation + var curveX = new AnimationCurve(); + var curveY = new AnimationCurve(); + var curveZ = new AnimationCurve(); + var curveW = new AnimationCurve(); + for (int i = 0; i < bvh.FrameCount; ++i) + { + var time = (float)(i * bvh.FrameTime.TotalSeconds); + var q = GetRotation(i).ReverseX(); + curveX.AddKey(time, q.x); + curveY.AddKey(time, q.y); + curveZ.AddKey(time, q.z); + curveW.AddKey(time, q.w); + } + clip.SetCurve(pathWithProp.Path, typeof(Transform), "localRotation.x", curveX); + clip.SetCurve(pathWithProp.Path, typeof(Transform), "localRotation.y", curveY); + clip.SetCurve(pathWithProp.Path, typeof(Transform), "localRotation.z", curveZ); + clip.SetCurve(pathWithProp.Path, typeof(Transform), "localRotation.w", curveW); + } + } + + public static AnimationClip CreateAnimationClip(Bvh bvh, float scaling) + { + var clip = new AnimationClip(); + clip.legacy = true; + + var curveMap = new Dictionary(); + + int j = 0; + foreach (var node in bvh.Root.Traverse()) + { + var set = new CurveSet(node); + curveMap[node] = set; + + for (int i = 0; i < node.Channels.Length; ++i, ++j) + { + var curve = bvh.Channels[j]; + switch (node.Channels[i]) + { + case Channel.Xposition: set.PositionX = curve; break; + case Channel.Yposition: set.PositionY = curve; break; + case Channel.Zposition: set.PositionZ = curve; break; + case Channel.Xrotation: set.RotationX = curve; break; + case Channel.Yrotation: set.RotationY = curve; break; + case Channel.Zrotation: set.RotationZ = curve; break; + default: throw new Exception(); + } + } + } + + foreach (var set in curveMap) + { + set.Value.AddCurves(bvh, clip, scaling); + } + + clip.EnsureQuaternionContinuity(); + + return clip; + } + + } +} diff --git a/Scripts/IO/BvhAnimationClip.cs.meta b/Scripts/IO/BvhAnimationClip.cs.meta new file mode 100644 index 000000000..0348a6a9e --- /dev/null +++ b/Scripts/IO/BvhAnimationClip.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: 45951aadbd5e6b34282cd17ee163f58b +timeCreated: 1517655805 +licenseType: Free +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Scripts/IO/BvhImporter.cs b/Scripts/IO/BvhImporter.cs new file mode 100644 index 000000000..9f5406f46 --- /dev/null +++ b/Scripts/IO/BvhImporter.cs @@ -0,0 +1,15 @@ +using System; + + +namespace UniHumanoid +{ + public static class BvhImporter + { + [Obsolete("use BvhImporter.Parse(path), then BvhImporter.Load()")] + public static void Import(BvhImporterContext context) + { + context.Parse(context.Path); + context.Load(); + } + } +} diff --git a/Scripts/IO/BvhImporter.cs.meta b/Scripts/IO/BvhImporter.cs.meta new file mode 100644 index 000000000..335ced190 --- /dev/null +++ b/Scripts/IO/BvhImporter.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: da8ccd6cbe1692448872063195e5c71b +timeCreated: 1517655539 +licenseType: Free +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Scripts/IO/BvhImporterContext.cs b/Scripts/IO/BvhImporterContext.cs new file mode 100644 index 000000000..d816d1cef --- /dev/null +++ b/Scripts/IO/BvhImporterContext.cs @@ -0,0 +1,247 @@ +using System; +using System.Linq; +using System.Collections.Generic; +using UnityEngine; +using System.IO; +using System.Text; +#if UNITY_EDITOR +using UnityEditor; +#endif + + +namespace UniHumanoid +{ + [Obsolete("use BvhImporterContext")] + public class ImporterContext: BvhImporterContext + { + } + + public class BvhImporterContext + { + #region Source + String m_path; + public String Path + { + get { return m_path; } + set + { + if (m_path == value) return; + m_path = value; + } + } + public String Source; // source + public Bvh Bvh; + #endregion + + #region Imported + public GameObject Root; + public List Nodes = new List(); + public AnimationClip Animation; + public AvatarDescription AvatarDescription; + public Avatar Avatar; + public Mesh Mesh; + public Material Material; + #endregion + + #region Load + [Obsolete("use Load(path)")] + public void Parse() + { + Parse(Path); + } + + public void Parse(string path) + { + Path = path; + Source = File.ReadAllText(Path, Encoding.UTF8); + Bvh = Bvh.Parse(Source); + } + + public void Load() + { + // + // build hierarchy + // + Root = new GameObject(System.IO.Path.GetFileNameWithoutExtension(Path)); + var hips = BuildHierarchy(Root.transform, Bvh.Root, 1.0f); + var skeleton = Skeleton.Estimate(hips); + var description = AvatarDescription.Create(hips.Traverse().ToArray(), skeleton); + + // + // 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 Root.transform.Traverse()) + { + x.localPosition *= scaling; + } + + var scaledHeight = hipHeight * scaling; + hips.position = new Vector3(0, scaledHeight, 0); // foot to ground + } + + // + // avatar + // + Avatar = description.CreateAvatar(Root.transform); + Avatar.name = "Avatar"; + AvatarDescription = description; + var animator = Root.AddComponent(); + animator.avatar = Avatar; + + // + // create AnimationClip + // + Animation = BvhAnimation.CreateAnimationClip(Bvh, scaling); + Animation.name = Root.name; + Animation.legacy = true; + Animation.wrapMode = WrapMode.Loop; + + var animation = Root.AddComponent(); + animation.AddClip(Animation, Animation.name); + animation.clip = Animation; + animation.Play(); + + var humanPoseTransfer = Root.AddComponent(); + humanPoseTransfer.Avatar = Avatar; + + // create SkinnedMesh for bone visualize + var renderer = SkeletonMeshUtility.CreateRenderer(animator); + Material = new Material(Shader.Find("Standard")); + renderer.sharedMaterial = Material; + Mesh = renderer.sharedMesh; + Mesh.name = "box-man"; + + Root.AddComponent(); + + } + + static Transform BuildHierarchy(Transform parent, BvhNode node, float toMeter) + { + var go = new GameObject(node.Name); + go.transform.localPosition = node.Offset.ToXReversedVector3() * toMeter; + go.transform.SetParent(parent, false); + + //var gizmo = go.AddComponent(); + //gizmo.Draw = true; + + foreach (var child in node.Children) + { + BuildHierarchy(go.transform, child, toMeter); + } + + return go.transform; + } + #endregion + +#if UNITY_EDITOR + protected virtual string GetPrefabPath() + { + var dir = System.IO.Path.GetDirectoryName(Path); + var name = System.IO.Path.GetFileNameWithoutExtension(Path); + var prefabPath = string.Format("{0}/{1}.prefab", dir, name); + if (!Application.isPlaying && File.Exists(prefabPath)) + { + // already exists + if (IsOwn(prefabPath)) + { + //Debug.LogFormat("already exist. own: {0}", prefabPath); + } + else + { + // but unknown prefab + var unique = AssetDatabase.GenerateUniqueAssetPath(prefabPath); + //Debug.LogFormat("already exist: {0} => {1}", prefabPath, unique); + prefabPath = unique; + } + } + return prefabPath; + } + + #region Assets + IEnumerable GetSubAssets(string path) + { + return AssetDatabase.LoadAllAssetsAtPath(path); + } + + protected virtual bool IsOwn(string path) + { + foreach (var x in GetSubAssets(path)) + { + //if (x is Transform) continue; + if (x is GameObject) continue; + if (x is Component) continue; + if (AssetDatabase.IsSubAsset(x)) + { + return true; + } + } + return false; + } + + IEnumerable ObjectsForSubAsset() + { + if (Animation != null) yield return Animation; + if (AvatarDescription != null) yield return AvatarDescription; + if (Avatar != null) yield return Avatar; + if (Mesh != null) yield return Mesh; + if (Material != null) yield return Material; + } + + public void SaveAsAsset() + { + var path = GetPrefabPath(); + if (File.Exists(path)) + { + // clear SubAssets + foreach (var x in GetSubAssets(path).Where(x => !(x is GameObject) && !(x is Component))) + { + GameObject.DestroyImmediate(x, true); + } + } + + // Add SubAsset + foreach (var o in ObjectsForSubAsset()) + { + AssetDatabase.AddObjectToAsset(o, path); + } + + // Create or upate Main Asset + if (File.Exists(path)) + { + Debug.LogFormat("replace prefab: {0}", path); + var prefab = AssetDatabase.LoadAssetAtPath(path); + PrefabUtility.ReplacePrefab(Root, prefab, ReplacePrefabOptions.ReplaceNameBased); + } + else + { + Debug.LogFormat("create prefab: {0}", path); + PrefabUtility.CreatePrefab(path, Root); + } + + AssetDatabase.ImportAsset(path); + } + #endregion +#endif + + public void Destroy(bool destroySubAssets) + { + if (Root != null) GameObject.DestroyImmediate(Root); + if (destroySubAssets) + { +#if UNITY_EDITOR + foreach (var o in ObjectsForSubAsset()) + { + UnityEngine.Object.DestroyImmediate(o, true); + } +#endif + } + } + } +} diff --git a/Scripts/IO/BvhImporterContext.cs.meta b/Scripts/IO/BvhImporterContext.cs.meta new file mode 100644 index 000000000..79a8c6e75 --- /dev/null +++ b/Scripts/IO/BvhImporterContext.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: 6c6231d0aafc9554ca596f35e8f395c4 +timeCreated: 1521112950 +licenseType: Free +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Scripts/IO/Extensions.meta b/Scripts/IO/Extensions.meta new file mode 100644 index 000000000..69f5a007f --- /dev/null +++ b/Scripts/IO/Extensions.meta @@ -0,0 +1,9 @@ +fileFormatVersion: 2 +guid: 9f2cfbad0949d5840879540d0fce61fe +folderAsset: yes +timeCreated: 1517655230 +licenseType: Free +DefaultImporter: + userData: + assetBundleName: + assetBundleVariant: diff --git a/Scripts/IO/Extensions/BvhExtensions.cs b/Scripts/IO/Extensions/BvhExtensions.cs new file mode 100644 index 000000000..2ac0e0179 --- /dev/null +++ b/Scripts/IO/Extensions/BvhExtensions.cs @@ -0,0 +1,45 @@ +using System; +using System.Linq; +using UnityEngine; + + +namespace UniHumanoid +{ + public static class BvhExtensions + { + public static Func GetEulerToRotation(this BvhNode bvh) + { + var order = bvh.Channels.Where(x => x == Channel.Xrotation || x == Channel.Yrotation || x == Channel.Zrotation).ToArray(); + + return (x, y, z) => + { + var xRot = Quaternion.Euler(x, 0, 0); + var yRot = Quaternion.Euler(0, y, 0); + var zRot = Quaternion.Euler(0, 0, z); + + var r = Quaternion.identity; + foreach (var ch in order) + { + switch (ch) + { + case Channel.Xrotation: r = r * xRot; break; + case Channel.Yrotation: r = r * yRot; break; + case Channel.Zrotation: r = r * zRot; break; + default: throw new BvhException("no rotation"); + } + } + return r; + }; + } + + public static Vector3 ToVector3(this Single3 s3) + { + return new Vector3(s3.x, s3.y, s3.z); + } + + public static Vector3 ToXReversedVector3(this Single3 s3) + { + return new Vector3(-s3.x, s3.y, s3.z); + } + } +} diff --git a/Scripts/IO/Extensions/BvhExtensions.cs.meta b/Scripts/IO/Extensions/BvhExtensions.cs.meta new file mode 100644 index 000000000..a078ba97e --- /dev/null +++ b/Scripts/IO/Extensions/BvhExtensions.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: 30d4db9a01d6aee4db9a77d762e608f6 +timeCreated: 1517655238 +licenseType: Free +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Scripts/IO/Extensions/UnityExtensions.cs b/Scripts/IO/Extensions/UnityExtensions.cs new file mode 100644 index 000000000..0e4fb48a3 --- /dev/null +++ b/Scripts/IO/Extensions/UnityExtensions.cs @@ -0,0 +1,49 @@ +using System.Collections.Generic; +using UnityEngine; + + +namespace UniHumanoid +{ + public static class UnityExtensions + { + public static Quaternion ReverseX(this Quaternion quaternion) + { + float angle; + Vector3 axis; + quaternion.ToAngleAxis(out angle, out axis); + + return Quaternion.AngleAxis(-angle, new Vector3(-axis.x, axis.y, axis.z)); + } + + public static IEnumerable GetChildren(this Transform parent) + { + foreach (Transform child in parent) + { + yield return child; + } + } + + public static IEnumerable Traverse(this Transform parent) + { + yield return parent; + + foreach (Transform child in parent) + { + foreach (Transform descendant in Traverse(child)) + { + yield return descendant; + } + } + } + + public static SkeletonBone ToSkeletonBone(this Transform t) + { + var sb = new SkeletonBone(); + sb.name = t.name; + sb.position = t.localPosition; + sb.rotation = t.localRotation; + sb.scale = t.localScale; + return sb; + } + } +} diff --git a/Scripts/IO/Extensions/UnityExtensions.cs.meta b/Scripts/IO/Extensions/UnityExtensions.cs.meta new file mode 100644 index 000000000..918c91cd7 --- /dev/null +++ b/Scripts/IO/Extensions/UnityExtensions.cs.meta @@ -0,0 +1,13 @@ +fileFormatVersion: 2 +guid: 6c4a2b80b74e499428637f36774bbdcf +timeCreated: 1517666834 +licenseType: Free +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Scripts/MuscleDebug.cs b/Scripts/MuscleDebug.cs new file mode 100644 index 000000000..f788416cc --- /dev/null +++ b/Scripts/MuscleDebug.cs @@ -0,0 +1,105 @@ +using System; +using System.Linq; +using UnityEngine; +#if UNITY_EDITOR +using UnityEditor; +#endif + + +namespace UniHumanoid +{ + public class MuscleDebug : MonoBehaviour + { + Avatar GetAvatar() + { + var animator = GetComponent(); + if (animator != null && animator.avatar != null) + { + return animator.avatar; + } + + var transfer = GetComponent(); + if (transfer != null && transfer.Avatar != null) + { + return transfer.Avatar; + } + + return null; + } + + HumanPoseHandler m_handler; + + public HumanPose m_pose; + + [Serializable] + public struct Muscle + { + public int Index; + public string Name; + public float Value; + } + + public Vector3 BodyPosition; + + public Muscle[] Muscles; + + private void OnEnable() + { + var avatar = GetAvatar(); + if (avatar == null) + { + enabled = false; + return; + } + + m_handler = new HumanPoseHandler(avatar, transform); + + Muscles = HumanTrait.MuscleName.Select((x, i) => + { + return new Muscle + { + Index = i, + Name = x, + }; + }) + .ToArray() + ; + } + + private void OnDisable() + { + } + + private void Update() + { + m_handler.GetHumanPose(ref m_pose); + + BodyPosition = m_pose.bodyPosition; + + for (int i = 0; i < m_pose.muscles.Length; ++i) + { + Muscles[i].Value = m_pose.muscles[i]; + } + } + } + +#if UNITY_EDITOR + [CustomPropertyDrawer(typeof(MuscleDebug.Muscle))] + public class MuscleDrawer : PropertyDrawer + { + public override void OnGUI(Rect position, + SerializedProperty property, GUIContent label) + { + var nameProp = property.FindPropertyRelative("Name"); + var valueProp = property.FindPropertyRelative("Value"); + /* + var labl = string.Format("{0}: {1}", + nameProp.stringValue, + valueProp.floatValue + ); + */ + EditorGUI.LabelField(position, nameProp.stringValue, string.Format("{0:0.00}", valueProp.floatValue)); + } + } +#endif +} diff --git a/Scripts/MuscleDebug.cs.meta b/Scripts/MuscleDebug.cs.meta new file mode 100644 index 000000000..0740b686b --- /dev/null +++ b/Scripts/MuscleDebug.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: 14aaa52c3da53004ea85dbb38e9e4698 +timeCreated: 1521114691 +licenseType: Free +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Scripts/MuscleInspector.cs b/Scripts/MuscleInspector.cs new file mode 100644 index 000000000..181fa3ee1 --- /dev/null +++ b/Scripts/MuscleInspector.cs @@ -0,0 +1,11 @@ +using System.Collections; +using System.Collections.Generic; +using UnityEngine; + + +namespace UniHumanoid +{ + public class MuscleInspector : MonoBehaviour + { + } +} diff --git a/Scripts/MuscleInspector.cs.meta b/Scripts/MuscleInspector.cs.meta new file mode 100644 index 000000000..e952b54f6 --- /dev/null +++ b/Scripts/MuscleInspector.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: d2cc8a27b7978e54d9226bc91e53a502 +timeCreated: 1541840537 +licenseType: Free +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Scripts/Skeleton.cs b/Scripts/Skeleton.cs new file mode 100644 index 000000000..bcdd74801 --- /dev/null +++ b/Scripts/Skeleton.cs @@ -0,0 +1,92 @@ +using System.Collections.Generic; +using UnityEngine; + + +namespace UniHumanoid +{ + /// + /// Mapping HumanBodyBones to BoneIndex + /// + public struct Skeleton + { + Dictionary m_indexMap; + public Dictionary Bones + { + get { return m_indexMap; } + } + public int GetBoneIndex(HumanBodyBones bone) + { + int index; + if (m_indexMap.TryGetValue(bone, out index)) + { + return index; + } + else + { + return -1; + } + } + +#if UNITY_EDITOR + /// + /// For UnitTest + /// + Dictionary m_nameMap; + /// + /// ForTest + /// + /// + /// + public string GetBoneName(HumanBodyBones bone) + { + string name; + if (m_nameMap.TryGetValue(bone, out name)) + { + return name; + } + else + { + return null; + } + } +#endif + + public static Skeleton Estimate(Transform hips) + { + var estimater = new BvhSkeletonEstimator(); + return estimater.Detect(hips); + } + + /// + /// Register bone's HumanBodyBones + /// + /// + /// + /// + public void Set(HumanBodyBones bone, IList bones, IBone b) + { + if (b != null) + { + Set(bone, bones.IndexOf(b), b.Name); + } + } + + public void Set(HumanBodyBones bone, int index, string name) + { + if (m_indexMap == null) + { + m_indexMap = new Dictionary(); + } + m_indexMap[bone] = index; + +#if UNITY_EDITOR + if (m_nameMap == null) + { + m_nameMap = new Dictionary(); + } + m_nameMap[bone] = name; +#endif + } + + } +} diff --git a/Scripts/Skeleton.cs.meta b/Scripts/Skeleton.cs.meta new file mode 100644 index 000000000..292da5e2c --- /dev/null +++ b/Scripts/Skeleton.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 883d8b6e07cbda24384ce65d613d128e +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Scripts/SkeletonEstimator.cs b/Scripts/SkeletonEstimator.cs new file mode 100644 index 000000000..1027769f9 --- /dev/null +++ b/Scripts/SkeletonEstimator.cs @@ -0,0 +1,244 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using UnityEngine; + + +namespace UniHumanoid +{ + public interface ISkeletonDetector + { + Skeleton Detect(IList bones); + } + + + public class BvhSkeletonEstimator : ISkeletonDetector + { + static IBone GetRoot(IList bones) + { + var hips = bones.Where(x => x.Parent == null).ToArray(); + if (hips.Length != 1) + { + throw new System.Exception("Require unique root"); + } + return hips[0]; + } + + static IBone SelectBone(Func selector, IList bones) + { + if (bones == null || bones.Count == 0) throw new Exception("no bones"); + var current = bones[0]; + for (var i = 1; i < bones.Count; ++i) + { + current = selector(current, bones[i]); + } + return current; + } + + static void GetSpineAndHips(IBone hips, out IBone spine, out IBone leg_L, out IBone leg_R) + { + if (hips.Children.Count != 3) throw new System.Exception("Hips require 3 children"); + spine = SelectBone((l, r) => l.CenterOfDescendant().y > r.CenterOfDescendant().y ? l : r, hips.Children); + leg_L = SelectBone((l, r) => l.CenterOfDescendant().x < r.CenterOfDescendant().x ? l : r, hips.Children); + leg_R = SelectBone((l, r) => l.CenterOfDescendant().x > r.CenterOfDescendant().x ? l : r, hips.Children); + } + + static void GetNeckAndArms(IBone chest, out IBone neck, out IBone arm_L, out IBone arm_R) + { + if (chest.Children.Count != 3) throw new System.Exception("Chest require 3 children"); + neck = SelectBone((l, r) => l.CenterOfDescendant().y > r.CenterOfDescendant().y ? l : r, chest.Children); + arm_L = SelectBone((l, r) => l.CenterOfDescendant().x < r.CenterOfDescendant().x ? l : r, chest.Children); + arm_R = SelectBone((l, r) => l.CenterOfDescendant().x > r.CenterOfDescendant().x ? l : r, chest.Children); + } + + struct Arm + { + public IBone Shoulder; + public IBone UpperArm; + public IBone LowerArm; + public IBone Hand; + } + + Arm GetArm(IBone shoulder) + { + var bones = shoulder.Traverse().ToArray(); + switch (bones.Length) + { + case 0: + case 1: + case 2: + case 3: + throw new NotImplementedException(); + + default: + return new Arm + { + Shoulder = bones[0], + UpperArm = bones[1], + LowerArm = bones[2], + Hand = bones[3], + }; + } + } + + struct Leg + { + public IBone UpperLeg; + public IBone LowerLeg; + public IBone Foot; + public IBone Toes; + } + + Leg GetLeg(IBone leg) + { + var bones = leg.Traverse().Where(x => !x.Name.ToLower().Contains("buttock")).ToArray(); + switch (bones.Length) + { + case 0: + case 1: + case 2: + throw new NotImplementedException(); + + case 3: + return new Leg + { + UpperLeg = bones[0], + LowerLeg = bones[1], + Foot = bones[2], + }; + + default: + return new Leg + { + UpperLeg = bones[bones.Length - 4], + LowerLeg = bones[bones.Length - 3], + Foot = bones[bones.Length - 2], + Toes = bones[bones.Length - 1], + }; + } + } + + public Skeleton Detect(IList bones) + { + // + // search bones + // + var root = GetRoot(bones); + var hips = root.Traverse().First(x => x.Children.Count == 3); + + IBone spine, hip_L, hip_R; + GetSpineAndHips(hips, out spine, out hip_L, out hip_R); + var legLeft = GetLeg(hip_L); + var legRight = GetLeg(hip_R); + + var spineToChest = new List(); + foreach(var x in spine.Traverse()) + { + spineToChest.Add(x); + if (x.Children.Count == 3) break; + } + + IBone neck, shoulder_L, shoulder_R; + GetNeckAndArms(spineToChest.Last(), out neck, out shoulder_L, out shoulder_R); + var armLeft = GetArm(shoulder_L); + var armRight = GetArm(shoulder_R); + + var neckToHead = neck.Traverse().ToArray(); + + // + // set result + // + var skeleton = new Skeleton(); + skeleton.Set(HumanBodyBones.Hips, bones, hips); + + switch (spineToChest.Count) + { + case 0: + throw new Exception(); + + case 1: + skeleton.Set(HumanBodyBones.Spine, bones, spineToChest[0]); + break; + + case 2: + skeleton.Set(HumanBodyBones.Spine, bones, spineToChest[0]); + skeleton.Set(HumanBodyBones.Chest, bones, spineToChest[1]); + break; + +#if UNITY_5_6_OR_NEWER + case 3: + skeleton.Set(HumanBodyBones.Spine, bones, spineToChest[0]); + skeleton.Set(HumanBodyBones.Chest, bones, spineToChest[1]); + skeleton.Set(HumanBodyBones.UpperChest, bones, spineToChest[2]); + break; +#endif + + default: + skeleton.Set(HumanBodyBones.Spine, bones, spineToChest[0]); +#if UNITY_5_6_OR_NEWER + skeleton.Set(HumanBodyBones.Chest, bones, spineToChest[1]); + skeleton.Set(HumanBodyBones.UpperChest, bones, spineToChest.Last()); +#else + skeleton.Set(HumanBodyBones.Chest, bones, spineToChest.Last()); +#endif + break; + } + + switch (neckToHead.Length) + { + case 0: + throw new Exception(); + + case 1: + skeleton.Set(HumanBodyBones.Head, bones, neckToHead[0]); + break; + + case 2: + skeleton.Set(HumanBodyBones.Neck, bones, neckToHead[0]); + skeleton.Set(HumanBodyBones.Head, bones, neckToHead[1]); + break; + + default: + skeleton.Set(HumanBodyBones.Neck, bones, neckToHead[0]); + skeleton.Set(HumanBodyBones.Head, bones, neckToHead.Where(x => x.Parent.Children.Count==1).Last()); + break; + } + + skeleton.Set(HumanBodyBones.LeftUpperLeg, bones, legLeft.UpperLeg); + skeleton.Set(HumanBodyBones.LeftLowerLeg, bones, legLeft.LowerLeg); + skeleton.Set(HumanBodyBones.LeftFoot, bones, legLeft.Foot); + skeleton.Set(HumanBodyBones.LeftToes, bones, legLeft.Toes); + + skeleton.Set(HumanBodyBones.RightUpperLeg, bones, legRight.UpperLeg); + skeleton.Set(HumanBodyBones.RightLowerLeg, bones, legRight.LowerLeg); + skeleton.Set(HumanBodyBones.RightFoot, bones, legRight.Foot); + skeleton.Set(HumanBodyBones.RightToes, bones, legRight.Toes); + + skeleton.Set(HumanBodyBones.LeftShoulder, bones, armLeft.Shoulder); + skeleton.Set(HumanBodyBones.LeftUpperArm, bones, armLeft.UpperArm); + skeleton.Set(HumanBodyBones.LeftLowerArm, bones, armLeft.LowerArm); + skeleton.Set(HumanBodyBones.LeftHand, bones, armLeft.Hand); + + skeleton.Set(HumanBodyBones.RightShoulder, bones, armRight.Shoulder); + skeleton.Set(HumanBodyBones.RightUpperArm, bones, armRight.UpperArm); + skeleton.Set(HumanBodyBones.RightLowerArm, bones, armRight.LowerArm); + skeleton.Set(HumanBodyBones.RightHand, bones, armRight.Hand); + + return skeleton; + } + + public Skeleton Detect(Bvh bvh) + { + var root = new BvhBone(bvh.Root.Name, Vector3.zero); + root.Build(bvh.Root); + return Detect(root.Traverse().Select(x => (IBone)x).ToList()); + } + + public Skeleton Detect(Transform t) + { + var root = new BvhBone(t.name, Vector3.zero); + root.Build(t); + return Detect(root.Traverse().Select(x => (IBone)x).ToList()); + } + } +} diff --git a/Scripts/SkeletonEstimator.cs.meta b/Scripts/SkeletonEstimator.cs.meta new file mode 100644 index 000000000..6969f98f4 --- /dev/null +++ b/Scripts/SkeletonEstimator.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: 4f0d2728cd7343f4ab2e7f7c2b961d5c +timeCreated: 1534751085 +licenseType: Free +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Scripts/SkeletonMeshUtility.cs b/Scripts/SkeletonMeshUtility.cs new file mode 100644 index 000000000..2892f7f6d --- /dev/null +++ b/Scripts/SkeletonMeshUtility.cs @@ -0,0 +1,232 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using UnityEngine; + + +namespace UniHumanoid +{ + public static class SkeletonMeshUtility + { + class MeshBuilder + { + List m_positioins = new List(); + List m_indices = new List(); + List m_boneWeights = new List(); + + public void AddBone(Vector3 head, Vector3 tail, int boneIndex, float xWidth, float zWidth) + { + var dir = (tail - head).normalized; + Vector3 xaxis; + Vector3 zaxis; + if (Vector3.Dot(dir, Vector3.forward) >= 1.0f - float.Epsilon) + { + xaxis = Vector3.right; + zaxis = Vector3.down; + } + else + { + xaxis = Vector3.Cross(dir, Vector3.forward).normalized; + zaxis = Vector3.forward; + } + AddBox((head+tail)*0.5f, + xaxis*xWidth, + (tail-head)*0.5f, + zaxis*zWidth, + boneIndex); + } + + void AddBox(Vector3 center, Vector3 xaxis, Vector3 yaxis, Vector3 zaxis, int boneIndex) + { + AddQuad( + center - yaxis - xaxis - zaxis, + center - yaxis + xaxis - zaxis, + center - yaxis + xaxis + zaxis, + center - yaxis - xaxis + zaxis, + boneIndex); + AddQuad( + center + yaxis - xaxis - zaxis, + center + yaxis + xaxis - zaxis, + center + yaxis + xaxis + zaxis, + center + yaxis - xaxis + zaxis, + boneIndex, true); + AddQuad( + center - xaxis - yaxis - zaxis, + center - xaxis + yaxis - zaxis, + center - xaxis + yaxis + zaxis, + center - xaxis - yaxis + zaxis, + boneIndex, true); + AddQuad( + center + xaxis - yaxis - zaxis, + center + xaxis + yaxis - zaxis, + center + xaxis + yaxis + zaxis, + center + xaxis - yaxis + zaxis, + boneIndex); + AddQuad( + center - zaxis - xaxis - yaxis, + center - zaxis + xaxis - yaxis, + center - zaxis + xaxis + yaxis, + center - zaxis - xaxis + yaxis, + boneIndex, true); + AddQuad( + center + zaxis - xaxis - yaxis, + center + zaxis + xaxis - yaxis, + center + zaxis + xaxis + yaxis, + center + zaxis - xaxis + yaxis, + boneIndex); + } + + void AddQuad(Vector3 v0, Vector3 v1, Vector3 v2, Vector3 v3, int boneIndex, bool reverse=false) + { + var i = m_positioins.Count; + m_positioins.Add(v0); + m_positioins.Add(v1); + m_positioins.Add(v2); + m_positioins.Add(v3); + + var bw = new BoneWeight + { + boneIndex0=boneIndex, + weight0=1.0f, + }; + m_boneWeights.Add(bw); + m_boneWeights.Add(bw); + m_boneWeights.Add(bw); + m_boneWeights.Add(bw); + + if (reverse) + { + m_indices.Add(i + 3); + m_indices.Add(i + 2); + m_indices.Add(i + 1); + + m_indices.Add(i + 1); + m_indices.Add(i); + m_indices.Add(i + 3); + } + else + { + m_indices.Add(i); + m_indices.Add(i + 1); + m_indices.Add(i + 2); + + m_indices.Add(i + 2); + m_indices.Add(i + 3); + m_indices.Add(i); + } + } + + public Mesh CreateMesh() + { + var mesh = new Mesh(); + mesh.SetVertices(m_positioins); + mesh.boneWeights = m_boneWeights.ToArray(); + mesh.triangles = m_indices.ToArray(); + mesh.RecalculateNormals(); + mesh.RecalculateBounds(); + return mesh; + } + } + + struct BoneHeadTail + { + public HumanBodyBones Head; + public HumanBodyBones Tail; + public Vector3 TailOffset; + public float XWidth; + public float ZWidth; + + public BoneHeadTail(HumanBodyBones head, HumanBodyBones tail, float xWidth = 0.05f, float zWidth = 0.05f) + { + Head = head; + Tail = tail; + TailOffset = Vector3.zero; + XWidth = xWidth; + ZWidth = zWidth; + } + + public BoneHeadTail(HumanBodyBones head, Vector3 tailOffset, float xWidth = 0.05f, float zWidth = 0.05f) + { + Head = head; + Tail = HumanBodyBones.LastBone; + TailOffset = tailOffset; + XWidth = xWidth; + ZWidth = zWidth; + } + } + + static BoneHeadTail[] Bones = new BoneHeadTail[] + { + new BoneHeadTail(HumanBodyBones.Hips, HumanBodyBones.Spine, 0.1f, 0.06f), + new BoneHeadTail(HumanBodyBones.Spine, HumanBodyBones.Chest), + new BoneHeadTail(HumanBodyBones.Chest, HumanBodyBones.Neck, 0.1f, 0.06f), + new BoneHeadTail(HumanBodyBones.Neck, HumanBodyBones.Head, 0.03f, 0.03f), + new BoneHeadTail(HumanBodyBones.Head, new Vector3(0, 0.1f, 0), 0.1f, 0.1f), + + new BoneHeadTail(HumanBodyBones.LeftShoulder, HumanBodyBones.LeftUpperArm), + new BoneHeadTail(HumanBodyBones.LeftUpperArm, HumanBodyBones.LeftLowerArm), + new BoneHeadTail(HumanBodyBones.LeftLowerArm, HumanBodyBones.LeftHand), + new BoneHeadTail(HumanBodyBones.LeftHand, new Vector3(-0.1f, 0, 0)), + + new BoneHeadTail(HumanBodyBones.LeftUpperLeg, HumanBodyBones.LeftLowerLeg), + new BoneHeadTail(HumanBodyBones.LeftLowerLeg, HumanBodyBones.LeftFoot), + new BoneHeadTail(HumanBodyBones.LeftFoot, HumanBodyBones.LeftToes), + new BoneHeadTail(HumanBodyBones.LeftToes, new Vector3(0, 0, 0.1f)), + + new BoneHeadTail(HumanBodyBones.RightShoulder, HumanBodyBones.RightUpperArm), + new BoneHeadTail(HumanBodyBones.RightUpperArm, HumanBodyBones.RightLowerArm), + new BoneHeadTail(HumanBodyBones.RightLowerArm, HumanBodyBones.RightHand), + new BoneHeadTail(HumanBodyBones.RightHand, new Vector3(0.1f, 0, 0)), + + new BoneHeadTail(HumanBodyBones.RightUpperLeg, HumanBodyBones.RightLowerLeg), + new BoneHeadTail(HumanBodyBones.RightLowerLeg, HumanBodyBones.RightFoot), + new BoneHeadTail(HumanBodyBones.RightFoot, HumanBodyBones.RightToes), + new BoneHeadTail(HumanBodyBones.RightToes, new Vector3(0, 0, 0.1f)), + }; + + public static SkinnedMeshRenderer CreateRenderer(Animator animator) + { + //var bodyBones = (HumanBodyBones[])Enum.GetValues(typeof(HumanBodyBones)); + var bones = animator.transform.Traverse().ToList(); + + var builder = new MeshBuilder(); + foreach(var headTail in Bones) + { + var head = animator.GetBoneTransform(headTail.Head); + if (head!=null) + { + Transform tail = null; + if(headTail.Tail!= HumanBodyBones.LastBone) + { + tail = animator.GetBoneTransform(headTail.Tail); + } + + if (tail != null) + { + builder.AddBone(head.position, tail.position, bones.IndexOf(head), headTail.XWidth, headTail.ZWidth); + } + else + { + builder.AddBone(head.position, head.position + headTail.TailOffset, bones.IndexOf(head), headTail.XWidth, headTail.ZWidth); + } + } + else + { + Debug.LogWarningFormat("{0} not found", headTail.Head); + } + } + + var mesh = builder.CreateMesh(); + mesh.name = "box-man"; + mesh.bindposes = bones.Select(x => + x.worldToLocalMatrix * animator.transform.localToWorldMatrix).ToArray(); + var renderer = animator.gameObject.AddComponent(); + renderer.bones = bones.ToArray(); + renderer.rootBone = animator.GetBoneTransform(HumanBodyBones.Hips); + renderer.sharedMesh = mesh; + //var bounds = new Bounds(Vector3.zero, mesh.bounds.size); + //renderer.localBounds = bounds; + return renderer; + } + } +} diff --git a/Scripts/SkeletonMeshUtility.cs.meta b/Scripts/SkeletonMeshUtility.cs.meta new file mode 100644 index 000000000..a3cf3125c --- /dev/null +++ b/Scripts/SkeletonMeshUtility.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: 92bf6005e8d6db94b8b55c67caea9ddd +timeCreated: 1522346056 +licenseType: Free +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/doc.meta b/doc.meta new file mode 100644 index 000000000..50c76b7f7 --- /dev/null +++ b/doc.meta @@ -0,0 +1,10 @@ +fileFormatVersion: 2 +guid: bd16aca6bf957944396bb28ff3678f05 +folderAsset: yes +timeCreated: 1517663952 +licenseType: Free +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/doc/BoneMappingGizmo.png b/doc/BoneMappingGizmo.png new file mode 100644 index 0000000000000000000000000000000000000000..9bba782d8b5f9a0bd32d59a0125a6acad7dd2fba GIT binary patch literal 65391 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 literal 0 HcmV?d00001 diff --git a/doc/BoneMappingInspector.png.meta b/doc/BoneMappingInspector.png.meta new file mode 100644 index 000000000..da2e5c7d2 --- /dev/null +++ b/doc/BoneMappingInspector.png.meta @@ -0,0 +1,77 @@ +fileFormatVersion: 2 +guid: 1e5ed0d1eaae21c46b59beeeb761a357 +timeCreated: 1517138581 +licenseType: Free +TextureImporter: + fileIDToRecycleName: {} + externalObjects: {} + serializedVersion: 4 + mipmaps: + mipMapMode: 0 + enableMipMap: 1 + sRGBTexture: 1 + linearTexture: 0 + fadeOut: 0 + borderMipMap: 0 + mipMapsPreserveCoverage: 0 + alphaTestReferenceValue: 0.5 + mipMapFadeDistanceStart: 1 + mipMapFadeDistanceEnd: 3 + bumpmap: + convertToNormalMap: 0 + externalNormalMap: 0 + heightScale: 0.25 + normalMapFilter: 0 + isReadable: 0 + grayScaleToAlpha: 0 + generateCubemap: 6 + cubemapConvolution: 0 + seamlessCubemap: 0 + textureFormat: 1 + maxTextureSize: 2048 + textureSettings: + serializedVersion: 2 + filterMode: -1 + aniso: -1 + mipBias: -1 + wrapU: -1 + wrapV: -1 + wrapW: -1 + nPOTScale: 1 + lightmap: 0 + compressionQuality: 50 + spriteMode: 0 + spriteExtrude: 1 + spriteMeshType: 1 + alignment: 0 + spritePivot: {x: 0.5, y: 0.5} + spriteBorder: {x: 0, y: 0, z: 0, w: 0} + spritePixelsToUnits: 100 + alphaUsage: 1 + alphaIsTransparency: 0 + spriteTessellationDetail: -1 + textureType: 0 + textureShape: 1 + maxTextureSizeSet: 0 + compressionQualitySet: 0 + textureFormatSet: 0 + platformSettings: + - buildTarget: DefaultTexturePlatform + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + spriteSheet: + serializedVersion: 2 + sprites: [] + outline: [] + physicsShape: [] + spritePackingTag: + userData: + assetBundleName: + assetBundleVariant: diff --git a/doc/assets.png b/doc/assets.png new file mode 100644 index 0000000000000000000000000000000000000000..14b65fba50467368321ed861c1c4d0b95a24928c GIT binary patch literal 7783 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 literal 0 HcmV?d00001 diff --git a/doc/bvh_gameobject.png.meta b/doc/bvh_gameobject.png.meta new file mode 100644 index 000000000..87746bc3e --- /dev/null +++ b/doc/bvh_gameobject.png.meta @@ -0,0 +1,77 @@ +fileFormatVersion: 2 +guid: 65ce8e194a35af1449cbc70c76e385ad +timeCreated: 1518016999 +licenseType: Free +TextureImporter: + fileIDToRecycleName: {} + externalObjects: {} + serializedVersion: 4 + mipmaps: + mipMapMode: 0 + enableMipMap: 1 + sRGBTexture: 1 + linearTexture: 0 + fadeOut: 0 + borderMipMap: 0 + mipMapsPreserveCoverage: 0 + alphaTestReferenceValue: 0.5 + mipMapFadeDistanceStart: 1 + mipMapFadeDistanceEnd: 3 + bumpmap: + convertToNormalMap: 0 + externalNormalMap: 0 + heightScale: 0.25 + normalMapFilter: 0 + isReadable: 0 + grayScaleToAlpha: 0 + generateCubemap: 6 + cubemapConvolution: 0 + seamlessCubemap: 0 + textureFormat: 1 + maxTextureSize: 2048 + textureSettings: + serializedVersion: 2 + filterMode: -1 + aniso: -1 + mipBias: -1 + wrapU: -1 + wrapV: -1 + wrapW: -1 + nPOTScale: 1 + lightmap: 0 + compressionQuality: 50 + spriteMode: 0 + spriteExtrude: 1 + spriteMeshType: 1 + alignment: 0 + spritePivot: {x: 0.5, y: 0.5} + spriteBorder: {x: 0, y: 0, z: 0, w: 0} + spritePixelsToUnits: 100 + alphaUsage: 1 + alphaIsTransparency: 0 + spriteTessellationDetail: -1 + textureType: 0 + textureShape: 1 + maxTextureSizeSet: 0 + compressionQualitySet: 0 + textureFormatSet: 0 + platformSettings: + - buildTarget: DefaultTexturePlatform + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + spriteSheet: + serializedVersion: 2 + sprites: [] + outline: [] + physicsShape: [] + spritePackingTag: + userData: + assetBundleName: + assetBundleVariant: diff --git a/doc/bvh_prefab.png b/doc/bvh_prefab.png new file mode 100644 index 0000000000000000000000000000000000000000..1492d7e3461a625de14b16b5e000df603886f14b GIT binary patch literal 3386 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 Qv;Y7A07*qoM6N<$g1dR8cK`qY literal 0 HcmV?d00001 diff --git a/doc/bvh_prefab.png.meta b/doc/bvh_prefab.png.meta new file mode 100644 index 000000000..41830c182 --- /dev/null +++ b/doc/bvh_prefab.png.meta @@ -0,0 +1,77 @@ +fileFormatVersion: 2 +guid: 0f274a7a8cb1663479e10b9b1820156d +timeCreated: 1518016802 +licenseType: Free +TextureImporter: + fileIDToRecycleName: {} + externalObjects: {} + serializedVersion: 4 + mipmaps: + mipMapMode: 0 + enableMipMap: 1 + sRGBTexture: 1 + linearTexture: 0 + fadeOut: 0 + borderMipMap: 0 + mipMapsPreserveCoverage: 0 + alphaTestReferenceValue: 0.5 + mipMapFadeDistanceStart: 1 + mipMapFadeDistanceEnd: 3 + bumpmap: + convertToNormalMap: 0 + externalNormalMap: 0 + heightScale: 0.25 + normalMapFilter: 0 + isReadable: 0 + grayScaleToAlpha: 0 + generateCubemap: 6 + cubemapConvolution: 0 + seamlessCubemap: 0 + textureFormat: 1 + maxTextureSize: 2048 + textureSettings: + serializedVersion: 2 + filterMode: -1 + aniso: -1 + mipBias: -1 + wrapU: -1 + wrapV: -1 + wrapW: -1 + nPOTScale: 1 + lightmap: 0 + compressionQuality: 50 + spriteMode: 0 + spriteExtrude: 1 + spriteMeshType: 1 + alignment: 0 + spritePivot: {x: 0.5, y: 0.5} + spriteBorder: {x: 0, y: 0, z: 0, w: 0} + spritePixelsToUnits: 100 + alphaUsage: 1 + alphaIsTransparency: 0 + spriteTessellationDetail: -1 + textureType: 0 + textureShape: 1 + maxTextureSizeSet: 0 + compressionQualitySet: 0 + textureFormatSet: 0 + platformSettings: + - buildTarget: DefaultTexturePlatform + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + spriteSheet: + serializedVersion: 2 + sprites: [] + outline: [] + physicsShape: [] + spritePackingTag: + userData: + assetBundleName: + assetBundleVariant: diff --git a/doc/humanoid.gif b/doc/humanoid.gif new file mode 100644 index 0000000000000000000000000000000000000000..0ca6dd2018836e528abde999633df260130b51a6 GIT binary patch literal 712407 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