Merge pull request 'feature-binding: 动作绑定完成' (#1) from feature-binding into master

Reviewed-on: https://rrricardo.top/git/MotionCapture/MotionCapture/pulls/1
This commit is contained in:
jackfiled 2023-03-30 21:32:33 +08:00
commit ca24486eac
46 changed files with 2283 additions and 349 deletions

View File

@ -39,7 +39,7 @@ ModelImporter:
animationDoRetargetingWarnings: 0
importAnimatedCustomProperties: 0
importConstraints: 0
animationCompression: 1
animationCompression: 3
animationRotationError: 0.5
animationPositionError: 0.5
animationScaleError: 0.5
@ -106,11 +106,11 @@ ModelImporter:
globalScale: 1
rootMotionBoneName: mixamorig:Hips
hasTranslationDoF: 0
hasExtraRoot: 0
hasExtraRoot: 1
skeletonHasParents: 1
lastHumanDescriptionAvatarSource: {instanceID: 0}
autoGenerateAvatarMappingIfUnspecified: 1
animationType: 2
animationType: 3
humanoidOversampling: 1
avatarSetup: 1
addHumanoidExtraRootOnlyWhenUsingAvatar: 1

8
Assets/Behaviours.meta Normal file
View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 83bce6e0ab5db38aaaae7ec73b738130
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,186 @@
using System.Collections.Generic;
using Models;
using UnityEngine;
using Utils.PoseTransformHandlers;
namespace Behaviours
{
public class BallStickBehaviour : MonoBehaviour
{
private readonly GameObject[] _nodes = new GameObject[PoseLandmarkType.MaxValue];
private readonly List<Bond> _bonds = new List<Bond>();
private readonly PoseTransform[] _poseTransforms = new PoseTransform[PoseLandmarkType.MaxValue];
private readonly UdpListener _listener = new UdpListener();
private const float Scale = 0.2f;
// Start is called before the first frame update
private void Start()
{
PoseTransform.AverageLength = 3;
CreateNodes();
CreateBonds();
_listener.AddHandler(OnReceive);
_listener.Connect(5000);
}
// Update is called once per frame
private void Update()
{
var mid = _poseTransforms[PoseLandmarkType.LeftHip] + _poseTransforms[PoseLandmarkType.RightHip];
mid = mid / 2;
for (var i = 0; i < PoseLandmarkType.MaxValue; i++)
{
_nodes[i].transform.position = (_poseTransforms[i].ResultPosition - mid) * -5f;
}
foreach (var bond in _bonds)
{
bond.UpdateBond();
}
}
private void OnDisable()
{
_listener.DisConnect();
}
private void OnReceive(List<PoseLandmark> landmarks)
{
foreach (var landmark in landmarks)
{
PoseTransform.UpdatePosition(ref _poseTransforms[landmark.Type.Value], landmark);
}
}
private void CreateNodes()
{
for (var i = 0; i < PoseLandmarkType.MaxValue; i++)
{
var ball = GameObject.CreatePrimitive(PrimitiveType.Sphere);
ball.transform.localScale = new Vector3(Scale, Scale, Scale);
if (i <= 10)
{
ball.GetComponent<Renderer>().material.color = Color.red; //给头部添加颜色
}
else if (i <= 22)
{
ball.GetComponent<Renderer>().material.color = Color.blue; //给手部添加颜色
}
else if (i <= 32)
{
ball.GetComponent<Renderer>().material.color = Color.green; //给脚部添加颜色
}
_nodes[i] = ball;
_poseTransforms[i] = new PoseTransform(i, new AverageHandler());
}
}
private void CreateBonds()
{
var headBonds = new[]
{
PoseLandmarkType.RightEar,
PoseLandmarkType.RightEyeOuter,
PoseLandmarkType.RightEye,
PoseLandmarkType.RightEyeInner,
PoseLandmarkType.Nose,
PoseLandmarkType.LeftEyeInner,
PoseLandmarkType.LeftEye,
PoseLandmarkType.LeftEyeOuter,
PoseLandmarkType.LeftEar
};
_bonds.AddRange(GenerateBondsList(headBonds));
var monthBonds = new[]
{
PoseLandmarkType.MouthLeft,
PoseLandmarkType.MouthRight,
};
_bonds.AddRange(GenerateBondsList(monthBonds));
var leftArmBonds = new[]
{
PoseLandmarkType.LeftShoulder,
PoseLandmarkType.LeftElbow,
PoseLandmarkType.LeftWrist,
PoseLandmarkType.LeftPinky,
PoseLandmarkType.LeftIndex,
PoseLandmarkType.LeftWrist,
PoseLandmarkType.LeftThumb
};
_bonds.AddRange(GenerateBondsList(leftArmBonds));
var rightArmBonds = new[]
{
PoseLandmarkType.RightShoulder,
PoseLandmarkType.RightElbow,
PoseLandmarkType.RightWrist,
PoseLandmarkType.RightPinky,
PoseLandmarkType.RightIndex,
PoseLandmarkType.RightWrist,
PoseLandmarkType.RightThumb
};
_bonds.AddRange(GenerateBondsList(rightArmBonds));
var leftLegBonds = new[]
{
PoseLandmarkType.LeftShoulder,
PoseLandmarkType.LeftHip,
PoseLandmarkType.LeftKnee,
PoseLandmarkType.LeftAnkle,
PoseLandmarkType.LeftHeel,
PoseLandmarkType.LeftFootIndex,
PoseLandmarkType.LeftAnkle
};
_bonds.AddRange(GenerateBondsList(leftLegBonds));
var rightLegBonds = new[]
{
PoseLandmarkType.RightShoulder,
PoseLandmarkType.RightHip,
PoseLandmarkType.RightKnee,
PoseLandmarkType.RightAnkle,
PoseLandmarkType.RightHeel,
PoseLandmarkType.RightFootIndex,
PoseLandmarkType.RightAnkle
};
_bonds.AddRange(GenerateBondsList(rightLegBonds));
// 最后手动添加身体上的两条横线
_bonds.Add(new Bond(
_nodes[PoseLandmarkType.LeftShoulder],
_nodes[PoseLandmarkType.RightShoulder],
Scale));
_bonds.Add(new Bond(
_nodes[PoseLandmarkType.LeftHip],
_nodes[PoseLandmarkType.RightHip],
Scale));
}
/// <summary>
/// 创建棍子列表
/// </summary>
/// <param name="nodes">需要连接起来的关键点 需要按顺序设置</param>
/// <returns></returns>
private List<Bond> GenerateBondsList(int[] nodes)
{
var bonds = new List<Bond>();
for (var i = 0; i < nodes.Length - 1; i++)
{
bonds.Add(new Bond(
_nodes[nodes[i]],
_nodes[nodes[i + 1]],
Scale));
}
return bonds;
}
}
}

View File

@ -1,5 +1,5 @@
fileFormatVersion: 2
guid: 11e8a976fe328b1c3bdf9f44ef9f6fa1
guid: 574a3724e040787f7afd9341fba89cf2
MonoImporter:
externalObjects: {}
serializedVersion: 2

View File

@ -0,0 +1,272 @@
using System;
using System.Collections.Generic;
using Models;
using UnityEngine;
namespace Behaviours
{
public class BoneBehaviour : MonoBehaviour
{
private readonly UdpListener _listener = new UdpListener();
private readonly GameObject[] _nodes = new GameObject[PoseLandmarkType.MaxValue];
private readonly List<Bond> _bonds = new List<Bond>();
private Dictionary<HumanBodyBones, RotationNode> _rotationNodes;
private List<PoseLandmark> _landmarks;
private Animator _animator;
private bool _isReceived;
private const float Scale = 0.2f;
private void Start()
{
_animator = GetComponent<Animator>();
CreateRotationNodes();
CreateNodes();
CreateBonds();
_listener.AddHandler(OnReceive);
_listener.Connect(5000);
}
private void Update()
{
if (_isReceived)
{
_animator.GetBoneTransform(HumanBodyBones.Hips).rotation =
_rotationNodes[HumanBodyBones.Hips].Rotation;
/*var rotation = _rotationNodes[HumanBodyBones.Hips].CalculateRotate
* Quaternion.Inverse(_rotationNodes[HumanBodyBones.RightUpperArm].CalculateRotate);
_animator.GetBoneTransform(HumanBodyBones.RightUpperArm).Rotate(rotation.eulerAngles);*/
var mid = (_landmarks[PoseLandmarkType.LeftHip].Position +
_landmarks[PoseLandmarkType.RightHip].Position) / 2;
for (var i = 0; i < PoseLandmarkType.MaxValue; i++)
{
_nodes[i].transform.position = (_landmarks[i].Position - mid) * 5f;
}
foreach (var bond in _bonds)
{
bond.UpdateBond();
}
_animator.GetBoneTransform(HumanBodyBones.LeftUpperArm).rotation = _bonds[9].Rotation;
_animator.GetBoneTransform(HumanBodyBones.LeftUpperArm).Rotate(0, -180, 0,
Space.Self);
_animator.GetBoneTransform(HumanBodyBones.LeftLowerArm).rotation = _bonds[10].Rotation;
_animator.GetBoneTransform(HumanBodyBones.LeftLowerArm).Rotate(0, -180, 0,
Space.Self);
_animator.GetBoneTransform(HumanBodyBones.RightUpperArm).rotation = _bonds[15].Rotation;
_animator.GetBoneTransform(HumanBodyBones.RightUpperArm).Rotate(0, -180, 0,
Space.Self);
_animator.GetBoneTransform(HumanBodyBones.RightLowerArm).rotation = _bonds[16].Rotation;
_animator.GetBoneTransform(HumanBodyBones.RightLowerArm).Rotate(0, -180, 0,
Space.Self);
_animator.GetBoneTransform(HumanBodyBones.LeftUpperLeg).rotation = _bonds[22].Rotation;
_animator.GetBoneTransform(HumanBodyBones.LeftUpperLeg).Rotate(0, -180, 0,
Space.Self);
_animator.GetBoneTransform(HumanBodyBones.LeftLowerLeg).rotation = _bonds[23].Rotation;
_animator.GetBoneTransform(HumanBodyBones.LeftLowerLeg).Rotate(0, -180, 0,
Space.Self);
_animator.GetBoneTransform(HumanBodyBones.RightUpperLeg).rotation = _bonds[28].Rotation;
_animator.GetBoneTransform(HumanBodyBones.RightUpperLeg).Rotate(0, -180, 0,
Space.Self);
_animator.GetBoneTransform(HumanBodyBones.RightLowerLeg).rotation = _bonds[29].Rotation;
_animator.GetBoneTransform(HumanBodyBones.RightLowerLeg).Rotate(0, -180, 0,
Space.Self);
_isReceived = false;
}
}
private void OnDisable()
{
_listener.DisConnect();
}
private void OnReceive(List<PoseLandmark> landmarks)
{
// 计算腰部
var frontLeft = Vector3.Cross(
landmarks[PoseLandmarkType.RightHip].Position - landmarks[PoseLandmarkType.LeftHip].Position,
landmarks[PoseLandmarkType.RightShoulder].Position - landmarks[PoseLandmarkType.LeftHip].Position);
var frontRight = Vector3.Cross(
landmarks[PoseLandmarkType.LeftShoulder].Position - landmarks[PoseLandmarkType.RightHip].Position,
landmarks[PoseLandmarkType.LeftHip].Position - landmarks[PoseLandmarkType.LeftHip].Position);
var front = frontLeft + frontRight;
front.Normalize();
var oldRotation = _rotationNodes[HumanBodyBones.Hips];
_rotationNodes[HumanBodyBones.Hips] = new RotationNode(
oldRotation.UnityName,
oldRotation.Rotation,
Quaternion.LookRotation(front));
_landmarks = landmarks;
_isReceived = true;
}
private void CreateRotationNodes()
{
_rotationNodes = new Dictionary<HumanBodyBones, RotationNode>();
var bonesArray = new[]
{
HumanBodyBones.Hips,
HumanBodyBones.LeftUpperArm,
HumanBodyBones.LeftLowerArm,
HumanBodyBones.RightUpperArm,
HumanBodyBones.RightLowerArm,
HumanBodyBones.LeftUpperLeg,
HumanBodyBones.LeftLowerLeg,
HumanBodyBones.RightUpperLeg,
HumanBodyBones.RightLowerLeg
};
foreach (var bone in bonesArray)
{
var rotation = _animator.GetBoneTransform(bone).rotation;
_rotationNodes.Add(bone, new RotationNode(bone, rotation, rotation));
}
}
private void CreateNodes()
{
for (var i = 0; i < PoseLandmarkType.MaxValue; i++)
{
var ball = GameObject.CreatePrimitive(PrimitiveType.Sphere);
ball.transform.localScale = new Vector3(Scale, Scale, Scale);
if (i <= 10)
{
ball.GetComponent<Renderer>().material.color = Color.red; //给头部添加颜色
}
else if (i <= 22)
{
ball.GetComponent<Renderer>().material.color = Color.blue; //给手部添加颜色
}
else if (i <= 32)
{
ball.GetComponent<Renderer>().material.color = Color.green; //给脚部添加颜色
}
_nodes[i] = ball;
}
}
private void CreateBonds()
{
var headBonds = new[]
{
PoseLandmarkType.RightEar,
PoseLandmarkType.RightEyeOuter,
PoseLandmarkType.RightEye,
PoseLandmarkType.RightEyeInner,
PoseLandmarkType.Nose,
PoseLandmarkType.LeftEyeInner,
PoseLandmarkType.LeftEye,
PoseLandmarkType.LeftEyeOuter,
PoseLandmarkType.LeftEar
};
_bonds.AddRange(GenerateBondsList(headBonds));
var monthBonds = new[]
{
PoseLandmarkType.MouthLeft,
PoseLandmarkType.MouthRight,
};
_bonds.AddRange(GenerateBondsList(monthBonds));
var leftArmBonds = new[]
{
PoseLandmarkType.LeftShoulder,
PoseLandmarkType.LeftElbow,
PoseLandmarkType.LeftWrist,
PoseLandmarkType.LeftPinky,
PoseLandmarkType.LeftIndex,
PoseLandmarkType.LeftWrist,
PoseLandmarkType.LeftThumb
};
_bonds.AddRange(GenerateBondsList(leftArmBonds));
var rightArmBonds = new[]
{
PoseLandmarkType.RightShoulder,
PoseLandmarkType.RightElbow,
PoseLandmarkType.RightWrist,
PoseLandmarkType.RightPinky,
PoseLandmarkType.RightIndex,
PoseLandmarkType.RightWrist,
PoseLandmarkType.RightThumb
};
_bonds.AddRange(GenerateBondsList(rightArmBonds));
var leftLegBonds = new[]
{
PoseLandmarkType.LeftShoulder,
PoseLandmarkType.LeftHip,
PoseLandmarkType.LeftKnee,
PoseLandmarkType.LeftAnkle,
PoseLandmarkType.LeftHeel,
PoseLandmarkType.LeftFootIndex,
PoseLandmarkType.LeftAnkle
};
_bonds.AddRange(GenerateBondsList(leftLegBonds));
var rightLegBonds = new[]
{
PoseLandmarkType.RightShoulder,
PoseLandmarkType.RightHip,
PoseLandmarkType.RightKnee,
PoseLandmarkType.RightAnkle,
PoseLandmarkType.RightHeel,
PoseLandmarkType.RightFootIndex,
PoseLandmarkType.RightAnkle
};
_bonds.AddRange(GenerateBondsList(rightLegBonds));
// 最后手动添加身体上的两条横线
_bonds.Add(new Bond(
_nodes[PoseLandmarkType.LeftShoulder],
_nodes[PoseLandmarkType.RightShoulder],
Scale));
_bonds.Add(new Bond(
_nodes[PoseLandmarkType.LeftHip],
_nodes[PoseLandmarkType.RightHip],
Scale));
}
/// <summary>
/// 创建棍子列表
/// </summary>
/// <param name="nodes">需要连接起来的关键点 需要按顺序设置</param>
/// <returns></returns>
private List<Bond> GenerateBondsList(int[] nodes)
{
var bonds = new List<Bond>();
for (var i = 0; i < nodes.Length - 1; i++)
{
bonds.Add(new Bond(
_nodes[nodes[i]],
_nodes[nodes[i + 1]],
Scale));
}
return bonds;
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: a2b060336532a01bfb4593b385955175
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,55 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!114 &-6275755417433151442
MonoBehaviour:
m_ObjectHideFlags: 1
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: a2b060336532a01bfb4593b385955175, type: 3}
m_Name:
m_EditorClassIdentifier:
--- !u!1107 &-4834836044711832442
AnimatorStateMachine:
serializedVersion: 6
m_ObjectHideFlags: 1
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_Name: Base Layer
m_ChildStates: []
m_ChildStateMachines: []
m_AnyStateTransitions: []
m_EntryTransitions: []
m_StateMachineTransitions: {}
m_StateMachineBehaviours: []
m_AnyStatePosition: {x: 50, y: 20, z: 0}
m_EntryPosition: {x: 50, y: 120, z: 0}
m_ExitPosition: {x: 800, y: 120, z: 0}
m_ParentStateMachinePosition: {x: 800, y: 20, z: 0}
m_DefaultState: {fileID: 0}
--- !u!91 &9100000
AnimatorController:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_Name: BoneAnimator
serializedVersion: 5
m_AnimatorParameters: []
m_AnimatorLayers:
- serializedVersion: 5
m_Name: Base Layer
m_StateMachine: {fileID: -4834836044711832442}
m_Mask: {fileID: 0}
m_Motions: []
m_Behaviours: []
m_BlendingMode: 0
m_SyncedLayerIndex: -1
m_DefaultWeight: 0
m_IKPass: 1
m_SyncedLayerAffectsTiming: 0
m_Controller: {fileID: 9100000}

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 7406b3b3cd1a7812ca7823cf800c679b
NativeFormatImporter:
externalObjects: {}
mainObjectFileID: 9100000
userData:
assetBundleName:
assetBundleVariant:

View File

@ -1,38 +0,0 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Text;
using Models;
using UnityEngine;
public class CubeBehaviour : MonoBehaviour
{
public Vector3 rotateAmount = new Vector3(0, 1, 0);
private readonly UdpListener _listener = new UdpListener();
// Start is called before the first frame update
private void Start()
{
_listener.AddHandler(LogLandmarks);
_listener.Connect(5000);
}
// Update is called once per frame
private void Update()
{
transform.Rotate(rotateAmount);
}
private void OnDisable()
{
_listener.DisConnect();
}
private static void LogLandmarks(List<PoseLandmark> landmarks)
{
foreach (var landmark in landmarks)
{
Debug.Log(landmark.ToString());
}
}
}

8
Assets/Materials.meta Normal file
View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: c2963c03969c7f6d480483b45e36590b
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,80 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!21 &2100000
Material:
serializedVersion: 8
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_Name: PlaneMaterial
m_Shader: {fileID: 46, guid: 0000000000000000f000000000000000, type: 0}
m_ValidKeywords: []
m_InvalidKeywords: []
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_Ints: []
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: 1, g: 0.29922488, b: 0, a: 1}
- _EmissionColor: {r: 0, g: 0, b: 0, a: 1}
m_BuildTextureStacks: []

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 6083fe04391087ca294999838984a59c
NativeFormatImporter:
externalObjects: {}
mainObjectFileID: 2100000
userData:
assetBundleName:
assetBundleVariant:

48
Assets/Models/Bond.cs Normal file
View File

@ -0,0 +1,48 @@
using UnityEngine;
using Utils;
namespace Models
{
/// <summary>
/// 捕捉点之间的连接线
/// </summary>
public class Bond
{
protected readonly GameObject Start;
protected readonly GameObject End;
private readonly GameObject _bond;
public Quaternion Rotation => _bond.transform.rotation;
public Vector3 Vector => End.transform.position - Start.transform.position;
public Bond(GameObject start,GameObject end,float scale)
{
Start = start;
End = end;
_bond = GameObject.CreatePrimitive(PrimitiveType.Cylinder);
_bond.transform.localScale = new Vector3(scale/2, scale/2, scale/2);
//这里可以设置材质,具体自己设置
_bond.GetComponent<Renderer>().material.color = Color.cyan;
}
public void UpdateBond()
{
var startPos = Start.transform.position;
var endPos = End.transform.position;
var rightPosition = (startPos + endPos) / 2;
var rightRotation = endPos - startPos;
var halfLength = Vector3.Distance(startPos, endPos) / 2;
var thickness = 0.1f;//线的粗细
//创建圆柱体
//bond.gameObject.transform.parent = transform;
_bond.transform.position = rightPosition;
_bond.transform.rotation = Quaternion.FromToRotation(Vector3.up, rightRotation);
_bond.transform.localScale = new Vector3(thickness, halfLength, thickness);
}
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 94a8f8c83e58421d9447173d38482d74
timeCreated: 1677230676

View File

@ -1,160 +1,163 @@
using System;
using System.Collections.Generic;
using System.Text;
using UnityEngine;
namespace Models
{
public class PoseLandmark
{
private const double Tolerance = 1E-6;
/// <summary>
/// 单个包的长度
/// </summary>
public const int PacketLength = 28;
/// <summary>
/// 坐标点的种类
/// </summary>
public PoseLandmarkType Type { get; }
/// <summary>
/// 真实世界x坐标
/// 以米为单位
/// 以臀部为重心 hip
/// </summary>
public float X { get; }
/// <summary>
/// 真实世界y坐标
/// 以米为单位
/// 以臀部为重心
/// </summary>
public float Y { get; }
/// <summary>
/// 真实世界z坐标
/// 以米为单位
/// 以臀部为重心
/// </summary>
public float Z { get; }
/// <summary>
/// 该坐标点估计的可见性
/// [0,1]
/// </summary>
public float Visibility { get; }
public long TimeStamp { get; }
public PoseLandmark(PoseLandmarkType type, float x, float y, float z, float visibility, long timeStamp)
{
Type = type;
X = x;
Y = y;
Z = z;
Visibility = visibility;
TimeStamp = timeStamp;
}
/// <summary>
/// 单个包的长度
/// </summary>
public const int PacketLength = 28;
/// <summary>
/// 转换成字节数组
/// 便于UDP发送
/// </summary>
/// <returns></returns>
public byte[] ToByteArray()
{
var result = new byte[PacketLength];
/// <summary>
/// 坐标点的种类
/// </summary>
public PoseLandmarkType Type { get; }
BitConverter.GetBytes((int)Type).CopyTo(result, 0);
BitConverter.GetBytes(X).CopyTo(result, 4);
BitConverter.GetBytes(Y).CopyTo(result, 8);
BitConverter.GetBytes(Z).CopyTo(result, 12);
BitConverter.GetBytes(Visibility).CopyTo(result, 16);
BitConverter.GetBytes(TimeStamp).CopyTo(result, 20);
return result;
}
/// <summary>
/// 真实世界x坐标
/// 以米为单位
/// 以臀部为重心 hip
/// </summary>
public float X { get; }
/// <summary>
/// 解析字节数组
/// </summary>
/// <param name="bytes">收到的字节数组</param>
/// <returns>字节数组中代表的坐标对象</returns>
public static PoseLandmark ValueOf(byte[] bytes)
{
var result = new PoseLandmark(
(PoseLandmarkType)BitConverter.ToInt32(bytes, 0),
BitConverter.ToSingle(bytes, 4),
BitConverter.ToSingle(bytes, 8),
BitConverter.ToSingle(bytes, 12),
BitConverter.ToSingle(bytes, 16),
BitConverter.ToInt64(bytes, 20));
/// <summary>
/// 真实世界y坐标
/// 以米为单位
/// 以臀部为重心
/// </summary>
public float Y { get; }
return result;
}
/// <summary>
/// 真实世界z坐标
/// 以米为单位
/// 以臀部为重心
/// </summary>
public float Z { get; }
/// <summary>
/// 解析字节数组为对象列表
/// 单个
/// </summary>
/// <param name="bytes">字节数组</param>
/// <returns></returns>
public static List<PoseLandmark> ArrayOf(byte[] bytes)
{
var result = new List<PoseLandmark>();
public Vector3 Position => new Vector3(-X, -Y, -0.2f * Z);
for (var i = 0; i < bytes.Length; i = i + PacketLength)
/// <summary>
/// 该坐标点估计的可见性
/// [0,1]
/// </summary>
public float Visibility { get; }
public long TimeStamp { get; }
public PoseLandmark(int type, float x, float y, float z, float visibility, long timeStamp)
{
var landmark = new PoseLandmark((PoseLandmarkType)BitConverter.ToInt32(bytes, i),
BitConverter.ToSingle(bytes, i + 4),
BitConverter.ToSingle(bytes, i + 8),
BitConverter.ToSingle(bytes, i + 12),
BitConverter.ToSingle(bytes, i + 16),
BitConverter.ToInt64(bytes, i + 20));
result.Add(landmark);
Type = new PoseLandmarkType(type);
X = x;
Y = y;
Z = z;
Visibility = visibility;
TimeStamp = timeStamp;
}
return result;
}
public override bool Equals(object obj)
{
if (obj is not PoseLandmark landmark)
/// <summary>
/// 转换成字节数组
/// 便于UDP发送
/// </summary>
/// <returns></returns>
public byte[] ToByteArray()
{
return false;
var result = new byte[PacketLength];
BitConverter.GetBytes(Type.Value).CopyTo(result, 0);
BitConverter.GetBytes(X).CopyTo(result, 4);
BitConverter.GetBytes(Y).CopyTo(result, 8);
BitConverter.GetBytes(Z).CopyTo(result, 12);
BitConverter.GetBytes(Visibility).CopyTo(result, 16);
BitConverter.GetBytes(TimeStamp).CopyTo(result, 20);
return result;
}
else
/// <summary>
/// 解析字节数组
/// </summary>
/// <param name="bytes">收到的字节数组</param>
/// <returns>字节数组中代表的坐标对象</returns>
public static PoseLandmark ValueOf(byte[] bytes)
{
return Type == landmark.Type
&& Math.Abs(X - landmark.X) < Tolerance
&& Math.Abs(Y - landmark.Y) < Tolerance
&& Math.Abs(Z - landmark.Z) < Tolerance
&& Math.Abs(Visibility - landmark.Visibility) < Tolerance
&& TimeStamp == landmark.TimeStamp;
var result = new PoseLandmark(
BitConverter.ToInt32(bytes, 0),
BitConverter.ToSingle(bytes, 4),
BitConverter.ToSingle(bytes, 8),
BitConverter.ToSingle(bytes, 12),
BitConverter.ToSingle(bytes, 16),
BitConverter.ToInt64(bytes, 20));
return result;
}
}
public override int GetHashCode()
{
var hashCode = Type.GetHashCode();
hashCode ^= X.GetHashCode();
hashCode ^= Y.GetHashCode();
hashCode ^= Z.GetHashCode();
hashCode ^= Visibility.GetHashCode();
hashCode ^= TimeStamp.GetHashCode();
/// <summary>
/// 解析字节数组为对象列表
/// 单个
/// </summary>
/// <param name="bytes">字节数组</param>
/// <returns></returns>
public static List<PoseLandmark> ArrayOf(byte[] bytes)
{
var result = new List<PoseLandmark>();
return hashCode;
}
for (var i = 0; i < bytes.Length; i = i + PacketLength)
{
var landmark = new PoseLandmark(BitConverter.ToInt32(bytes, i),
BitConverter.ToSingle(bytes, i + 4),
BitConverter.ToSingle(bytes, i + 8),
BitConverter.ToSingle(bytes, i + 12),
BitConverter.ToSingle(bytes, i + 16),
BitConverter.ToInt64(bytes, i + 20));
public override string ToString()
{
var builder = new StringBuilder();
builder.Append($"Time: {TimeStamp}, Type: {Type}:\n");
builder.Append($"\tX:{X}, Y:{Y}, Z:{Z}, Visibility: {Visibility}\n");
result.Add(landmark);
}
return builder.ToString();
}
return result;
}
public override bool Equals(object obj)
{
if (obj is not PoseLandmark landmark)
{
return false;
}
else
{
return Type.Value == landmark.Type.Value
&& Math.Abs(X - landmark.X) < Tolerance
&& Math.Abs(Y - landmark.Y) < Tolerance
&& Math.Abs(Z - landmark.Z) < Tolerance
&& Math.Abs(Visibility - landmark.Visibility) < Tolerance
&& TimeStamp == landmark.TimeStamp;
}
}
public override int GetHashCode()
{
var hashCode = Type.GetHashCode();
hashCode ^= X.GetHashCode();
hashCode ^= Y.GetHashCode();
hashCode ^= Z.GetHashCode();
hashCode ^= Visibility.GetHashCode();
hashCode ^= TimeStamp.GetHashCode();
return hashCode;
}
public override string ToString()
{
var builder = new StringBuilder();
builder.Append($"Time: {TimeStamp}, Type: {Type}:\n");
builder.Append($"\tX:{X}, Y:{Y}, Z:{Z}, Visibility: {Visibility}\n");
return builder.ToString();
}
}
}

View File

@ -1,40 +1,70 @@
using System;
namespace Models
{
//名字
public enum PoseLandmarkType
{
Nose,
LeftEyeInner,
LeftEye,
LeftEyeOuter,
RightEyeInner,
RightEye,
RightEyeOuter,
LeftEar,
RightEar,
MouthLeft,
MouthRight,
LeftShoulder,
RightShoulder,
LeftElbow,
RightElbow,
LeftWrist,
RightWrist,
LeftPinky,
RightPinky,
LeftIndex,
RightIndex,
LeftThumb,
RightThumb,
LeftHip,
RightHip,
LeftKnee,
RightKnee,
LeftAnkle,
RightAnkle,
LeftHeel,
RightHeel,
LeftFootIndex,
RightFootIndex
}
public readonly struct PoseLandmarkType
{
public const int Nose = 0;
public const int LeftEyeInner = 1;
public const int LeftEye = 2;
public const int LeftEyeOuter = 3;
public const int RightEyeInner = 4;
public const int RightEye = 5;
public const int RightEyeOuter = 6;
public const int LeftEar = 7;
public const int RightEar = 8;
public const int MouthLeft = 9;
public const int MouthRight = 10;
public const int LeftShoulder = 11;
public const int RightShoulder = 12;
public const int LeftElbow = 13;
public const int RightElbow = 14;
public const int LeftWrist = 15;
public const int RightWrist = 16;
public const int LeftPinky = 17;
public const int RightPinky = 18;
public const int LeftIndex = 19;
public const int RightIndex = 20;
public const int LeftThumb = 21;
public const int RightThumb = 22;
public const int LeftHip = 23;
public const int RightHip = 24;
public const int LeftKnee = 25;
public const int RightKnee = 26;
public const int LeftAnkle = 27;
public const int RightAnkle = 28;
public const int LeftHeel = 29;
public const int RightHeel = 30;
public const int LeftFootIndex = 31;
public const int RightFootIndex = 32;
public const int MaxValue = 33;
public int Value { get; }
public PoseLandmarkType(int value)
{
if (value >= MaxValue)
{
throw new ArgumentOutOfRangeException(nameof(value));
}
Value = value;
}
public override bool Equals(object obj)
{
return Value.Equals(obj);
}
public override int GetHashCode()
{
return Value.GetHashCode();
}
public override string ToString()
{
return Value.ToString();
}
}
}

View File

@ -0,0 +1,120 @@
using System.Collections.Generic;
using UnityEngine;
using Utils;
namespace Models
{
/// <summary>
/// 捕捉骨骼的动作转换
/// </summary>
public struct PoseTransform
{
/// <summary>
/// MediaPipe中动捕节点标识
/// </summary>
public readonly PoseLandmarkType MediaPipeName;
/// <summary>
/// Unity中绑定骨骼名称
/// </summary>
public readonly HumanBodyBones UnityName;
/// <summary>
/// 取平均的长度
/// </summary>
public static int AverageLength = 3;
/// <summary>
/// 取平均之后的结果
/// </summary>
public Vector3 ResultPosition => _transformHandler.GetResultPosition();
/// <summary>
/// 节点的父节点列表
/// </summary>
public readonly List<PoseLandmarkType> Parent;
private readonly IPoseTransformHandler _transformHandler;
public PoseTransform(
int type,
IPoseTransformHandler handler
)
{
MediaPipeName = new PoseLandmarkType(type);
UnityName = GetRelatedBone(MediaPipeName);
Parent = new List<PoseLandmarkType>();
_transformHandler = handler;
}
public static void UpdatePosition(ref PoseTransform pose, PoseLandmark landmark)
{
pose._transformHandler.ReceivePoseLandmark(landmark);
}
public static Vector3 operator +(PoseTransform a) => a.ResultPosition;
public static Vector3 operator -(PoseTransform a) => -a.ResultPosition;
public static Vector3 operator +(PoseTransform a, PoseTransform b) =>
a.ResultPosition + b.ResultPosition;
public static Vector3 operator -(PoseTransform a, PoseTransform b) =>
a.ResultPosition - b.ResultPosition;
public static Vector3 operator *(PoseTransform a, int b) =>
a.ResultPosition * b;
public static Vector3 operator *(int a, PoseTransform b) =>
a * b.ResultPosition;
public static Vector3 operator /(PoseTransform a, int b) =>
a.ResultPosition / b;
/// <summary>
/// 获得同相关捕捉点关联的骨骼
/// </summary>
/// <param name="type">捕捉点的种类</param>
/// <returns>关联骨骼的种类</returns>
private static HumanBodyBones GetRelatedBone(PoseLandmarkType type)
{
switch (type.Value)
{
case PoseLandmarkType.LeftHip:
return HumanBodyBones.LeftUpperLeg;
case PoseLandmarkType.RightHip:
return HumanBodyBones.RightUpperLeg;
case PoseLandmarkType.LeftShoulder:
return HumanBodyBones.LeftUpperArm;
case PoseLandmarkType.RightShoulder:
return HumanBodyBones.RightUpperArm;
case PoseLandmarkType.Nose:
return HumanBodyBones.Head;
case PoseLandmarkType.LeftElbow:
return HumanBodyBones.LeftLowerArm;
case PoseLandmarkType.RightElbow:
return HumanBodyBones.RightLowerArm;
case PoseLandmarkType.LeftWrist:
return HumanBodyBones.LeftHand;
case PoseLandmarkType.RightWrist:
return HumanBodyBones.RightHand;
case PoseLandmarkType.LeftKnee:
return HumanBodyBones.LeftLowerLeg;
case PoseLandmarkType.RightKnee:
return HumanBodyBones.RightLowerLeg;
case PoseLandmarkType.LeftAnkle:
return HumanBodyBones.LeftFoot;
case PoseLandmarkType.RightAnkle:
return HumanBodyBones.RightFoot;
case PoseLandmarkType.LeftFootIndex:
return HumanBodyBones.LeftToes;
case PoseLandmarkType.RightFootIndex:
return HumanBodyBones.RightToes;
default:
return HumanBodyBones.LastBone;
}
}
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 9e38fe43bac14375a4f16690d139411f
timeCreated: 1676859644

View File

@ -0,0 +1,29 @@
using UnityEngine;
namespace Models
{
public struct RotationNode
{
public readonly HumanBodyBones UnityName;
public Quaternion PreviousRotation;
public Quaternion Rotation;
public Quaternion CalculateRotate => PreviousRotation * Quaternion.Inverse(Rotation);
public RotationNode(HumanBodyBones humanBodyBone, Quaternion previousRotation, Quaternion rotation)
{
UnityName = humanBodyBone;
PreviousRotation = previousRotation;
Rotation = rotation;
}
public RotationNode(RotationNode node, PoseLandmark begin, PoseLandmark end)
{
UnityName = node.UnityName;
PreviousRotation = node.Rotation;
var forward = end.Position - begin.Position;
Rotation = Quaternion.LookRotation(forward);
}
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 87e238cb51f34339a39945af13f43248
timeCreated: 1680090845

View File

@ -0,0 +1,35 @@
using UnityEngine;
namespace Models
{
public class VirtualSkeleton : Bond
{
private readonly GameObject _virtualSkeleton;
public VirtualSkeleton(GameObject start,GameObject end,float scale) : base(start,end,scale)
{
_virtualSkeleton = GameObject.CreatePrimitive(PrimitiveType.Cylinder);
_virtualSkeleton.transform.localScale = new Vector3(scale/2, scale/2, scale/2);
_virtualSkeleton.GetComponent<Renderer>().material.color = Color.white;
}
/// <summary>
/// 覆盖基类的更新方法
/// </summary>
public new void UpdateBond()
{
var startPos = Start.transform.position;
var endPos = End.transform.position;
var rightPosition = (startPos + endPos) / 2;
var rightRotation = endPos - startPos;
var lThickness = 0.2f;
_virtualSkeleton.transform.position = rightPosition;
_virtualSkeleton.transform.rotation = Quaternion.FromToRotation(Vector3.up, rightRotation);
_virtualSkeleton.transform.localScale = new Vector3(lThickness, lThickness, lThickness);
}
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 5f725336fc6a4481a4c7d74ec0e3b17b
timeCreated: 1677413910

View File

@ -0,0 +1,441 @@
%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.18029127, g: 0.22572401, b: 0.3069303, a: 1}
m_UseRadianceAmbientProbe: 0
--- !u!157 &3
LightmapSettings:
m_ObjectHideFlags: 0
serializedVersion: 12
m_GIWorkflowMode: 1
m_GISettings:
serializedVersion: 2
m_BounceScale: 1
m_IndirectOutputScale: 1
m_AlbedoBoost: 1
m_EnvironmentLightingMode: 0
m_EnableBakedLightmaps: 1
m_EnableRealtimeLightmaps: 0
m_LightmapEditorSettings:
serializedVersion: 12
m_Resolution: 2
m_BakeResolution: 40
m_AtlasSize: 1024
m_AO: 0
m_AOMaxDistance: 1
m_CompAOExponent: 1
m_CompAOExponentDirect: 0
m_ExtractAmbientOcclusion: 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: 512
m_PVRBounces: 2
m_PVREnvironmentSampleCount: 256
m_PVREnvironmentReferencePointCount: 2048
m_PVRFilteringMode: 1
m_PVRDenoiserTypeDirect: 1
m_PVRDenoiserTypeIndirect: 1
m_PVRDenoiserTypeAO: 1
m_PVRFilterTypeDirect: 0
m_PVRFilterTypeIndirect: 0
m_PVRFilterTypeAO: 0
m_PVREnvironmentMIS: 1
m_PVRCulling: 1
m_PVRFilteringGaussRadiusDirect: 1
m_PVRFilteringGaussRadiusIndirect: 5
m_PVRFilteringGaussRadiusAO: 2
m_PVRFilteringAtrousPositionSigmaDirect: 0.5
m_PVRFilteringAtrousPositionSigmaIndirect: 2
m_PVRFilteringAtrousPositionSigmaAO: 1
m_ExportTrainingData: 0
m_TrainingDataDestination: TrainingData
m_LightProbeSampleCountMultiplier: 4
m_LightingDataAsset: {fileID: 0}
m_LightingSettings: {fileID: 0}
--- !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