Cailean Finn
1 year ago
commit
b58369a6d1
458 changed files with 161296 additions and 0 deletions
@ -0,0 +1,58 @@ |
|||
# 3D models |
|||
*.3dm filter=lfs diff=lfs merge=lfs -text |
|||
*.3ds filter=lfs diff=lfs merge=lfs -text |
|||
*.blend filter=lfs diff=lfs merge=lfs -text |
|||
*.c4d filter=lfs diff=lfs merge=lfs -text |
|||
*.collada filter=lfs diff=lfs merge=lfs -text |
|||
*.dae filter=lfs diff=lfs merge=lfs -text |
|||
*.dxf filter=lfs diff=lfs merge=lfs -text |
|||
*.fbx filter=lfs diff=lfs merge=lfs -text |
|||
*.jas filter=lfs diff=lfs merge=lfs -text |
|||
*.lws filter=lfs diff=lfs merge=lfs -text |
|||
*.lxo filter=lfs diff=lfs merge=lfs -text |
|||
*.ma filter=lfs diff=lfs merge=lfs -text |
|||
*.max filter=lfs diff=lfs merge=lfs -text |
|||
*.mb filter=lfs diff=lfs merge=lfs -text |
|||
*.obj filter=lfs diff=lfs merge=lfs -text |
|||
*.ply filter=lfs diff=lfs merge=lfs -text |
|||
*.skp filter=lfs diff=lfs merge=lfs -text |
|||
*.stl filter=lfs diff=lfs merge=lfs -text |
|||
*.ztl filter=lfs diff=lfs merge=lfs -text |
|||
# Audio |
|||
*.aif filter=lfs diff=lfs merge=lfs -text |
|||
*.aiff filter=lfs diff=lfs merge=lfs -text |
|||
*.it filter=lfs diff=lfs merge=lfs -text |
|||
*.mod filter=lfs diff=lfs merge=lfs -text |
|||
*.mp3 filter=lfs diff=lfs merge=lfs -text |
|||
*.ogg filter=lfs diff=lfs merge=lfs -text |
|||
*.s3m filter=lfs diff=lfs merge=lfs -text |
|||
*.wav filter=lfs diff=lfs merge=lfs -text |
|||
*.xm filter=lfs diff=lfs merge=lfs -text |
|||
# Video |
|||
*.mp4 filter=lfs diff=lfs merge=lfs -text |
|||
# Fonts |
|||
*.otf filter=lfs diff=lfs merge=lfs -text |
|||
*.ttf filter=lfs diff=lfs merge=lfs -text |
|||
# Images |
|||
*.bmp filter=lfs diff=lfs merge=lfs -text |
|||
*.exr filter=lfs diff=lfs merge=lfs -text |
|||
*.gif filter=lfs diff=lfs merge=lfs -text |
|||
*.hdr filter=lfs diff=lfs merge=lfs -text |
|||
*.iff filter=lfs diff=lfs merge=lfs -text |
|||
*.jpeg filter=lfs diff=lfs merge=lfs -text |
|||
*.jpg filter=lfs diff=lfs merge=lfs -text |
|||
*.pict filter=lfs diff=lfs merge=lfs -text |
|||
*.png filter=lfs diff=lfs merge=lfs -text |
|||
*.psd filter=lfs diff=lfs merge=lfs -text |
|||
*.tga filter=lfs diff=lfs merge=lfs -text |
|||
*.tif filter=lfs diff=lfs merge=lfs -text |
|||
*.tiff filter=lfs diff=lfs merge=lfs -text |
|||
# Collapse Unity-generated files on GitHub |
|||
*.asset linguist-generated |
|||
*.mat linguist-generated |
|||
*.meta linguist-generated |
|||
*.prefab linguist-generated |
|||
*.unity linguist-generated |
|||
*.aar filter=lfs diff=lfs merge=lfs -text |
|||
# ONNX Models |
|||
*.onnx filter=lfs diff=lfs merge=lfs -text |
@ -0,0 +1,76 @@ |
|||
# This .gitignore file should be placed at the root of your Unity project directory |
|||
# |
|||
# Get latest from https://github.com/github/gitignore/blob/main/Unity.gitignore |
|||
# |
|||
/[Ll]ibrary/ |
|||
/[Tt]emp/ |
|||
/[Oo]bj/ |
|||
/[Bb]uild/ |
|||
/[Bb]uilds/ |
|||
/[Ll]ogs/ |
|||
/[Uu]ser[Ss]ettings/ |
|||
|
|||
# MemoryCaptures can get excessive in size. |
|||
# They also could contain extremely sensitive data |
|||
/[Mm]emoryCaptures/ |
|||
|
|||
# Recordings can get excessive in size |
|||
/[Rr]ecordings/ |
|||
|
|||
# Uncomment this line if you wish to ignore the asset store tools plugin |
|||
# /[Aa]ssets/AssetStoreTools* |
|||
|
|||
# Autogenerated Jetbrains Rider plugin |
|||
/[Aa]ssets/Plugins/Editor/JetBrains* |
|||
|
|||
# Visual Studio cache directory |
|||
.vs/ |
|||
|
|||
# Gradle cache directory |
|||
.gradle/ |
|||
|
|||
# Autogenerated VS/MD/Consulo solution and project files |
|||
ExportedObj/ |
|||
.consulo/ |
|||
*.csproj |
|||
*.unityproj |
|||
*.sln |
|||
*.suo |
|||
*.tmp |
|||
*.user |
|||
*.userprefs |
|||
*.pidb |
|||
*.booproj |
|||
*.svd |
|||
*.pdb |
|||
*.mdb |
|||
*.opendb |
|||
*.VC.db |
|||
|
|||
# Unity3D generated meta files |
|||
*.pidb.meta |
|||
*.pdb.meta |
|||
*.mdb.meta |
|||
|
|||
# Unity3D generated file on crash reports |
|||
sysinfo.txt |
|||
|
|||
# Builds |
|||
*.apk |
|||
*.aab |
|||
*.unitypackage |
|||
*.app |
|||
|
|||
# Crashlytics generated file |
|||
crashlytics-build.properties |
|||
|
|||
# Packed Addressables |
|||
/[Aa]ssets/[Aa]ddressable[Aa]ssets[Dd]ata/*/*.bin* |
|||
|
|||
# Temporary auto-generated Android Assets |
|||
/[Aa]ssets/[Ss]treamingAssets/aa.meta |
|||
/[Aa]ssets/[Ss]treamingAssets/aa/* |
|||
|
|||
# Dlls |
|||
|
|||
*.dll |
@ -0,0 +1,6 @@ |
|||
{ |
|||
"version": "1.0", |
|||
"components": [ |
|||
"Microsoft.VisualStudio.Workload.ManagedGame" |
|||
] |
|||
} |
File diff suppressed because it is too large
@ -0,0 +1,7 @@ |
|||
fileFormatVersion: 2 |
|||
guid: 39a7265aa0add1a4996e7a5f6b29bdb3 |
|||
DefaultImporter: |
|||
externalObjects: {} |
|||
userData: |
|||
assetBundleName: |
|||
assetBundleVariant: |
File diff suppressed because it is too large
@ -0,0 +1,7 @@ |
|||
fileFormatVersion: 2 |
|||
guid: 5deb965500846b64ab123fdbbbc2de24 |
|||
DefaultImporter: |
|||
externalObjects: {} |
|||
userData: |
|||
assetBundleName: |
|||
assetBundleVariant: |
@ -0,0 +1,8 @@ |
|||
fileFormatVersion: 2 |
|||
guid: e41940127110d0147ae02474cfa63fe9 |
|||
folderAsset: yes |
|||
DefaultImporter: |
|||
externalObjects: {} |
|||
userData: |
|||
assetBundleName: |
|||
assetBundleVariant: |
@ -0,0 +1,8 @@ |
|||
fileFormatVersion: 2 |
|||
guid: 5a85012640094184b9a055de55b8416c |
|||
folderAsset: yes |
|||
DefaultImporter: |
|||
externalObjects: {} |
|||
userData: |
|||
assetBundleName: |
|||
assetBundleVariant: |
File diff suppressed because it is too large
@ -0,0 +1,8 @@ |
|||
fileFormatVersion: 2 |
|||
guid: 06c52c07402f7274cab64e484b1bddc9 |
|||
MonoImporter: |
|||
serializedVersion: 2 |
|||
defaultReferences: [] |
|||
executionOrder: 0 |
|||
icon: {instanceID: 0} |
|||
userData: |
@ -0,0 +1,85 @@ |
|||
using UnityEngine; |
|||
//using Windows.Kinect;
|
|||
|
|||
using System; |
|||
using System.Collections; |
|||
using System.Collections.Generic; |
|||
using System.Runtime.InteropServices; |
|||
using System.IO; |
|||
using System.Text; |
|||
|
|||
|
|||
namespace com.rfilkov.components |
|||
{ |
|||
/// <summary>
|
|||
/// Avatar controller is the component that transfers the captured user motion to a humanoid model (avatar). Avatar controller classic allows manual assignment of model's rigged bones to the tracked body joints.
|
|||
/// </summary>
|
|||
public class AvatarControllerClassic : AvatarController |
|||
{ |
|||
// Public variables that will get matched to bones. If empty, the Kinect will simply not track it.
|
|||
public Transform Pelvis; |
|||
public Transform SpineNaval; |
|||
public Transform SpineChest; |
|||
public Transform Neck; |
|||
//public Transform Head;
|
|||
|
|||
public Transform ClavicleLeft; |
|||
public Transform ShoulderLeft; |
|||
public Transform ElbowLeft; |
|||
public Transform WristLeft; |
|||
|
|||
public Transform ClavicleRight; |
|||
public Transform ShoulderRight; |
|||
public Transform ElbowRight; |
|||
public Transform WristRight; |
|||
|
|||
public Transform HipLeft; |
|||
public Transform KneeLeft; |
|||
public Transform AnkleLeft; |
|||
//private Transform FootLeft = null;
|
|||
|
|||
public Transform HipRight; |
|||
public Transform KneeRight; |
|||
public Transform AnkleRight; |
|||
//private Transform FootRight = null;
|
|||
|
|||
[Tooltip("The body root node (optional).")] |
|||
public Transform BodyRoot; |
|||
|
|||
|
|||
// map the bones to the model.
|
|||
protected override void MapBones() |
|||
{ |
|||
bones[0] = Pelvis; |
|||
bones[1] = SpineNaval; |
|||
bones[2] = SpineChest; |
|||
bones[3] = Neck; |
|||
//bones[4] = Head;
|
|||
|
|||
bones[5] = ClavicleLeft; |
|||
bones[6] = ShoulderLeft; |
|||
bones[7] = ElbowLeft; |
|||
bones[8] = WristLeft; |
|||
|
|||
bones[9] = ClavicleRight; |
|||
bones[10] = ShoulderRight; |
|||
bones[11] = ElbowRight; |
|||
bones[12] = WristRight; |
|||
|
|||
bones[13] = HipLeft; |
|||
bones[14] = KneeLeft; |
|||
bones[15] = AnkleLeft; |
|||
//bones[16] = FootLeft;
|
|||
|
|||
bones[17] = HipRight; |
|||
bones[18] = KneeRight; |
|||
bones[19] = AnkleRight; |
|||
//bones[20] = FootRight;
|
|||
|
|||
// body root
|
|||
bodyRoot = BodyRoot; |
|||
} |
|||
|
|||
} |
|||
} |
|||
|
@ -0,0 +1,8 @@ |
|||
fileFormatVersion: 2 |
|||
guid: 7b38f8c0e60334143a1b54003b3a0ae5 |
|||
MonoImporter: |
|||
serializedVersion: 2 |
|||
defaultReferences: [] |
|||
executionOrder: 0 |
|||
icon: {instanceID: 0} |
|||
userData: |
@ -0,0 +1,771 @@ |
|||
using UnityEngine; |
|||
using System.Collections; |
|||
using com.rfilkov.kinect; |
|||
|
|||
|
|||
namespace com.rfilkov.components |
|||
{ |
|||
/// <summary>
|
|||
/// Avatar scaler is the component that scales avatar's body, according to the user's body and bone sizes.
|
|||
/// </summary>
|
|||
[RequireComponent(typeof(Animator))] |
|||
public class AvatarScaler : MonoBehaviour |
|||
{ |
|||
[Tooltip("Index of the player, tracked by this component. 0 means the 1st player, 1 - the 2nd one, 2 - the 3rd one, etc.")] |
|||
public int playerIndex = 0; |
|||
|
|||
[Tooltip("Whether the avatar is facing the player or not.")] |
|||
public bool mirroredAvatar = false; |
|||
|
|||
[Tooltip("Minimum distance to the user.")] |
|||
public float minUserDistance = 1.0f; |
|||
|
|||
[Tooltip("Body scale factor (incl. arms and legs) that may be used for fine tuning of model-scale.")] |
|||
[Range(0.0f, 2.0f)] |
|||
public float bodyScaleFactor = 1f; |
|||
|
|||
[Tooltip("Body width scale factor that may be used for fine tuning of model-width scale.")] |
|||
[Range(0.0f, 2.0f)] |
|||
public float bodyWidthFactor = 0f; |
|||
|
|||
[Tooltip("Additional scale factor for arms that may be used for fine tuning of model arm-scale.")] |
|||
[Range(0.0f, 2.0f)] |
|||
public float armScaleFactor = 0f; |
|||
|
|||
[Tooltip("Additional scale factor for legs that may be used for fine tuning of model leg-scale.")] |
|||
[Range(0.0f, 2.0f)] |
|||
public float legScaleFactor = 0f; |
|||
|
|||
[Tooltip("Whether the scale is updated continuously or just after the calibration pose.")] |
|||
public bool continuousScaling = true; |
|||
|
|||
[Tooltip("Scale smoothing factor used in case of continuous scaling.")] |
|||
public float smoothFactor = 5f; |
|||
|
|||
[Tooltip("Camera used to overlay the model over the background.")] |
|||
public Camera foregroundCamera; |
|||
|
|||
[Tooltip("Plane used to render the color camera background.")] |
|||
private Transform backgroundPlane = null; |
|||
|
|||
[Tooltip("Index of the depth sensor that generates the color camera background. 0 is the 1st one, 1 - the 2nd one, etc.")] |
|||
private int sensorIndex = 0; |
|||
|
|||
// [Tooltip("Whether to put the clothing model hip and shoulder joints where the user joints are.")]
|
|||
// public bool fixModelHipsAndShoulders = false;
|
|||
|
|||
[Tooltip("UI-Text to display the avatar-scaler debug messages.")] |
|||
public UnityEngine.UI.Text debugText; |
|||
|
|||
// used by category selector
|
|||
[System.NonSerialized] |
|||
public ulong currentUserId = 0; |
|||
|
|||
// used by category selector
|
|||
[System.NonSerialized] |
|||
public bool scalerInited = false; |
|||
|
|||
// class references
|
|||
private KinectManager kinectManager = null; |
|||
private AvatarController avtController = null; |
|||
|
|||
// model transforms for scaling
|
|||
private Transform bodyScaleTransform; |
|||
//private Transform bodyHipsTransform;
|
|||
|
|||
private Transform leftShoulderScaleTransform; |
|||
private Transform leftElbowScaleTransform; |
|||
private Transform rightShoulderScaleTransform; |
|||
private Transform rightElbowScaleTransform; |
|||
private Transform leftHipScaleTransform; |
|||
private Transform leftKneeScaleTransform; |
|||
private Transform rightHipScaleTransform; |
|||
private Transform rightKneeScaleTransform; |
|||
|
|||
private Vector3 modelBodyScale = Vector3.one; |
|||
private Vector3 modelLeftShoulderScale = Vector3.one; |
|||
private Vector3 modelLeftElbowScale = Vector3.one; |
|||
private Vector3 modelRightShoulderScale = Vector3.one; |
|||
private Vector3 modelRightElbowScale = Vector3.one; |
|||
private Vector3 modelLeftHipScale = Vector3.one; |
|||
private Vector3 modelLeftKneeScale = Vector3.one; |
|||
private Vector3 modelRightHipScale = Vector3.one; |
|||
private Vector3 modelRightKneeScale = Vector3.one; |
|||
|
|||
// model bone sizes and original scales
|
|||
private float modelBodyHeight = 0f; |
|||
private float modelBodyWidth = 0f; |
|||
private float modelLeftUpperArmLength = 0f; |
|||
private float modelLeftLowerArmLength = 0f; |
|||
private float modelRightUpperArmLength = 0f; |
|||
private float modelRightLowerArmLength = 0f; |
|||
private float modelLeftUpperLegLength = 0f; |
|||
private float modelLeftLowerLegLength = 0f; |
|||
private float modelRightUpperLegLength = 0f; |
|||
private float modelRightLowerLegLength = 0f; |
|||
|
|||
// user bone sizes
|
|||
private float userBodyHeight = 0f; |
|||
private float userBodyWidth = 0f; |
|||
private float leftUpperArmLength = 0f; |
|||
private float leftLowerArmLength = 0f; |
|||
private float rightUpperArmLength = 0f; |
|||
private float rightLowerArmLength = 0f; |
|||
private float leftUpperLegLength = 0f; |
|||
private float leftLowerLegLength = 0f; |
|||
private float rightUpperLegLength = 0f; |
|||
private float rightLowerLegLength = 0f; |
|||
|
|||
// user bone scale factors
|
|||
private float fScaleBodyHeight = 0f; |
|||
private float fScaleBodyWidth = 0f; |
|||
private float fScaleLeftUpperArm = 0f; |
|||
private float fScaleLeftLowerArm = 0f; |
|||
private float fScaleRightUpperArm = 0f; |
|||
private float fScaleRightLowerArm = 0f; |
|||
private float fScaleLeftUpperLeg = 0f; |
|||
private float fScaleLeftLowerLeg = 0f; |
|||
private float fScaleRightUpperLeg = 0f; |
|||
private float fScaleRightLowerLeg = 0f; |
|||
|
|||
// background plane rectangle
|
|||
private Rect planeRect = new Rect(); |
|||
private bool planeRectSet = false; |
|||
|
|||
// user body lengths
|
|||
private bool gotUserBodySize = false; |
|||
private bool gotUserArmsSize = false; |
|||
private bool gotUserLegsSize = false; |
|||
|
|||
// mesh renderer
|
|||
private SkinnedMeshRenderer meshRenderer = null; |
|||
|
|||
|
|||
public void Start() |
|||
{ |
|||
// get references to other components
|
|||
kinectManager = KinectManager.Instance; |
|||
avtController = gameObject.GetComponent<AvatarController>(); |
|||
|
|||
// get model transforms
|
|||
Animator animatorComponent = GetComponent<Animator>(); |
|||
AvatarController avatarController = GetComponent<AvatarController>(); |
|||
|
|||
// get mesh renderer
|
|||
meshRenderer = GetComponentInChildren<SkinnedMeshRenderer>(); |
|||
|
|||
// use the root transform for body scale
|
|||
bodyScaleTransform = transform; |
|||
|
|||
if (animatorComponent && animatorComponent.GetBoneTransform(HumanBodyBones.Hips)) |
|||
{ |
|||
//bodyHipsTransform = animatorComponent.GetBoneTransform (HumanBodyBones.Hips);
|
|||
|
|||
leftShoulderScaleTransform = animatorComponent.GetBoneTransform(HumanBodyBones.LeftUpperArm); |
|||
leftElbowScaleTransform = animatorComponent.GetBoneTransform(HumanBodyBones.LeftLowerArm); |
|||
rightShoulderScaleTransform = animatorComponent.GetBoneTransform(HumanBodyBones.RightUpperArm); |
|||
rightElbowScaleTransform = animatorComponent.GetBoneTransform(HumanBodyBones.RightLowerArm); |
|||
|
|||
leftHipScaleTransform = animatorComponent.GetBoneTransform(HumanBodyBones.LeftUpperLeg); |
|||
leftKneeScaleTransform = animatorComponent.GetBoneTransform(HumanBodyBones.LeftLowerLeg); |
|||
rightHipScaleTransform = animatorComponent.GetBoneTransform(HumanBodyBones.RightUpperLeg); |
|||
rightKneeScaleTransform = animatorComponent.GetBoneTransform(HumanBodyBones.RightLowerLeg); |
|||
} |
|||
else if (avatarController) |
|||
{ |
|||
//bodyHipsTransform = avatarController.GetBoneTransform(avatarController.GetBoneIndexByJoint(KinectInterop.JointType.SpineBase, false));
|
|||
|
|||
leftShoulderScaleTransform = avatarController.GetBoneTransform(avatarController.GetBoneIndexByJoint(KinectInterop.JointType.ShoulderLeft, false)); |
|||
leftElbowScaleTransform = avatarController.GetBoneTransform(avatarController.GetBoneIndexByJoint(KinectInterop.JointType.ElbowLeft, false)); |
|||
rightShoulderScaleTransform = avatarController.GetBoneTransform(avatarController.GetBoneIndexByJoint(KinectInterop.JointType.ShoulderRight, false)); |
|||
rightElbowScaleTransform = avatarController.GetBoneTransform(avatarController.GetBoneIndexByJoint(KinectInterop.JointType.ElbowRight, false)); |
|||
|
|||
leftHipScaleTransform = avatarController.GetBoneTransform(avatarController.GetBoneIndexByJoint(KinectInterop.JointType.HipLeft, false)); |
|||
leftKneeScaleTransform = avatarController.GetBoneTransform(avatarController.GetBoneIndexByJoint(KinectInterop.JointType.KneeLeft, false)); |
|||
rightHipScaleTransform = avatarController.GetBoneTransform(avatarController.GetBoneIndexByJoint(KinectInterop.JointType.HipRight, false)); |
|||
rightKneeScaleTransform = avatarController.GetBoneTransform(avatarController.GetBoneIndexByJoint(KinectInterop.JointType.KneeRight, false)); |
|||
} |
|||
else |
|||
{ |
|||
// needed transforms could not be found
|
|||
return; |
|||
} |
|||
|
|||
// get model bone scales
|
|||
modelBodyScale = bodyScaleTransform ? bodyScaleTransform.localScale : Vector3.one; |
|||
|
|||
modelLeftShoulderScale = leftShoulderScaleTransform ? leftShoulderScaleTransform.localScale : Vector3.one; |
|||
modelLeftElbowScale = leftElbowScaleTransform ? leftElbowScaleTransform.localScale : Vector3.one; |
|||
modelRightShoulderScale = rightShoulderScaleTransform ? rightShoulderScaleTransform.localScale : Vector3.one; |
|||
modelRightElbowScale = rightElbowScaleTransform ? rightElbowScaleTransform.localScale : Vector3.one; |
|||
|
|||
modelLeftHipScale = leftHipScaleTransform ? leftHipScaleTransform.localScale : Vector3.one; |
|||
modelLeftKneeScale = leftKneeScaleTransform ? leftKneeScaleTransform.localScale : Vector3.one; |
|||
modelRightHipScale = rightHipScaleTransform ? rightHipScaleTransform.localScale : Vector3.one; |
|||
modelRightKneeScale = rightKneeScaleTransform ? rightKneeScaleTransform.localScale : Vector3.one; |
|||
|
|||
if (animatorComponent && animatorComponent.GetBoneTransform(HumanBodyBones.Hips)) |
|||
{ |
|||
GetModelBodyHeight(animatorComponent, ref modelBodyHeight, ref modelBodyWidth); |
|||
//Debug.Log (string.Format("MW: {0:F3}, MH: {1:F3}", modelBodyWidth, modelBodyHeight));
|
|||
|
|||
GetModelBoneLength(animatorComponent, HumanBodyBones.LeftUpperArm, HumanBodyBones.LeftLowerArm, ref modelLeftUpperArmLength); |
|||
GetModelBoneLength(animatorComponent, HumanBodyBones.LeftLowerArm, HumanBodyBones.LeftHand, ref modelLeftLowerArmLength); |
|||
GetModelBoneLength(animatorComponent, HumanBodyBones.RightUpperArm, HumanBodyBones.RightLowerArm, ref modelRightUpperArmLength); |
|||
GetModelBoneLength(animatorComponent, HumanBodyBones.RightLowerArm, HumanBodyBones.RightHand, ref modelRightLowerArmLength); |
|||
|
|||
GetModelBoneLength(animatorComponent, HumanBodyBones.LeftUpperLeg, HumanBodyBones.LeftLowerLeg, ref modelLeftUpperLegLength); |
|||
GetModelBoneLength(animatorComponent, HumanBodyBones.LeftLowerLeg, HumanBodyBones.LeftFoot, ref modelLeftLowerLegLength); |
|||
GetModelBoneLength(animatorComponent, HumanBodyBones.RightUpperLeg, HumanBodyBones.RightLowerLeg, ref modelRightUpperLegLength); |
|||
GetModelBoneLength(animatorComponent, HumanBodyBones.RightLowerLeg, HumanBodyBones.RightFoot, ref modelRightLowerLegLength); |
|||
|
|||
scalerInited = true; |
|||
} |
|||
else if (avatarController) |
|||
{ |
|||
GetModelBodyHeight(avatarController, ref modelBodyHeight, ref modelBodyWidth); |
|||
//Debug.Log (string.Format("MW: {0:F3}, MH: {1:F3}", modelBodyWidth, modelBodyHeight));
|
|||
|
|||
GetModelBoneLength(avatarController, KinectInterop.JointType.ShoulderLeft, KinectInterop.JointType.ElbowLeft, ref modelLeftUpperArmLength); |
|||
GetModelBoneLength(avatarController, KinectInterop.JointType.ElbowLeft, KinectInterop.JointType.WristLeft, ref modelLeftLowerArmLength); |
|||
GetModelBoneLength(avatarController, KinectInterop.JointType.ShoulderRight, KinectInterop.JointType.ElbowRight, ref modelRightUpperArmLength); |
|||
GetModelBoneLength(avatarController, KinectInterop.JointType.ElbowRight, KinectInterop.JointType.WristRight, ref modelRightLowerArmLength); |
|||
|
|||
GetModelBoneLength(avatarController, KinectInterop.JointType.HipLeft, KinectInterop.JointType.KneeLeft, ref modelLeftUpperLegLength); |
|||
GetModelBoneLength(avatarController, KinectInterop.JointType.KneeLeft, KinectInterop.JointType.AnkleLeft, ref modelLeftLowerLegLength); |
|||
GetModelBoneLength(avatarController, KinectInterop.JointType.HipRight, KinectInterop.JointType.KneeRight, ref modelRightUpperLegLength); |
|||
GetModelBoneLength(avatarController, KinectInterop.JointType.KneeRight, KinectInterop.JointType.AnkleRight, ref modelRightLowerLegLength); |
|||
|
|||
scalerInited = true; |
|||
} |
|||
|
|||
// update the scale immediately
|
|||
Update(); |
|||
} |
|||
|
|||
public void Update() |
|||
{ |
|||
if (scalerInited && kinectManager && kinectManager.IsInitialized()) |
|||
{ |
|||
// get the plane rectangle to be used for object overlay
|
|||
if (backgroundPlane && !planeRectSet) |
|||
{ |
|||
planeRectSet = true; |
|||
|
|||
planeRect.width = 10f * Mathf.Abs(backgroundPlane.localScale.x); |
|||
planeRect.height = 10f * Mathf.Abs(backgroundPlane.localScale.z); |
|||
planeRect.x = backgroundPlane.position.x - planeRect.width / 2f; |
|||
planeRect.y = backgroundPlane.position.y - planeRect.height / 2f; |
|||
} |
|||
|
|||
ulong userId = kinectManager.GetUserIdByIndex(playerIndex); |
|||
|
|||
// check user distance and hand positions
|
|||
if (userId != 0 && minUserDistance > 0f) |
|||
{ |
|||
Vector3 userPos = kinectManager.GetUserPosition(userId); |
|||
|
|||
//bool lHandTracked = kinectManager.IsJointTracked(userId, (int)KinectInterop.JointType.WristLeft);
|
|||
//Vector3 lHandPos = lHandTracked ? kinectManager.GetJointPosition(userId, (int)KinectInterop.JointType.WristLeft) : Vector3.zero;
|
|||
|
|||
//bool rHandTracked = kinectManager.IsJointTracked(userId, (int)KinectInterop.JointType.WristRight);
|
|||
//Vector3 rHandPos = rHandTracked ? kinectManager.GetJointPosition(userId, (int)KinectInterop.JointType.WristRight) : Vector3.zero;
|
|||
|
|||
if (userPos.z < minUserDistance) // ||
|
|||
//!lHandTracked || (lHandPos.z - userPos.z) <= -0.3f ||
|
|||
//!rHandTracked || (rHandPos.z - userPos.z) <= -0.3f)
|
|||
{ |
|||
// don't scale the model
|
|||
userId = 0; |
|||
//Debug.Log ("Avatar scaling skipped.");
|
|||
} |
|||
|
|||
} |
|||
|
|||
if (userId != currentUserId) |
|||
{ |
|||
currentUserId = userId; |
|||
|
|||
if (userId != 0) |
|||
{ |
|||
GetUserBodySize(true, true, true); |
|||
|
|||
if (gotUserBodySize) |
|||
{ |
|||
// show the mesh
|
|||
if (meshRenderer && !meshRenderer.gameObject.activeSelf) |
|||
meshRenderer.gameObject.SetActive(true); |
|||
|
|||
// scale avatar initially
|
|||
ScaleAvatar(0f, true); |
|||
} |
|||
else |
|||
{ |
|||
// hide the mesh
|
|||
if (meshRenderer && meshRenderer.gameObject.activeSelf) |
|||
meshRenderer.gameObject.SetActive(false); |
|||
|
|||
// consider the user as not tracked
|
|||
currentUserId = 0; |
|||
} |
|||
} |
|||
else |
|||
{ |
|||
// user not tracked
|
|||
gotUserBodySize = gotUserArmsSize = gotUserLegsSize = false; |
|||
} |
|||
} |
|||
} |
|||
|
|||
if (currentUserId != 0 && continuousScaling) |
|||
{ |
|||
// scale avatar continuously
|
|||
GetUserBodySize(true, true, true); |
|||
ScaleAvatar(smoothFactor, false); |
|||
} |
|||
} |
|||
|
|||
// gets the the actual sizes of the user bones
|
|||
public void GetUserBodySize(bool bBody, bool bArms, bool bLegs) |
|||
{ |
|||
//KinectManager kinectManager = KinectManager.Instance;
|
|||
if (kinectManager == null) |
|||
return; |
|||
|
|||
if (bBody) |
|||
{ |
|||
gotUserBodySize = GetUserBodyHeight(kinectManager, bodyScaleFactor, bodyWidthFactor, ref userBodyHeight, ref userBodyWidth); |
|||
} |
|||
|
|||
if (bArms) |
|||
{ |
|||
bool gotLeftArmSize = GetUserBoneLength(kinectManager, KinectInterop.JointType.ShoulderLeft, KinectInterop.JointType.ElbowLeft, armScaleFactor, ref leftUpperArmLength); |
|||
gotLeftArmSize &= GetUserBoneLength(kinectManager, KinectInterop.JointType.ElbowLeft, KinectInterop.JointType.WristLeft, armScaleFactor, ref leftLowerArmLength); |
|||
bool gotRightArmSize = GetUserBoneLength(kinectManager, KinectInterop.JointType.ShoulderRight, KinectInterop.JointType.ElbowRight, armScaleFactor, ref rightUpperArmLength); |
|||
gotRightArmSize &= GetUserBoneLength(kinectManager, KinectInterop.JointType.ElbowRight, KinectInterop.JointType.WristRight, armScaleFactor, ref rightLowerArmLength); |
|||
|
|||
gotUserArmsSize = gotLeftArmSize | gotRightArmSize; |
|||
if(gotUserArmsSize) |
|||
{ |
|||
EqualizeBoneLength(ref leftUpperArmLength, ref rightUpperArmLength); |
|||
EqualizeBoneLength(ref leftLowerArmLength, ref rightLowerArmLength); |
|||
} |
|||
} |
|||
|
|||
if (bLegs) |
|||
{ |
|||
bool gotLeftLegSize = GetUserBoneLength(kinectManager, KinectInterop.JointType.HipLeft, KinectInterop.JointType.KneeLeft, legScaleFactor, ref leftUpperLegLength); |
|||
gotLeftLegSize &= GetUserBoneLength(kinectManager, KinectInterop.JointType.KneeLeft, KinectInterop.JointType.AnkleLeft, legScaleFactor, ref leftLowerLegLength); |
|||
bool gotRightLegSize = GetUserBoneLength(kinectManager, KinectInterop.JointType.HipRight, KinectInterop.JointType.KneeRight, legScaleFactor, ref rightUpperLegLength); |
|||
gotRightLegSize &= GetUserBoneLength(kinectManager, KinectInterop.JointType.KneeRight, KinectInterop.JointType.AnkleRight, legScaleFactor, ref rightLowerLegLength); |
|||
|
|||
gotUserLegsSize = gotLeftLegSize | gotRightLegSize; |
|||
if(gotUserLegsSize) |
|||
{ |
|||
EqualizeBoneLength(ref leftUpperLegLength, ref rightUpperLegLength); |
|||
EqualizeBoneLength(ref leftLowerLegLength, ref rightLowerLegLength); |
|||
} |
|||
} |
|||
} |
|||
|
|||
// scales the avatar as needed
|
|||
public void ScaleAvatar(float fSmooth, bool bInitialScale) |
|||
{ |
|||
// scale body
|
|||
if (bodyScaleFactor > 0f && gotUserBodySize) |
|||
{ |
|||
SetupBodyScale(bodyScaleTransform, modelBodyScale, modelBodyHeight, modelBodyWidth, userBodyHeight, userBodyWidth, |
|||
fSmooth, ref fScaleBodyHeight, ref fScaleBodyWidth); |
|||
|
|||
if (avtController) |
|||
{ |
|||
// recalibrate avatar position due to transform scale change
|
|||
avtController.offsetCalibrated = false; |
|||
|
|||
// set AC smooth-factor to 0 to prevent flickering (r618-issue)
|
|||
if (avtController.smoothFactor != 0f) |
|||
{ |
|||
avtController.smoothFactor = 0f; |
|||
} |
|||
} |
|||
} |
|||
|
|||
// scale arms
|
|||
if (/**bInitialScale &&*/ armScaleFactor > 0f && gotUserArmsSize) |
|||
{ |
|||
float fLeftUpperArmLength = !mirroredAvatar ? leftUpperArmLength : rightUpperArmLength; |
|||
SetupBoneScale(leftShoulderScaleTransform, modelLeftShoulderScale, modelLeftUpperArmLength, |
|||
fLeftUpperArmLength, fScaleBodyHeight, fSmooth, ref fScaleLeftUpperArm); |
|||
|
|||
float fLeftLowerArmLength = !mirroredAvatar ? leftLowerArmLength : rightLowerArmLength; |
|||
SetupBoneScale(leftElbowScaleTransform, modelLeftElbowScale, modelLeftLowerArmLength, |
|||
fLeftLowerArmLength, fScaleLeftUpperArm, fSmooth, ref fScaleLeftLowerArm); |
|||
|
|||
float fRightUpperArmLength = !mirroredAvatar ? rightUpperArmLength : leftUpperArmLength; |
|||
SetupBoneScale(rightShoulderScaleTransform, modelRightShoulderScale, modelRightUpperArmLength, |
|||
fRightUpperArmLength, fScaleBodyHeight, fSmooth, ref fScaleRightUpperArm); |
|||
|
|||
float fRightLowerArmLength = !mirroredAvatar ? rightLowerArmLength : leftLowerArmLength; |
|||
SetupBoneScale(rightElbowScaleTransform, modelRightElbowScale, modelLeftLowerArmLength, |
|||
fRightLowerArmLength, fScaleRightUpperArm, fSmooth, ref fScaleRightLowerArm); |
|||
} |
|||
|
|||
// scale legs
|
|||
if (/**bInitialScale &&*/ legScaleFactor > 0 && gotUserLegsSize) |
|||
{ |
|||
float fLeftUpperLegLength = !mirroredAvatar ? leftUpperLegLength : rightUpperLegLength; |
|||
SetupBoneScale(leftHipScaleTransform, modelLeftHipScale, modelLeftUpperLegLength, |
|||
fLeftUpperLegLength, fScaleBodyHeight, fSmooth, ref fScaleLeftUpperLeg); |
|||
|
|||
float fLeftLowerLegLength = !mirroredAvatar ? leftLowerLegLength : rightLowerLegLength; |
|||
SetupBoneScale(leftKneeScaleTransform, modelLeftKneeScale, modelLeftLowerLegLength, |
|||
fLeftLowerLegLength, fScaleLeftUpperLeg, fSmooth, ref fScaleLeftLowerLeg); |
|||
|
|||
float fRightUpperLegLength = !mirroredAvatar ? rightUpperLegLength : leftUpperLegLength; |
|||
SetupBoneScale(rightHipScaleTransform, modelRightHipScale, modelRightUpperLegLength, |
|||
fRightUpperLegLength, fScaleBodyHeight, fSmooth, ref fScaleRightUpperLeg); |
|||
|
|||
float fRightLowerLegLength = !mirroredAvatar ? rightLowerLegLength : leftLowerLegLength; |
|||
SetupBoneScale(rightKneeScaleTransform, modelRightKneeScale, modelRightLowerLegLength, |
|||
fRightLowerLegLength, fScaleRightUpperLeg, fSmooth, ref fScaleRightLowerLeg); |
|||
} |
|||
|
|||
if (debugText != null) |
|||
{ |
|||
string sDebug = string.Format("BW: {0:F2}/{1:F3}, BH: {2:F2}/{3:F3}\nLUA: {4:F3}, LLA: {5:F3}; RUA: {6:F3}, RLA: {7:F3}\nLUL: {8:F3}, LLL: {9:F3}; RUL: {10:F3}, RLL: {11:F3}", |
|||
userBodyWidth, fScaleBodyWidth, userBodyHeight, fScaleBodyHeight, |
|||
fScaleLeftUpperArm, fScaleLeftLowerArm, |
|||
fScaleRightUpperArm, fScaleRightLowerArm, |
|||
fScaleLeftUpperLeg, fScaleLeftLowerLeg, |
|||
fScaleRightUpperLeg, fScaleRightLowerLeg); |
|||
debugText.text = sDebug; |
|||
} |
|||
|
|||
} |
|||
|
|||
private bool GetModelBodyHeight(Animator animatorComponent, ref float height, ref float width) |
|||
{ |
|||
height = 0f; |
|||
|
|||
if (animatorComponent) |
|||
{ |
|||
//Transform hipCenter = animatorComponent.GetBoneTransform(HumanBodyBones.Hips);
|
|||
|
|||
Transform leftUpperArm = animatorComponent.GetBoneTransform(HumanBodyBones.LeftUpperArm); |
|||
Transform rightUpperArm = animatorComponent.GetBoneTransform(HumanBodyBones.RightUpperArm); |
|||
|
|||
Transform leftUpperLeg = animatorComponent.GetBoneTransform(HumanBodyBones.LeftUpperLeg); |
|||
Transform rightUpperLeg = animatorComponent.GetBoneTransform(HumanBodyBones.RightUpperLeg); |
|||
|
|||
if (leftUpperArm && rightUpperArm && leftUpperLeg && rightUpperLeg) |
|||
{ |
|||
Vector3 posShoulderCenter = (leftUpperArm.position + rightUpperArm.position) / 2f; |
|||
Vector3 posHipCenter = (leftUpperLeg.position + rightUpperLeg.position) / 2f; // hipCenter.position
|
|||
|
|||
//height = (posShoulderCenter.y - posHipCenter.y);
|
|||
height = (posShoulderCenter - posHipCenter).magnitude; |
|||
width = (rightUpperArm.position - leftUpperArm.position).magnitude; |
|||
|
|||
return true; |
|||
} |
|||
} |
|||
|
|||
return false; |
|||
} |
|||
|
|||
private bool GetModelBodyHeight(AvatarController avatarController, ref float height, ref float width) |
|||
{ |
|||
height = 0f; |
|||
|
|||
if (avatarController) |
|||
{ |
|||
Transform leftUpperArm = avatarController.GetBoneTransform(avatarController.GetBoneIndexByJoint(KinectInterop.JointType.ShoulderLeft, false)); |
|||
Transform rightUpperArm = avatarController.GetBoneTransform(avatarController.GetBoneIndexByJoint(KinectInterop.JointType.ShoulderRight, false)); |
|||
|
|||
Transform leftUpperLeg = avatarController.GetBoneTransform(avatarController.GetBoneIndexByJoint(KinectInterop.JointType.HipLeft, false)); |
|||
Transform rightUpperLeg = avatarController.GetBoneTransform(avatarController.GetBoneIndexByJoint(KinectInterop.JointType.HipRight, false)); |
|||
|
|||
if (leftUpperArm && rightUpperArm && leftUpperLeg && rightUpperLeg) |
|||
{ |
|||
Vector3 posShoulderCenter = (leftUpperArm.position + rightUpperArm.position) / 2f; |
|||
Vector3 posHipCenter = (leftUpperLeg.position + rightUpperLeg.position) / 2f; // hipCenter.position
|
|||
|
|||
//height = (posShoulderCenter.y - posHipCenter.y);
|
|||
height = (posShoulderCenter - posHipCenter).magnitude; |
|||
width = (rightUpperArm.position - leftUpperArm.position).magnitude; |
|||
|
|||
return true; |
|||
} |
|||
} |
|||
|
|||
return false; |
|||
} |
|||
|
|||
private bool GetModelBoneLength(Animator animatorComponent, HumanBodyBones baseJoint, HumanBodyBones endJoint, ref float length) |
|||
{ |
|||
length = 0f; |
|||
|
|||
if (animatorComponent) |
|||
{ |
|||
Transform joint1 = animatorComponent.GetBoneTransform(baseJoint); |
|||
Transform joint2 = animatorComponent.GetBoneTransform(endJoint); |
|||
|
|||
if (joint1 && joint2) |
|||
{ |
|||
length = (joint2.position - joint1.position).magnitude; |
|||
return true; |
|||
} |
|||
} |
|||
|
|||
return false; |
|||
} |
|||
|
|||
private bool GetModelBoneLength(AvatarController avatarController, KinectInterop.JointType baseJoint, KinectInterop.JointType endJoint, ref float length) |
|||
{ |
|||
length = 0f; |
|||
|
|||
if (avatarController) |
|||
{ |
|||
Transform joint1 = avatarController.GetBoneTransform(avatarController.GetBoneIndexByJoint(baseJoint, false)); |
|||
Transform joint2 = avatarController.GetBoneTransform(avatarController.GetBoneIndexByJoint(endJoint, false)); |
|||
|
|||
if (joint1 && joint2) |
|||
{ |
|||
length = (joint2.position - joint1.position).magnitude; |
|||
return true; |
|||
} |
|||
} |
|||
|
|||
return false; |
|||
} |
|||
|
|||
private bool GetUserBodyHeight(KinectManager manager, float scaleFactor, float widthFactor, ref float height, ref float width) |
|||
{ |
|||
height = 0f; |
|||
width = 0f; |
|||
|
|||
Vector3 posHipLeft = GetJointPosition(manager, (int)KinectInterop.JointType.HipLeft); |
|||
Vector3 posHipRight = GetJointPosition(manager, (int)KinectInterop.JointType.HipRight); |
|||
Vector3 posShoulderLeft = GetJointPosition(manager, (int)KinectInterop.JointType.ShoulderLeft); |
|||
Vector3 posShoulderRight = GetJointPosition(manager, (int)KinectInterop.JointType.ShoulderRight); |
|||
|
|||
if (posHipLeft != Vector3.zero && posHipRight != Vector3.zero && |
|||
posShoulderLeft != Vector3.zero && posShoulderRight != Vector3.zero) |
|||
{ |
|||
Vector3 posHipCenter = (posHipLeft + posHipRight) / 2f; |
|||
Vector3 posShoulderCenter = (posShoulderLeft + posShoulderRight) / 2f; |
|||
//height = (posShoulderCenter.y - posHipCenter.y) * scaleFactor;
|
|||
|
|||
height = (posShoulderCenter - posHipCenter).magnitude * scaleFactor; |
|||
width = (posShoulderRight - posShoulderLeft).magnitude * widthFactor; |
|||
|
|||
return true; |
|||
} |
|||
|
|||
return false; |
|||
} |
|||
|
|||
private bool GetUserBoneLength(KinectManager manager, KinectInterop.JointType baseJoint, KinectInterop.JointType endJoint, float scaleFactor, ref float length) |
|||
{ |
|||
length = 0f; |
|||
|
|||
Vector3 vPos1 = GetJointPosition(manager, (int)baseJoint); |
|||
Vector3 vPos2 = GetJointPosition(manager, (int)endJoint); |
|||
|
|||
if (vPos1 != Vector3.zero && vPos2 != Vector3.zero) |
|||
{ |
|||
length = (vPos2 - vPos1).magnitude * scaleFactor; |
|||
return true; |
|||
} |
|||
|
|||
return false; |
|||
} |
|||
|
|||
private void EqualizeBoneLength(ref float boneLen1, ref float boneLen2) |
|||
{ |
|||
if (boneLen1 < boneLen2) |
|||
{ |
|||
boneLen1 = boneLen2; |
|||
} |
|||
else |
|||
{ |
|||
boneLen2 = boneLen1; |
|||
} |
|||
} |
|||
|
|||
private bool SetupBodyScale(Transform scaleTrans, Vector3 modelBodyScale, float modelHeight, float modelWidth, float userHeight, float userWidth, |
|||
float fSmooth, ref float heightScale, ref float widthScale) |
|||
{ |
|||
if (modelHeight > 0f && userHeight > 0f) |
|||
{ |
|||
heightScale = userHeight / modelHeight; |
|||
} |
|||
|
|||
if (modelWidth > 0f && userWidth > 0f) |
|||
{ |
|||
widthScale = userWidth / modelWidth; |
|||
} |
|||
else |
|||
{ |
|||
widthScale = heightScale; |
|||
} |
|||
|
|||
if (scaleTrans && heightScale > 0f && widthScale > 0f) |
|||
{ |
|||
float depthScale = heightScale; // (heightScale + widthScale) / 2f;
|
|||
Vector3 newLocalScale = new Vector3(modelBodyScale.x * widthScale, modelBodyScale.y * heightScale, modelBodyScale.z * depthScale); |
|||
|
|||
if (fSmooth != 0f) |
|||
scaleTrans.localScale = Vector3.Lerp(scaleTrans.localScale, newLocalScale, fSmooth * Time.deltaTime); |
|||
else |
|||
scaleTrans.localScale = newLocalScale; |
|||
|
|||
return true; |
|||
} |
|||
|
|||
return false; |
|||
} |
|||
|
|||
|
|||
private bool SetupBoneScale(Transform scaleTrans, Vector3 modelBoneScale, float modelBoneLen, float userBoneLen, float parentScale, float fSmooth, ref float boneScale) |
|||
{ |
|||
if (modelBoneLen > 0f && userBoneLen > 0f) |
|||
{ |
|||
boneScale = userBoneLen / modelBoneLen; |
|||
} |
|||
|
|||
float localScale = boneScale; |
|||
if (boneScale > 0f && parentScale > 0f) |
|||
{ |
|||
localScale = boneScale / parentScale; |
|||
} |
|||
|
|||
if (scaleTrans && localScale > 0f) |
|||
{ |
|||
if (fSmooth != 0f) |
|||
scaleTrans.localScale = Vector3.Lerp(scaleTrans.localScale, modelBoneScale * localScale, fSmooth * Time.deltaTime); |
|||
else |
|||
scaleTrans.localScale = modelBoneScale * localScale; |
|||
|
|||
return true; |
|||
} |
|||
|
|||
return false; |
|||
} |
|||
|
|||
|
|||
public bool FixJointsBeforeScale() |
|||
{ |
|||
Animator animatorComponent = GetComponent<Animator>(); |
|||
KinectManager manager = KinectManager.Instance; |
|||
|
|||
if (animatorComponent && modelBodyHeight > 0f && userBodyHeight > 0f) |
|||
{ |
|||
Transform hipCenter = animatorComponent.GetBoneTransform(HumanBodyBones.Hips); |
|||
if ((hipCenter.localScale - Vector3.one).magnitude > 0.01f) |
|||
return false; |
|||
|
|||
Transform leftUpperLeg = animatorComponent.GetBoneTransform(HumanBodyBones.LeftUpperLeg); |
|||
Transform rightUpperLeg = animatorComponent.GetBoneTransform(HumanBodyBones.RightUpperLeg); |
|||
|
|||
Transform leftUpperArm = animatorComponent.GetBoneTransform(HumanBodyBones.LeftUpperArm); |
|||
Transform rightUpperArm = animatorComponent.GetBoneTransform(HumanBodyBones.RightUpperArm); |
|||
|
|||
if (leftUpperArm && rightUpperArm && leftUpperLeg && rightUpperLeg) |
|||
{ |
|||
Vector3 posHipCenter = GetJointPosition(manager, (int)KinectInterop.JointType.Pelvis); |
|||
|
|||
Vector3 posHipLeft = GetJointPosition(manager, (int)KinectInterop.JointType.HipLeft); |
|||
Vector3 posHipRight = GetJointPosition(manager, (int)KinectInterop.JointType.HipRight); |
|||
|
|||
Vector3 posShoulderLeft = GetJointPosition(manager, (int)KinectInterop.JointType.ShoulderLeft); |
|||
Vector3 posShoulderRight = GetJointPosition(manager, (int)KinectInterop.JointType.ShoulderRight); |
|||
|
|||
if (posHipCenter != Vector3.zero && posHipLeft != Vector3.zero && posHipRight != Vector3.zero && |
|||
posShoulderLeft != Vector3.zero && posShoulderRight != Vector3.zero) |
|||
{ |
|||
SetupUnscaledJoint(hipCenter, leftUpperLeg, posHipCenter, (!mirroredAvatar ? posHipLeft : posHipRight), modelBodyHeight, userBodyHeight); |
|||
SetupUnscaledJoint(hipCenter, rightUpperLeg, posHipCenter, (!mirroredAvatar ? posHipRight : posHipLeft), modelBodyHeight, userBodyHeight); |
|||
|
|||
SetupUnscaledJoint(hipCenter, leftUpperArm, posHipCenter, (!mirroredAvatar ? posShoulderLeft : posShoulderRight), modelBodyHeight, userBodyHeight); |
|||
SetupUnscaledJoint(hipCenter, rightUpperArm, posHipCenter, (!mirroredAvatar ? posShoulderRight : posShoulderLeft), modelBodyHeight, userBodyHeight); |
|||
|
|||
// recalculate model joints
|
|||
Start(); |
|||
|
|||
return true; |
|||
} |
|||
} |
|||
} |
|||
|
|||
return false; |
|||
} |
|||
|
|||
|
|||
// gets the joint position in space
|
|||
private Vector3 GetJointPosition(KinectManager manager, int joint) |
|||
{ |
|||
Vector3 vPosJoint = Vector3.zero; |
|||
|
|||
if (manager.IsJointTracked(currentUserId, joint)) |
|||
{ |
|||
if (backgroundPlane && planeRectSet) |
|||
{ |
|||
// get the plane overlay position
|
|||
vPosJoint = manager.GetJointPosColorOverlay(currentUserId, joint, sensorIndex, planeRect); |
|||
vPosJoint.z = backgroundPlane.position.z; |
|||
} |
|||
else if (foregroundCamera) |
|||
{ |
|||
// get the background rectangle (use the portrait background, if available)
|
|||
Rect backgroundRect = foregroundCamera.pixelRect; |
|||
PortraitBackground portraitBack = PortraitBackground.Instance; |
|||
|
|||
if (portraitBack && portraitBack.enabled) |
|||
{ |
|||
backgroundRect = portraitBack.GetBackgroundRect(); |
|||
} |
|||
|
|||
// get the color overlay position
|
|||
vPosJoint = manager.GetJointPosColorOverlay(currentUserId, joint, sensorIndex, foregroundCamera, backgroundRect); |
|||
} |
|||
|
|||
// else
|
|||
if (vPosJoint == Vector3.zero) |
|||
{ |
|||
vPosJoint = manager.GetJointPosition(currentUserId, joint); |
|||
} |
|||
} |
|||
|
|||
return vPosJoint; |
|||
} |
|||
|
|||
|
|||
// sets the joint position before scaling
|
|||
private bool SetupUnscaledJoint(Transform hipCenter, Transform joint, Vector3 posHipCenter, Vector3 posJoint, float modelBoneLen, float userBoneLen) |
|||
{ |
|||
float boneScale = 0f; |
|||
|
|||
if (modelBoneLen > 0f && userBoneLen > 0f) |
|||
{ |
|||
boneScale = userBoneLen / modelBoneLen; |
|||
//boneScale = 1f;
|
|||
} |
|||
|
|||
if (boneScale > 0f) |
|||
{ |
|||
Vector3 posDiff = (posJoint - posHipCenter) / boneScale; |
|||
if (foregroundCamera == null && backgroundPlane == null) |
|||
posDiff.z = 0f; // ignore difference in z (non-overlay mode)
|
|||
|
|||
Vector3 posJointNew = hipCenter.position + posDiff; |
|||
joint.position = posJointNew; |
|||
|
|||
return true; |
|||
} |
|||
|
|||
return false; |
|||
} |
|||
|
|||
} |
|||
} |
@ -0,0 +1,11 @@ |
|||
fileFormatVersion: 2 |
|||
guid: de8f786bc13e05e4ebcbf32c68cf3515 |
|||
MonoImporter: |
|||
externalObjects: {} |
|||
serializedVersion: 2 |
|||
defaultReferences: [] |
|||
executionOrder: 0 |
|||
icon: {instanceID: 0} |
|||
userData: |
|||
assetBundleName: |
|||
assetBundleVariant: |
@ -0,0 +1,280 @@ |
|||
using UnityEngine; |
|||
using System.Collections; |
|||
using com.rfilkov.kinect; |
|||
using System; |
|||
|
|||
namespace com.rfilkov.components |
|||
{ |
|||
/// <summary>
|
|||
/// BackgroundColorCamDepthImage is component that displays the color camera aligned depth image on RawImage texture, usually the scene background.
|
|||
/// </summary>
|
|||
public class BackgroundColorCamDepthImage : MonoBehaviour |
|||
{ |
|||
[Tooltip("Depth sensor index - 0 is the 1st one, 1 - the 2nd one, etc.")] |
|||
public int sensorIndex = 0; |
|||
|
|||
[Tooltip("RawImage used to display the color camera feed.")] |
|||
public UnityEngine.UI.RawImage backgroundImage; |
|||
|
|||
[Tooltip("Camera used to display the background image. Set it, if you'd like to allow background image to resize, to match the color image's aspect ratio.")] |
|||
public Camera backgroundCamera; |
|||
|
|||
|
|||
// last camera rect width & height
|
|||
private float lastCamRectW = 0; |
|||
private float lastCamRectH = 0; |
|||
|
|||
// reference to the kinectManager
|
|||
private KinectManager kinectManager = null; |
|||
private KinectInterop.SensorData sensorData = null; |
|||
private Vector2 initialAnchorPos = Vector2.zero; |
|||
|
|||
// color-camera aligned frames
|
|||
private ulong lastColorCamDepthFrameTime = 0; |
|||
|
|||
// color-camera aligned texture and buffers
|
|||
private RenderTexture depthImageTexture = null; |
|||
private Material depthImageMaterial = null; |
|||
|
|||
private ComputeBuffer depthImageBuffer = null; |
|||
private ComputeBuffer depthHistBuffer = null; |
|||
|
|||
// depth image hist data
|
|||
protected int[] depthHistBufferData = null; |
|||
protected int[] equalHistBufferData = null; |
|||
protected int depthHistTotalPoints = 0; |
|||
|
|||
|
|||
void Start() |
|||
{ |
|||
if (backgroundImage == null) |
|||
{ |
|||
backgroundImage = GetComponent<UnityEngine.UI.RawImage>(); |
|||
} |
|||
|
|||
kinectManager = KinectManager.Instance; |
|||
sensorData = kinectManager != null ? kinectManager.GetSensorData(sensorIndex) : null; |
|||
|
|||
if(sensorData != null) |
|||
{ |
|||
// enable color camera aligned depth frames
|
|||
sensorData.sensorInterface.EnableColorCameraDepthFrame(sensorData, true); |
|||
|
|||
// create the output texture and needed buffers
|
|||
depthImageTexture = KinectInterop.CreateRenderTexture(depthImageTexture, sensorData.colorImageWidth, sensorData.colorImageHeight); |
|||
depthImageMaterial = new Material(Shader.Find("Kinect/DepthHistImageShader")); |
|||
|
|||
//int depthBufferLength = sensorData.colorImageWidth * sensorData.colorImageHeight >> 1;
|
|||
//depthImageBuffer = KinectInterop.CreateComputeBuffer(depthImageBuffer, depthBufferLength, sizeof(uint));
|
|||
|
|||
depthHistBuffer = KinectInterop.CreateComputeBuffer(depthHistBuffer, DepthSensorBase.MAX_DEPTH_DISTANCE_MM + 1, sizeof(int)); |
|||
|
|||
depthHistBufferData = new int[DepthSensorBase.MAX_DEPTH_DISTANCE_MM + 1]; |
|||
equalHistBufferData = new int[DepthSensorBase.MAX_DEPTH_DISTANCE_MM + 1]; |
|||
} |
|||
} |
|||
|
|||
|
|||
void OnDestroy() |
|||
{ |
|||
if (depthImageTexture) |
|||
{ |
|||
depthImageTexture.Release(); |
|||
depthImageTexture = null; |
|||
} |
|||
|
|||
if (depthImageBuffer != null) |
|||
{ |
|||
depthImageBuffer.Dispose(); |
|||
depthImageBuffer = null; |
|||
} |
|||
|
|||
if (depthHistBuffer != null) |
|||
{ |
|||
depthHistBuffer.Dispose(); |
|||
depthHistBuffer = null; |
|||
} |
|||
|
|||
if (sensorData != null) |
|||
{ |
|||
// disable color camera aligned depth frames
|
|||
sensorData.sensorInterface.EnableColorCameraDepthFrame(sensorData, false); |
|||
} |
|||
} |
|||
|
|||
|
|||
void Update() |
|||
{ |
|||
if (kinectManager && kinectManager.IsInitialized()) |
|||
{ |
|||
float cameraWidth = backgroundCamera ? backgroundCamera.pixelRect.width : 0f; |
|||
float cameraHeight = backgroundCamera ? backgroundCamera.pixelRect.height : 0f; |
|||
|
|||
// check for new color camera aligned frames
|
|||
UpdateTextureWithNewFrame(); |
|||
|
|||
if (backgroundImage && depthImageTexture != null && (backgroundImage.texture == null || |
|||
backgroundImage.texture.width != depthImageTexture.width || backgroundImage.texture.height != depthImageTexture.height || |
|||
lastCamRectW != cameraWidth || lastCamRectH != cameraHeight)) |
|||
{ |
|||
lastCamRectW = cameraWidth; |
|||
lastCamRectH = cameraHeight; |
|||
|
|||
// enable color camera aligned depth frames
|
|||
sensorData = kinectManager.GetSensorData(sensorIndex); // sensor data may be re-created after sensor-int restart
|
|||
sensorData.sensorInterface.EnableColorCameraDepthFrame(sensorData, true); |
|||
|
|||
// set background texture
|
|||
backgroundImage.texture = depthImageTexture; |
|||
backgroundImage.rectTransform.localScale = sensorData.colorImageScale; // kinectManager.GetColorImageScale(sensorIndex);
|
|||
backgroundImage.color = Color.white; |
|||
|
|||
if (backgroundCamera != null) |
|||
{ |
|||
// adjust image's size and position to match the stream aspect ratio
|
|||
int colorImageWidth = sensorData.colorImageWidth; // kinectManager.GetColorImageWidth(sensorIndex);
|
|||
int colorImageHeight = sensorData.colorImageHeight; // kinectManager.GetColorImageHeight(sensorIndex);
|
|||
if (colorImageWidth == 0 || colorImageHeight == 0) |
|||
return; |
|||
|
|||
RectTransform rectImage = backgroundImage.rectTransform; |
|||
float rectWidth = (rectImage.anchorMin.x != rectImage.anchorMax.x) ? cameraWidth * (rectImage.anchorMax.x - rectImage.anchorMin.x) : rectImage.sizeDelta.x; |
|||
float rectHeight = (rectImage.anchorMin.y != rectImage.anchorMax.y) ? cameraHeight * (rectImage.anchorMax.y - rectImage.anchorMin.y) : rectImage.sizeDelta.y; |
|||
|
|||
if (colorImageWidth > colorImageHeight) |
|||
rectWidth = rectHeight * colorImageWidth / colorImageHeight; |
|||
else |
|||
rectHeight = rectWidth * colorImageHeight / colorImageWidth; |
|||
|
|||
Vector2 pivotOffset = (rectImage.pivot - new Vector2(0.5f, 0.5f)) * 2f; |
|||
Vector2 imageScale = sensorData.colorImageScale; // (Vector2)kinectManager.GetColorImageScale(sensorIndex);
|
|||
Vector2 anchorPos = rectImage.anchoredPosition + pivotOffset * imageScale * new Vector2(rectWidth, rectHeight); |
|||
|
|||
if (rectImage.anchorMin.x != rectImage.anchorMax.x) |
|||
{ |
|||
rectWidth = -(cameraWidth - rectWidth); |
|||
} |
|||
|
|||
if (rectImage.anchorMin.y != rectImage.anchorMax.y) |
|||
{ |
|||
rectHeight = -(cameraHeight - rectHeight); |
|||
} |
|||
|
|||
rectImage.sizeDelta = new Vector2(rectWidth, rectHeight); |
|||
rectImage.anchoredPosition = initialAnchorPos = anchorPos; |
|||
} |
|||
} |
|||
|
|||
//if (backgroundImage)
|
|||
//{
|
|||
// // update the anchor position, if needed
|
|||
// if (sensorData != null && sensorData.sensorInterface != null)
|
|||
// {
|
|||
// Vector2 updatedAnchorPos = initialAnchorPos + sensorData.sensorInterface.GetBackgroundImageAnchorPos(sensorData);
|
|||
// if (backgroundImage.rectTransform.anchoredPosition != updatedAnchorPos)
|
|||
// {
|
|||
// backgroundImage.rectTransform.anchoredPosition = updatedAnchorPos;
|
|||
// }
|
|||
// }
|
|||
//}
|
|||
} |
|||
else |
|||
{ |
|||
// reset the background texture, if needed
|
|||
if (backgroundImage && backgroundImage.texture != null) |
|||
{ |
|||
backgroundImage.texture = null; |
|||
|
|||
// disable color camera aligned depth frames
|
|||
if(sensorData != null && sensorData.sensorInterface != null) |
|||
{ |
|||
sensorData.sensorInterface.EnableColorCameraDepthFrame(sensorData, false); |
|||
} |
|||
} |
|||
} |
|||
|
|||
//RectTransform rectTransform = backgroundImage.rectTransform;
|
|||
//Debug.Log("pivot: " + rectTransform.pivot + ", anchorPos: " + rectTransform.anchoredPosition + ", \nanchorMin: " + rectTransform.anchorMin + ", anchorMax: " + rectTransform.anchorMax);
|
|||
} |
|||
|
|||
|
|||
// checks for new color-camera aligned frames, and composes an updated body-index texture, if needed
|
|||
private void UpdateTextureWithNewFrame() |
|||
{ |
|||
if (sensorData == null || sensorData.sensorInterface == null || sensorData.colorCamDepthImage == null) |
|||
return; |
|||
|
|||
// get the updated depth frame
|
|||
if (lastColorCamDepthFrameTime != sensorData.lastColorCamDepthFrameTime) |
|||
{ |
|||
lastColorCamDepthFrameTime = sensorData.lastColorCamDepthFrameTime; |
|||
|
|||
if (depthImageTexture.width != sensorData.colorImageWidth || depthImageTexture.height != sensorData.colorImageHeight) |
|||
{ |
|||
depthImageTexture = KinectInterop.CreateRenderTexture(depthImageTexture, sensorData.colorImageWidth, sensorData.colorImageHeight); |
|||
} |
|||
|
|||
Array.Clear(depthHistBufferData, 0, depthHistBufferData.Length); |
|||
Array.Clear(equalHistBufferData, 0, equalHistBufferData.Length); |
|||
depthHistTotalPoints = 0; |
|||
|
|||
// get configured min & max distances
|
|||
float minDistance = ((DepthSensorBase)sensorData.sensorInterface).minDepthDistance; |
|||
float maxDistance = ((DepthSensorBase)sensorData.sensorInterface).maxDepthDistance; |
|||
|
|||
int depthMinDistance = (int)(minDistance * 1000f); |
|||
int depthMaxDistance = (int)(maxDistance * 1000f); |
|||
|
|||
int frameLen = sensorData.colorCamDepthImage.Length; |
|||
for (int i = 0; i < frameLen; i++) |
|||
{ |
|||
int depth = sensorData.colorCamDepthImage[i]; |
|||
int limDepth = (depth <= DepthSensorBase.MAX_DEPTH_DISTANCE_MM) ? depth : 0; |
|||
|
|||
if (limDepth > 0) |
|||
{ |
|||
depthHistBufferData[limDepth]++; |
|||
depthHistTotalPoints++; |
|||
} |
|||
} |
|||
|
|||
equalHistBufferData[0] = depthHistBufferData[0]; |
|||
for (int i = 1; i < depthHistBufferData.Length; i++) |
|||
{ |
|||
equalHistBufferData[i] = equalHistBufferData[i - 1] + depthHistBufferData[i]; |
|||
} |
|||
|
|||
// make depth 0 equal to the max-depth
|
|||
equalHistBufferData[0] = equalHistBufferData[equalHistBufferData.Length - 1]; |
|||
|
|||
int depthBufferLength = sensorData.colorCamDepthImage.Length >> 1; |
|||
if(depthImageBuffer == null || depthImageBuffer.count != depthBufferLength) |
|||
{ |
|||
depthImageBuffer = KinectInterop.CreateComputeBuffer(depthImageBuffer, depthBufferLength, sizeof(uint)); |
|||
} |
|||
|
|||
KinectInterop.SetComputeBufferData(depthImageBuffer, sensorData.colorCamDepthImage, depthBufferLength, sizeof(uint)); |
|||
|
|||
if (depthHistBuffer != null) |
|||
{ |
|||
KinectInterop.SetComputeBufferData(depthHistBuffer, equalHistBufferData, equalHistBufferData.Length, sizeof(int)); |
|||
} |
|||
|
|||
depthImageMaterial.SetInt("_TexResX", sensorData.colorImageWidth); |
|||
depthImageMaterial.SetInt("_TexResY", sensorData.colorImageHeight); |
|||
depthImageMaterial.SetInt("_MinDepth", depthMinDistance); |
|||
depthImageMaterial.SetInt("_MaxDepth", depthMaxDistance); |
|||
|
|||
depthImageMaterial.SetBuffer("_DepthMap", depthImageBuffer); |
|||
depthImageMaterial.SetBuffer("_HistMap", depthHistBuffer); |
|||
depthImageMaterial.SetInt("_TotalPoints", depthHistTotalPoints); |
|||
|
|||
Graphics.Blit(null, depthImageTexture, depthImageMaterial); |
|||
} |
|||
|
|||
|
|||
} |
|||
|
|||
} |
|||
} |
|||
|
@ -0,0 +1,12 @@ |
|||
fileFormatVersion: 2 |
|||
guid: bcdfb9864840151469f8a2a0ba31d093 |
|||
timeCreated: 1481733120 |
|||
licenseType: Store |
|||
MonoImporter: |
|||
serializedVersion: 2 |
|||
defaultReferences: [] |
|||
executionOrder: 0 |
|||
icon: {instanceID: 0} |
|||
userData: |
|||
assetBundleName: |
|||
assetBundleVariant: |
@ -0,0 +1,223 @@ |
|||
using UnityEngine; |
|||
using System.Collections; |
|||
using com.rfilkov.kinect; |
|||
using System; |
|||
|
|||
namespace com.rfilkov.components |
|||
{ |
|||
/// <summary>
|
|||
/// BackgroundColorCamInfraredImage is component that displays the color camera aligned infrared image on RawImage texture, usually the scene background.
|
|||
/// </summary>
|
|||
public class BackgroundColorCamInfraredImage : MonoBehaviour |
|||
{ |
|||
[Tooltip("Depth sensor index - 0 is the 1st one, 1 - the 2nd one, etc.")] |
|||
public int sensorIndex = 0; |
|||
|
|||
[Tooltip("RawImage used to display the color camera feed.")] |
|||
public UnityEngine.UI.RawImage backgroundImage; |
|||
|
|||
[Tooltip("Camera used to display the background image. Set it, if you'd like to allow background image to resize, to match the color image's aspect ratio.")] |
|||
public Camera backgroundCamera; |
|||
|
|||
|
|||
// last camera rect width & height
|
|||
private float lastCamRectW = 0; |
|||
private float lastCamRectH = 0; |
|||
|
|||
// reference to the kinectManager
|
|||
private KinectManager kinectManager = null; |
|||
private KinectInterop.SensorData sensorData = null; |
|||
private Vector2 initialAnchorPos = Vector2.zero; |
|||
|
|||
// color-camera aligned frames
|
|||
private ulong lastColorCamInfraredFrameTime = 0; |
|||
|
|||
// color-camera aligned texture and buffers
|
|||
private RenderTexture infraredImageTexture = null; |
|||
private Material infraredImageMaterial = null; |
|||
private ComputeBuffer infraredImageBuffer = null; |
|||
|
|||
|
|||
void Start() |
|||
{ |
|||
if (backgroundImage == null) |
|||
{ |
|||
backgroundImage = GetComponent<UnityEngine.UI.RawImage>(); |
|||
} |
|||
|
|||
kinectManager = KinectManager.Instance; |
|||
sensorData = kinectManager != null ? kinectManager.GetSensorData(sensorIndex) : null; |
|||
|
|||
if(sensorData != null) |
|||
{ |
|||
// enable color camera aligned infrared frames
|
|||
sensorData.sensorInterface.EnableColorCameraInfraredFrame(sensorData, true, false); |
|||
|
|||
// create the output texture and needed buffers
|
|||
infraredImageTexture = KinectInterop.CreateRenderTexture(infraredImageTexture, sensorData.colorImageWidth, sensorData.colorImageHeight); |
|||
infraredImageMaterial = new Material(Shader.Find("Kinect/InfraredImageShader")); |
|||
|
|||
//int infraredBufferLength = sensorData.colorImageWidth * sensorData.colorImageHeight >> 1;
|
|||
//infraredImageBuffer = KinectInterop.CreateComputeBuffer(infraredImageBuffer, infraredBufferLength, sizeof(uint));
|
|||
} |
|||
} |
|||
|
|||
|
|||
void OnDestroy() |
|||
{ |
|||
if (infraredImageTexture) |
|||
{ |
|||
infraredImageTexture.Release(); |
|||
infraredImageTexture = null; |
|||
} |
|||
|
|||
if (infraredImageBuffer != null) |
|||
{ |
|||
infraredImageBuffer.Dispose(); |
|||
infraredImageBuffer = null; |
|||
} |
|||
|
|||
if (sensorData != null) |
|||
{ |
|||
// disable color camera aligned infrared frames
|
|||
sensorData.sensorInterface.EnableColorCameraInfraredFrame(sensorData, false, false); |
|||
} |
|||
} |
|||
|
|||
|
|||
void Update() |
|||
{ |
|||
if (kinectManager && kinectManager.IsInitialized()) |
|||
{ |
|||
float cameraWidth = backgroundCamera ? backgroundCamera.pixelRect.width : 0f; |
|||
float cameraHeight = backgroundCamera ? backgroundCamera.pixelRect.height : 0f; |
|||
|
|||
// check for new color camera aligned frames
|
|||
UpdateTextureWithNewFrame(); |
|||
|
|||
if (backgroundImage && infraredImageTexture != null && (backgroundImage.texture == null || |
|||
backgroundImage.texture.width != infraredImageTexture.width || backgroundImage.texture.height != infraredImageTexture.height || |
|||
lastCamRectW != cameraWidth || lastCamRectH != cameraHeight)) |
|||
{ |
|||
lastCamRectW = cameraWidth; |
|||
lastCamRectH = cameraHeight; |
|||
|
|||
// enable color camera aligned infrared frames
|
|||
sensorData = kinectManager.GetSensorData(sensorIndex); // sensor data may be re-created after sensor-int restart
|
|||
sensorData.sensorInterface.EnableColorCameraInfraredFrame(sensorData, true, false); |
|||
|
|||
backgroundImage.texture = infraredImageTexture; |
|||
backgroundImage.rectTransform.localScale = sensorData.colorImageScale; // kinectManager.GetColorImageScale(sensorIndex);
|
|||
backgroundImage.color = Color.white; |
|||
|
|||
if (backgroundCamera != null) |
|||
{ |
|||
// adjust image's size and position to match the stream aspect ratio
|
|||
int colorImageWidth = sensorData.colorImageWidth; // kinectManager.GetColorImageWidth(sensorIndex);
|
|||
int colorImageHeight = sensorData.colorImageHeight; // kinectManager.GetColorImageHeight(sensorIndex);
|
|||
if (colorImageWidth == 0 || colorImageHeight == 0) |
|||
return; |
|||
|
|||
RectTransform rectImage = backgroundImage.rectTransform; |
|||
float rectWidth = (rectImage.anchorMin.x != rectImage.anchorMax.x) ? cameraWidth * (rectImage.anchorMax.x - rectImage.anchorMin.x) : rectImage.sizeDelta.x; |
|||
float rectHeight = (rectImage.anchorMin.y != rectImage.anchorMax.y) ? cameraHeight * (rectImage.anchorMax.y - rectImage.anchorMin.y) : rectImage.sizeDelta.y; |
|||
|
|||
if (colorImageWidth > colorImageHeight) |
|||
rectWidth = rectHeight * colorImageWidth / colorImageHeight; |
|||
else |
|||
rectHeight = rectWidth * colorImageHeight / colorImageWidth; |
|||
|
|||
Vector2 pivotOffset = (rectImage.pivot - new Vector2(0.5f, 0.5f)) * 2f; |
|||
Vector2 imageScale = sensorData.colorImageScale; // (Vector2)kinectManager.GetColorImageScale(sensorIndex);
|
|||
Vector2 anchorPos = rectImage.anchoredPosition + pivotOffset * imageScale * new Vector2(rectWidth, rectHeight); |
|||
|
|||
if (rectImage.anchorMin.x != rectImage.anchorMax.x) |
|||
{ |
|||
rectWidth = -(cameraWidth - rectWidth); |
|||
} |
|||
|
|||
if (rectImage.anchorMin.y != rectImage.anchorMax.y) |
|||
{ |
|||
rectHeight = -(cameraHeight - rectHeight); |
|||
} |
|||
|
|||
rectImage.sizeDelta = new Vector2(rectWidth, rectHeight); |
|||
rectImage.anchoredPosition = initialAnchorPos = anchorPos; |
|||
} |
|||
} |
|||
|
|||
//if (backgroundImage)
|
|||
//{
|
|||
// // update the anchor position, if needed
|
|||
// if (sensorData != null && sensorData.sensorInterface != null)
|
|||
// {
|
|||
// Vector2 updatedAnchorPos = initialAnchorPos + sensorData.sensorInterface.GetBackgroundImageAnchorPos(sensorData);
|
|||
// if (backgroundImage.rectTransform.anchoredPosition != updatedAnchorPos)
|
|||
// {
|
|||
// backgroundImage.rectTransform.anchoredPosition = updatedAnchorPos;
|
|||
// }
|
|||
// }
|
|||
//}
|
|||
} |
|||
else |
|||
{ |
|||
// reset the background texture, if needed
|
|||
if (backgroundImage && backgroundImage.texture != null) |
|||
{ |
|||
backgroundImage.texture = null; |
|||
|
|||
// disable color camera aligned infrared frames
|
|||
if (sensorData != null && sensorData.sensorInterface != null) |
|||
{ |
|||
sensorData.sensorInterface.EnableColorCameraInfraredFrame(sensorData, false, false); |
|||
} |
|||
} |
|||
} |
|||
|
|||
//RectTransform rectTransform = backgroundImage.rectTransform;
|
|||
//Debug.Log("pivot: " + rectTransform.pivot + ", anchorPos: " + rectTransform.anchoredPosition + ", \nanchorMin: " + rectTransform.anchorMin + ", anchorMax: " + rectTransform.anchorMax);
|
|||
} |
|||
|
|||
|
|||
// checks for new color-camera aligned frames, and composes an updated body-index texture, if needed
|
|||
private void UpdateTextureWithNewFrame() |
|||
{ |
|||
if (sensorData == null || sensorData.sensorInterface == null || sensorData.colorCamInfraredImage == null) |
|||
return; |
|||
|
|||
// get the updated infrared
|
|||
if (lastColorCamInfraredFrameTime != sensorData.lastColorCamInfraredFrameTime) |
|||
{ |
|||
lastColorCamInfraredFrameTime = sensorData.lastColorCamInfraredFrameTime; |
|||
|
|||
if (infraredImageTexture.width != sensorData.colorImageWidth || infraredImageTexture.height != sensorData.colorImageHeight) |
|||
{ |
|||
infraredImageTexture = KinectInterop.CreateRenderTexture(infraredImageTexture, sensorData.colorImageWidth, sensorData.colorImageHeight); |
|||
} |
|||
|
|||
int infraredBufferLength = sensorData.colorCamInfraredImage.Length >> 1; |
|||
if (infraredImageBuffer == null || infraredImageBuffer.count != infraredBufferLength) |
|||
{ |
|||
infraredImageBuffer = KinectInterop.CreateComputeBuffer(infraredImageBuffer, infraredBufferLength, sizeof(uint)); |
|||
} |
|||
|
|||
KinectInterop.SetComputeBufferData(infraredImageBuffer, sensorData.colorCamInfraredImage, infraredBufferLength, sizeof(uint)); |
|||
|
|||
float minInfraredValue = ((DepthSensorBase)sensorData.sensorInterface).GetMinInfraredValue(); |
|||
float maxInfraredValue = ((DepthSensorBase)sensorData.sensorInterface).GetMaxInfraredValue(); |
|||
|
|||
infraredImageMaterial.SetInt("_TexResX", sensorData.colorImageWidth); |
|||
infraredImageMaterial.SetInt("_TexResY", sensorData.colorImageHeight); |
|||
infraredImageMaterial.SetFloat("_MinValue", minInfraredValue); |
|||
infraredImageMaterial.SetFloat("_MaxValue", maxInfraredValue); |
|||
infraredImageMaterial.SetBuffer("_InfraredMap", infraredImageBuffer); |
|||
|
|||
Graphics.Blit(null, infraredImageTexture, infraredImageMaterial); |
|||
} |
|||
|
|||
|
|||
} |
|||
|
|||
} |
|||
} |
|||
|
@ -0,0 +1,12 @@ |
|||
fileFormatVersion: 2 |
|||
guid: db2e5a5269099984a990878f9ccceb23 |
|||
timeCreated: 1481733120 |
|||
licenseType: Store |
|||
MonoImporter: |
|||
serializedVersion: 2 |
|||
defaultReferences: [] |
|||
executionOrder: 0 |
|||
icon: {instanceID: 0} |
|||
userData: |
|||
assetBundleName: |
|||
assetBundleVariant: |
@ -0,0 +1,322 @@ |
|||
using UnityEngine; |
|||
using System.Collections; |
|||
using com.rfilkov.kinect; |
|||
using System; |
|||
|
|||
namespace com.rfilkov.components |
|||
{ |
|||
/// <summary>
|
|||
/// BackgroundColorCamUserImage is component that displays the color camera aligned user-body image on RawImage texture, usually the scene background.
|
|||
/// </summary>
|
|||
public class BackgroundColorCamUserImage : MonoBehaviour |
|||
{ |
|||
[Tooltip("Depth sensor index - 0 is the 1st one, 1 - the 2nd one, etc.")] |
|||
public int sensorIndex = 0; |
|||
|
|||
[Tooltip("Index of the player, tracked by this component. -1 means all players, 0 - the 1st player, 1 - the 2nd one, 2 - the 3rd one, etc.")] |
|||
public int playerIndex = -1; |
|||
|
|||
[Tooltip("RawImage used to display the color camera feed.")] |
|||
public UnityEngine.UI.RawImage backgroundImage; |
|||
|
|||
[Tooltip("Camera used to display the background image. Set it, if you'd like to allow background image to resize, to match the color image's aspect ratio.")] |
|||
public Camera backgroundCamera; |
|||
|
|||
|
|||
// last camera rect width & height
|
|||
private float lastCamRectW = 0; |
|||
private float lastCamRectH = 0; |
|||
|
|||
// reference to the kinectManager
|
|||
private KinectManager kinectManager = null; |
|||
private KinectInterop.SensorData sensorData = null; |
|||
private Vector2 initialAnchorPos = Vector2.zero; |
|||
|
|||
// color-camera aligned frames
|
|||
private ulong lastColorCamDepthFrameTime = 0; |
|||
private ulong lastColorCamBodyIndexFrameTime = 0; |
|||
|
|||
// color-camera aligned texture and buffers
|
|||
private RenderTexture bodyImageTexture = null; |
|||
private Material bodyImageMaterial = null; |
|||
|
|||
private ComputeBuffer bodyIndexBuffer = null; |
|||
private ComputeBuffer depthImageBuffer = null; |
|||
private ComputeBuffer bodyHistBuffer = null; |
|||
|
|||
// body image hist data
|
|||
protected int[] depthBodyBufferData = null; |
|||
protected int[] equalBodyBufferData = null; |
|||
protected int bodyHistTotalPoints = 0; |
|||
|
|||
|
|||
void Start() |
|||
{ |
|||
if (backgroundImage == null) |
|||
{ |
|||
backgroundImage = GetComponent<UnityEngine.UI.RawImage>(); |
|||
} |
|||
|
|||
kinectManager = KinectManager.Instance; |
|||
sensorData = kinectManager != null ? kinectManager.GetSensorData(sensorIndex) : null; |
|||
|
|||
if(sensorData != null) |
|||
{ |
|||
// enable color camera aligned depth & body-index frames
|
|||
sensorData.sensorInterface.EnableColorCameraDepthFrame(sensorData, true); |
|||
sensorData.sensorInterface.EnableColorCameraBodyIndexFrame(sensorData, true); |
|||
|
|||
// create the user texture and needed buffers
|
|||
//bodyImageTexture = KinectInterop.CreateRenderTexture(bodyImageTexture, sensorData.colorImageWidth, sensorData.colorImageHeight);
|
|||
bodyImageMaterial = new Material(Shader.Find("Kinect/UserHistImageShader")); |
|||
|
|||
bodyHistBuffer = KinectInterop.CreateComputeBuffer(bodyHistBuffer, DepthSensorBase.MAX_DEPTH_DISTANCE_MM + 1, sizeof(int)); |
|||
|
|||
depthBodyBufferData = new int[DepthSensorBase.MAX_DEPTH_DISTANCE_MM + 1]; |
|||
equalBodyBufferData = new int[DepthSensorBase.MAX_DEPTH_DISTANCE_MM + 1]; |
|||
} |
|||
} |
|||
|
|||
|
|||
void OnDestroy() |
|||
{ |
|||
if (bodyImageTexture) |
|||
{ |
|||
bodyImageTexture.Release(); |
|||
bodyImageTexture = null; |
|||
} |
|||
|
|||
if (bodyIndexBuffer != null) |
|||
{ |
|||
bodyIndexBuffer.Dispose(); |
|||
bodyIndexBuffer = null; |
|||
} |
|||
|
|||
if (depthImageBuffer != null) |
|||
{ |
|||
depthImageBuffer.Dispose(); |
|||
depthImageBuffer = null; |
|||
} |
|||
|
|||
if (bodyHistBuffer != null) |
|||
{ |
|||
bodyHistBuffer.Dispose(); |
|||
bodyHistBuffer = null; |
|||
} |
|||
|
|||
if (sensorData != null) |
|||
{ |
|||
// disable color camera aligned depth & body-index frames
|
|||
sensorData.sensorInterface.EnableColorCameraDepthFrame(sensorData, false); |
|||
sensorData.sensorInterface.EnableColorCameraBodyIndexFrame(sensorData, false); |
|||
} |
|||
} |
|||
|
|||
|
|||
void Update() |
|||
{ |
|||
if (kinectManager && kinectManager.IsInitialized()) |
|||
{ |
|||
float cameraWidth = backgroundCamera ? backgroundCamera.pixelRect.width : 0f; |
|||
float cameraHeight = backgroundCamera ? backgroundCamera.pixelRect.height : 0f; |
|||
|
|||
// check for new color camera aligned frames
|
|||
UpdateTextureWithNewFrame(); |
|||
|
|||
if (backgroundImage && bodyImageTexture != null && (backgroundImage.texture == null || |
|||
backgroundImage.texture.width != bodyImageTexture.width || backgroundImage.texture.height != bodyImageTexture.height || |
|||
lastCamRectW != cameraWidth || lastCamRectH != cameraHeight)) |
|||
{ |
|||
lastCamRectW = cameraWidth; |
|||
lastCamRectH = cameraHeight; |
|||
|
|||
// enable color camera aligned depth & body-index frames
|
|||
sensorData = kinectManager.GetSensorData(sensorIndex); // sensor data may be re-created after sensor-int restart
|
|||
sensorData.sensorInterface.EnableColorCameraDepthFrame(sensorData, true); |
|||
sensorData.sensorInterface.EnableColorCameraBodyIndexFrame(sensorData, true); |
|||
|
|||
backgroundImage.texture = bodyImageTexture; |
|||
backgroundImage.rectTransform.localScale = sensorData.colorImageScale; // kinectManager.GetColorImageScale(sensorIndex);
|
|||
backgroundImage.color = Color.white; |
|||
|
|||
if (backgroundCamera != null) |
|||
{ |
|||
// adjust image's size and position to match the stream aspect ratio
|
|||
int colorImageWidth = sensorData.colorImageWidth; // kinectManager.GetColorImageWidth(sensorIndex);
|
|||
int colorImageHeight = sensorData.colorImageHeight; // kinectManager.GetColorImageHeight(sensorIndex);
|
|||
if (colorImageWidth == 0 || colorImageHeight == 0) |
|||
return; |
|||
|
|||
RectTransform rectImage = backgroundImage.rectTransform; |
|||
float rectWidth = (rectImage.anchorMin.x != rectImage.anchorMax.x) ? cameraWidth * (rectImage.anchorMax.x - rectImage.anchorMin.x) : rectImage.sizeDelta.x; |
|||
float rectHeight = (rectImage.anchorMin.y != rectImage.anchorMax.y) ? cameraHeight * (rectImage.anchorMax.y - rectImage.anchorMin.y) : rectImage.sizeDelta.y; |
|||
|
|||
if (colorImageWidth > colorImageHeight) |
|||
rectWidth = rectHeight * colorImageWidth / colorImageHeight; |
|||
else |
|||
rectHeight = rectWidth * colorImageHeight / colorImageWidth; |
|||
|
|||
Vector2 pivotOffset = (rectImage.pivot - new Vector2(0.5f, 0.5f)) * 2f; |
|||
Vector2 imageScale = sensorData.colorImageScale; // (Vector2)kinectManager.GetColorImageScale(sensorIndex);
|
|||
Vector2 anchorPos = rectImage.anchoredPosition + pivotOffset * imageScale * new Vector2(rectWidth, rectHeight); |
|||
|
|||
if (rectImage.anchorMin.x != rectImage.anchorMax.x) |
|||
{ |
|||
rectWidth = -(cameraWidth - rectWidth); |
|||
} |
|||
|
|||
if (rectImage.anchorMin.y != rectImage.anchorMax.y) |
|||
{ |
|||
rectHeight = -(cameraHeight - rectHeight); |
|||
} |
|||
|
|||
rectImage.sizeDelta = new Vector2(rectWidth, rectHeight); |
|||
rectImage.anchoredPosition = initialAnchorPos = anchorPos; |
|||
} |
|||
} |
|||
|
|||
//if (backgroundImage)
|
|||
//{
|
|||
// // update the anchor position, if needed
|
|||
// if (sensorData != null && sensorData.sensorInterface != null)
|
|||
// {
|
|||
// Vector2 updatedAnchorPos = initialAnchorPos + sensorData.sensorInterface.GetBackgroundImageAnchorPos(sensorData);
|
|||
// if (backgroundImage.rectTransform.anchoredPosition != updatedAnchorPos)
|
|||
// {
|
|||
// backgroundImage.rectTransform.anchoredPosition = updatedAnchorPos;
|
|||
// }
|
|||
// }
|
|||
//}
|
|||
} |
|||
else |
|||
{ |
|||
// reset the background texture, if needed
|
|||
if (backgroundImage && backgroundImage.texture != null) |
|||
{ |
|||
backgroundImage.texture = null; |
|||
|
|||
if (sensorData != null) |
|||
{ |
|||
// disable color camera aligned depth & body-index frames
|
|||
sensorData.sensorInterface.EnableColorCameraDepthFrame(sensorData, false); |
|||
sensorData.sensorInterface.EnableColorCameraBodyIndexFrame(sensorData, false); |
|||
} |
|||
} |
|||
} |
|||
|
|||
//RectTransform rectTransform = backgroundImage.rectTransform;
|
|||
//Debug.Log("pivot: " + rectTransform.pivot + ", anchorPos: " + rectTransform.anchoredPosition + ", \nanchorMin: " + rectTransform.anchorMin + ", anchorMax: " + rectTransform.anchorMax);
|
|||
} |
|||
|
|||
|
|||
// checks for new color-camera aligned frames, and composes an updated body-index texture, if needed
|
|||
private void UpdateTextureWithNewFrame() |
|||
{ |
|||
if (sensorData == null || sensorData.sensorInterface == null || sensorData.colorCamBodyIndexImage == null || sensorData.colorCamDepthImage == null) |
|||
return; |
|||
if (sensorData.colorImageWidth == 0 || sensorData.colorImageHeight == 0 || sensorData.lastColorCamDepthFrameTime == 0 || sensorData.lastColorCamBodyIndexFrameTime == 0) |
|||
return; |
|||
|
|||
// get body index frame
|
|||
if (lastColorCamDepthFrameTime != sensorData.lastColorCamDepthFrameTime || lastColorCamBodyIndexFrameTime != sensorData.lastColorCamBodyIndexFrameTime) |
|||
{ |
|||
lastColorCamDepthFrameTime = sensorData.lastColorCamDepthFrameTime; |
|||
lastColorCamBodyIndexFrameTime = sensorData.lastColorCamBodyIndexFrameTime; |
|||
|
|||
if(bodyImageTexture == null || bodyImageTexture.width != sensorData.colorImageWidth || bodyImageTexture.height != sensorData.colorImageHeight) |
|||
{ |
|||
bodyImageTexture = KinectInterop.CreateRenderTexture(bodyImageTexture, sensorData.colorImageWidth, sensorData.colorImageHeight); |
|||
} |
|||
|
|||
Array.Clear(depthBodyBufferData, 0, depthBodyBufferData.Length); |
|||
Array.Clear(equalBodyBufferData, 0, equalBodyBufferData.Length); |
|||
bodyHistTotalPoints = 0; |
|||
|
|||
// get configured min & max distances
|
|||
float minDistance = ((DepthSensorBase)sensorData.sensorInterface).minDepthDistance; |
|||
float maxDistance = ((DepthSensorBase)sensorData.sensorInterface).maxDepthDistance; |
|||
|
|||
int depthMinDistance = (int)(minDistance * 1000f); |
|||
int depthMaxDistance = (int)(maxDistance * 1000f); |
|||
|
|||
int frameLen = sensorData.colorCamDepthImage.Length; |
|||
for (int i = 0; i < frameLen; i++) |
|||
{ |
|||
int depth = sensorData.colorCamDepthImage[i]; |
|||
int limDepth = (depth >= depthMinDistance && depth <= depthMaxDistance) ? depth : 0; |
|||
|
|||
if (/**rawBodyIndexImage[i] != 255 &&*/ limDepth > 0) |
|||
{ |
|||
depthBodyBufferData[limDepth]++; |
|||
bodyHistTotalPoints++; |
|||
} |
|||
} |
|||
|
|||
if (bodyHistTotalPoints > 0) |
|||
{ |
|||
equalBodyBufferData[0] = depthBodyBufferData[0]; |
|||
for (int i = 1; i < depthBodyBufferData.Length; i++) |
|||
{ |
|||
equalBodyBufferData[i] = equalBodyBufferData[i - 1] + depthBodyBufferData[i]; |
|||
} |
|||
} |
|||
|
|||
int bodyIndexBufferLength = sensorData.colorCamBodyIndexImage.Length >> 2; |
|||
if (bodyIndexBuffer == null || bodyIndexBuffer.count != bodyIndexBufferLength) |
|||
{ |
|||
bodyIndexBuffer = KinectInterop.CreateComputeBuffer(bodyIndexBuffer, bodyIndexBufferLength, sizeof(uint)); |
|||
} |
|||
|
|||
KinectInterop.SetComputeBufferData(bodyIndexBuffer, sensorData.colorCamBodyIndexImage, bodyIndexBufferLength, sizeof(uint)); |
|||
|
|||
int depthBufferLength = sensorData.colorCamDepthImage.Length >> 1; |
|||
if(depthImageBuffer == null || depthImageBuffer.count != depthBufferLength) |
|||
{ |
|||
depthImageBuffer = KinectInterop.CreateComputeBuffer(depthImageBuffer, depthBufferLength, sizeof(uint)); |
|||
} |
|||
|
|||
KinectInterop.SetComputeBufferData(depthImageBuffer, sensorData.colorCamDepthImage, depthBufferLength, sizeof(uint)); |
|||
|
|||
if (bodyHistBuffer != null) |
|||
{ |
|||
KinectInterop.SetComputeBufferData(bodyHistBuffer, equalBodyBufferData, equalBodyBufferData.Length, sizeof(int)); |
|||
} |
|||
|
|||
float minDist = minDistance; // kinectManager.minUserDistance != 0f ? kinectManager.minUserDistance : minDistance;
|
|||
float maxDist = maxDistance; // kinectManager.maxUserDistance != 0f ? kinectManager.maxUserDistance : maxDistance;
|
|||
|
|||
bodyImageMaterial.SetInt("_TexResX", sensorData.colorImageWidth); |
|||
bodyImageMaterial.SetInt("_TexResY", sensorData.colorImageHeight); |
|||
bodyImageMaterial.SetInt("_MinDepth", (int)(minDist * 1000f)); |
|||
bodyImageMaterial.SetInt("_MaxDepth", (int)(maxDist * 1000f)); |
|||
|
|||
bodyImageMaterial.SetBuffer("_BodyIndexMap", bodyIndexBuffer); |
|||
bodyImageMaterial.SetBuffer("_DepthMap", depthImageBuffer); |
|||
bodyImageMaterial.SetBuffer("_HistMap", bodyHistBuffer); |
|||
bodyImageMaterial.SetInt("_TotalPoints", bodyHistTotalPoints); |
|||
|
|||
Color[] bodyIndexColors = kinectManager.GetBodyIndexColors(); |
|||
if(playerIndex >= 0) |
|||
{ |
|||
ulong userId = kinectManager.GetUserIdByIndex(playerIndex); |
|||
int bodyIndex = kinectManager.GetBodyIndexByUserId(userId); |
|||
|
|||
int numBodyIndices = bodyIndexColors.Length; |
|||
Color clrNone = new Color(0f, 0f, 0f, 0f); |
|||
|
|||
for (int i = 0; i < numBodyIndices; i++) |
|||
{ |
|||
if (i != bodyIndex) |
|||
bodyIndexColors[i] = clrNone; |
|||
} |
|||
} |
|||
|
|||
bodyImageMaterial.SetColorArray("_BodyIndexColors", bodyIndexColors); |
|||
|
|||
Graphics.Blit(null, bodyImageTexture, bodyImageMaterial); |
|||
} |
|||
} |
|||
|
|||
} |
|||
} |
|||
|
@ -0,0 +1,12 @@ |
|||
fileFormatVersion: 2 |
|||
guid: 5460d666a1cb85f418682ed53b4b3d26 |
|||
timeCreated: 1481733120 |
|||
licenseType: Store |
|||
MonoImporter: |
|||
serializedVersion: 2 |
|||
defaultReferences: [] |
|||
executionOrder: 0 |
|||
icon: {instanceID: 0} |
|||
userData: |
|||
assetBundleName: |
|||
assetBundleVariant: |
@ -0,0 +1,132 @@ |
|||
using UnityEngine; |
|||
using System.Collections; |
|||
using com.rfilkov.kinect; |
|||
|
|||
|
|||
namespace com.rfilkov.components |
|||
{ |
|||
/// <summary>
|
|||
/// Background color image is component that displays the color camera feed on RawImage texture, usually the scene background.
|
|||
/// </summary>
|
|||
public class BackgroundColorImage : MonoBehaviour |
|||
{ |
|||
[Tooltip("Depth sensor index - 0 is the 1st one, 1 - the 2nd one, etc.")] |
|||
public int sensorIndex = 0; |
|||
|
|||
[Tooltip("RawImage used to display the color camera feed.")] |
|||
public UnityEngine.UI.RawImage backgroundImage; |
|||
|
|||
[Tooltip("Camera used to display the background image. Set it, if you'd like to allow background image to resize, to match the color image's aspect ratio.")] |
|||
public Camera backgroundCamera; |
|||
|
|||
|
|||
// last camera rect width & height
|
|||
private float lastCamRectW = 0; |
|||
private float lastCamRectH = 0; |
|||
|
|||
// references
|
|||
private KinectManager kinectManager = null; |
|||
private KinectInterop.SensorData sensorData = null; |
|||
private Vector2 initialAnchorPos = Vector2.zero; |
|||
|
|||
|
|||
void Start() |
|||
{ |
|||
if (backgroundImage == null) |
|||
{ |
|||
backgroundImage = GetComponent<UnityEngine.UI.RawImage>(); |
|||
} |
|||
|
|||
kinectManager = KinectManager.Instance; |
|||
sensorData = kinectManager != null ? kinectManager.GetSensorData(sensorIndex) : null; |
|||
} |
|||
|
|||
|
|||
void Update() |
|||
{ |
|||
if (kinectManager && kinectManager.IsInitialized()) |
|||
{ |
|||
float cameraWidth = backgroundCamera ? backgroundCamera.pixelRect.width : 0f; |
|||
float cameraHeight = backgroundCamera ? backgroundCamera.pixelRect.height : 0f; |
|||
|
|||
Texture imageTex = kinectManager.GetColorImageTex(sensorIndex); |
|||
if (backgroundImage && imageTex != null && (backgroundImage.texture == null || |
|||
backgroundImage.texture.width != imageTex.width || backgroundImage.texture.height != imageTex.height || |
|||
lastCamRectW != cameraWidth || lastCamRectH != cameraHeight)) |
|||
{ |
|||
lastCamRectW = cameraWidth; |
|||
lastCamRectH = cameraHeight; |
|||
|
|||
backgroundImage.texture = imageTex; |
|||
backgroundImage.rectTransform.localScale = sensorData.colorImageScale; // kinectManager.GetColorImageScale(sensorIndex);
|
|||
backgroundImage.color = Color.white; |
|||
|
|||
//Debug.Log("aPos: " + backgroundImage.rectTransform.anchoredPosition + ", aMin: " + backgroundImage.rectTransform.anchorMin +
|
|||
// ", aMax:" + backgroundImage.rectTransform.anchorMax + ", pivot: " + backgroundImage.rectTransform.pivot +
|
|||
// ", size: " + backgroundImage.rectTransform.sizeDelta);
|
|||
|
|||
if (backgroundCamera != null) |
|||
{ |
|||
// adjust image's size and position to match the stream aspect ratio
|
|||
int colorImageWidth = sensorData.colorImageWidth; // kinectManager.GetColorImageWidth(sensorIndex);
|
|||
int colorImageHeight = sensorData.colorImageHeight; // kinectManager.GetColorImageHeight(sensorIndex);
|
|||
if (colorImageWidth == 0 || colorImageHeight == 0) |
|||
return; |
|||
|
|||
RectTransform rectImage = backgroundImage.rectTransform; |
|||
float rectWidth = (rectImage.anchorMin.x != rectImage.anchorMax.x) ? cameraWidth * (rectImage.anchorMax.x - rectImage.anchorMin.x) : rectImage.sizeDelta.x; |
|||
float rectHeight = (rectImage.anchorMin.y != rectImage.anchorMax.y) ? cameraHeight * (rectImage.anchorMax.y - rectImage.anchorMin.y) : rectImage.sizeDelta.y; |
|||
|
|||
if (colorImageWidth > colorImageHeight) |
|||
rectWidth = rectHeight * colorImageWidth / colorImageHeight; |
|||
else |
|||
rectHeight = rectWidth * colorImageHeight / colorImageWidth; |
|||
|
|||
Vector2 pivotOffset = (rectImage.pivot - new Vector2(0.5f, 0.5f)) * 2f; |
|||
Vector2 imageScale = sensorData.colorImageScale; // (Vector2)kinectManager.GetColorImageScale(sensorIndex);
|
|||
Vector2 anchorPos = rectImage.anchoredPosition + pivotOffset * imageScale * new Vector2(rectWidth, rectHeight); |
|||
|
|||
if (rectImage.anchorMin.x != rectImage.anchorMax.x) |
|||
{ |
|||
rectWidth = -(cameraWidth - rectWidth); |
|||
} |
|||
|
|||
if (rectImage.anchorMin.y != rectImage.anchorMax.y) |
|||
{ |
|||
rectHeight = -(cameraHeight - rectHeight); |
|||
} |
|||
|
|||
rectImage.sizeDelta = new Vector2(rectWidth, rectHeight); |
|||
rectImage.anchoredPosition = initialAnchorPos = anchorPos; |
|||
} |
|||
} |
|||
|
|||
//if(backgroundImage)
|
|||
//{
|
|||
// // update the anchor position, if needed
|
|||
// if(sensorData != null && sensorData.sensorInterface != null)
|
|||
// {
|
|||
// Vector2 updatedAnchorPos = initialAnchorPos + sensorData.sensorInterface.GetBackgroundImageAnchorPos(sensorData);
|
|||
// if(backgroundImage.rectTransform.anchoredPosition != updatedAnchorPos)
|
|||
// {
|
|||
// backgroundImage.rectTransform.anchoredPosition = updatedAnchorPos;
|
|||
// }
|
|||
// }
|
|||
//}
|
|||
} |
|||
else |
|||
{ |
|||
// reset the background texture, if needed
|
|||
if (backgroundImage && backgroundImage.texture != null) |
|||
{ |
|||
backgroundImage.texture = null; |
|||
} |
|||
} |
|||
|
|||
//RectTransform rectTransform = backgroundImage.rectTransform;
|
|||
//Debug.Log("pivot: " + rectTransform.pivot + ", anchorPos: " + rectTransform.anchoredPosition + ", \nanchorMin: " + rectTransform.anchorMin + ", anchorMax: " + rectTransform.anchorMax);
|
|||
} |
|||
|
|||
} |
|||
} |
|||
|
@ -0,0 +1,12 @@ |
|||
fileFormatVersion: 2 |
|||
guid: 4c68913127bedcd4bbd001b610521305 |
|||
timeCreated: 1481733120 |
|||
licenseType: Store |
|||
MonoImporter: |
|||
serializedVersion: 2 |
|||
defaultReferences: [] |
|||
executionOrder: 0 |
|||
icon: {instanceID: 0} |
|||
userData: |
|||
assetBundleName: |
|||
assetBundleVariant: |
@ -0,0 +1,146 @@ |
|||
using UnityEngine; |
|||
using System.Collections; |
|||
using com.rfilkov.kinect; |
|||
using System; |
|||
|
|||
namespace com.rfilkov.components |
|||
{ |
|||
/// <summary>
|
|||
/// BackgroundDepthCamColorImage is component that displays the depth camera aligned color image on RawImage texture, usually the scene background.
|
|||
/// </summary>
|
|||
public class BackgroundDepthCamColorImage : MonoBehaviour |
|||
{ |
|||
[Tooltip("Depth sensor index - 0 is the 1st one, 1 - the 2nd one, etc.")] |
|||
public int sensorIndex = 0; |
|||
|
|||
[Tooltip("RawImage used to display the color camera feed.")] |
|||
public UnityEngine.UI.RawImage backgroundImage; |
|||
|
|||
[Tooltip("Camera used to display the background image. Set it, if you'd like to allow background image to resize, to match the color image's aspect ratio.")] |
|||
public Camera backgroundCamera; |
|||
|
|||
|
|||
// last camera rect width & height
|
|||
private float lastCamRectW = 0; |
|||
private float lastCamRectH = 0; |
|||
|
|||
// reference to the kinectManager
|
|||
private KinectManager kinectManager = null; |
|||
private KinectInterop.SensorData sensorData = null; |
|||
private Vector2 initialAnchorPos = Vector2.zero; |
|||
|
|||
//// depth-camera aligned frames
|
|||
//private ulong lastDepthCamColorFrameTime = 0;
|
|||
|
|||
|
|||
void Start() |
|||
{ |
|||
if (backgroundImage == null) |
|||
{ |
|||
backgroundImage = GetComponent<UnityEngine.UI.RawImage>(); |
|||
} |
|||
|
|||
kinectManager = KinectManager.Instance; |
|||
sensorData = kinectManager != null ? kinectManager.GetSensorData(sensorIndex) : null; |
|||
|
|||
if(sensorData != null) |
|||
{ |
|||
// enable depth camera aligned color frames
|
|||
sensorData.sensorInterface.EnableDepthCameraColorFrame(sensorData, true); |
|||
} |
|||
} |
|||
|
|||
|
|||
void OnDestroy() |
|||
{ |
|||
if (sensorData != null) |
|||
{ |
|||
// disable depth camera aligned color frames
|
|||
sensorData.sensorInterface.EnableDepthCameraColorFrame(sensorData, false); |
|||
} |
|||
} |
|||
|
|||
|
|||
void Update() |
|||
{ |
|||
if (kinectManager && kinectManager.IsInitialized()) |
|||
{ |
|||
float cameraWidth = backgroundCamera ? backgroundCamera.pixelRect.width : 0f; |
|||
float cameraHeight = backgroundCamera ? backgroundCamera.pixelRect.height : 0f; |
|||
|
|||
if(sensorData.depthCamColorImageTexture == null) |
|||
{ |
|||
// enable depth camera aligned color frames
|
|||
sensorData = kinectManager.GetSensorData(sensorIndex); // sensor data may be re-created after sensor-int restart
|
|||
sensorData.sensorInterface.EnableDepthCameraColorFrame(sensorData, true); |
|||
} |
|||
|
|||
if (backgroundImage && sensorData.depthCamColorImageTexture != null && (backgroundImage.texture == null || |
|||
backgroundImage.texture.width != sensorData.depthCamColorImageTexture.width || backgroundImage.texture.height != sensorData.depthCamColorImageTexture.height || |
|||
lastCamRectW != cameraWidth || lastCamRectH != cameraHeight)) |
|||
{ |
|||
lastCamRectW = cameraWidth; |
|||
lastCamRectH = cameraHeight; |
|||
|
|||
backgroundImage.texture = sensorData.depthCamColorImageTexture; |
|||
backgroundImage.rectTransform.localScale = sensorData.depthImageScale; |
|||
backgroundImage.color = Color.white; |
|||
|
|||
if (backgroundCamera != null) |
|||
{ |
|||
// adjust image's size and position to match the stream aspect ratio
|
|||
int colorImageWidth = sensorData.depthImageWidth; |
|||
int colorImageHeight = sensorData.depthImageHeight; |
|||
if (colorImageWidth == 0 || colorImageHeight == 0) |
|||
return; |
|||
|
|||
RectTransform rectImage = backgroundImage.rectTransform; |
|||
float rectWidth = (rectImage.anchorMin.x != rectImage.anchorMax.x) ? cameraWidth * (rectImage.anchorMax.x - rectImage.anchorMin.x) : rectImage.sizeDelta.x; |
|||
float rectHeight = (rectImage.anchorMin.y != rectImage.anchorMax.y) ? cameraHeight * (rectImage.anchorMax.y - rectImage.anchorMin.y) : rectImage.sizeDelta.y; |
|||
|
|||
if (colorImageWidth > colorImageHeight) |
|||
rectWidth = rectHeight * colorImageWidth / colorImageHeight; |
|||
else |
|||
rectHeight = rectWidth * colorImageHeight / colorImageWidth; |
|||
|
|||
Vector2 pivotOffset = (rectImage.pivot - new Vector2(0.5f, 0.5f)) * 2f; |
|||
Vector2 imageScale = sensorData.depthImageScale; |
|||
Vector2 anchorPos = rectImage.anchoredPosition + pivotOffset * imageScale * new Vector2(rectWidth, rectHeight); |
|||
|
|||
if (rectImage.anchorMin.x != rectImage.anchorMax.x) |
|||
{ |
|||
rectWidth = -(cameraWidth - rectWidth); |
|||
} |
|||
|
|||
if (rectImage.anchorMin.y != rectImage.anchorMax.y) |
|||
{ |
|||
rectHeight = -(cameraHeight - rectHeight); |
|||
} |
|||
|
|||
rectImage.sizeDelta = new Vector2(rectWidth, rectHeight); |
|||
rectImage.anchoredPosition = initialAnchorPos = anchorPos; |
|||
} |
|||
} |
|||
} |
|||
else |
|||
{ |
|||
// reset the background texture, if needed
|
|||
if (backgroundImage && backgroundImage.texture != null) |
|||
{ |
|||
backgroundImage.texture = null; |
|||
|
|||
if (sensorData != null) |
|||
{ |
|||
// disable depth camera aligned color frames
|
|||
sensorData.sensorInterface.EnableDepthCameraColorFrame(sensorData, false); |
|||
} |
|||
} |
|||
} |
|||
|
|||
//RectTransform rectTransform = backgroundImage.rectTransform;
|
|||
//Debug.Log("pivot: " + rectTransform.pivot + ", anchorPos: " + rectTransform.anchoredPosition + ", \nanchorMin: " + rectTransform.anchorMin + ", anchorMax: " + rectTransform.anchorMax);
|
|||
} |
|||
|
|||
} |
|||
} |
|||
|
@ -0,0 +1,12 @@ |
|||
fileFormatVersion: 2 |
|||
guid: 0cf0a53458b5548acb078c1bc6d443c8 |
|||
timeCreated: 1481733120 |
|||
licenseType: Store |
|||
MonoImporter: |
|||
serializedVersion: 2 |
|||
defaultReferences: [] |
|||
executionOrder: 0 |
|||
icon: {instanceID: 0} |
|||
userData: |
|||
assetBundleName: |
|||
assetBundleVariant: |
@ -0,0 +1,132 @@ |
|||
using UnityEngine; |
|||
using System.Collections; |
|||
using com.rfilkov.kinect; |
|||
|
|||
|
|||
namespace com.rfilkov.components |
|||
{ |
|||
/// <summary>
|
|||
/// Background depth image is component that displays the depth camera image on RawImage texture, usually the scene background.
|
|||
/// </summary>
|
|||
public class BackgroundDepthImage : MonoBehaviour |
|||
{ |
|||
[Tooltip("Depth sensor index - 0 is the 1st one, 1 - the 2nd one, etc.")] |
|||
public int sensorIndex = 0; |
|||
|
|||
[Tooltip("RawImage used to display the depth image.")] |
|||
public UnityEngine.UI.RawImage backgroundImage; |
|||
|
|||
[Tooltip("Camera used to display the background image. Set it, if you'd like to allow background image to resize, to match the depth image's aspect ratio.")] |
|||
public Camera backgroundCamera; |
|||
|
|||
|
|||
// last camera rect width & height
|
|||
private float lastCamRectW = 0; |
|||
private float lastCamRectH = 0; |
|||
|
|||
// references
|
|||
private KinectManager kinectManager = null; |
|||
private KinectInterop.SensorData sensorData = null; |
|||
private Vector2 initialAnchorPos = Vector2.zero; |
|||
|
|||
|
|||
void Start() |
|||
{ |
|||
if (backgroundImage == null) |
|||
{ |
|||
backgroundImage = GetComponent<UnityEngine.UI.RawImage>(); |
|||
} |
|||
|
|||
kinectManager = KinectManager.Instance; |
|||
sensorData = kinectManager != null ? kinectManager.GetSensorData(sensorIndex) : null; |
|||
} |
|||
|
|||
|
|||
void Update() |
|||
{ |
|||
if (kinectManager && kinectManager.IsInitialized()) |
|||
{ |
|||
float cameraWidth = backgroundCamera ? backgroundCamera.pixelRect.width : 0f; |
|||
float cameraHeight = backgroundCamera ? backgroundCamera.pixelRect.height : 0f; |
|||
|
|||
Texture imageTex = kinectManager.GetDepthImageTex(sensorIndex); |
|||
if (backgroundImage && imageTex != null && (backgroundImage.texture == null || |
|||
backgroundImage.texture.width != imageTex.width || backgroundImage.texture.height != imageTex.height || |
|||
lastCamRectW != cameraWidth || lastCamRectH != cameraHeight)) |
|||
{ |
|||
lastCamRectW = cameraWidth; |
|||
lastCamRectH = cameraHeight; |
|||
|
|||
backgroundImage.texture = imageTex; |
|||
backgroundImage.rectTransform.localScale = sensorData.depthImageScale; // kinectManager.GetDepthImageScale(sensorIndex);
|
|||
backgroundImage.color = Color.white; |
|||
|
|||
//Debug.Log("aPos: " + backgroundImage.rectTransform.anchoredPosition + ", aMin: " + backgroundImage.rectTransform.anchorMin +
|
|||
// ", aMax:" + backgroundImage.rectTransform.anchorMax + ", pivot: " + backgroundImage.rectTransform.pivot +
|
|||
// ", size: " + backgroundImage.rectTransform.sizeDelta);
|
|||
|
|||
if (backgroundCamera != null) |
|||
{ |
|||
// adjust image's size and position to match the stream aspect ratio
|
|||
int depthImageWidth = sensorData.depthImageWidth; // kinectManager.GetDepthImageWidth(sensorIndex);
|
|||
int depthImageHeight = sensorData.depthImageHeight; // kinectManager.GetDepthImageHeight(sensorIndex);
|
|||
if (depthImageWidth == 0 || depthImageHeight == 0) |
|||
return; |
|||
|
|||
RectTransform rectImage = backgroundImage.rectTransform; |
|||
float rectWidth = (rectImage.anchorMin.x != rectImage.anchorMax.x) ? cameraWidth * (rectImage.anchorMax.x - rectImage.anchorMin.x) : rectImage.sizeDelta.x; |
|||
float rectHeight = (rectImage.anchorMin.y != rectImage.anchorMax.y) ? cameraHeight * (rectImage.anchorMax.y - rectImage.anchorMin.y) : rectImage.sizeDelta.y; |
|||
|
|||
if (depthImageWidth > depthImageHeight) |
|||
rectWidth = rectHeight * depthImageWidth / depthImageHeight; |
|||
else |
|||
rectHeight = rectWidth * depthImageHeight / depthImageWidth; |
|||
|
|||
Vector2 pivotOffset = (rectImage.pivot - new Vector2(0.5f, 0.5f)) * 2f; |
|||
Vector2 imageScale = sensorData.depthImageScale; // (Vector2)kinectManager.GetDepthImageScale(sensorIndex);
|
|||
Vector2 anchorPos = rectImage.anchoredPosition + pivotOffset * imageScale * new Vector2(rectWidth, rectHeight); |
|||
|
|||
if (rectImage.anchorMin.x != rectImage.anchorMax.x) |
|||
{ |
|||
rectWidth = -(cameraWidth - rectWidth); |
|||
} |
|||
|
|||
if (rectImage.anchorMin.y != rectImage.anchorMax.y) |
|||
{ |
|||
rectHeight = -(cameraHeight - rectHeight); |
|||
} |
|||
|
|||
rectImage.sizeDelta = new Vector2(rectWidth, rectHeight); |
|||
rectImage.anchoredPosition = initialAnchorPos = anchorPos; |
|||
} |
|||
} |
|||
|
|||
//if (backgroundImage)
|
|||
//{
|
|||
// // update the anchor position, if needed
|
|||
// if (sensorData != null && sensorData.sensorInterface != null)
|
|||
// {
|
|||
// Vector2 updatedAnchorPos = initialAnchorPos + sensorData.sensorInterface.GetBackgroundImageAnchorPos(sensorData);
|
|||
// if (backgroundImage.rectTransform.anchoredPosition != updatedAnchorPos)
|
|||
// {
|
|||
// backgroundImage.rectTransform.anchoredPosition = updatedAnchorPos;
|
|||
// }
|
|||
// }
|
|||
//}
|
|||
} |
|||
else |
|||
{ |
|||
// reset the background texture, if needed
|
|||
if (backgroundImage && backgroundImage.texture != null) |
|||
{ |
|||
backgroundImage.texture = null; |
|||
} |
|||
} |
|||
|
|||
//RectTransform rectTransform = backgroundImage.rectTransform;
|
|||
//Debug.Log("pivot: " + rectTransform.pivot + ", anchorPos: " + rectTransform.anchoredPosition + ", \nanchorMin: " + rectTransform.anchorMin + ", anchorMax: " + rectTransform.anchorMax);
|
|||
} |
|||
|
|||
} |
|||
} |
|||
|
@ -0,0 +1,12 @@ |
|||
fileFormatVersion: 2 |
|||
guid: 65268b386677b334bbfd2f6ec3e726fb |
|||
timeCreated: 1483707326 |
|||
licenseType: Store |
|||
MonoImporter: |
|||
serializedVersion: 2 |
|||
defaultReferences: [] |
|||
executionOrder: 0 |
|||
icon: {instanceID: 0} |
|||
userData: |
|||
assetBundleName: |
|||
assetBundleVariant: |
@ -0,0 +1,128 @@ |
|||
using UnityEngine; |
|||
using System.Collections; |
|||
using com.rfilkov.kinect; |
|||
|
|||
|
|||
namespace com.rfilkov.components |
|||
{ |
|||
/// <summary>
|
|||
/// Background infrared image is component that displays the infrared camera image on RawImage texture, usually the scene background.
|
|||
/// </summary>
|
|||
public class BackgroundInfraredImage : MonoBehaviour |
|||
{ |
|||
[Tooltip("Depth sensor index - 0 is the 1st one, 1 - the 2nd one, etc.")] |
|||
public int sensorIndex = 0; |
|||
|
|||
[Tooltip("RawImage used to display the depth image.")] |
|||
public UnityEngine.UI.RawImage backgroundImage; |
|||
|
|||
[Tooltip("Camera used to display the background image. Set it, if you'd like to allow background image to resize, to match the depth image's aspect ratio.")] |
|||
public Camera backgroundCamera; |
|||
|
|||
|
|||
// last camera rect width & height
|
|||
private float lastCamRectW = 0; |
|||
private float lastCamRectH = 0; |
|||
|
|||
// references
|
|||
private KinectManager kinectManager = null; |
|||
private KinectInterop.SensorData sensorData = null; |
|||
private Vector2 initialAnchorPos = Vector2.zero; |
|||
|
|||
|
|||
void Start() |
|||
{ |
|||
if (backgroundImage == null) |
|||
{ |
|||
backgroundImage = GetComponent<UnityEngine.UI.RawImage>(); |
|||
} |
|||
|
|||
kinectManager = KinectManager.Instance; |
|||
sensorData = kinectManager != null ? kinectManager.GetSensorData(sensorIndex) : null; |
|||
} |
|||
|
|||
|
|||
void Update() |
|||
{ |
|||
if (kinectManager && kinectManager.IsInitialized()) |
|||
{ |
|||
float cameraWidth = backgroundCamera ? backgroundCamera.pixelRect.width : 0f; |
|||
float cameraHeight = backgroundCamera ? backgroundCamera.pixelRect.height : 0f; |
|||
|
|||
Texture imageTex = kinectManager.GetInfraredImageTex(sensorIndex); |
|||
if (backgroundImage && imageTex != null && (backgroundImage.texture == null || |
|||
backgroundImage.texture.width != imageTex.width || backgroundImage.texture.height != imageTex.height || |
|||
lastCamRectW != cameraWidth || lastCamRectH != cameraHeight)) |
|||
{ |
|||
lastCamRectW = cameraWidth; |
|||
lastCamRectH = cameraHeight; |
|||
|
|||
backgroundImage.texture = imageTex; |
|||
backgroundImage.rectTransform.localScale = sensorData.infraredImageScale; // kinectManager.GetInfraredImageScale(sensorIndex);
|
|||
backgroundImage.color = Color.white; |
|||
|
|||
if (backgroundCamera != null) |
|||
{ |
|||
// adjust image's size and position to match the stream aspect ratio
|
|||
int depthImageWidth = sensorData.depthImageWidth; // kinectManager.GetDepthImageWidth(sensorIndex);
|
|||
int depthImageHeight = sensorData.depthImageHeight; // kinectManager.GetDepthImageHeight(sensorIndex);
|
|||
if (depthImageWidth == 0 || depthImageHeight == 0) |
|||
return; |
|||
|
|||
RectTransform rectImage = backgroundImage.rectTransform; |
|||
float rectWidth = (rectImage.anchorMin.x != rectImage.anchorMax.x) ? cameraWidth * (rectImage.anchorMax.x - rectImage.anchorMin.x) : rectImage.sizeDelta.x; |
|||
float rectHeight = (rectImage.anchorMin.y != rectImage.anchorMax.y) ? cameraHeight * (rectImage.anchorMax.y - rectImage.anchorMin.y) : rectImage.sizeDelta.y; |
|||
|
|||
if (depthImageWidth > depthImageHeight) |
|||
rectWidth = rectHeight * depthImageWidth / depthImageHeight; |
|||
else |
|||
rectHeight = rectWidth * depthImageHeight / depthImageWidth; |
|||
|
|||
Vector2 pivotOffset = (rectImage.pivot - new Vector2(0.5f, 0.5f)) * 2f; |
|||
Vector2 imageScale = sensorData.infraredImageScale; // (Vector2)kinectManager.GetDepthImageScale(sensorIndex);
|
|||
Vector2 anchorPos = rectImage.anchoredPosition + pivotOffset * imageScale * new Vector2(rectWidth, rectHeight); |
|||
|
|||
if (rectImage.anchorMin.x != rectImage.anchorMax.x) |
|||
{ |
|||
rectWidth = -(cameraWidth - rectWidth); |
|||
} |
|||
|
|||
if (rectImage.anchorMin.y != rectImage.anchorMax.y) |
|||
{ |
|||
rectHeight = -(cameraHeight - rectHeight); |
|||
} |
|||
|
|||
rectImage.sizeDelta = new Vector2(rectWidth, rectHeight); |
|||
rectImage.anchoredPosition = initialAnchorPos = anchorPos; |
|||
} |
|||
} |
|||
|
|||
//if (backgroundImage)
|
|||
//{
|
|||
// // update the anchor position, if needed
|
|||
// if (sensorData != null && sensorData.sensorInterface != null)
|
|||
// {
|
|||
// Vector2 updatedAnchorPos = initialAnchorPos + sensorData.sensorInterface.GetBackgroundImageAnchorPos(sensorData);
|
|||
// if (backgroundImage.rectTransform.anchoredPosition != updatedAnchorPos)
|
|||
// {
|
|||
// backgroundImage.rectTransform.anchoredPosition = updatedAnchorPos;
|
|||
// }
|
|||
// }
|
|||
//}
|
|||
} |
|||
else |
|||
{ |
|||
// reset the background texture, if needed
|
|||
if (backgroundImage && backgroundImage.texture != null) |
|||
{ |
|||
backgroundImage.texture = null; |
|||
} |
|||
} |
|||
|
|||
//RectTransform rectTransform = backgroundImage.rectTransform;
|
|||
//Debug.Log("pivot: " + rectTransform.pivot + ", anchorPos: " + rectTransform.anchoredPosition + ", \nanchorMin: " + rectTransform.anchorMin + ", anchorMax: " + rectTransform.anchorMax);
|
|||
} |
|||
|
|||
} |
|||
} |
|||
|
@ -0,0 +1,12 @@ |
|||
fileFormatVersion: 2 |
|||
guid: fd43548cc180bc7498e01c3b7606c18e |
|||
timeCreated: 1483707326 |
|||
licenseType: Store |
|||
MonoImporter: |
|||
serializedVersion: 2 |
|||
defaultReferences: [] |
|||
executionOrder: 0 |
|||
icon: {instanceID: 0} |
|||
userData: |
|||
assetBundleName: |
|||
assetBundleVariant: |
@ -0,0 +1,171 @@ |
|||
using com.rfilkov.components; |
|||
using System.Collections; |
|||
using System.Collections.Generic; |
|||
using UnityEngine; |
|||
|
|||
|
|||
namespace com.rfilkov.kinect |
|||
{ |
|||
/// <summary>
|
|||
/// BackgroundRemovalByBodyBounds filters user silhouettes, according to the bounds determined by the positions of the body joints.
|
|||
/// </summary>
|
|||
public class BackgroundRemovalByBodyBounds : MonoBehaviour |
|||
{ |
|||
[Tooltip("Offset from the lowest body joint to the floor.")] |
|||
[Range(-0.1f, 0.4f)] |
|||
public float offsetToFloor = 0.05f; |
|||
|
|||
[Tooltip("Offset from the highest body joint to the top of the body.")] |
|||
[Range(-0.1f, 0.4f)] |
|||
public float headOffset = 0.2f; |
|||
|
|||
[Tooltip("Offset from the leftmost body joint to the left end of the body.")] |
|||
[Range(-0.1f, 0.4f)] |
|||
public float leftOffset = 0.2f; |
|||
|
|||
[Tooltip("Offset from the rightmost body joint to the right end of the body.")] |
|||
[Range(-0.1f, 0.4f)] |
|||
public float rightOffset = 0.2f; |
|||
|
|||
[Tooltip("Offset from the frontmost body joint to the front end of the body.")] |
|||
[Range(-0.1f, 0.4f)] |
|||
public float frontOffset = 0.2f; |
|||
|
|||
[Tooltip("Offset from the backmost body joint to the back end of the body.")] |
|||
[Range(-0.1f, 0.4f)] |
|||
public float backOffset = 0.2f; |
|||
|
|||
|
|||
// foreground filter shader
|
|||
private ComputeShader foregroundFilterShader = null; |
|||
private int foregroundFilterKernel = -1; |
|||
|
|||
//private Vector4[] foregroundFilterPos = null;
|
|||
private Vector4[] bodyPosMin = null; |
|||
private Vector4[] bodyPosMaxX = null; |
|||
private Vector4[] bodyPosMaxY = null; |
|||
private Vector4[] bodyPosMaxZ = null; |
|||
private Vector4[] bodyPosDot = null; |
|||
|
|||
|
|||
// initializes background removal with shaders
|
|||
public bool InitBackgroundRemoval(KinectInterop.SensorData sensorData, int maxBodyCount) |
|||
{ |
|||
foregroundFilterShader = Resources.Load("ForegroundFiltBodyShader") as ComputeShader; |
|||
foregroundFilterKernel = foregroundFilterShader != null ? foregroundFilterShader.FindKernel("FgFiltBody") : -1; |
|||
|
|||
//foregroundFilterPos = new Vector4[KinectInterop.Constants.MaxBodyCount];
|
|||
bodyPosMin = new Vector4[maxBodyCount]; |
|||
bodyPosMaxX = new Vector4[maxBodyCount]; |
|||
bodyPosMaxY = new Vector4[maxBodyCount]; |
|||
bodyPosMaxZ = new Vector4[maxBodyCount]; |
|||
bodyPosDot = new Vector4[maxBodyCount]; |
|||
|
|||
return true; |
|||
} |
|||
|
|||
|
|||
// releases background removal shader resources
|
|||
public void FinishBackgroundRemoval(KinectInterop.SensorData sensorData) |
|||
{ |
|||
if (foregroundFilterShader != null) |
|||
{ |
|||
foregroundFilterShader = null; |
|||
} |
|||
|
|||
//foregroundFilterPos = null;
|
|||
bodyPosMin = null; |
|||
bodyPosMaxX = null; |
|||
bodyPosMaxY = null; |
|||
bodyPosMaxZ = null; |
|||
bodyPosDot = null; |
|||
} |
|||
|
|||
|
|||
/// <summary>
|
|||
/// Applies foreground filter by body bounds.
|
|||
/// </summary>
|
|||
public void ApplyForegroundFilterByBody(Texture vertexTexture, RenderTexture alphaTexture, int playerIndex, int sensorIndex, int maxBodyCount, |
|||
Matrix4x4 matKinectWorld, KinectManager kinectManager, Camera foregroundCamera) |
|||
{ |
|||
Matrix4x4 matWorldKinect = matKinectWorld.inverse; |
|||
if (kinectManager != null && kinectManager.userManager != null) |
|||
{ |
|||
List<ulong> alUserIds = null; |
|||
|
|||
if (playerIndex < 0) |
|||
{ |
|||
alUserIds = kinectManager.userManager.alUserIds; |
|||
} |
|||
else |
|||
{ |
|||
alUserIds = new List<ulong>(); |
|||
|
|||
ulong userId = kinectManager.GetUserIdByIndex(playerIndex); |
|||
if (userId != 0) |
|||
alUserIds.Add(userId); |
|||
} |
|||
|
|||
int uCount = Mathf.Min(alUserIds.Count, maxBodyCount); |
|||
foregroundFilterShader.SetInt("_NumBodies", uCount); |
|||
|
|||
//if (uCount > 0)
|
|||
//{
|
|||
// Debug.Log("playerIndex: " + playerIndex + ", uCount: " + uCount + ", userId: " + (uCount > 0 ? alUserIds[0] : 0));
|
|||
//}
|
|||
|
|||
// get the background rectangle (use the portrait background, if available)
|
|||
Rect backgroundRect = foregroundCamera.pixelRect; |
|||
PortraitBackground portraitBack = PortraitBackground.Instance; |
|||
|
|||
if (portraitBack && portraitBack.enabled) |
|||
{ |
|||
backgroundRect = portraitBack.GetBackgroundRect(); |
|||
} |
|||
|
|||
int jCount = kinectManager.GetJointCount(); |
|||
for (int i = 0; i < uCount; i++) |
|||
{ |
|||
ulong userId = alUserIds[i]; |
|||
|
|||
bool bSuccess = kinectManager.GetUserBoundingBox(userId, /**foregroundCamera*/ null, sensorIndex, backgroundRect, |
|||
out Vector3 pMin, out Vector3 pMax); |
|||
//Debug.Log("pMin: " + pMin + ", pMax: " + pMax);
|
|||
|
|||
if (bSuccess) |
|||
{ |
|||
Vector3 posMin = new Vector3(pMin.x - leftOffset, pMin.y - offsetToFloor, pMin.z - frontOffset); |
|||
Vector3 posMaxX = new Vector3(pMax.x + rightOffset, posMin.y, posMin.z); |
|||
Vector3 posMaxY = new Vector3(posMin.x, pMax.y + headOffset, posMin.z); |
|||
Vector3 posMaxZ = new Vector3(posMin.x, posMin.y, pMax.z + backOffset); |
|||
|
|||
//foregroundFilterDistXY[i] = new Vector4(xMin - 0.1f, xMax + 0.1f, yMin - offsetToFloor, yMax + 0.1f);
|
|||
//foregroundFilterDistZ[i] = new Vector4(zMin - 0.2f, zMax + 0.0f, 0f, 0f);
|
|||
bodyPosMin[i] = matWorldKinect.MultiplyPoint3x4(posMin); |
|||
bodyPosMaxX[i] = matWorldKinect.MultiplyPoint3x4(posMaxX) - (Vector3)bodyPosMin[i]; |
|||
bodyPosMaxY[i] = matWorldKinect.MultiplyPoint3x4(posMaxY) - (Vector3)bodyPosMin[i]; |
|||
bodyPosMaxZ[i] = matWorldKinect.MultiplyPoint3x4(posMaxZ) - (Vector3)bodyPosMin[i]; |
|||
|
|||
bodyPosDot[i] = new Vector3(Vector3.Dot(bodyPosMaxX[i], bodyPosMaxX[i]), Vector3.Dot(bodyPosMaxY[i], bodyPosMaxY[i]), Vector3.Dot(bodyPosMaxZ[i], bodyPosMaxZ[i])); |
|||
//Debug.Log("pMin: " + (Vector3)posMin + ", pMaxX: " + (Vector3)bodyPosMaxX[i] + ", pMaxY: " + (Vector3)bodyPosMaxY[i] + ", pMaxZ: " + (Vector3)bodyPosMaxZ[i] + ", pDot: " + (Vector3)bodyPosDot[i]);
|
|||
} |
|||
|
|||
//string sMessage2 = string.Format("Xmin: {0:F1}; Xmax: {1:F1}", bodyPosMin[i].x, bodyPosMaxX[i].x);
|
|||
//Debug.Log(sMessage2);
|
|||
} |
|||
} |
|||
|
|||
//foregroundFilterShader.SetVectorArray("BodyPos", foregroundFilterPos);
|
|||
foregroundFilterShader.SetVectorArray("_BodyPosMin", bodyPosMin); |
|||
foregroundFilterShader.SetVectorArray("_BodyPosMaxX", bodyPosMaxX); |
|||
foregroundFilterShader.SetVectorArray("_BodyPosMaxY", bodyPosMaxY); |
|||
foregroundFilterShader.SetVectorArray("_BodyPosMaxZ", bodyPosMaxZ); |
|||
foregroundFilterShader.SetVectorArray("_BodyPosDot", bodyPosDot); |
|||
|
|||
foregroundFilterShader.SetTexture(foregroundFilterKernel, "_VertexTex", vertexTexture); |
|||
foregroundFilterShader.SetTexture(foregroundFilterKernel, "_AlphaTex", alphaTexture); |
|||
foregroundFilterShader.Dispatch(foregroundFilterKernel, vertexTexture.width / 8, vertexTexture.height / 8, 1); |
|||
} |
|||
|
|||
} |
|||
} |
@ -0,0 +1,11 @@ |
|||
fileFormatVersion: 2 |
|||
guid: 314091ebf33d9fd47a58c6cfd6213f1d |
|||
MonoImporter: |
|||
externalObjects: {} |
|||
serializedVersion: 2 |
|||
defaultReferences: [] |
|||
executionOrder: 0 |
|||
icon: {instanceID: 0} |
|||
userData: |
|||
assetBundleName: |
|||
assetBundleVariant: |
@ -0,0 +1,115 @@ |
|||
using System.Collections; |
|||
using System.Collections.Generic; |
|||
using UnityEngine; |
|||
|
|||
|
|||
namespace com.rfilkov.kinect |
|||
{ |
|||
/// <summary>
|
|||
/// BackgroundRemovalByBodyIndex filters user silhouettes, according to the body index frames coming from the body tracking SDK.
|
|||
/// </summary>
|
|||
public class BackgroundRemovalByBodyIndex : MonoBehaviour |
|||
{ |
|||
// whether the color-bi-buffer is created or not
|
|||
private bool bColorBiBufferCreated = false; |
|||
|
|||
// foreground filter shader
|
|||
private ComputeShader foregroundFilterShader = null; |
|||
private int foregroundFilterKernel = -1; |
|||
|
|||
// current body indices
|
|||
private int[] userBodyIndex = null; |
|||
|
|||
|
|||
// initializes background removal with shaders
|
|||
public bool InitBackgroundRemoval(KinectInterop.SensorData sensorData) |
|||
{ |
|||
if (sensorData != null && sensorData.colorImageWidth > 0 && sensorData.colorImageHeight > 0) |
|||
{ |
|||
if(sensorData.colorBodyIndexBuffer == null) |
|||
{ |
|||
int bufferLength = sensorData.colorImageWidth * sensorData.colorImageHeight / 4; |
|||
sensorData.colorBodyIndexBuffer = new ComputeBuffer(bufferLength, sizeof(uint)); |
|||
bColorBiBufferCreated = true; |
|||
} |
|||
|
|||
foregroundFilterShader = Resources.Load("ForegroundFiltBodyIndexShader") as ComputeShader; |
|||
foregroundFilterKernel = foregroundFilterShader != null ? foregroundFilterShader.FindKernel("FgFiltBodyIndex") : -1; |
|||
|
|||
return true; |
|||
} |
|||
|
|||
return false; |
|||
} |
|||
|
|||
|
|||
// releases background removal shader resources
|
|||
public void FinishBackgroundRemoval(KinectInterop.SensorData sensorData) |
|||
{ |
|||
if (bColorBiBufferCreated && sensorData.colorBodyIndexBuffer != null) |
|||
{ |
|||
sensorData.colorBodyIndexBuffer.Dispose(); |
|||
sensorData.colorBodyIndexBuffer = null; |
|||
} |
|||
} |
|||
|
|||
|
|||
/// <summary>
|
|||
/// Applies foreground filter by body index.
|
|||
/// </summary>
|
|||
public void ApplyForegroundFilterByBodyIndex(RenderTexture alphaTexture, KinectInterop.SensorData sensorData, |
|||
KinectManager kinectManager, int playerIndex, int maxBodyCount) |
|||
{ |
|||
if (kinectManager != null && kinectManager.userManager != null && sensorData.colorBodyIndexBuffer != null) |
|||
{ |
|||
List<ulong> alUserIds = null; |
|||
|
|||
if (playerIndex < 0) |
|||
{ |
|||
alUserIds = kinectManager.userManager.alUserIds; // new List<ulong>(); //
|
|||
} |
|||
else |
|||
{ |
|||
alUserIds = new List<ulong>(); |
|||
|
|||
ulong userId = kinectManager.GetUserIdByIndex(playerIndex); |
|||
if (userId != 0) |
|||
alUserIds.Add(userId); |
|||
} |
|||
|
|||
maxBodyCount = 5; // limit to 5 body indices in the shader, because SetInts() doesn't work correctly
|
|||
if (userBodyIndex == null) |
|||
{ |
|||
userBodyIndex = new int[maxBodyCount]; |
|||
} |
|||
|
|||
int uCount = Mathf.Min(alUserIds.Count, maxBodyCount); |
|||
for (int i = 0; i < uCount; i++) |
|||
{ |
|||
ulong userId = alUserIds[i]; |
|||
userBodyIndex[i] = kinectManager.GetBodyIndexByUserId(userId); |
|||
} |
|||
|
|||
foregroundFilterShader.SetInt("_TexResX", alphaTexture.width); |
|||
foregroundFilterShader.SetInt("_TexResY", alphaTexture.height); |
|||
|
|||
//foregroundFilterShader.SetInt("_NumBodies", uCount);
|
|||
//foregroundFilterShader.SetInts("_BodyIndices", userBodyIndex); // ComputeShader.SetInts() doesn't work correctly
|
|||
|
|||
foregroundFilterShader.SetInt("_BodyIndexAll", playerIndex < 0 ? 1 : 0); |
|||
|
|||
foregroundFilterShader.SetInt("_BodyIndex0", uCount > 0 ? userBodyIndex[0] : -1); |
|||
foregroundFilterShader.SetInt("_BodyIndex1", uCount > 1 ? userBodyIndex[1] : -1); |
|||
foregroundFilterShader.SetInt("_BodyIndex2", uCount > 2 ? userBodyIndex[2] : -1); |
|||
foregroundFilterShader.SetInt("_BodyIndex3", uCount > 3 ? userBodyIndex[3] : -1); |
|||
foregroundFilterShader.SetInt("_BodyIndex4", uCount > 4 ? userBodyIndex[4] : -1); |
|||
|
|||
foregroundFilterShader.SetBuffer(foregroundFilterKernel, "_BodyIndexMap", sensorData.colorBodyIndexBuffer); |
|||
foregroundFilterShader.SetTexture(foregroundFilterKernel, "_AlphaTex", alphaTexture); |
|||
|
|||
foregroundFilterShader.Dispatch(foregroundFilterKernel, alphaTexture.width / 8, alphaTexture.height / 8, 1); |
|||
} |
|||
} |
|||
|
|||
} |
|||
} |
@ -0,0 +1,11 @@ |
|||
fileFormatVersion: 2 |
|||
guid: e4e45b4f33dd18f419753482798bf289 |
|||
MonoImporter: |
|||
externalObjects: {} |
|||
serializedVersion: 2 |
|||
defaultReferences: [] |
|||
executionOrder: 0 |
|||
icon: {instanceID: 0} |
|||
userData: |
|||
assetBundleName: |
|||
assetBundleVariant: |
@ -0,0 +1,93 @@ |
|||
using System.Collections; |
|||
using System.Collections.Generic; |
|||
using UnityEngine; |
|||
|
|||
|
|||
namespace com.rfilkov.kinect |
|||
{ |
|||
/// <summary>
|
|||
/// BackgroundRemovalByDist filters part of the real environment, according to the given spatial limits.
|
|||
/// </summary>
|
|||
public class BackgroundRemovalByDist : MonoBehaviour |
|||
{ |
|||
[Tooltip("Whether or not to apply the sensor pose to background removal estimation.")] |
|||
public bool applySensorPose = true; |
|||
|
|||
[Space] |
|||
|
|||
[Tooltip("Horizontal limit - minimum, in meters.")] |
|||
[Range(-5f, 5f)] |
|||
public float xMin = -1.5f; |
|||
|
|||
[Tooltip("Horizontal limit - maximum, in meters.")] |
|||
[Range(-5f, 5f)] |
|||
public float xMax = 1.5f; |
|||
|
|||
[Tooltip("Vertical limit - minimum, in meters.")] |
|||
[Range(-5f, 5f)] |
|||
public float yMin = 0f; |
|||
|
|||
[Tooltip("Vertical limit - maximum, in meters.")] |
|||
[Range(-5f, 5f)] |
|||
public float yMax = 3f; |
|||
|
|||
[Tooltip("Distance limit - minimum, in meters.")] |
|||
[Range(0.5f, 10f)] |
|||
public float zMin = 0.5f; |
|||
|
|||
[Tooltip("Distance limit - maximum at the left end, in meters.")] |
|||
[Range(0.5f, 10f)] |
|||
public float zMaxLeft = 3f; |
|||
|
|||
[Tooltip("Distance limit - maximum at the right end, in meters.")] |
|||
[Range(0.5f, 10f)] |
|||
public float zMaxRight = 3f; |
|||
|
|||
// foreground filter shader
|
|||
private ComputeShader foregroundFilterShader = null; |
|||
private int foregroundFilterKernel = -1; |
|||
|
|||
|
|||
void Start() |
|||
{ |
|||
foregroundFilterShader = Resources.Load("ForegroundFiltDistShader") as ComputeShader; |
|||
foregroundFilterKernel = foregroundFilterShader != null ? foregroundFilterShader.FindKernel("FgFiltDist") : -1; |
|||
} |
|||
|
|||
|
|||
/// <summary>
|
|||
/// Applies vertex filter by distance.
|
|||
/// </summary>
|
|||
/// <param name="vertexTexture">The vertex texture</param>
|
|||
/// <param name="alphaTexture">The alpha texture</param>
|
|||
public void ApplyVertexFilter(RenderTexture vertexTexture, RenderTexture alphaTexture, Matrix4x4 sensorWorldMatrix) |
|||
{ |
|||
foregroundFilterShader.SetMatrix("_Transform", applySensorPose ? sensorWorldMatrix : Matrix4x4.identity); |
|||
//Matrix4x4 matWorldKinect = sensorWorldMatrix.inverse;
|
|||
|
|||
Vector3 posMin = new Vector3(xMin, yMin, zMin); // matWorldKinect.MultiplyPoint3x4(new Vector3(xMin, yMin, zMin));
|
|||
Vector3 posMaxX = new Vector3(xMax, yMin, zMin) - posMin; // matWorldKinect.MultiplyPoint3x4(new Vector3(xMax, yMin, zMin)) - posMin;
|
|||
Vector3 posMaxY = new Vector3(xMin, yMax, zMin) - posMin; // matWorldKinect.MultiplyPoint3x4(new Vector3(xMin, yMax, zMin)) - posMin;
|
|||
|
|||
Vector3 posMaxZLeft = new Vector3(xMin, yMin, zMaxLeft) - posMin; // matWorldKinect.MultiplyPoint3x4(new Vector3(xMin, yMin, zMaxRight)) - posMin;
|
|||
Vector3 posMaxZRight = new Vector3(xMin, yMin, zMaxRight) - posMin; // matWorldKinect.MultiplyPoint3x4(new Vector3(xMin, yMin, zMaxLeft)) - posMin;
|
|||
|
|||
Vector3 posMaxZ = (posMaxZLeft + posMaxZRight) / 2; |
|||
|
|||
Vector3 posDot = new Vector3(Vector3.Dot(posMaxX, posMaxX), Vector3.Dot(posMaxY, posMaxY), Vector3.Dot(posMaxZ, posMaxZ)); |
|||
|
|||
foregroundFilterShader.SetVector("_PosMin", posMin); |
|||
foregroundFilterShader.SetVector("_PosMaxX", posMaxX); |
|||
foregroundFilterShader.SetVector("_PosMaxY", posMaxY); |
|||
//foregroundFilterShader.SetVector("_PosMaxZ", posMaxZ);
|
|||
foregroundFilterShader.SetVector("_PosMaxZLeft", posMaxZLeft); |
|||
foregroundFilterShader.SetVector("_PosMaxZRight", posMaxZRight); |
|||
foregroundFilterShader.SetVector("_PosDot", posDot); |
|||
|
|||
foregroundFilterShader.SetTexture(foregroundFilterKernel, "_VertexTex", vertexTexture); |
|||
foregroundFilterShader.SetTexture(foregroundFilterKernel, "_AlphaTex", alphaTexture); |
|||
foregroundFilterShader.Dispatch(foregroundFilterKernel, vertexTexture.width / 8, vertexTexture.height / 8, 1); |
|||
} |
|||
|
|||
} |
|||
} |
@ -0,0 +1,11 @@ |
|||
fileFormatVersion: 2 |
|||
guid: 3fa7b39e46e63a24c9a12a224406a1f4 |
|||
MonoImporter: |
|||
externalObjects: {} |
|||
serializedVersion: 2 |
|||
defaultReferences: [] |
|||
executionOrder: 0 |
|||
icon: {instanceID: 0} |
|||
userData: |
|||
assetBundleName: |
|||
assetBundleVariant: |
@ -0,0 +1,89 @@ |
|||
using System.Collections; |
|||
using System.Collections.Generic; |
|||
using UnityEngine; |
|||
|
|||
|
|||
namespace com.rfilkov.kinect |
|||
{ |
|||
/// <summary>
|
|||
/// BackgroundRemovalByGreenScreen filters color camera data, according to its similarity or difference to the color of the green-screen.
|
|||
/// </summary>
|
|||
public class BackgroundRemovalByGreenScreen : MonoBehaviour |
|||
{ |
|||
[Tooltip("The color of the 'green screen'.")] |
|||
public Color greenScreenColor = Color.green; |
|||
|
|||
[Tooltip("Allowed similarity between the 'green screen' color and the texture color.")] |
|||
public float greenScreenColorRange = 0.5f; |
|||
|
|||
[Tooltip("Alpha values below this value will be set to fully transparent.")] |
|||
[Range(0f, 1f)] |
|||
public float setAsTransparentBelow = 0f; |
|||
|
|||
[Tooltip("Alpha values above this value will be set to fully opaque.")] |
|||
[Range(0f, 1f)] |
|||
public float setAsOpaqueAbove = 1f; |
|||
|
|||
[Tooltip("Green screen rectangle in normalized coordinates (between 0 and 1).")] |
|||
public Rect greenScreenRect = new Rect(0, 0, 1, 1); |
|||
|
|||
|
|||
// foreground filter shader
|
|||
private ComputeShader foregroundFilterShader = null; |
|||
private int foregroundFilterKernel = -1; |
|||
|
|||
|
|||
// initializes background removal shaders
|
|||
public bool InitBackgroundRemoval(KinectInterop.SensorData sensorData) |
|||
{ |
|||
if (sensorData != null && sensorData.colorImageWidth > 0 && sensorData.colorImageHeight > 0) |
|||
{ |
|||
foregroundFilterShader = Resources.Load("ForegroundFiltGreenScreenShader") as ComputeShader; |
|||
foregroundFilterKernel = foregroundFilterShader != null ? foregroundFilterShader.FindKernel("FgFiltFreenScreen") : -1; |
|||
|
|||
return true; |
|||
} |
|||
|
|||
return false; |
|||
} |
|||
|
|||
|
|||
//// releases background removal shader resources
|
|||
//public void FinishBackgroundRemoval(KinectInterop.SensorData sensorData)
|
|||
//{
|
|||
//}
|
|||
|
|||
|
|||
/// <summary>
|
|||
/// Applies foreground filter by green screen.
|
|||
/// </summary>
|
|||
public void ApplyForegroundFilterByGreenScreen(RenderTexture alphaTexture, KinectInterop.SensorData sensorData, |
|||
KinectManager kinectManager, RenderTexture colorTexture) |
|||
{ |
|||
if (foregroundFilterShader != null && colorTexture != null && alphaTexture != null) |
|||
{ |
|||
foregroundFilterShader.SetVector("_GreenScreenColor", greenScreenColor); |
|||
foregroundFilterShader.SetFloat("_GreenScreenColorRange", greenScreenColorRange); |
|||
|
|||
foregroundFilterShader.SetTexture(foregroundFilterKernel, "_ColorTex", colorTexture); |
|||
foregroundFilterShader.SetTexture(foregroundFilterKernel, "_AlphaTex", alphaTexture); |
|||
|
|||
foregroundFilterShader.SetFloat("_SetTranspBelow", setAsTransparentBelow); |
|||
foregroundFilterShader.SetFloat("_SetOpaqueAbove", setAsOpaqueAbove); |
|||
|
|||
float xMin = sensorData.colorImageScale.x > 0 ? greenScreenRect.xMin * colorTexture.width : (1f - greenScreenRect.xMax) * colorTexture.width; |
|||
float yMin = sensorData.colorImageScale.y > 0 ? greenScreenRect.yMin * colorTexture.height : (1f - greenScreenRect.yMax) * colorTexture.height; |
|||
float xMax = sensorData.colorImageScale.x > 0 ? greenScreenRect.xMax * colorTexture.width : (1f - greenScreenRect.xMin) * colorTexture.width; |
|||
float yMax = sensorData.colorImageScale.y > 0 ? greenScreenRect.yMax * colorTexture.height : (1f - greenScreenRect.yMin) * colorTexture.height; |
|||
|
|||
Vector4 vGreenScreenRect = new Vector4(xMin, yMin, xMax, yMax); |
|||
foregroundFilterShader.SetVector("_GreenScreenRect", vGreenScreenRect); |
|||
//Debug.Log(vGreenScreenRect);
|
|||
|
|||
foregroundFilterShader.Dispatch(foregroundFilterKernel, alphaTexture.width / 8, alphaTexture.height / 8, 1); |
|||
//Debug.Log("ApplyForegroundFilterByGreenScreen()");
|
|||
} |
|||
} |
|||
|
|||
} |
|||
} |
@ -0,0 +1,11 @@ |
|||
fileFormatVersion: 2 |
|||
guid: db05d85cc2655464b934a2b1d7f5658c |
|||
MonoImporter: |
|||
externalObjects: {} |
|||
serializedVersion: 2 |
|||
defaultReferences: [] |
|||
executionOrder: 0 |
|||
icon: {instanceID: 0} |
|||
userData: |
|||
assetBundleName: |
|||
assetBundleVariant: |
@ -0,0 +1,623 @@ |
|||
using UnityEngine; |
|||
using System; |
|||
using System.Collections; |
|||
using System.Collections.Generic; |
|||
using System.IO; |
|||
using com.rfilkov.components; |
|||
|
|||
|
|||
namespace com.rfilkov.kinect |
|||
{ |
|||
/// <summary>
|
|||
/// Background removal manager is the component that filters and renders user body silhouettes.
|
|||
/// </summary>
|
|||
public class BackgroundRemovalManager : MonoBehaviour |
|||
{ |
|||
[Tooltip("Depth sensor index - 0 is the 1st one, 1 - the 2nd one, etc.")] |
|||
public int sensorIndex = 0; |
|||
|
|||
[Tooltip("Index of the player, tracked by this component. -1 means all players, 0 - the 1st player only, 1 - the 2nd player only, etc.")] |
|||
public int playerIndex = -1; |
|||
|
|||
[Tooltip("RawImage used for displaying the foreground image.")] |
|||
public UnityEngine.UI.RawImage foregroundImage; |
|||
|
|||
[Tooltip("Camera used for alignment of bodies to color camera image.")] |
|||
public Camera foregroundCamera; |
|||
|
|||
[Tooltip("Resolution of the generated foreground textures.")] |
|||
private DepthSensorBase.PointCloudResolution foregroundImageResolution = DepthSensorBase.PointCloudResolution.ColorCameraResolution; |
|||
|
|||
[Tooltip("Whether only the alpha texture is needed.")] |
|||
public bool computeAlphaMaskOnly = false; |
|||
|
|||
[Tooltip("Whether the alpha texture will be inverted or not..")] |
|||
public bool invertAlphaMask = false; |
|||
|
|||
[Tooltip("(Advanced) Whether to apply the median filter before the other filters.")] |
|||
public bool applyMedianFilter = false; |
|||
|
|||
[Tooltip("(Advanced) Number of iterations used by the alpha texture's erode filter 0.")] |
|||
[Range(0, 9)] |
|||
public int erodeIterations0 = 0; // 1
|
|||
|
|||
[Tooltip("(Advanced) Number of iterations used by the alpha texture's dilate filter 1.")] |
|||
[Range(0, 9)] |
|||
public int dilateIterations = 0; // 3;
|
|||
|
|||
[Tooltip("(Advanced) Whether to apply the gradient filter.")] |
|||
private bool applyGradientFilter = true; |
|||
|
|||
[Tooltip("(Advanced) Number of iterations used by the alpha texture's erode filter 2.")] |
|||
[Range(0, 9)] |
|||
public int erodeIterations = 0; // 4;
|
|||
|
|||
[Tooltip("(Advanced) Whether to apply the blur filter after at the end.")] |
|||
public bool applyBlurFilter = true; |
|||
|
|||
[Tooltip("(Advanced) Color applied to the body contour after the filters.")] |
|||
public Color bodyContourColor = Color.black; |
|||
|
|||
[Tooltip("UI-Text to display the BR-Manager debug messages.")] |
|||
public UnityEngine.UI.Text debugText; |
|||
|
|||
|
|||
// max number of bodies to track
|
|||
private const int MAX_BODY_COUNT = 10; |
|||
|
|||
// primary sensor data structure
|
|||
private KinectInterop.SensorData sensorData = null; |
|||
private KinectManager kinectManager = null; |
|||
|
|||
// sensor interface
|
|||
private DepthSensorBase sensorInt = null; |
|||
|
|||
// render texture resolution
|
|||
private Vector2Int textureRes; |
|||
|
|||
// Bool to keep track whether Kinect and BR library have been initialized
|
|||
private bool bBackgroundRemovalInited = false; |
|||
private int lastColorW = 0, lastColorH = 0; |
|||
|
|||
// The single instance of BackgroundRemovalManager
|
|||
//private static BackgroundRemovalManager instance;
|
|||
|
|||
// last point cloud frame time
|
|||
private ulong lastDepth2SpaceFrameTime = 0; |
|||
private ulong lastColorBodyIndexBufferTime = 0; |
|||
|
|||
// render textures used by the shaders
|
|||
private RenderTexture colorTexture = null; |
|||
private RenderTexture vertexTexture = null; |
|||
private RenderTexture alphaTexture = null; |
|||
private RenderTexture foregroundTexture = null; |
|||
|
|||
// Materials used to apply the shaders
|
|||
private Material medianFilterMat = null; |
|||
private Material erodeFilterMat = null; |
|||
private Material dilateFilterMat = null; |
|||
private Material gradientFilterMat = null; |
|||
private Material blurFilterMat = null; |
|||
private Material invertAlphaMat = null; |
|||
private Material foregroundMat = null; |
|||
|
|||
// reference to filter-by-distance component
|
|||
private BackgroundRemovalByBodyBounds filterByBody = null; |
|||
private BackgroundRemovalByDist filterByDist = null; |
|||
private BackgroundRemovalByBodyIndex filterByBI = null; |
|||
private BackgroundRemovalByGreenScreen filterByGS = null; |
|||
|
|||
// whether the textures are created or not
|
|||
private bool bColorTextureCreated = false; |
|||
private bool bVertexTextureCreated = false; |
|||
|
|||
|
|||
///// <summary>
|
|||
///// Gets the single BackgroundRemovalManager instance.
|
|||
///// </summary>
|
|||
///// <value>The BackgroundRemovalManager instance.</value>
|
|||
//public static BackgroundRemovalManager Instance
|
|||
//{
|
|||
// get
|
|||
// {
|
|||
// return instance;
|
|||
// }
|
|||
//}
|
|||
|
|||
/// <summary>
|
|||
/// Determines whether the BackgroundRemovalManager was successfully initialized.
|
|||
/// </summary>
|
|||
/// <returns><c>true</c> if the BackgroundRemovalManager was successfully initialized; otherwise, <c>false</c>.</returns>
|
|||
public bool IsBackgroundRemovalInited() |
|||
{ |
|||
return bBackgroundRemovalInited; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Gets the foreground image texture.
|
|||
/// </summary>
|
|||
/// <returns>The foreground image texture.</returns>
|
|||
public Texture GetForegroundTex() |
|||
{ |
|||
return foregroundTexture; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Gets the alpha texture.
|
|||
/// </summary>
|
|||
/// <returns>The alpha texture.</returns>
|
|||
public Texture GetAlphaTex() |
|||
{ |
|||
return alphaTexture; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Gets the color texture.
|
|||
/// </summary>
|
|||
/// <returns>The color texture.</returns>
|
|||
public Texture GetColorTex() |
|||
{ |
|||
return colorTexture; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Gets the last background removal frame time.
|
|||
/// </summary>
|
|||
/// <returns>The last background removal time.</returns>
|
|||
public ulong GetLastBackgroundRemovalTime() |
|||
{ |
|||
return lastDepth2SpaceFrameTime; |
|||
} |
|||
|
|||
|
|||
//----------------------------------- end of public functions --------------------------------------//
|
|||
|
|||
|
|||
//void Awake()
|
|||
//{
|
|||
// instance = this;
|
|||
//}
|
|||
|
|||
public void Start() |
|||
{ |
|||
try |
|||
{ |
|||
// get sensor data
|
|||
kinectManager = KinectManager.Instance; |
|||
if (kinectManager && kinectManager.IsInitialized()) |
|||
{ |
|||
sensorData = kinectManager.GetSensorData(sensorIndex); |
|||
} |
|||
|
|||
if (sensorData == null || sensorData.sensorInterface == null) |
|||
{ |
|||
throw new Exception("Background removal cannot be started, because KinectManager is missing or not initialized."); |
|||
} |
|||
|
|||
if(foregroundImage == null) |
|||
{ |
|||
// look for a foreground image
|
|||
foregroundImage = GetComponent<UnityEngine.UI.RawImage>(); |
|||
} |
|||
|
|||
if (!foregroundCamera) |
|||
{ |
|||
// by default - the main camera
|
|||
foregroundCamera = Camera.main; |
|||
} |
|||
|
|||
// try to get reference to other filter components
|
|||
filterByBody = GetComponent<BackgroundRemovalByBodyBounds>(); |
|||
if(filterByBody == null) |
|||
filterByDist = GetComponent<BackgroundRemovalByDist>(); |
|||
if (filterByBody == null && filterByDist == null) |
|||
filterByBI = GetComponent<BackgroundRemovalByBodyIndex>(); |
|||
if (filterByBody == null && filterByDist == null && filterByBI == null) |
|||
filterByGS = GetComponent<BackgroundRemovalByGreenScreen>(); |
|||
|
|||
if (filterByBody == null && filterByDist == null && filterByBI == null && filterByGS == null) |
|||
filterByBI = gameObject.AddComponent<BackgroundRemovalByBodyIndex>(); // fallback
|
|||
|
|||
//// Initialize the background removal
|
|||
//bBackgroundRemovalInited = InitBackgroundRemoval(sensorData);
|
|||
|
|||
//if (bBackgroundRemovalInited)
|
|||
//{
|
|||
// if (debugText != null)
|
|||
// debugText.text = string.Empty;
|
|||
//}
|
|||
//else
|
|||
//{
|
|||
// throw new Exception("Background removal could not be initialized.");
|
|||
//}
|
|||
|
|||
//bBackgroundRemovalInited = bSuccess;
|
|||
} |
|||
catch (DllNotFoundException ex) |
|||
{ |
|||
Debug.LogError(ex.ToString()); |
|||
if (debugText != null) |
|||
debugText.text = "Please check the SDK installations."; |
|||
} |
|||
catch (Exception ex) |
|||
{ |
|||
Debug.LogException(ex); |
|||
if (debugText != null) |
|||
debugText.text = ex.Message; |
|||
} |
|||
} |
|||
|
|||
public void OnDestroy() |
|||
{ |
|||
if (bBackgroundRemovalInited) |
|||
{ |
|||
// finish background removal
|
|||
FinishBackgroundRemoval(sensorData); |
|||
} |
|||
|
|||
bBackgroundRemovalInited = false; |
|||
//instance = null;
|
|||
} |
|||
|
|||
void Update() |
|||
{ |
|||
if (sensorData == null) |
|||
return; |
|||
|
|||
if(!bBackgroundRemovalInited || lastColorW != sensorData.colorImageWidth || lastColorH != sensorData.colorImageHeight) |
|||
{ |
|||
lastColorW = sensorData.colorImageWidth; |
|||
lastColorH = sensorData.colorImageHeight; |
|||
|
|||
if (bBackgroundRemovalInited) |
|||
{ |
|||
FinishBackgroundRemoval(sensorData); |
|||
if(foregroundImage != null) |
|||
foregroundImage.texture = null; |
|||
//bBackgroundRemovalInited = false;
|
|||
|
|||
// dispose the used shaders & buffers, as well
|
|||
if(sensorInt != null) |
|||
sensorInt.UpdateTransformedFrameTextures(sensorData, kinectManager); |
|||
} |
|||
|
|||
bBackgroundRemovalInited = InitBackgroundRemoval(sensorData); |
|||
|
|||
if (bBackgroundRemovalInited) |
|||
{ |
|||
if (debugText != null) |
|||
debugText.text = string.Empty; |
|||
} |
|||
} |
|||
|
|||
if (bBackgroundRemovalInited) |
|||
{ |
|||
// update the background removal
|
|||
UpdateBackgroundRemoval(sensorData); |
|||
|
|||
// check for valid foreground image texture
|
|||
if(foregroundImage != null && foregroundImage.texture == null) |
|||
{ |
|||
foregroundImage.texture = foregroundTexture; |
|||
foregroundImage.rectTransform.localScale = kinectManager.GetColorImageScale(sensorIndex); |
|||
foregroundImage.color = Color.white; |
|||
} |
|||
} |
|||
} |
|||
|
|||
|
|||
// initializes background removal with shaders
|
|||
private bool InitBackgroundRemoval(KinectInterop.SensorData sensorData) |
|||
{ |
|||
if (sensorData != null && sensorData.sensorInterface != null && KinectInterop.IsDirectX11Available()) |
|||
{ |
|||
if(filterByBody != null) |
|||
{ |
|||
if (!filterByBody.InitBackgroundRemoval(sensorData, MAX_BODY_COUNT)) |
|||
{ |
|||
Debug.LogError("Could not init the background removal by body bounds!"); |
|||
return false; |
|||
} |
|||
} |
|||
else if(filterByBI != null) |
|||
{ |
|||
if(!filterByBI.InitBackgroundRemoval(sensorData)) |
|||
{ |
|||
Debug.LogError("Could not init the background removal by body index!"); |
|||
return false; |
|||
} |
|||
} |
|||
else if (filterByGS != null) |
|||
{ |
|||
if (!filterByGS.InitBackgroundRemoval(sensorData)) |
|||
{ |
|||
Debug.LogError("Could not init the background removal by green screen!"); |
|||
return false; |
|||
} |
|||
} |
|||
|
|||
sensorInt = (DepthSensorBase)sensorData.sensorInterface; |
|||
|
|||
// set the texture resolution
|
|||
if (sensorInt.pointCloudColorTexture == null && sensorInt.pointCloudVertexTexture == null) |
|||
{ |
|||
sensorInt.pointCloudResolution = foregroundImageResolution; |
|||
} |
|||
|
|||
textureRes = sensorInt.GetPointCloudTexResolution(sensorData); |
|||
|
|||
if(sensorInt.pointCloudColorTexture == null) |
|||
{ |
|||
colorTexture = KinectInterop.CreateRenderTexture(colorTexture, textureRes.x, textureRes.y, RenderTextureFormat.ARGB32); |
|||
sensorInt.pointCloudColorTexture = colorTexture; |
|||
bColorTextureCreated = true; |
|||
} |
|||
else |
|||
{ |
|||
colorTexture = sensorInt.pointCloudColorTexture; |
|||
bColorTextureCreated = false; |
|||
} |
|||
|
|||
if (filterByBody != null || filterByDist != null) |
|||
{ |
|||
if(sensorInt.pointCloudVertexTexture == null) |
|||
{ |
|||
vertexTexture = KinectInterop.CreateRenderTexture(vertexTexture, textureRes.x, textureRes.y, RenderTextureFormat.ARGBHalf); |
|||
sensorInt.pointCloudVertexTexture = vertexTexture; |
|||
bVertexTextureCreated = true; |
|||
} |
|||
else |
|||
{ |
|||
vertexTexture = sensorInt.pointCloudVertexTexture; |
|||
bVertexTextureCreated = false; |
|||
} |
|||
} |
|||
|
|||
alphaTexture = KinectInterop.CreateRenderTexture(alphaTexture, textureRes.x, textureRes.y, RenderTextureFormat.ARGB32); |
|||
foregroundTexture = KinectInterop.CreateRenderTexture(foregroundTexture, textureRes.x, textureRes.y, RenderTextureFormat.ARGB32); |
|||
|
|||
Shader erodeShader = Shader.Find("Kinect/ErodeShader"); |
|||
erodeFilterMat = new Material(erodeShader); |
|||
erodeFilterMat.SetFloat("_TexResX", (float)textureRes.x); |
|||
erodeFilterMat.SetFloat("_TexResY", (float)textureRes.y); |
|||
//sensorData.erodeBodyMaterial.SetTexture("_MainTex", sensorData.bodyIndexTexture);
|
|||
|
|||
Shader dilateShader = Shader.Find("Kinect/DilateShader"); |
|||
dilateFilterMat = new Material(dilateShader); |
|||
dilateFilterMat.SetFloat("_TexResX", (float)textureRes.x); |
|||
dilateFilterMat.SetFloat("_TexResY", (float)textureRes.y); |
|||
//sensorData.dilateBodyMaterial.SetTexture("_MainTex", sensorData.bodyIndexTexture);
|
|||
|
|||
Shader gradientShader = Shader.Find("Kinect/GradientShader"); |
|||
gradientFilterMat = new Material(gradientShader); |
|||
|
|||
Shader medianShader = Shader.Find("Kinect/MedianShader"); |
|||
medianFilterMat = new Material(medianShader); |
|||
//sensorData.medianBodyMaterial.SetFloat("_Amount", 1.0f);
|
|||
|
|||
Shader blurShader = Shader.Find("Kinect/BlurShader"); |
|||
blurFilterMat = new Material(blurShader); |
|||
|
|||
Shader invertShader = Shader.Find("Kinect/InvertShader"); |
|||
invertAlphaMat = new Material(invertShader); |
|||
|
|||
Shader foregroundShader = Shader.Find("Kinect/ForegroundShader"); |
|||
foregroundMat = new Material(foregroundShader); |
|||
|
|||
return true; |
|||
} |
|||
|
|||
return false; |
|||
} |
|||
|
|||
|
|||
// releases background removal shader resources
|
|||
private void FinishBackgroundRemoval(KinectInterop.SensorData sensorData) |
|||
{ |
|||
if(filterByBody != null) |
|||
{ |
|||
filterByBody.FinishBackgroundRemoval(sensorData); |
|||
} |
|||
else if(filterByBI != null) |
|||
{ |
|||
filterByBI.FinishBackgroundRemoval(sensorData); |
|||
} |
|||
|
|||
if (sensorInt) |
|||
{ |
|||
sensorInt.pointCloudColorTexture = null; |
|||
sensorInt.pointCloudVertexTexture = null; |
|||
} |
|||
|
|||
if (bColorTextureCreated && colorTexture) |
|||
{ |
|||
colorTexture.Release(); |
|||
colorTexture = null; |
|||
} |
|||
|
|||
if (bVertexTextureCreated && vertexTexture) |
|||
{ |
|||
vertexTexture.Release(); |
|||
vertexTexture = null; |
|||
} |
|||
|
|||
if (alphaTexture) |
|||
{ |
|||
alphaTexture.Release(); |
|||
alphaTexture = null; |
|||
} |
|||
|
|||
if(foregroundTexture) |
|||
{ |
|||
foregroundTexture.Release(); |
|||
foregroundTexture = null; |
|||
} |
|||
|
|||
erodeFilterMat = null; |
|||
dilateFilterMat = null; |
|||
medianFilterMat = null; |
|||
blurFilterMat = null; |
|||
invertAlphaMat = null; |
|||
foregroundMat = null; |
|||
} |
|||
|
|||
|
|||
// computes current background removal texture
|
|||
private bool UpdateBackgroundRemoval(KinectInterop.SensorData sensorData) |
|||
{ |
|||
if (bBackgroundRemovalInited && (lastDepth2SpaceFrameTime != sensorData.lastDepth2SpaceFrameTime || |
|||
lastColorBodyIndexBufferTime != sensorData.lastColorBodyIndexBufferTime || filterByGS != null)) |
|||
{ |
|||
lastDepth2SpaceFrameTime = sensorData.lastDepth2SpaceFrameTime; |
|||
lastColorBodyIndexBufferTime = sensorData.lastColorBodyIndexBufferTime; |
|||
//Debug.Log("BR Depth2SpaceFrameTime: " + lastDepth2SpaceFrameTime + " ColorBodyIndexBufferTime: " + lastColorBodyIndexBufferTime);
|
|||
|
|||
RenderTexture[] tempTextures = new RenderTexture[2]; |
|||
tempTextures[0] = RenderTexture.GetTemporary(textureRes.x, textureRes.y, 0); |
|||
tempTextures[1] = RenderTexture.GetTemporary(textureRes.x, textureRes.y, 0); |
|||
|
|||
RenderTexture[] tempGradTextures = null; |
|||
if (applyGradientFilter) |
|||
{ |
|||
tempGradTextures = new RenderTexture[2]; |
|||
tempGradTextures[0] = RenderTexture.GetTemporary(textureRes.x, textureRes.y, 0); |
|||
tempGradTextures[1] = RenderTexture.GetTemporary(textureRes.x, textureRes.y, 0); |
|||
} |
|||
|
|||
// filter
|
|||
if(filterByBody != null && sensorInt != null) |
|||
{ |
|||
filterByBody.ApplyForegroundFilterByBody(vertexTexture, alphaTexture, playerIndex, sensorIndex, MAX_BODY_COUNT, |
|||
sensorInt.GetSensorToWorldMatrix(), kinectManager, foregroundCamera); |
|||
} |
|||
else if(filterByDist != null && sensorInt != null) |
|||
{ |
|||
// filter by distance
|
|||
filterByDist.ApplyVertexFilter(vertexTexture, alphaTexture, sensorInt.GetSensorToWorldMatrix()); |
|||
} |
|||
else if(filterByBI != null) |
|||
{ |
|||
// filter by body index
|
|||
filterByBI.ApplyForegroundFilterByBodyIndex(alphaTexture, sensorData, kinectManager, playerIndex, MAX_BODY_COUNT); |
|||
} |
|||
else if (filterByGS != null) |
|||
{ |
|||
// filter by green screen
|
|||
filterByGS.ApplyForegroundFilterByGreenScreen(alphaTexture, sensorData, kinectManager, colorTexture); |
|||
} |
|||
|
|||
//if(filterByBI == null)
|
|||
//{
|
|||
// Graphics.Blit(vertexTexture, alphaTexture);
|
|||
//}
|
|||
|
|||
// median
|
|||
if (applyMedianFilter) |
|||
{ |
|||
ApplySimpleFilter(alphaTexture, alphaTexture, medianFilterMat, tempTextures); |
|||
} |
|||
//else
|
|||
//{
|
|||
// Graphics.Blit(vertexTexture, alphaTexture);
|
|||
//}
|
|||
|
|||
// erode0
|
|||
ApplyIterableFilter(alphaTexture, alphaTexture, erodeFilterMat, erodeIterations0, tempTextures); |
|||
if(applyGradientFilter) |
|||
{ |
|||
Graphics.CopyTexture(alphaTexture, tempGradTextures[0]); |
|||
} |
|||
|
|||
// dilate
|
|||
ApplyIterableFilter(alphaTexture, alphaTexture, dilateFilterMat, dilateIterations, tempTextures); |
|||
if (applyGradientFilter) |
|||
{ |
|||
//Graphics.Blit(alphaTexture, tempGradTextures[1]);
|
|||
gradientFilterMat.SetTexture("_ErodeTex", tempGradTextures[0]); |
|||
ApplySimpleFilter(alphaTexture, tempGradTextures[1], gradientFilterMat, tempTextures); |
|||
} |
|||
|
|||
// erode
|
|||
ApplyIterableFilter(alphaTexture, alphaTexture, erodeFilterMat, erodeIterations, tempTextures); |
|||
if (tempGradTextures != null) |
|||
{ |
|||
Graphics.Blit(alphaTexture, tempGradTextures[0]); |
|||
} |
|||
|
|||
// blur
|
|||
if(applyBlurFilter) |
|||
{ |
|||
ApplySimpleFilter(alphaTexture, alphaTexture, blurFilterMat, tempTextures); |
|||
} |
|||
|
|||
if(invertAlphaMask) |
|||
{ |
|||
ApplySimpleFilter(alphaTexture, alphaTexture, invertAlphaMat, tempTextures); |
|||
} |
|||
|
|||
if(!computeAlphaMaskOnly) |
|||
{ |
|||
foregroundMat.SetTexture("_ColorTex", colorTexture); |
|||
foregroundMat.SetTexture("_GradientTex", tempGradTextures[1]); |
|||
|
|||
Color gradientColor = (erodeIterations0 != 0 || dilateIterations != 0 || erodeIterations != 0) ? bodyContourColor : Color.clear; |
|||
foregroundMat.SetColor("_GradientColor", gradientColor); |
|||
|
|||
ApplySimpleFilter(alphaTexture, foregroundTexture, foregroundMat, tempTextures); |
|||
} |
|||
else |
|||
{ |
|||
Graphics.CopyTexture(alphaTexture, foregroundTexture); |
|||
} |
|||
|
|||
// cleanup
|
|||
if (tempGradTextures != null) |
|||
{ |
|||
RenderTexture.ReleaseTemporary(tempGradTextures[0]); |
|||
RenderTexture.ReleaseTemporary(tempGradTextures[1]); |
|||
} |
|||
|
|||
RenderTexture.ReleaseTemporary(tempTextures[0]); |
|||
RenderTexture.ReleaseTemporary(tempTextures[1]); |
|||
|
|||
//sensorData.usedColorBodyIndexBufferTime = sensorData.lastColorBodyIndexBufferTime;
|
|||
} |
|||
|
|||
return true; |
|||
} |
|||
|
|||
// applies iterable filter to the source texture
|
|||
private void ApplyIterableFilter(RenderTexture source, RenderTexture destination, Material filterMat, int numIterations, RenderTexture[] tempTextures) |
|||
{ |
|||
if (!source || !destination || !filterMat || numIterations == 0) |
|||
return; |
|||
|
|||
Graphics.Blit(source, tempTextures[0]); |
|||
|
|||
for (int i = 0; i < numIterations; i++) |
|||
{ |
|||
Graphics.Blit(tempTextures[i % 2], tempTextures[(i + 1) % 2], filterMat); |
|||
} |
|||
|
|||
if ((numIterations % 2) == 0) |
|||
{ |
|||
Graphics.Blit(tempTextures[0], destination); |
|||
} |
|||
else |
|||
{ |
|||
Graphics.Blit(tempTextures[1], destination); |
|||
} |
|||
} |
|||
|
|||
// applies simple filter to the source texture
|
|||
private void ApplySimpleFilter(RenderTexture source, RenderTexture destination, Material filterMat, RenderTexture[] tempTextures) |
|||
{ |
|||
if (!source || !destination || !filterMat) |
|||
return; |
|||
|
|||
Graphics.Blit(source, tempTextures[0], filterMat); |
|||
Graphics.Blit(tempTextures[0], destination); |
|||
} |
|||
|
|||
} |
|||
} |
@ -0,0 +1,12 @@ |
|||
fileFormatVersion: 2 |
|||
guid: a559e12c3f373eb4c8d0e1096fec8a67 |
|||
timeCreated: 1426178739 |
|||
licenseType: Store |
|||
MonoImporter: |
|||
serializedVersion: 2 |
|||
defaultReferences: [] |
|||
executionOrder: 0 |
|||
icon: {instanceID: 0} |
|||
userData: |
|||
assetBundleName: |
|||
assetBundleVariant: |
@ -0,0 +1,107 @@ |
|||
using UnityEngine; |
|||
using System.Collections; |
|||
using com.rfilkov.kinect; |
|||
|
|||
|
|||
namespace com.rfilkov.components |
|||
{ |
|||
/// <summary>
|
|||
/// Background static image is component that displays the static image on RawImage texture, usually the scene background.
|
|||
/// </summary>
|
|||
public class BackgroundStaticImage : MonoBehaviour |
|||
{ |
|||
[Tooltip("Image dimensions in pixels.")] |
|||
public Vector2Int imageSize = Vector2Int.zero; |
|||
|
|||
[Tooltip("Image scale in X and Y directions.")] |
|||
public Vector2 imageScale = Vector2.one; |
|||
|
|||
[Tooltip("RawImage used to display the color camera feed.")] |
|||
public UnityEngine.UI.RawImage backgroundImage; |
|||
|
|||
[Tooltip("Camera used to display the background image. Set it, if you'd like to allow background image to resize, to match the color image's aspect ratio.")] |
|||
public Camera backgroundCamera; |
|||
|
|||
|
|||
// last camera rect width & height
|
|||
private float lastCamRectW = 0; |
|||
private float lastCamRectH = 0; |
|||
|
|||
private Vector2 initialAnchorPos = Vector2.zero; |
|||
|
|||
|
|||
void Start() |
|||
{ |
|||
if (backgroundImage == null) |
|||
{ |
|||
backgroundImage = GetComponent<UnityEngine.UI.RawImage>(); |
|||
} |
|||
|
|||
if(imageSize == Vector2.zero && backgroundImage != null && backgroundImage.texture != null) |
|||
{ |
|||
imageSize = new Vector2Int(backgroundImage.texture.width, backgroundImage.texture.height); |
|||
} |
|||
} |
|||
|
|||
|
|||
void Update() |
|||
{ |
|||
float cameraWidth = backgroundCamera ? backgroundCamera.pixelRect.width : 0f; |
|||
float cameraHeight = backgroundCamera ? backgroundCamera.pixelRect.height : 0f; |
|||
|
|||
if (backgroundImage && (lastCamRectW != cameraWidth || lastCamRectH != cameraHeight)) |
|||
{ |
|||
lastCamRectW = cameraWidth; |
|||
lastCamRectH = cameraHeight; |
|||
|
|||
backgroundImage.rectTransform.localScale = new Vector3(imageScale.x, imageScale.y, 1f); |
|||
backgroundImage.color = Color.white; |
|||
|
|||
//Debug.Log("aPos: " + backgroundImage.rectTransform.anchoredPosition + ", aMin: " + backgroundImage.rectTransform.anchorMin +
|
|||
// ", aMax:" + backgroundImage.rectTransform.anchorMax + ", pivot: " + backgroundImage.rectTransform.pivot +
|
|||
// ", size: " + backgroundImage.rectTransform.sizeDelta);
|
|||
|
|||
if (backgroundCamera != null) |
|||
{ |
|||
// adjust image's size and position to match the stream aspect ratio
|
|||
int imageWidth = imageSize.x; |
|||
int imageHeight = imageSize.y; |
|||
if (imageWidth == 0 || imageHeight == 0) |
|||
return; |
|||
|
|||
RectTransform rectImage = backgroundImage.rectTransform; |
|||
float rectWidth = (rectImage.anchorMin.x != rectImage.anchorMax.x) ? cameraWidth * (rectImage.anchorMax.x - rectImage.anchorMin.x) : rectImage.sizeDelta.x; |
|||
float rectHeight = (rectImage.anchorMin.y != rectImage.anchorMax.y) ? cameraHeight * (rectImage.anchorMax.y - rectImage.anchorMin.y) : rectImage.sizeDelta.y; |
|||
|
|||
if (imageWidth > imageHeight) |
|||
rectWidth = rectHeight * imageWidth / imageHeight; |
|||
else |
|||
rectHeight = rectWidth * imageHeight / imageWidth; |
|||
|
|||
Vector2 pivotOffset = (rectImage.pivot - new Vector2(0.5f, 0.5f)) * 2f; |
|||
//Vector2 imageScale = this.imageScale;
|
|||
Vector2 anchorPos = rectImage.anchoredPosition + pivotOffset * imageScale * new Vector2(rectWidth, rectHeight); |
|||
|
|||
if (rectImage.anchorMin.x != rectImage.anchorMax.x) |
|||
{ |
|||
rectWidth = -(cameraWidth - rectWidth); |
|||
} |
|||
|
|||
if (rectImage.anchorMin.y != rectImage.anchorMax.y) |
|||
{ |
|||
rectHeight = -(cameraHeight - rectHeight); |
|||
} |
|||
|
|||
rectImage.sizeDelta = new Vector2(rectWidth, rectHeight); |
|||
rectImage.anchoredPosition = initialAnchorPos = anchorPos; |
|||
//Debug.Log("imgSize: " + imageSize + ", camW: " + cameraWidth + ", camH: " + cameraHeight + ", sizeDelta: " + rectImage.sizeDelta + ", anchoredPosition: " + rectImage.anchoredPosition);
|
|||
} |
|||
} |
|||
|
|||
//RectTransform rectTransform = backgroundImage.rectTransform;
|
|||
//Debug.Log("pivot: " + rectTransform.pivot + ", anchorPos: " + rectTransform.anchoredPosition + ", \nanchorMin: " + rectTransform.anchorMin + ", anchorMax: " + rectTransform.anchorMax);
|
|||
} |
|||
|
|||
} |
|||
} |
|||
|
@ -0,0 +1,12 @@ |
|||
fileFormatVersion: 2 |
|||
guid: d93e8d33f3227485e88a407ecc3a14b0 |
|||
timeCreated: 1481733120 |
|||
licenseType: Store |
|||
MonoImporter: |
|||
serializedVersion: 2 |
|||
defaultReferences: [] |
|||
executionOrder: 0 |
|||
icon: {instanceID: 0} |
|||
userData: |
|||
assetBundleName: |
|||
assetBundleVariant: |
@ -0,0 +1,300 @@ |
|||
using UnityEngine; |
|||
using System.Collections; |
|||
using com.rfilkov.kinect; |
|||
using System; |
|||
|
|||
namespace com.rfilkov.components |
|||
{ |
|||
/// <summary>
|
|||
/// Background user-body image is component that displays the user-body image on RawImage texture, usually the scene background.
|
|||
/// </summary>
|
|||
public class BackgroundUserBodyImage : MonoBehaviour |
|||
{ |
|||
[Tooltip("Depth sensor index - 0 is the 1st one, 1 - the 2nd one, etc.")] |
|||
public int sensorIndex = 0; |
|||
|
|||
[Tooltip("Index of the player, tracked by this component. -1 means all players, 0 - the 1st player, 1 - the 2nd one, 2 - the 3rd one, etc.")] |
|||
public int playerIndex = -1; |
|||
|
|||
[Tooltip("RawImage used to display the user-body image.")] |
|||
public UnityEngine.UI.RawImage backgroundImage; |
|||
|
|||
[Tooltip("Camera used to display the background image. Set it, if you'd like to allow background image to resize, to match the depth image's aspect ratio.")] |
|||
public Camera backgroundCamera; |
|||
|
|||
|
|||
// last camera rect width & height
|
|||
private float lastCamRectW = 0; |
|||
private float lastCamRectH = 0; |
|||
|
|||
// references
|
|||
private KinectManager kinectManager = null; |
|||
private KinectInterop.SensorData sensorData = null; |
|||
private Vector2 initialAnchorPos = Vector2.zero; |
|||
|
|||
// color-camera aligned frames
|
|||
private ulong lastDepthFrameTime = 0; |
|||
private ulong lastBodyIndexFrameTime = 0; |
|||
|
|||
// color-camera aligned texture and buffers
|
|||
private RenderTexture bodyImageTexture = null; |
|||
private Material bodyImageMaterial = null; |
|||
|
|||
private ComputeBuffer bodyIndexBuffer = null; |
|||
private ComputeBuffer depthImageBuffer = null; |
|||
private ComputeBuffer bodyHistBuffer = null; |
|||
|
|||
// body image hist data
|
|||
protected int[] depthBodyBufferData = null; |
|||
protected int[] equalBodyBufferData = null; |
|||
protected int bodyHistTotalPoints = 0; |
|||
|
|||
|
|||
void Start() |
|||
{ |
|||
if (backgroundImage == null) |
|||
{ |
|||
backgroundImage = GetComponent<UnityEngine.UI.RawImage>(); |
|||
} |
|||
|
|||
kinectManager = KinectManager.Instance; |
|||
sensorData = kinectManager != null ? kinectManager.GetSensorData(sensorIndex) : null; |
|||
|
|||
if (sensorData != null) |
|||
{ |
|||
// create the user texture and needed buffers
|
|||
//bodyImageTexture = KinectInterop.CreateRenderTexture(bodyImageTexture, sensorData.depthImageWidth, sensorData.depthImageHeight);
|
|||
bodyImageMaterial = new Material(Shader.Find("Kinect/UserHistImageShader")); |
|||
|
|||
bodyHistBuffer = KinectInterop.CreateComputeBuffer(bodyHistBuffer, DepthSensorBase.MAX_DEPTH_DISTANCE_MM + 1, sizeof(int)); |
|||
|
|||
depthBodyBufferData = new int[DepthSensorBase.MAX_DEPTH_DISTANCE_MM + 1]; |
|||
equalBodyBufferData = new int[DepthSensorBase.MAX_DEPTH_DISTANCE_MM + 1]; |
|||
} |
|||
} |
|||
|
|||
|
|||
void OnDestroy() |
|||
{ |
|||
if (bodyImageTexture) |
|||
{ |
|||
bodyImageTexture.Release(); |
|||
bodyImageTexture = null; |
|||
} |
|||
|
|||
if (bodyIndexBuffer != null) |
|||
{ |
|||
bodyIndexBuffer.Dispose(); |
|||
bodyIndexBuffer = null; |
|||
} |
|||
|
|||
if (depthImageBuffer != null) |
|||
{ |
|||
depthImageBuffer.Dispose(); |
|||
depthImageBuffer = null; |
|||
} |
|||
|
|||
if (bodyHistBuffer != null) |
|||
{ |
|||
bodyHistBuffer.Dispose(); |
|||
bodyHistBuffer = null; |
|||
} |
|||
} |
|||
|
|||
|
|||
void Update() |
|||
{ |
|||
if (kinectManager && kinectManager.IsInitialized() && sensorData != null) |
|||
{ |
|||
float cameraWidth = backgroundCamera ? backgroundCamera.pixelRect.width : 0f; |
|||
float cameraHeight = backgroundCamera ? backgroundCamera.pixelRect.height : 0f; |
|||
|
|||
// check for new depth and body-index frames
|
|||
UpdateTextureWithNewFrame(); |
|||
|
|||
//Texture bodyImageTexture = kinectManager.GetUsersImageTex(sensorIndex);
|
|||
if (backgroundImage && bodyImageTexture != null && (backgroundImage.texture == null || |
|||
backgroundImage.texture.width != bodyImageTexture.width || backgroundImage.texture.height != bodyImageTexture.height || |
|||
lastCamRectW != cameraWidth || lastCamRectH != cameraHeight)) |
|||
{ |
|||
lastCamRectW = cameraWidth; |
|||
lastCamRectH = cameraHeight; |
|||
|
|||
backgroundImage.texture = bodyImageTexture; |
|||
backgroundImage.rectTransform.localScale = sensorData.depthImageScale; // kinectManager.GetDepthImageScale(sensorIndex);
|
|||
backgroundImage.color = Color.white; |
|||
|
|||
if (backgroundCamera != null) |
|||
{ |
|||
// adjust image's size and position to match the stream aspect ratio
|
|||
int depthImageWidth = sensorData.depthImageWidth; // kinectManager.GetDepthImageWidth(sensorIndex);
|
|||
int depthImageHeight = sensorData.depthImageHeight; // kinectManager.GetDepthImageHeight(sensorIndex);
|
|||
if (depthImageWidth == 0 || depthImageHeight == 0) |
|||
return; |
|||
|
|||
RectTransform rectImage = backgroundImage.rectTransform; |
|||
float rectWidth = (rectImage.anchorMin.x != rectImage.anchorMax.x) ? cameraWidth * (rectImage.anchorMax.x - rectImage.anchorMin.x) : rectImage.sizeDelta.x; |
|||
float rectHeight = (rectImage.anchorMin.y != rectImage.anchorMax.y) ? cameraHeight * (rectImage.anchorMax.y - rectImage.anchorMin.y) : rectImage.sizeDelta.y; |
|||
|
|||
if (depthImageWidth > depthImageHeight) |
|||
rectWidth = rectHeight * depthImageWidth / depthImageHeight; |
|||
else |
|||
rectHeight = rectWidth * depthImageHeight / depthImageWidth; |
|||
|
|||
Vector2 pivotOffset = (rectImage.pivot - new Vector2(0.5f, 0.5f)) * 2f; |
|||
Vector2 imageScale = sensorData.depthImageScale; // (Vector2)kinectManager.GetDepthImageScale(sensorIndex);
|
|||
Vector2 anchorPos = rectImage.anchoredPosition + pivotOffset * imageScale * new Vector2(rectWidth, rectHeight); |
|||
|
|||
if (rectImage.anchorMin.x != rectImage.anchorMax.x) |
|||
{ |
|||
rectWidth = -(cameraWidth - rectWidth); |
|||
} |
|||
|
|||
if (rectImage.anchorMin.y != rectImage.anchorMax.y) |
|||
{ |
|||
rectHeight = -(cameraHeight - rectHeight); |
|||
} |
|||
|
|||
rectImage.sizeDelta = new Vector2(rectWidth, rectHeight); |
|||
rectImage.anchoredPosition = initialAnchorPos = anchorPos; |
|||
} |
|||
} |
|||
|
|||
//if (backgroundImage)
|
|||
//{
|
|||
// // update the anchor position, if needed
|
|||
// if (sensorData != null && sensorData.sensorInterface != null)
|
|||
// {
|
|||
// Vector2 updatedAnchorPos = initialAnchorPos + sensorData.sensorInterface.GetBackgroundImageAnchorPos(sensorData);
|
|||
// if (backgroundImage.rectTransform.anchoredPosition != updatedAnchorPos)
|
|||
// {
|
|||
// backgroundImage.rectTransform.anchoredPosition = updatedAnchorPos;
|
|||
// }
|
|||
// }
|
|||
//}
|
|||
} |
|||
else |
|||
{ |
|||
// reset the background texture, if needed
|
|||
if (backgroundImage && backgroundImage.texture != null) |
|||
{ |
|||
backgroundImage.texture = null; |
|||
} |
|||
} |
|||
|
|||
//RectTransform rectTransform = backgroundImage.rectTransform;
|
|||
//Debug.Log("pivot: " + rectTransform.pivot + ", anchorPos: " + rectTransform.anchoredPosition + ", \nanchorMin: " + rectTransform.anchorMin + ", anchorMax: " + rectTransform.anchorMax);
|
|||
} |
|||
|
|||
|
|||
// checks for new color-camera aligned frames, and composes an updated body-index texture, if needed
|
|||
private void UpdateTextureWithNewFrame() |
|||
{ |
|||
if (sensorData == null || sensorData.sensorInterface == null || sensorData.bodyIndexImage == null || sensorData.depthImage == null) |
|||
return; |
|||
if (sensorData.depthImageWidth == 0 || sensorData.depthImageHeight == 0 || sensorData.lastDepthFrameTime == 0 || sensorData.lastBodyIndexFrameTime == 0) |
|||
return; |
|||
|
|||
// get body index frame
|
|||
if (lastDepthFrameTime != sensorData.lastDepthFrameTime || lastBodyIndexFrameTime != sensorData.lastBodyIndexFrameTime) |
|||
{ |
|||
lastDepthFrameTime = sensorData.lastDepthFrameTime; |
|||
lastBodyIndexFrameTime = sensorData.lastBodyIndexFrameTime; |
|||
|
|||
if (bodyImageTexture == null || bodyImageTexture.width != sensorData.depthImageWidth || bodyImageTexture.height != sensorData.depthImageHeight) |
|||
{ |
|||
bodyImageTexture = KinectInterop.CreateRenderTexture(bodyImageTexture, sensorData.depthImageWidth, sensorData.depthImageHeight); |
|||
} |
|||
|
|||
Array.Clear(depthBodyBufferData, 0, depthBodyBufferData.Length); |
|||
Array.Clear(equalBodyBufferData, 0, equalBodyBufferData.Length); |
|||
bodyHistTotalPoints = 0; |
|||
|
|||
// get configured min & max distances
|
|||
float minDistance = ((DepthSensorBase)sensorData.sensorInterface).minDepthDistance; |
|||
float maxDistance = ((DepthSensorBase)sensorData.sensorInterface).maxDepthDistance; |
|||
|
|||
int depthMinDistance = (int)(minDistance * 1000f); |
|||
int depthMaxDistance = (int)(maxDistance * 1000f); |
|||
|
|||
int frameLen = sensorData.depthImage.Length; |
|||
for (int i = 0; i < frameLen; i++) |
|||
{ |
|||
int depth = sensorData.depthImage[i]; |
|||
int limDepth = (depth >= depthMinDistance && depth <= depthMaxDistance) ? depth : 0; |
|||
|
|||
if (/**rawBodyIndexImage[i] != 255 &&*/ limDepth > 0) |
|||
{ |
|||
depthBodyBufferData[limDepth]++; |
|||
bodyHistTotalPoints++; |
|||
} |
|||
} |
|||
|
|||
if (bodyHistTotalPoints > 0) |
|||
{ |
|||
equalBodyBufferData[0] = depthBodyBufferData[0]; |
|||
for (int i = 1; i < depthBodyBufferData.Length; i++) |
|||
{ |
|||
equalBodyBufferData[i] = equalBodyBufferData[i - 1] + depthBodyBufferData[i]; |
|||
} |
|||
} |
|||
|
|||
int bodyIndexBufferLength = sensorData.bodyIndexImage.Length >> 2; |
|||
if (bodyIndexBuffer == null || bodyIndexBuffer.count != bodyIndexBufferLength) |
|||
{ |
|||
bodyIndexBuffer = KinectInterop.CreateComputeBuffer(bodyIndexBuffer, bodyIndexBufferLength, sizeof(uint)); |
|||
} |
|||
|
|||
KinectInterop.SetComputeBufferData(bodyIndexBuffer, sensorData.bodyIndexImage, bodyIndexBufferLength, sizeof(uint)); |
|||
|
|||
int depthBufferLength = sensorData.depthImage.Length >> 1; |
|||
if (depthImageBuffer == null || depthImageBuffer.count != depthBufferLength) |
|||
{ |
|||
depthImageBuffer = KinectInterop.CreateComputeBuffer(depthImageBuffer, depthBufferLength, sizeof(uint)); |
|||
} |
|||
|
|||
KinectInterop.SetComputeBufferData(depthImageBuffer, sensorData.depthImage, depthBufferLength, sizeof(uint)); |
|||
|
|||
if (bodyHistBuffer != null) |
|||
{ |
|||
KinectInterop.SetComputeBufferData(bodyHistBuffer, equalBodyBufferData, equalBodyBufferData.Length, sizeof(int)); |
|||
} |
|||
|
|||
float minDist = minDistance; // kinectManager.minUserDistance != 0f ? kinectManager.minUserDistance : minDistance;
|
|||
float maxDist = maxDistance; // kinectManager.maxUserDistance != 0f ? kinectManager.maxUserDistance : maxDistance;
|
|||
|
|||
bodyImageMaterial.SetInt("_TexResX", sensorData.depthImageWidth); |
|||
bodyImageMaterial.SetInt("_TexResY", sensorData.depthImageHeight); |
|||
bodyImageMaterial.SetInt("_MinDepth", (int)(minDist * 1000f)); |
|||
bodyImageMaterial.SetInt("_MaxDepth", (int)(maxDist * 1000f)); |
|||
|
|||
bodyImageMaterial.SetBuffer("_BodyIndexMap", bodyIndexBuffer); |
|||
bodyImageMaterial.SetBuffer("_DepthMap", depthImageBuffer); |
|||
bodyImageMaterial.SetBuffer("_HistMap", bodyHistBuffer); |
|||
bodyImageMaterial.SetInt("_TotalPoints", bodyHistTotalPoints); |
|||
|
|||
Color[] bodyIndexColors = kinectManager.GetBodyIndexColors(); |
|||
if (playerIndex >= 0) |
|||
{ |
|||
ulong userId = kinectManager.GetUserIdByIndex(playerIndex); |
|||
int bodyIndex = kinectManager.GetBodyIndexByUserId(userId); |
|||
|
|||
int numBodyIndices = bodyIndexColors.Length; |
|||
Color clrNone = new Color(0f, 0f, 0f, 0f); |
|||
|
|||
for (int i = 0; i < numBodyIndices; i++) |
|||
{ |
|||
if (i != bodyIndex) |
|||
bodyIndexColors[i] = clrNone; |
|||
} |
|||
} |
|||
|
|||
bodyImageMaterial.SetColorArray("_BodyIndexColors", bodyIndexColors); |
|||
|
|||
Graphics.Blit(null, bodyImageTexture, bodyImageMaterial); |
|||
} |
|||
} |
|||
|
|||
} |
|||
} |
|||
|
@ -0,0 +1,12 @@ |
|||
fileFormatVersion: 2 |
|||
guid: df3491a43e1ff0f4d84b03fdfd277aad |
|||
timeCreated: 1483707326 |
|||
licenseType: Store |
|||
MonoImporter: |
|||
serializedVersion: 2 |
|||
defaultReferences: [] |
|||
executionOrder: 0 |
|||
icon: {instanceID: 0} |
|||
userData: |
|||
assetBundleName: |
|||
assetBundleVariant: |
@ -0,0 +1,408 @@ |
|||
using UnityEngine; |
|||
using System.Collections; |
|||
using System.IO; |
|||
using com.rfilkov.kinect; |
|||
|
|||
|
|||
namespace com.rfilkov.components |
|||
{ |
|||
/// <summary>
|
|||
/// BodyDataRecorderPlayer is the component that can be used for recording and replaying of body-data files.
|
|||
/// </summary>
|
|||
public class BodyDataRecorderPlayer : MonoBehaviour |
|||
{ |
|||
[Tooltip("Path to the file used to record or replay the recorded data.")] |
|||
public string filePath = "BodyRecording.txt"; |
|||
|
|||
[Tooltip("UI-Text to display information messages.")] |
|||
public UnityEngine.UI.Text infoText; |
|||
|
|||
[Tooltip("Whether to start playing the recorded data, right after the scene start.")] |
|||
public bool playAtStart = false; |
|||
|
|||
|
|||
// singleton instance of the class
|
|||
private static BodyDataRecorderPlayer instance = null; |
|||
|
|||
// whether it is recording or playing saved data at the moment
|
|||
private bool isRecording = false; |
|||
private bool isPlaying = false; |
|||
|
|||
// reference to the KM
|
|||
private KinectManager kinectManager = null; |
|||
private KinectInterop.SensorData sensorData = null; |
|||
|
|||
// time variables used for recording and playing
|
|||
private ulong liRelTime = 0; |
|||
private float fStartTime = 0f; |
|||
private float fCurrentTime = 0f; |
|||
private int fCurrentFrame = 0; |
|||
|
|||
// player variables
|
|||
private StreamReader fileReader = null; |
|||
private float fPlayTime = 0f; |
|||
private string sPlayLine = string.Empty; |
|||
private Vector3 sensorSpaceScale = Vector3.one; |
|||
|
|||
|
|||
/// <summary>
|
|||
/// Gets the singleton BodyDataRecorderPlayer instance.
|
|||
/// </summary>
|
|||
/// <value>The KinectRecorderPlayer instance.</value>
|
|||
public static BodyDataRecorderPlayer Instance |
|||
{ |
|||
get |
|||
{ |
|||
return instance; |
|||
} |
|||
} |
|||
|
|||
|
|||
// starts recording
|
|||
public void StartRecording() |
|||
{ |
|||
if (isRecording) |
|||
return; |
|||
|
|||
isRecording = true; |
|||
|
|||
// avoid recording an playing at the same time
|
|||
if (isPlaying && isRecording) |
|||
{ |
|||
CloseFile(); |
|||
isPlaying = false; |
|||
|
|||
Debug.Log("Playing stopped."); |
|||
} |
|||
|
|||
// stop recording if there is no file name specified
|
|||
if (filePath.Length == 0) |
|||
{ |
|||
isRecording = false; |
|||
|
|||
Debug.LogError("No file to save."); |
|||
if (infoText != null) |
|||
{ |
|||
infoText.text = "No file to save."; |
|||
} |
|||
} |
|||
else if(filePath.IndexOf('/') < 0 && filePath.IndexOf('\\') < 0) |
|||
{ |
|||
#if UNITY_EDITOR || UNITY_STANDALONE
|
|||
string saveFolder = "."; |
|||
#else
|
|||
string saveFolder = Application.persistentDataPath; |
|||
#endif
|
|||
if (saveFolder.Length > 0 && saveFolder[saveFolder.Length - 1] != '/' && saveFolder[saveFolder.Length - 1] != '\\') |
|||
{ |
|||
saveFolder += "/"; |
|||
} |
|||
|
|||
filePath = saveFolder + filePath; |
|||
} |
|||
|
|||
if (isRecording) |
|||
{ |
|||
Debug.Log("Recording started. File: " + filePath); |
|||
if (infoText != null) |
|||
{ |
|||
infoText.text = "Recording..."; |
|||
} |
|||
|
|||
// delete the old csv file
|
|||
if (filePath.Length > 0 && File.Exists(filePath)) |
|||
{ |
|||
File.Delete(filePath); |
|||
} |
|||
|
|||
// initialize times
|
|||
fStartTime = fCurrentTime = Time.time; |
|||
fCurrentFrame = 0; |
|||
} |
|||
|
|||
//return isRecording;
|
|||
} |
|||
|
|||
|
|||
// starts playing
|
|||
public void StartPlaying() |
|||
{ |
|||
if (isPlaying) |
|||
return; |
|||
|
|||
isPlaying = true; |
|||
|
|||
// avoid recording an playing at the same time
|
|||
if (isRecording && isPlaying) |
|||
{ |
|||
isRecording = false; |
|||
Debug.Log("Recording stopped."); |
|||
} |
|||
|
|||
if (filePath.Length > 0 && filePath.IndexOf('/') < 0 && filePath.IndexOf('\\') < 0) |
|||
{ |
|||
#if UNITY_EDITOR || UNITY_STANDALONE
|
|||
string saveFolder = "."; |
|||
#else
|
|||
string saveFolder = Application.persistentDataPath; |
|||
#endif
|
|||
if (saveFolder.Length > 0 && saveFolder[saveFolder.Length - 1] != '/' && saveFolder[saveFolder.Length - 1] != '\\') |
|||
{ |
|||
saveFolder += "/"; |
|||
} |
|||
|
|||
filePath = saveFolder + filePath; |
|||
} |
|||
|
|||
// stop playing if there is no file name specified
|
|||
if (filePath.Length == 0 || !File.Exists(filePath)) |
|||
{ |
|||
isPlaying = false; |
|||
Debug.LogError("File not found: " + filePath); |
|||
|
|||
if (infoText != null) |
|||
{ |
|||
infoText.text = "File not found: " + filePath; |
|||
} |
|||
} |
|||
|
|||
if (isPlaying) |
|||
{ |
|||
Debug.Log("Playing started. File: " + filePath); |
|||
if (infoText != null) |
|||
{ |
|||
infoText.text = "Playing..."; |
|||
} |
|||
|
|||
// initialize times
|
|||
fStartTime = fCurrentTime = Time.time; |
|||
fCurrentFrame = -1; |
|||
|
|||
// open the file and read a line
|
|||
#if !UNITY_WSA
|
|||
fileReader = new StreamReader(filePath); |
|||
#endif
|
|||
ReadLineFromFile(); |
|||
|
|||
// enable the play mode
|
|||
if (kinectManager) |
|||
{ |
|||
kinectManager.EnablePlayMode(true); |
|||
} |
|||
} |
|||
|
|||
//return isPlaying;
|
|||
} |
|||
|
|||
|
|||
// stops recording or playing
|
|||
public void StopRecordingOrPlaying() |
|||
{ |
|||
if (isRecording) |
|||
{ |
|||
isRecording = false; |
|||
|
|||
string sSavedTimeAndFrames = string.Format("{0:F3}s., {1} frames.", (fCurrentTime - fStartTime), fCurrentFrame); |
|||
Debug.Log("Recording stopped @ " + sSavedTimeAndFrames); |
|||
|
|||
if (infoText != null) |
|||
{ |
|||
infoText.text = "Recording stopped @ " + sSavedTimeAndFrames; |
|||
} |
|||
} |
|||
|
|||
if (isPlaying) |
|||
{ |
|||
// restore the space scale
|
|||
if(sensorData != null) |
|||
{ |
|||
sensorData.sensorSpaceScale = sensorSpaceScale; |
|||
} |
|||
|
|||
// close the file, if it is playing
|
|||
CloseFile(); |
|||
isPlaying = false; |
|||
|
|||
Debug.Log("Playing stopped."); |
|||
if (infoText != null) |
|||
{ |
|||
infoText.text = "Playing stopped."; |
|||
} |
|||
} |
|||
|
|||
//if (infoText != null)
|
|||
//{
|
|||
// infoText.text = "Say: 'Record' to start the recorder, or 'Play' to start the player.";
|
|||
//}
|
|||
} |
|||
|
|||
// returns if file recording is in progress at the moment
|
|||
public bool IsRecording() |
|||
{ |
|||
return isRecording; |
|||
} |
|||
|
|||
// returns if file-play is in progress at the moment
|
|||
public bool IsPlaying() |
|||
{ |
|||
return isPlaying; |
|||
} |
|||
|
|||
|
|||
// ----- end of public functions -----
|
|||
|
|||
|
|||
void Awake() |
|||
{ |
|||
instance = this; |
|||
} |
|||
|
|||
void Start() |
|||
{ |
|||
//if (infoText != null)
|
|||
//{
|
|||
// infoText.text = "Say: 'Record' to start the recorder, or 'Play' to start the player.";
|
|||
//}
|
|||
|
|||
kinectManager = KinectManager.Instance; |
|||
sensorData = kinectManager ? kinectManager.GetSensorData(0) : null; |
|||
sensorSpaceScale = sensorData != null ? sensorData.sensorSpaceScale : Vector3.one; |
|||
|
|||
if (!kinectManager) |
|||
{ |
|||
Debug.Log("KinectManager not found, probably not initialized."); |
|||
|
|||
if (infoText != null) |
|||
{ |
|||
infoText.text = "KinectManager not found, probably not initialized."; |
|||
} |
|||
} |
|||
|
|||
if (playAtStart) |
|||
{ |
|||
StartPlaying(); |
|||
} |
|||
} |
|||
|
|||
void Update() |
|||
{ |
|||
if (isRecording) |
|||
{ |
|||
// save the body frame, if any
|
|||
if (kinectManager && kinectManager.IsInitialized() && liRelTime != kinectManager.GetBodyFrameTimestamp()) |
|||
{ |
|||
liRelTime = kinectManager.GetBodyFrameTimestamp(); |
|||
string sBodyFrame = kinectManager.GetBodyFrameData(ref fCurrentTime, ';'); |
|||
|
|||
System.Globalization.CultureInfo invCulture = System.Globalization.CultureInfo.InvariantCulture; |
|||
|
|||
if (sBodyFrame.Length > 0) |
|||
{ |
|||
#if !UNITY_WSA
|
|||
using (StreamWriter writer = File.AppendText(filePath)) |
|||
{ |
|||
string sRelTime = string.Format(invCulture, "{0:F3}", (fCurrentTime - fStartTime)); |
|||
writer.WriteLine(sRelTime + "|" + sBodyFrame); |
|||
|
|||
if (infoText != null) |
|||
{ |
|||
infoText.text = string.Format("Recording @ {0}s., frame {1}.", sRelTime, fCurrentFrame); |
|||
} |
|||
|
|||
fCurrentFrame++; |
|||
} |
|||
#else
|
|||
string sRelTime = string.Format(invCulture, "{0:F3}", (fCurrentTime - fStartTime)); |
|||
Debug.Log(sRelTime + "|" + sBodyFrame); |
|||
#endif
|
|||
} |
|||
} |
|||
} |
|||
|
|||
if (isPlaying) |
|||
{ |
|||
// wait for the right time
|
|||
fCurrentTime = Time.time; |
|||
float fRelTime = fCurrentTime - fStartTime; |
|||
|
|||
if (sPlayLine != null && fRelTime >= fPlayTime) |
|||
{ |
|||
// then play the line
|
|||
if (kinectManager && sPlayLine.Length > 0) |
|||
{ |
|||
kinectManager.SetBodyFrameData(sPlayLine); |
|||
} |
|||
|
|||
// and read the next line
|
|||
ReadLineFromFile(); |
|||
} |
|||
|
|||
if (sPlayLine == null) |
|||
{ |
|||
// finish playing, if we reached the EOF
|
|||
StopRecordingOrPlaying(); |
|||
} |
|||
} |
|||
} |
|||
|
|||
void OnDestroy() |
|||
{ |
|||
// don't forget to release the resources
|
|||
CloseFile(); |
|||
isRecording = isPlaying = false; |
|||
} |
|||
|
|||
// reads a line from the file
|
|||
private bool ReadLineFromFile() |
|||
{ |
|||
if (fileReader == null) |
|||
return false; |
|||
|
|||
// read a line
|
|||
sPlayLine = fileReader.ReadLine(); |
|||
if (sPlayLine == null) |
|||
return false; |
|||
|
|||
System.Globalization.CultureInfo invCulture = System.Globalization.CultureInfo.InvariantCulture; |
|||
System.Globalization.NumberStyles numFloat = System.Globalization.NumberStyles.Float; |
|||
|
|||
// extract the unity time and the body frame
|
|||
char[] delimiters = { '|' }; |
|||
string[] sLineParts = sPlayLine.Split(delimiters); |
|||
|
|||
if (sLineParts.Length >= 2) |
|||
{ |
|||
float.TryParse(sLineParts[0], numFloat, invCulture, out fPlayTime); |
|||
sPlayLine = sLineParts[1]; |
|||
fCurrentFrame++; |
|||
|
|||
if (infoText != null) |
|||
{ |
|||
infoText.text = string.Format("Playing @ {0:F3}s., frame {1}.", fPlayTime, fCurrentFrame); |
|||
} |
|||
|
|||
return true; |
|||
} |
|||
|
|||
return false; |
|||
} |
|||
|
|||
// close the file and disable the play mode
|
|||
private void CloseFile() |
|||
{ |
|||
// close the file
|
|||
if (fileReader != null) |
|||
{ |
|||
fileReader.Dispose(); |
|||
fileReader = null; |
|||
} |
|||
|
|||
// disable the play mode
|
|||
if (kinectManager) |
|||
{ |
|||
kinectManager.EnablePlayMode(false); |
|||
} |
|||
} |
|||
|
|||
} |
|||
} |
@ -0,0 +1,12 @@ |
|||
fileFormatVersion: 2 |
|||
guid: d8a2276ae0c75c9439e36365c3e7554d |
|||
timeCreated: 1438963698 |
|||
licenseType: Store |
|||
MonoImporter: |
|||
serializedVersion: 2 |
|||
defaultReferences: [] |
|||
executionOrder: 0 |
|||
icon: {instanceID: 0} |
|||
userData: |
|||
assetBundleName: |
|||
assetBundleVariant: |
@ -0,0 +1,655 @@ |
|||
using UnityEngine; |
|||
using com.rfilkov.kinect; |
|||
|
|||
|
|||
namespace com.rfilkov.components |
|||
{ |
|||
/// <summary>
|
|||
/// Body slice enum.
|
|||
/// </summary>
|
|||
public enum BodySlice |
|||
{ |
|||
HEIGHT = 0, |
|||
WIDTH = 1, |
|||
|
|||
TORSO_1 = 2, |
|||
TORSO_2 = 3, |
|||
TORSO_3 = 4, |
|||
TORSO_4 = 5, |
|||
TORSO_5 = 6, |
|||
|
|||
COUNT = 7 |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Data structure used by the body slicer.
|
|||
/// </summary>
|
|||
public struct BodySliceData |
|||
{ |
|||
public BodySlice sliceType; |
|||
public bool isSliceValid; |
|||
|
|||
public float diameter; |
|||
public int depthPointsLength; |
|||
public int colorPointsLength; |
|||
|
|||
// public ushort[] depths;
|
|||
public Vector2 startDepthPoint; |
|||
public Vector2 endDepthPoint; |
|||
|
|||
public Vector2 startColorPoint; |
|||
public Vector2 endColorPoint; |
|||
|
|||
public Vector3 startKinectPoint; |
|||
public Vector3 endKinectPoint; |
|||
} |
|||
|
|||
|
|||
/// <summary>
|
|||
/// Body slicer is component that estimates the user height from the depth data, as well as several other body sizes.
|
|||
/// </summary>
|
|||
public class BodySlicer : MonoBehaviour |
|||
{ |
|||
[Tooltip("Depth sensor index - 0 is the 1st one, 1 - the 2nd one, etc.")] |
|||
public int sensorIndex = 0; |
|||
|
|||
[Tooltip("Index of the player, tracked by this component. 0 means the 1st player, 1 - the 2nd one, 2 - the 3rd one, etc.")] |
|||
public int playerIndex = 0; |
|||
|
|||
[Tooltip("Camera used to estimate the overlay positions of 3D-objects over the background. By default it is the main camera.")] |
|||
public Camera foregroundCamera; |
|||
|
|||
[Tooltip("Whether the sensor is in portrait mode or not.")] |
|||
public bool portraitMode = false; |
|||
|
|||
[Tooltip("Whether the body height should estimated or not.")] |
|||
public bool estimateBodyHeight = true; |
|||
|
|||
[Tooltip("Whether the body width should estimated or not.")] |
|||
public bool estimateBodyWidth = false; |
|||
|
|||
[Tooltip("Whether the body slices should estimated or not.")] |
|||
public bool estimateBodySlices = false; |
|||
|
|||
[Tooltip("Whether the slicing should be done on all updates, or only after the user calibration.")] |
|||
public bool continuousSlicing = false; |
|||
|
|||
[Tooltip("Whether the detected body slices should be displayed on the screen.")] |
|||
public bool displayBodySlices = false; |
|||
|
|||
|
|||
private ulong calibratedUserId; |
|||
private byte userBodyIndex; |
|||
|
|||
|
|||
// The singleton instance of BodySlicer
|
|||
//private static BodySlicer instance = null;
|
|||
private KinectManager kinectManager; |
|||
private KinectInterop.SensorData sensorData; |
|||
private ulong lastDepthFrameTime; |
|||
|
|||
// body slice data
|
|||
private BodySliceData[] bodySlices = new BodySliceData[(int)BodySlice.COUNT]; |
|||
|
|||
// depth image resolution
|
|||
private int depthImageWidth; |
|||
private int depthImageHeight; |
|||
|
|||
// depth scale
|
|||
private Vector3 depthScale = Vector3.one; |
|||
|
|||
// screen rectangle taken by the foreground image (in pixels)
|
|||
private Rect foregroundImgRect; |
|||
|
|||
|
|||
///// <summary>
|
|||
///// Gets the singleton BodySlicer instance.
|
|||
///// </summary>
|
|||
///// <value>The singleton BodySlicer instance.</value>
|
|||
//public static BodySlicer Instance
|
|||
//{
|
|||
// get
|
|||
// {
|
|||
// return instance;
|
|||
// }
|
|||
//}
|
|||
|
|||
|
|||
/// <summary>
|
|||
/// Gets the height of the user.
|
|||
/// </summary>
|
|||
/// <returns>The user height.</returns>
|
|||
public float getUserHeight() |
|||
{ |
|||
return getSliceWidth(BodySlice.HEIGHT); |
|||
} |
|||
|
|||
|
|||
/// <summary>
|
|||
/// Gets the slice width.
|
|||
/// </summary>
|
|||
/// <returns>The slice width.</returns>
|
|||
/// <param name="slice">Slice.</param>
|
|||
public float getSliceWidth(BodySlice slice) |
|||
{ |
|||
int iSlice = (int)slice; |
|||
|
|||
if (bodySlices[iSlice].isSliceValid) |
|||
{ |
|||
return bodySlices[iSlice].diameter; |
|||
} |
|||
|
|||
return 0f; |
|||
} |
|||
|
|||
|
|||
/// <summary>
|
|||
/// Gets the body slice count.
|
|||
/// </summary>
|
|||
/// <returns>The body slice count.</returns>
|
|||
public int getBodySliceCount() |
|||
{ |
|||
return bodySlices != null ? bodySlices.Length : 0; |
|||
} |
|||
|
|||
|
|||
/// <summary>
|
|||
/// Gets the body slice data.
|
|||
/// </summary>
|
|||
/// <returns>The body slice data.</returns>
|
|||
/// <param name="slice">Slice.</param>
|
|||
public BodySliceData getBodySliceData(BodySlice slice) |
|||
{ |
|||
return bodySlices[(int)slice]; |
|||
} |
|||
|
|||
|
|||
/// <summary>
|
|||
/// Gets the calibrated user ID.
|
|||
/// </summary>
|
|||
/// <returns>The calibrated user ID.</returns>
|
|||
public ulong getCalibratedUserId() |
|||
{ |
|||
return calibratedUserId; |
|||
} |
|||
|
|||
|
|||
/// <summary>
|
|||
/// Gets the last frame time.
|
|||
/// </summary>
|
|||
/// <returns>The last frame time.</returns>
|
|||
public ulong getLastFrameTime() |
|||
{ |
|||
return lastDepthFrameTime; |
|||
} |
|||
|
|||
|
|||
////////////////////////////////////////////////////////////////////////
|
|||
|
|||
|
|||
void Awake() |
|||
{ |
|||
//instance = this;
|
|||
} |
|||
|
|||
void Start() |
|||
{ |
|||
kinectManager = KinectManager.Instance; |
|||
sensorData = kinectManager ? kinectManager.GetSensorData(sensorIndex) : null; |
|||
|
|||
if(kinectManager && kinectManager.IsInitialized()) |
|||
{ |
|||
depthImageWidth = kinectManager.GetDepthImageWidth(sensorIndex); |
|||
depthImageHeight = kinectManager.GetDepthImageHeight(sensorIndex); |
|||
|
|||
depthScale = kinectManager.GetDepthImageScale(sensorIndex); |
|||
} |
|||
|
|||
if (foregroundCamera == null) |
|||
{ |
|||
// by default use the main camera
|
|||
foregroundCamera = Camera.main; |
|||
} |
|||
} |
|||
|
|||
void Update() |
|||
{ |
|||
if (!kinectManager || !kinectManager.IsInitialized() || sensorData == null) |
|||
return; |
|||
|
|||
// calculate the foreground rectangle
|
|||
foregroundImgRect = kinectManager.GetForegroundRectDepth(sensorIndex, foregroundCamera); |
|||
|
|||
// get required player
|
|||
ulong userId = kinectManager.GetUserIdByIndex(playerIndex); |
|||
|
|||
if (calibratedUserId == 0) |
|||
{ |
|||
if (userId != 0) |
|||
{ |
|||
OnCalibrationSuccess(userId); |
|||
} |
|||
} |
|||
else |
|||
{ |
|||
if (calibratedUserId != userId) |
|||
{ |
|||
OnUserLost(calibratedUserId); |
|||
} |
|||
else if (continuousSlicing) |
|||
{ |
|||
EstimateBodySlices(calibratedUserId); |
|||
} |
|||
} |
|||
} |
|||
|
|||
void OnRenderObject() |
|||
{ |
|||
if (displayBodySlices) |
|||
{ |
|||
DrawBodySlice(bodySlices[(int)BodySlice.HEIGHT]); |
|||
|
|||
DrawBodySlice(bodySlices[(int)BodySlice.TORSO_1]); |
|||
DrawBodySlice(bodySlices[(int)BodySlice.TORSO_2]); |
|||
DrawBodySlice(bodySlices[(int)BodySlice.TORSO_3]); |
|||
DrawBodySlice(bodySlices[(int)BodySlice.TORSO_4]); |
|||
DrawBodySlice(bodySlices[(int)BodySlice.TORSO_5]); |
|||
} |
|||
} |
|||
|
|||
|
|||
// draws a body slice line
|
|||
private void DrawBodySlice(BodySliceData bodySlice) |
|||
{ |
|||
if (bodySlice.isSliceValid && bodySlice.startDepthPoint != Vector2.zero && bodySlice.endDepthPoint != Vector2.zero) |
|||
{ |
|||
float rectX = foregroundImgRect.xMin; |
|||
float rectY = foregroundImgRect.yMin; |
|||
|
|||
float scaleX = foregroundImgRect.width / depthImageWidth; |
|||
float scaleY = foregroundImgRect.height / depthImageHeight; |
|||
|
|||
float x1 = rectX + (depthScale.x >= 0f ? bodySlice.startDepthPoint.x : depthImageWidth - bodySlice.startDepthPoint.x) * scaleX; |
|||
float y1 = rectY + (depthScale.y >= 0f ? bodySlice.startDepthPoint.y : depthImageHeight - bodySlice.startDepthPoint.y) * scaleY; |
|||
|
|||
float x2 = rectX + (depthScale.x >= 0f ? bodySlice.endDepthPoint.x : depthImageWidth - bodySlice.endDepthPoint.x) * scaleX; |
|||
float y2 = rectY + (depthScale.y >= 0f ? bodySlice.endDepthPoint.y : depthImageHeight - bodySlice.endDepthPoint.y) * scaleY; |
|||
|
|||
KinectInterop.DrawLine((int)x1, (int)y1, (int)x2, (int)y2, 2f, Color.red); |
|||
} |
|||
} |
|||
|
|||
|
|||
public void OnCalibrationSuccess(ulong userId) |
|||
{ |
|||
calibratedUserId = userId; |
|||
|
|||
// estimate body slices
|
|||
EstimateBodySlices(calibratedUserId); |
|||
} |
|||
|
|||
|
|||
void OnUserLost(ulong userId) |
|||
{ |
|||
calibratedUserId = 0; |
|||
|
|||
// invalidate the body slice data
|
|||
for (int i = 0; i < bodySlices.Length; i++) |
|||
{ |
|||
bodySlices[i].isSliceValid = false; |
|||
} |
|||
} |
|||
|
|||
|
|||
// estimates the body slice data for the given user
|
|||
public bool EstimateBodySlices(ulong userId) |
|||
{ |
|||
if (userId <= 0) |
|||
userId = calibratedUserId; |
|||
|
|||
if (!kinectManager || userId == 0) |
|||
return false; |
|||
|
|||
userBodyIndex = (byte)kinectManager.GetBodyIndexByUserId(userId); |
|||
if (userBodyIndex == 255) |
|||
return false; |
|||
|
|||
bool bSliceSuccess = false; |
|||
|
|||
if (sensorData.bodyIndexImage != null && sensorData.depthImage != null && |
|||
sensorData.lastDepthFrameTime != lastDepthFrameTime) |
|||
{ |
|||
lastDepthFrameTime = sensorData.lastDepthFrameTime; |
|||
bSliceSuccess = true; |
|||
|
|||
Vector2 pointPelvis = kinectManager.MapSpacePointToDepthCoords(sensorIndex, kinectManager.GetJointKinectPosition(userId, (int)KinectInterop.JointType.Pelvis, false)); |
|||
|
|||
if (estimateBodyHeight) |
|||
{ |
|||
bodySlices[(int)BodySlice.HEIGHT] = !portraitMode ? GetUserHeightParams(pointPelvis) : GetUserWidthParams(pointPelvis); |
|||
} |
|||
|
|||
if (estimateBodyWidth) |
|||
{ |
|||
bodySlices[(int)BodySlice.WIDTH] = !portraitMode ? GetUserWidthParams(pointPelvis) : GetUserHeightParams(pointPelvis); |
|||
} |
|||
|
|||
if (estimateBodySlices && kinectManager.IsJointTracked(userId, (int)KinectInterop.JointType.Pelvis) && kinectManager.IsJointTracked(userId, (int)KinectInterop.JointType.Neck)) |
|||
{ |
|||
Vector2 point1 = pointPelvis; |
|||
Vector2 point2 = kinectManager.MapSpacePointToDepthCoords(sensorIndex, kinectManager.GetJointKinectPosition(userId, (int)KinectInterop.JointType.Neck, false)); |
|||
Vector2 sliceDir = (point2 - point1) / 4f; |
|||
|
|||
bool sliceOnX = !portraitMode ? true : false; |
|||
bool sliceOnY = !portraitMode ? false : true; |
|||
|
|||
Vector2 vSlicePoint = point1; |
|||
bodySlices[(int)BodySlice.TORSO_1] = GetBodySliceParams(BodySlice.TORSO_1, vSlicePoint, sliceOnX, sliceOnY, -1); |
|||
|
|||
vSlicePoint += sliceDir; |
|||
bodySlices[(int)BodySlice.TORSO_2] = GetBodySliceParams(BodySlice.TORSO_2, vSlicePoint, sliceOnX, sliceOnY, -1); |
|||
|
|||
vSlicePoint += sliceDir; |
|||
bodySlices[(int)BodySlice.TORSO_3] = GetBodySliceParams(BodySlice.TORSO_3, vSlicePoint, sliceOnX, sliceOnY, -1); |
|||
|
|||
vSlicePoint += sliceDir; |
|||
bodySlices[(int)BodySlice.TORSO_4] = GetBodySliceParams(BodySlice.TORSO_4, vSlicePoint, sliceOnX, sliceOnY, -1); |
|||
|
|||
vSlicePoint = point2; |
|||
bodySlices[(int)BodySlice.TORSO_5] = GetBodySliceParams(BodySlice.TORSO_5, vSlicePoint, sliceOnX, sliceOnY, -1); |
|||
} |
|||
} |
|||
|
|||
return bSliceSuccess; |
|||
} |
|||
|
|||
|
|||
// creates body slice data for user height
|
|||
private BodySliceData GetUserHeightParams(Vector2 pointSpineBase) |
|||
{ |
|||
int depthLength = sensorData.depthImage.Length; |
|||
int depthWidth = sensorData.depthImageWidth; |
|||
int depthHeight = sensorData.depthImageHeight; |
|||
|
|||
Vector2 posTop = new Vector2(0, depthHeight); |
|||
for (int i = 0, x = 0, y = 0; i < depthLength; i++) |
|||
{ |
|||
if (sensorData.bodyIndexImage[i] == userBodyIndex) |
|||
{ |
|||
//if (posTop.y > y)
|
|||
posTop = new Vector2(x, y); |
|||
break; |
|||
} |
|||
|
|||
x++; |
|||
if (x >= depthWidth) |
|||
{ |
|||
x = 0; |
|||
y++; |
|||
} |
|||
} |
|||
|
|||
Vector2 posBottom = new Vector2(0, -1); |
|||
for (int i = depthLength - 1, x = depthWidth - 1, y = depthHeight - 1; i >= 0; i--) |
|||
{ |
|||
if (sensorData.bodyIndexImage[i] == userBodyIndex) |
|||
{ |
|||
//if (posBottom.y < y)
|
|||
posBottom = new Vector2(x, y); |
|||
break; |
|||
} |
|||
|
|||
x--; |
|||
if (x < 0) |
|||
{ |
|||
x = depthWidth - 1; |
|||
y--; |
|||
} |
|||
} |
|||
|
|||
BodySliceData sliceData = new BodySliceData(); |
|||
sliceData.sliceType = BodySlice.HEIGHT; |
|||
sliceData.isSliceValid = false; |
|||
|
|||
if (posBottom.y >= 0) |
|||
{ |
|||
sliceData.startDepthPoint = posTop; |
|||
sliceData.endDepthPoint = posBottom; |
|||
sliceData.depthPointsLength = (int)posBottom.y - (int)posTop.y + 1; |
|||
|
|||
int index1 = (int)posTop.y * depthWidth + (int)posTop.x; |
|||
ushort depth1 = sensorData.depthImage[index1]; |
|||
sliceData.startKinectPoint = kinectManager.MapDepthPointToSpaceCoords(sensorIndex, sliceData.startDepthPoint, depth1, true); |
|||
|
|||
int index2 = (int)posBottom.y * depthWidth + (int)posBottom.x; |
|||
ushort depth2 = sensorData.depthImage[index2]; |
|||
sliceData.endKinectPoint = kinectManager.MapDepthPointToSpaceCoords(sensorIndex, sliceData.endDepthPoint, depth2, true); |
|||
|
|||
sliceData.startColorPoint = kinectManager.MapDepthPointToColorCoords(sensorIndex, sliceData.startDepthPoint, depth1); |
|||
sliceData.endColorPoint = kinectManager.MapDepthPointToColorCoords(sensorIndex, sliceData.endDepthPoint, depth2); |
|||
|
|||
if (sliceData.startColorPoint.y < 0) |
|||
sliceData.startColorPoint.y = 0; |
|||
if (sliceData.endColorPoint.y >= sensorData.colorImageHeight) |
|||
sliceData.endColorPoint.y = sensorData.colorImageHeight - 1; |
|||
sliceData.colorPointsLength = (int)sliceData.endColorPoint.y - (int)sliceData.startColorPoint.y + 1; |
|||
|
|||
// correct x-positions of depth points
|
|||
sliceData.startDepthPoint.x = pointSpineBase.x; |
|||
sliceData.endDepthPoint.x = pointSpineBase.x; |
|||
|
|||
sliceData.diameter = (sliceData.endKinectPoint - sliceData.startKinectPoint).magnitude; |
|||
sliceData.isSliceValid = true; |
|||
} |
|||
|
|||
return sliceData; |
|||
} |
|||
|
|||
// creates body slice data for user width
|
|||
private BodySliceData GetUserWidthParams(Vector2 pointSpineBase) |
|||
{ |
|||
int depthLength = sensorData.depthImage.Length; |
|||
int depthWidth = sensorData.depthImageWidth; |
|||
//int depthHeight = sensorData.depthImageHeight;
|
|||
|
|||
Vector2 posLeft = new Vector2(depthWidth, 0); |
|||
Vector2 posRight = new Vector2(-1, 0); |
|||
|
|||
for (int i = 0, x = 0, y = 0; i < depthLength; i++) |
|||
{ |
|||
if (sensorData.bodyIndexImage[i] == userBodyIndex) |
|||
{ |
|||
if (posLeft.x > x) |
|||
posLeft = new Vector2(x, y); |
|||
if (posRight.x < x) |
|||
posRight = new Vector2(x, y); |
|||
} |
|||
|
|||
x++; |
|||
if (x >= depthWidth) |
|||
{ |
|||
x = 0; |
|||
y++; |
|||
} |
|||
} |
|||
|
|||
BodySliceData sliceData = new BodySliceData(); |
|||
sliceData.sliceType = BodySlice.WIDTH; |
|||
sliceData.isSliceValid = false; |
|||
|
|||
if (posRight.x >= 0) |
|||
{ |
|||
sliceData.startDepthPoint = posLeft; |
|||
sliceData.endDepthPoint = posRight; |
|||
sliceData.depthPointsLength = (int)posRight.x - (int)posLeft.x + 1; |
|||
|
|||
int index1 = (int)posLeft.y * depthWidth + (int)posLeft.x; |
|||
ushort depth1 = sensorData.depthImage[index1]; |
|||
sliceData.startKinectPoint = kinectManager.MapDepthPointToSpaceCoords(sensorIndex, sliceData.startDepthPoint, depth1, true); |
|||
|
|||
int index2 = (int)posRight.y * depthWidth + (int)posRight.x; |
|||
ushort depth2 = sensorData.depthImage[index2]; |
|||
sliceData.endKinectPoint = kinectManager.MapDepthPointToSpaceCoords(sensorIndex, sliceData.endDepthPoint, depth2, true); |
|||
|
|||
sliceData.startColorPoint = kinectManager.MapDepthPointToColorCoords(sensorIndex, sliceData.startDepthPoint, depth1); |
|||
sliceData.endColorPoint = kinectManager.MapDepthPointToColorCoords(sensorIndex, sliceData.endDepthPoint, depth2); |
|||
|
|||
if (sliceData.startColorPoint.x < 0) |
|||
sliceData.startColorPoint.x = 0; |
|||
if (sliceData.endColorPoint.x >= sensorData.colorImageWidth) |
|||
sliceData.endColorPoint.x = sensorData.colorImageWidth - 1; |
|||
sliceData.colorPointsLength = (int)sliceData.endColorPoint.x - (int)sliceData.startColorPoint.x + 1; |
|||
|
|||
// correct y-positions of depth points
|
|||
sliceData.startDepthPoint.y = pointSpineBase.y; |
|||
sliceData.endDepthPoint.y = pointSpineBase.y; |
|||
|
|||
sliceData.diameter = (sliceData.endKinectPoint - sliceData.startKinectPoint).magnitude; |
|||
sliceData.isSliceValid = true; |
|||
} |
|||
|
|||
return sliceData; |
|||
} |
|||
|
|||
// creates body slice data for the given body slice
|
|||
private BodySliceData GetBodySliceParams(BodySlice sliceType, Vector2 middlePoint, bool bSliceOnX, bool bSliceOnY, int maxDepthLength) |
|||
{ |
|||
BodySliceData sliceData = new BodySliceData(); |
|||
sliceData.sliceType = sliceType; |
|||
|
|||
sliceData.isSliceValid = false; |
|||
sliceData.depthPointsLength = 0; |
|||
|
|||
if (!kinectManager || middlePoint == Vector2.zero) |
|||
return sliceData; |
|||
if (!bSliceOnX && !bSliceOnY) |
|||
return sliceData; |
|||
|
|||
middlePoint.x = Mathf.FloorToInt(middlePoint.x + 0.5f); |
|||
middlePoint.y = Mathf.FloorToInt(middlePoint.y + 0.5f); |
|||
|
|||
int depthWidth = sensorData.depthImageWidth; |
|||
int depthHeight = sensorData.depthImageHeight; |
|||
|
|||
int indexMid = (int)middlePoint.y * depthWidth + (int)middlePoint.x; |
|||
byte userIndex = sensorData.bodyIndexImage[indexMid]; |
|||
|
|||
if (userIndex != userBodyIndex) |
|||
return sliceData; |
|||
|
|||
sliceData.startDepthPoint = middlePoint; |
|||
sliceData.endDepthPoint = middlePoint; |
|||
|
|||
int indexDiff1 = 0; |
|||
int indexDiff2 = 0; |
|||
|
|||
if (bSliceOnX) |
|||
{ |
|||
// min-max
|
|||
int minIndex = (int)middlePoint.y * depthWidth; |
|||
int maxIndex = (int)(middlePoint.y + 1) * depthWidth; |
|||
|
|||
// horizontal left
|
|||
int stepIndex = -1; |
|||
indexDiff1 = TrackSliceInDirection(indexMid, stepIndex, minIndex, maxIndex, userIndex); |
|||
|
|||
// horizontal right
|
|||
stepIndex = 1; |
|||
indexDiff2 = TrackSliceInDirection(indexMid, stepIndex, minIndex, maxIndex, userIndex); |
|||
} |
|||
else if (bSliceOnY) |
|||
{ |
|||
// min-max
|
|||
int minIndex = 0; |
|||
int maxIndex = depthHeight * depthWidth; |
|||
|
|||
// vertical up
|
|||
int stepIndex = -depthWidth; |
|||
indexDiff1 = TrackSliceInDirection(indexMid, stepIndex, minIndex, maxIndex, userIndex); |
|||
|
|||
// vertical down
|
|||
stepIndex = depthWidth; |
|||
indexDiff2 = TrackSliceInDirection(indexMid, stepIndex, minIndex, maxIndex, userIndex); |
|||
} |
|||
|
|||
// calculate depth length
|
|||
sliceData.depthPointsLength = indexDiff1 + indexDiff2 + 1; |
|||
|
|||
// check for max length (used by upper legs)
|
|||
if (maxDepthLength > 0 && sliceData.depthPointsLength > maxDepthLength) |
|||
{ |
|||
if (indexDiff1 > indexDiff2) |
|||
indexDiff1 = indexDiff2; |
|||
else |
|||
indexDiff2 = indexDiff1; |
|||
|
|||
sliceData.depthPointsLength = indexDiff1 + indexDiff2 + 1; |
|||
} |
|||
|
|||
// set start and end depth points
|
|||
if (bSliceOnX) |
|||
{ |
|||
sliceData.startDepthPoint.x -= indexDiff1; |
|||
sliceData.endDepthPoint.x += indexDiff2; |
|||
} |
|||
else if (bSliceOnY) |
|||
{ |
|||
sliceData.startDepthPoint.y -= indexDiff1; |
|||
sliceData.endDepthPoint.y += indexDiff2; |
|||
} |
|||
|
|||
// start point
|
|||
int index1 = (int)sliceData.startDepthPoint.y * depthWidth + (int)sliceData.startDepthPoint.x; |
|||
ushort depth1 = sensorData.depthImage[index1]; |
|||
sliceData.startKinectPoint = kinectManager.MapDepthPointToSpaceCoords(sensorIndex, sliceData.startDepthPoint, depth1, true); |
|||
|
|||
// end point
|
|||
int index2 = (int)sliceData.endDepthPoint.y * depthWidth + (int)sliceData.endDepthPoint.x; |
|||
ushort depth2 = sensorData.depthImage[index2]; |
|||
sliceData.endKinectPoint = kinectManager.MapDepthPointToSpaceCoords(sensorIndex, sliceData.endDepthPoint, depth2, true); |
|||
|
|||
sliceData.startColorPoint = kinectManager.MapDepthPointToColorCoords(sensorIndex, sliceData.startDepthPoint, depth1); |
|||
sliceData.endColorPoint = kinectManager.MapDepthPointToColorCoords(sensorIndex, sliceData.endDepthPoint, depth2); |
|||
|
|||
if (sliceData.startColorPoint.x < 0) |
|||
sliceData.startColorPoint.x = 0; |
|||
if (sliceData.endColorPoint.x >= sensorData.colorImageWidth) |
|||
sliceData.endColorPoint.x = sensorData.colorImageWidth - 1; |
|||
sliceData.colorPointsLength = (int)sliceData.endColorPoint.x - (int)sliceData.startColorPoint.x + 1; |
|||
|
|||
// diameter
|
|||
sliceData.diameter = (sliceData.endKinectPoint - sliceData.startKinectPoint).magnitude; |
|||
sliceData.isSliceValid = true; |
|||
|
|||
return sliceData; |
|||
} |
|||
|
|||
// determines the number of points in the given direction
|
|||
private int TrackSliceInDirection(int index, int stepIndex, int minIndex, int maxIndex, byte userIndex) |
|||
{ |
|||
int indexDiff = 0; |
|||
int errCount = 0; |
|||
|
|||
index += stepIndex; |
|||
while (index >= minIndex && index < maxIndex) |
|||
{ |
|||
if (sensorData.bodyIndexImage[index] != userIndex) |
|||
{ |
|||
errCount++; |
|||
if (errCount > 0) // allow 0 error(s)
|
|||
break; |
|||
} |
|||
else |
|||
{ |
|||
errCount = 0; |
|||
} |
|||
|
|||
index += stepIndex; |
|||
indexDiff++; |
|||
} |
|||
|
|||
return indexDiff; |
|||
} |
|||
|
|||
} |
|||
} |
@ -0,0 +1,12 @@ |
|||
fileFormatVersion: 2 |
|||
guid: ff0269bc02f81454bb8fbc41688637a4 |
|||
timeCreated: 1468761285 |
|||
licenseType: Store |
|||
MonoImporter: |
|||
serializedVersion: 2 |
|||
defaultReferences: [] |
|||
executionOrder: 0 |
|||
icon: {instanceID: 0} |
|||
userData: |
|||
assetBundleName: |
|||
assetBundleVariant: |
@ -0,0 +1,328 @@ |
|||
using UnityEngine; |
|||
using System; |
|||
using System.Collections; |
|||
using com.rfilkov.kinect; |
|||
using static com.rfilkov.kinect.KinectInterop; |
|||
|
|||
|
|||
namespace com.rfilkov.components |
|||
{ |
|||
/// <summary>
|
|||
/// Cubeman controller transfers the captured user motion to a cubeman model.
|
|||
/// </summary>
|
|||
public class CubemanController : MonoBehaviour |
|||
{ |
|||
[Tooltip("Index of the player, tracked by this component. 0 means the 1st player, 1 - the 2nd one, 2 - the 3rd one, etc.")] |
|||
public int playerIndex = 0; |
|||
|
|||
[Tooltip("Whether the cubeman is allowed to move vertically or not.")] |
|||
public bool verticalMovement = true; |
|||
|
|||
[Tooltip("Whether the cubeman is facing the player or not.")] |
|||
public bool mirroredMovement = false; |
|||
|
|||
[Tooltip("Scene object that will be used to represent the sensor's position and rotation in the scene.")] |
|||
public Transform sensorTransform; |
|||
|
|||
[Tooltip("Rate at which the cubeman will move through the scene.")] |
|||
public float moveRate = 1f; |
|||
|
|||
public GameObject Pelvis; |
|||
public GameObject SpineNaval; |
|||
public GameObject SpineChest; |
|||
public GameObject Neck; |
|||
public GameObject Head; |
|||
|
|||
public GameObject ClavicleLeft; |
|||
public GameObject ShoulderLeft; |
|||
public GameObject ElbowLeft; |
|||
public GameObject WristLeft; |
|||
public GameObject HandLeft; |
|||
|
|||
public GameObject ClavicleRight; |
|||
public GameObject ShoulderRight; |
|||
public GameObject ElbowRight; |
|||
public GameObject WristRight; |
|||
public GameObject HandRight; |
|||
|
|||
public GameObject HipLeft; |
|||
public GameObject KneeLeft; |
|||
public GameObject AnkleLeft; |
|||
public GameObject FootLeft; |
|||
|
|||
public GameObject HipRight; |
|||
public GameObject KneeRight; |
|||
public GameObject AnkleRight; |
|||
public GameObject FootRight; |
|||
|
|||
public GameObject Nose; |
|||
public GameObject EyeLeft; |
|||
public GameObject EarLeft; |
|||
public GameObject EyeRight; |
|||
public GameObject EarRight; |
|||
|
|||
public GameObject HandtipLeft; |
|||
public GameObject ThumbLeft; |
|||
public GameObject HandtipRight; |
|||
public GameObject ThumbRight; |
|||
|
|||
[Tooltip("Line renderer to draw the skeleton lines.")] |
|||
public LineRenderer skeletonLine; |
|||
//public LineRenderer debugLine;
|
|||
|
|||
[Tooltip("UI Text to display debug information.")] |
|||
public UnityEngine.UI.Text debugInfo; |
|||
|
|||
|
|||
private GameObject[] bones; |
|||
private LineRenderer[] lines; |
|||
|
|||
private LineRenderer lineTLeft; |
|||
private LineRenderer lineTRight; |
|||
private LineRenderer lineFLeft; |
|||
private LineRenderer lineFRight; |
|||
|
|||
private Vector3 initialPosition; |
|||
private Quaternion initialRotation; |
|||
|
|||
private Vector3 initialPosUser = Vector3.zero; |
|||
private Vector3 initialPosOffset = Vector3.zero; |
|||
private ulong initialPosUserID = 0; |
|||
private ulong lastBodyTimestamp = 0; |
|||
|
|||
|
|||
void Start() |
|||
{ |
|||
//store bones in a list for easier access
|
|||
bones = new GameObject[] |
|||
{ |
|||
Pelvis, |
|||
SpineNaval, |
|||
SpineChest, |
|||
Neck, |
|||
Head, |
|||
|
|||
ClavicleLeft, |
|||
ShoulderLeft, |
|||
ElbowLeft, |
|||
WristLeft, |
|||
HandLeft, |
|||
|
|||
ClavicleRight, |
|||
ShoulderRight, |
|||
ElbowRight, |
|||
WristRight, |
|||
HandRight, |
|||
|
|||
HipLeft, |
|||
KneeLeft, |
|||
AnkleLeft, |
|||
FootLeft, |
|||
|
|||
HipRight, |
|||
KneeRight, |
|||
AnkleRight, |
|||
FootRight, |
|||
|
|||
Nose, |
|||
EyeLeft, |
|||
EarLeft, |
|||
EyeRight, |
|||
EarRight, |
|||
|
|||
HandtipLeft, |
|||
ThumbLeft, |
|||
HandtipRight, |
|||
ThumbRight |
|||
}; |
|||
|
|||
// array holding the skeleton lines
|
|||
lines = new LineRenderer[bones.Length]; |
|||
|
|||
initialPosition = transform.position; |
|||
initialRotation = transform.rotation; |
|||
} |
|||
|
|||
|
|||
void Update() |
|||
{ |
|||
KinectManager kinectManager = KinectManager.Instance; |
|||
|
|||
// get 1st player
|
|||
ulong userID = kinectManager ? kinectManager.GetUserIdByIndex(playerIndex) : 0; |
|||
|
|||
if (userID == 0) |
|||
{ |
|||
initialPosUserID = 0; |
|||
initialPosOffset = Vector3.zero; |
|||
initialPosUser = Vector3.zero; |
|||
|
|||
// reset the pointman position and rotation
|
|||
if (transform.position != initialPosition) |
|||
{ |
|||
transform.position = initialPosition; |
|||
} |
|||
|
|||
if (transform.rotation != initialRotation) |
|||
{ |
|||
transform.rotation = initialRotation; |
|||
} |
|||
|
|||
for (int i = 0; i < bones.Length; i++) |
|||
{ |
|||
bones[i].gameObject.SetActive(true); |
|||
|
|||
bones[i].transform.localPosition = Vector3.zero; |
|||
bones[i].transform.localRotation = Quaternion.identity; |
|||
|
|||
if (lines[i] != null) |
|||
{ |
|||
lines[i].gameObject.SetActive(false); |
|||
} |
|||
} |
|||
|
|||
return; |
|||
} |
|||
|
|||
// set the position in space
|
|||
Vector3 posPointMan = !sensorTransform ? kinectManager.GetUserPosition(userID) : kinectManager.GetUserKinectPosition(userID, true); |
|||
|
|||
if (sensorTransform) |
|||
{ |
|||
posPointMan = sensorTransform.TransformPoint(posPointMan); |
|||
} |
|||
|
|||
Vector3 posPointManMP = new Vector3(posPointMan.x, posPointMan.y, !mirroredMovement ? -posPointMan.z : posPointMan.z); |
|||
|
|||
// store the initial position
|
|||
if (initialPosUserID != userID) |
|||
{ |
|||
initialPosUserID = userID; |
|||
//initialPosOffset = transform.position - (verticalMovement ? posPointMan * moveRate : new Vector3(posPointMan.x, 0, posPointMan.z) * moveRate);
|
|||
initialPosOffset = posPointMan; |
|||
|
|||
initialPosUser = initialPosition; |
|||
if (verticalMovement) |
|||
initialPosUser.y = 0f; // posPointManMP.y provides the vertical position in this case
|
|||
} |
|||
|
|||
Vector3 relPosUser = (posPointMan - initialPosOffset); |
|||
relPosUser.z = !mirroredMovement ? -relPosUser.z : relPosUser.z; |
|||
|
|||
transform.position = verticalMovement ? initialPosUser + posPointManMP * moveRate : |
|||
initialPosUser + new Vector3(posPointManMP.x, 0, posPointManMP.z) * moveRate; |
|||
|
|||
//Debug.Log (userID + ", pos: " + posPointMan + ", ipos: " + initialPosUser + ", rpos: " + posPointManMP + ", tpos: " + transform.position);
|
|||
|
|||
//Vector3 rotPelvis = kinectManager.GetJointOrientation(userID, (int)KinectInterop.JointType.Pelvis, true).eulerAngles;
|
|||
//if(rotPelvis.y > 90 && rotPelvis.y < 270)
|
|||
// Debug.Log($"Time: {DateTime.Now.ToString("HH.mm.ss.fff")} - pelRot: {rotPelvis}");
|
|||
|
|||
ulong bodyTimestamp = kinectManager.GetBodyFrameTime(0); |
|||
if (lastBodyTimestamp != bodyTimestamp) |
|||
{ |
|||
BodyData bodyData = kinectManager.GetUserBodyData(userID); |
|||
|
|||
JointData pelvis = bodyData.joint[(int)JointType.Pelvis]; |
|||
JointData neck = bodyData.joint[(int)JointType.Neck]; |
|||
|
|||
if (debugInfo != null) |
|||
{ |
|||
debugInfo.text = $"Pelvis: {GetJointDataString(pelvis)}, Neck: {GetJointDataString(neck)}"; |
|||
} |
|||
|
|||
lastBodyTimestamp = bodyTimestamp; |
|||
} |
|||
|
|||
// update the local positions of the bones
|
|||
for (int i = 0; i < bones.Length; i++) |
|||
{ |
|||
if (bones[i] != null) |
|||
{ |
|||
int joint = !mirroredMovement ? i : (int)KinectInterop.GetMirrorJoint((KinectInterop.JointType)i); |
|||
if (joint < 0) |
|||
continue; |
|||
|
|||
if (kinectManager.IsJointTracked(userID, joint)) |
|||
{ |
|||
bones[i].gameObject.SetActive(true); |
|||
|
|||
Vector3 posJoint = !sensorTransform ? kinectManager.GetJointPosition(userID, joint) : kinectManager.GetJointKinectPosition(userID, joint, true); |
|||
|
|||
if (sensorTransform) |
|||
{ |
|||
posJoint = sensorTransform.TransformPoint(posJoint); |
|||
} |
|||
|
|||
posJoint.z = !mirroredMovement ? -posJoint.z : posJoint.z; |
|||
|
|||
Quaternion rotJoint = kinectManager.GetJointOrientation(userID, joint, !mirroredMovement); |
|||
rotJoint = initialRotation * rotJoint; |
|||
|
|||
posJoint -= posPointManMP; |
|||
|
|||
if (mirroredMovement) |
|||
{ |
|||
posJoint.x = -posJoint.x; |
|||
posJoint.z = -posJoint.z; |
|||
} |
|||
|
|||
bones[i].transform.localPosition = posJoint; |
|||
bones[i].transform.rotation = rotJoint; |
|||
|
|||
if (lines[i] == null && skeletonLine != null) |
|||
{ |
|||
lines[i] = Instantiate(skeletonLine) as LineRenderer; |
|||
lines[i].transform.parent = transform; |
|||
} |
|||
|
|||
int parJoint = (int)kinectManager.GetParentJoint((KinectInterop.JointType)joint); |
|||
if (lines[i] != null) |
|||
{ |
|||
lines[i].gameObject.SetActive(true); |
|||
Vector3 posJoint2 = bones[i].transform.position; |
|||
|
|||
parJoint = !mirroredMovement ? parJoint : (int)KinectInterop.GetMirrorJoint((KinectInterop.JointType)parJoint); |
|||
Vector3 posParent = bones[parJoint].transform.position; |
|||
|
|||
//Vector3 dirFromParent = kinectManager.GetJointDirection(userID, joint, false, false);
|
|||
//dirFromParent.z = !mirroredMovement ? -dirFromParent.z : dirFromParent.z;
|
|||
//Vector3 posParent = posJoint2 - dirFromParent;
|
|||
|
|||
//Vector3 posParent = !sensorTransform ? kinectManager.GetJointPosition(userID, parJoint) : kinectManager.GetJointKinectPosition(userID, parJoint, true);
|
|||
|
|||
//if (sensorTransform)
|
|||
//{
|
|||
// posParent = sensorTransform.TransformPoint(posParent);
|
|||
//}
|
|||
|
|||
//posParent.z = !mirroredMovement ? -posParent.z : posParent.z;
|
|||
|
|||
//lines[i].SetVertexCount(2);
|
|||
lines[i].SetPosition(0, posParent); |
|||
lines[i].SetPosition(1, posJoint2); |
|||
} |
|||
|
|||
} |
|||
else |
|||
{ |
|||
bones[i].gameObject.SetActive(false); |
|||
|
|||
if (lines[i] != null) |
|||
{ |
|||
lines[i].gameObject.SetActive(false); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
|
|||
// returns the joint data string
|
|||
private string GetJointDataString(JointData jd) |
|||
{ |
|||
return $"{jd.trackingState.ToString()[0]} {jd.position.ToString("F2")}"; |
|||
} |
|||
|
|||
} |
|||
} |
@ -0,0 +1,8 @@ |
|||
fileFormatVersion: 2 |
|||
guid: 9f828727d4cfb7a4b8984b4e0310ae67 |
|||
MonoImporter: |
|||
serializedVersion: 2 |
|||
defaultReferences: [] |
|||
executionOrder: 0 |
|||
icon: {instanceID: 0} |
|||
userData: |
@ -0,0 +1,202 @@ |
|||
using UnityEngine; |
|||
using System.Collections; |
|||
using com.rfilkov.kinect; |
|||
|
|||
|
|||
namespace com.rfilkov.components |
|||
{ |
|||
/// <summary>
|
|||
/// DepthIrFilterImage filters the sensor's IR image with the raw depth. The resulting image is displayed on the given RawImage.
|
|||
/// </summary>
|
|||
public class DepthIrFilterImage : MonoBehaviour |
|||
{ |
|||
[Tooltip("Index of the used depth sensor. 0 is the 1st one, 1 - the 2nd one, etc.")] |
|||
public int sensorIndex = 0; |
|||
|
|||
[Tooltip("RawImage used to display the depth filtered IR image.")] |
|||
public UnityEngine.UI.RawImage backgroundImage; |
|||
|
|||
[Tooltip("Camera used to display the background image. Set it, if you'd like to allow background image to resize, to match the depth image's aspect ratio.")] |
|||
public Camera backgroundCamera; |
|||
|
|||
private KinectManager kinectManager = null; |
|||
private KinectInterop.SensorData sensorData = null; |
|||
|
|||
private Material depthFilterMat = null; |
|||
private RenderTexture depthFilterTex = null; |
|||
private ComputeBuffer depthImageBuffer = null; |
|||
private ulong lastDepthFrameTime = 0; |
|||
|
|||
// last camera rect width & height
|
|||
private float lastCamRectW = 0; |
|||
private float lastCamRectH = 0; |
|||
|
|||
|
|||
/// <summary>
|
|||
/// Gets depth filtered IR texture.
|
|||
/// </summary>
|
|||
/// <returns>Depth filtered IR texture.</returns>
|
|||
public Texture GetDepthFilterIrTex() |
|||
{ |
|||
return depthFilterTex; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Gets depth filtered IR material.
|
|||
/// </summary>
|
|||
/// <returns>Depth filtered IR material.</returns>
|
|||
public Material GetDepthFilterIrMat() |
|||
{ |
|||
return depthFilterMat; |
|||
} |
|||
|
|||
|
|||
/// <summary>
|
|||
/// Sets the maximum IR value, used to convert the raw IR values to texture.
|
|||
/// </summary>
|
|||
/// <param name="maxIrValue">Max IR value.</param>
|
|||
public void SetMaxIrValue(float maxIrValue) |
|||
{ |
|||
if(kinectManager) |
|||
{ |
|||
kinectManager.SetSensorMinMaxIrValues(sensorIndex, 0, maxIrValue); |
|||
} |
|||
} |
|||
|
|||
|
|||
void Start() |
|||
{ |
|||
kinectManager = KinectManager.Instance; |
|||
|
|||
if (kinectManager && kinectManager.IsInitialized()) |
|||
{ |
|||
Shader depthFilterShader = Shader.Find("Kinect/DepthIrFilterShader"); |
|||
sensorData = kinectManager.GetSensorData(sensorIndex); |
|||
|
|||
if (depthFilterShader != null && sensorData != null) |
|||
{ |
|||
depthFilterMat = new Material(depthFilterShader); |
|||
} |
|||
} |
|||
} |
|||
|
|||
void OnDestroy() |
|||
{ |
|||
if (depthImageBuffer != null) |
|||
{ |
|||
depthImageBuffer.Dispose(); |
|||
depthImageBuffer = null; |
|||
} |
|||
|
|||
if(depthFilterTex != null) |
|||
{ |
|||
depthFilterTex.Release(); |
|||
depthFilterTex = null; |
|||
|
|||
if (backgroundImage) |
|||
{ |
|||
backgroundImage.texture = null; |
|||
} |
|||
} |
|||
} |
|||
|
|||
void LateUpdate() |
|||
{ |
|||
if (kinectManager && kinectManager.IsInitialized() && depthFilterMat != null) |
|||
{ |
|||
if (sensorData != null && sensorData.infraredImageTexture != null && sensorData.depthImage != null && |
|||
lastDepthFrameTime != sensorData.lastDepthFrameTime) |
|||
{ |
|||
lastDepthFrameTime = sensorData.lastDepthFrameTime; |
|||
|
|||
int depthBufferLength = (sensorData.depthImageWidth * sensorData.depthImageHeight) >> 1; |
|||
if (depthImageBuffer == null || depthImageBuffer.count != depthBufferLength) |
|||
{ |
|||
depthImageBuffer = KinectInterop.CreateComputeBuffer(depthImageBuffer, depthBufferLength, sizeof(uint)); |
|||
} |
|||
|
|||
if (depthFilterTex == null || depthFilterTex.width != sensorData.depthImageWidth || depthFilterTex.height != sensorData.depthImageHeight) |
|||
{ |
|||
depthFilterTex = KinectInterop.CreateRenderTexture(depthFilterTex, sensorData.depthImageWidth, sensorData.depthImageHeight); |
|||
|
|||
if (backgroundImage) |
|||
{ |
|||
backgroundImage.texture = depthFilterTex; |
|||
backgroundImage.rectTransform.localScale = kinectManager.GetDepthImageScale(sensorIndex); |
|||
backgroundImage.color = Color.white; |
|||
} |
|||
} |
|||
|
|||
float minDistance = ((DepthSensorBase)sensorData.sensorInterface).minDepthDistance; |
|||
float maxDistance = ((DepthSensorBase)sensorData.sensorInterface).maxDepthDistance; |
|||
|
|||
depthFilterMat.SetInt("_TexResX", sensorData.depthImageWidth); |
|||
depthFilterMat.SetInt("_TexResY", sensorData.depthImageHeight); |
|||
depthFilterMat.SetInt("_MinDepth", (int)(minDistance * 1000f)); |
|||
depthFilterMat.SetInt("_MaxDepth", (int)(maxDistance * 1000f)); |
|||
|
|||
KinectInterop.SetComputeBufferData(depthImageBuffer, sensorData.depthImage, depthBufferLength, sizeof(uint)); |
|||
depthFilterMat.SetBuffer("_DepthMap", depthImageBuffer); |
|||
|
|||
depthFilterMat.SetTexture("_IrTex", sensorData.infraredImageTexture); |
|||
|
|||
Graphics.Blit(null, depthFilterTex, depthFilterMat); |
|||
} |
|||
|
|||
// check for resolution change
|
|||
float cameraWidth = backgroundCamera ? backgroundCamera.pixelRect.width : 0f; |
|||
float cameraHeight = backgroundCamera ? backgroundCamera.pixelRect.height : 0f; |
|||
|
|||
if (backgroundImage && (lastCamRectW != cameraWidth || lastCamRectH != cameraHeight)) |
|||
{ |
|||
SetImageResolution(cameraWidth, cameraHeight); |
|||
} |
|||
} |
|||
} |
|||
|
|||
// sets new image resolution
|
|||
private void SetImageResolution(float cameraWidth, float cameraHeight) |
|||
{ |
|||
lastCamRectW = cameraWidth; |
|||
lastCamRectH = cameraHeight; |
|||
|
|||
//Debug.Log("aPos: " + backgroundImage.rectTransform.anchoredPosition + ", aMin: " + backgroundImage.rectTransform.anchorMin +
|
|||
// ", aMax:" + backgroundImage.rectTransform.anchorMax + ", pivot: " + backgroundImage.rectTransform.pivot +
|
|||
// ", size: " + backgroundImage.rectTransform.sizeDelta);
|
|||
|
|||
if (backgroundCamera != null) |
|||
{ |
|||
// adjust image's size and position to match the stream aspect ratio
|
|||
int depthImageWidth = kinectManager.GetDepthImageWidth(sensorIndex); |
|||
int depthImageHeight = kinectManager.GetDepthImageHeight(sensorIndex); |
|||
|
|||
RectTransform rectImage = backgroundImage.rectTransform; |
|||
float rectWidth = (rectImage.anchorMin.x != rectImage.anchorMax.x) ? cameraWidth * (rectImage.anchorMax.x - rectImage.anchorMin.x) : rectImage.sizeDelta.x; |
|||
float rectHeight = (rectImage.anchorMin.y != rectImage.anchorMax.y) ? cameraHeight * (rectImage.anchorMax.y - rectImage.anchorMin.y) : rectImage.sizeDelta.y; |
|||
|
|||
if (depthImageWidth > depthImageHeight) |
|||
rectWidth = rectHeight * depthImageWidth / depthImageHeight; |
|||
else |
|||
rectHeight = rectWidth * depthImageHeight / depthImageWidth; |
|||
|
|||
Vector2 pivotOffset = (rectImage.pivot - new Vector2(0.5f, 0.5f)) * 2f; |
|||
Vector2 imageScale = (Vector2)kinectManager.GetDepthImageScale(sensorIndex); |
|||
Vector2 anchorPos = rectImage.anchoredPosition + pivotOffset * imageScale * new Vector2(rectWidth, rectHeight); |
|||
|
|||
if (rectImage.anchorMin.x != rectImage.anchorMax.x) |
|||
{ |
|||
rectWidth = -(cameraWidth - rectWidth); |
|||
} |
|||
|
|||
if (rectImage.anchorMin.y != rectImage.anchorMax.y) |
|||
{ |
|||
rectHeight = -(cameraHeight - rectHeight); |
|||
} |
|||
|
|||
rectImage.sizeDelta = new Vector2(rectWidth, rectHeight); |
|||
rectImage.anchoredPosition = anchorPos; |
|||
} |
|||
} |
|||
|
|||
} |
|||
} |
@ -0,0 +1,12 @@ |
|||
fileFormatVersion: 2 |
|||
guid: f44231b5677b1c3488a66da7d3bd995c |
|||
timeCreated: 1463267447 |
|||
licenseType: Store |
|||
MonoImporter: |
|||
serializedVersion: 2 |
|||
defaultReferences: [] |
|||
executionOrder: 0 |
|||
icon: {instanceID: 0} |
|||
userData: |
|||
assetBundleName: |
|||
assetBundleVariant: |
@ -0,0 +1,8 @@ |
|||
fileFormatVersion: 2 |
|||
guid: 50bc3aae16556ef4da4f40d878f1e934 |
|||
folderAsset: yes |
|||
DefaultImporter: |
|||
externalObjects: {} |
|||
userData: |
|||
assetBundleName: |
|||
assetBundleVariant: |
@ -0,0 +1,593 @@ |
|||
using UnityEngine; |
|||
|
|||
using System; |
|||
using System.Collections; |
|||
using System.Collections.Generic; |
|||
using UnityEngine.UIElements; |
|||
|
|||
namespace com.rfilkov.kinect |
|||
{ |
|||
|
|||
/// <summary>
|
|||
/// Type of body-spin filter.
|
|||
/// </summary>
|
|||
public enum BodySpinType : int { None = 0, FixBodySpinAndLegCross = 1, FixBodySpinOnly = 2, FixLegCrossOnly = 3 } |
|||
|
|||
|
|||
/// <summary>
|
|||
/// Detects and corrects body-spins caused by wrong body-part recognitions.
|
|||
/// </summary>
|
|||
public class BodySpinFilter |
|||
{ |
|||
// criteria to block body spinning
|
|||
public enum FACING_DIRECTION : int { FORWARD_AND_BACKWARD = 0, FORWARD_ONLY = 1, BACKWARD_ONLY = 2 }; |
|||
public FACING_DIRECTION isForwardFacing = FACING_DIRECTION.FORWARD_ONLY; // whether the user is always facing to/against the camera. otherwise uses MAX_SPIN_TIME
|
|||
public bool tryRecoverPose = false; // whether to try to recover the correct pose, or restore the last correct pose
|
|||
|
|||
public float maxSpinTime = 0.5f; // in seconds, in case it's not set as always-forward/backward-facing
|
|||
private const float MIN_ANGLE_COS = 0f; // cos(a) used for spin detection
|
|||
|
|||
private bool _fixBodySpin = true; // whether to fix the temporary body-spin issue
|
|||
private bool _fixLegCross = true; // whether to fix the leg-cross issue
|
|||
//private const bool FIX_JOINT_ANGLE = false; // whether to fix the incorrect angles at knee and ankle joints
|
|||
|
|||
// history data
|
|||
private BodyHistoryData[] history = null; |
|||
private Dictionary<ulong, Vector3> userRightDir = new Dictionary<ulong, Vector3>(); |
|||
|
|||
|
|||
// Initializes a new instance of the class.
|
|||
public BodySpinFilter() |
|||
{ |
|||
Reset(); |
|||
} |
|||
|
|||
// Initializes a new instance of the class.
|
|||
public BodySpinFilter(BodySpinType bodySpinType) |
|||
{ |
|||
switch(bodySpinType) |
|||
{ |
|||
case BodySpinType.None: |
|||
_fixBodySpin = false; |
|||
_fixLegCross = false; |
|||
break; |
|||
|
|||
case BodySpinType.FixBodySpinAndLegCross: |
|||
_fixBodySpin = true; |
|||
_fixLegCross = true; |
|||
break; |
|||
|
|||
case BodySpinType.FixBodySpinOnly: |
|||
_fixBodySpin = true; |
|||
_fixLegCross = false; |
|||
break; |
|||
|
|||
case BodySpinType.FixLegCrossOnly: |
|||
_fixBodySpin = false; |
|||
_fixLegCross = true; |
|||
break; |
|||
} |
|||
|
|||
Reset(); |
|||
} |
|||
|
|||
// Resets the filter to default values.
|
|||
public void Reset(ulong userId = 0) |
|||
{ |
|||
KinectManager kinectManager = KinectManager.Instance; |
|||
int maxBodyCount = 10; // kinectManager.GetMaxBodyCount();
|
|||
int jointCount = kinectManager.GetJointCount(); |
|||
|
|||
if(userId == 0) |
|||
{ |
|||
// create the history data
|
|||
history = new BodyHistoryData[maxBodyCount]; |
|||
for (int i = 0; i < maxBodyCount; i++) |
|||
{ |
|||
history[i] = new BodyHistoryData(jointCount); |
|||
} |
|||
|
|||
userRightDir.Clear(); |
|||
} |
|||
else |
|||
{ |
|||
// clean the history of the given user only
|
|||
if (userRightDir.ContainsKey(userId)) |
|||
{ |
|||
userRightDir.Remove(userId); |
|||
} |
|||
|
|||
for (int i = 0; i < maxBodyCount; i++) |
|||
{ |
|||
if (history[i].userId == userId) |
|||
{ |
|||
history[i].userId = 0; |
|||
history[i].lastTimestamp = 0; |
|||
history[i].lastUpdateTime = 0; |
|||
history[i].frameCount = 0; |
|||
|
|||
//Debug.Log("Removed history for userId " + userId + ", index: " + i);
|
|||
} |
|||
} |
|||
} |
|||
|
|||
} |
|||
|
|||
// Update the filter with a new frame of data and smooth.
|
|||
public void UpdateFilter(ref KinectInterop.BodyData bodyData, long bodyTimestamp, Matrix4x4 s2wMatrix, Vector3 spaceScale) |
|||
{ |
|||
if (bodyData.bIsTracked) |
|||
{ |
|||
// get body index
|
|||
int bodyIndex = GetUserIndex(bodyData.liTrackingID); |
|||
if (bodyIndex < 0) |
|||
{ |
|||
bodyIndex = GetFreeIndex(); |
|||
if (bodyIndex >= 0) |
|||
history[bodyIndex].userId = bodyData.liTrackingID; |
|||
|
|||
Vector3 lShPos = bodyData.joint[(int)KinectInterop.JointType.ShoulderLeft].position; |
|||
Vector3 rShPos = bodyData.joint[(int)KinectInterop.JointType.ShoulderRight].position; |
|||
|
|||
Vector3 curDirLR = (rShPos - lShPos).normalized; |
|||
var dotRight = Vector3.Dot(curDirLR, Vector3.right); |
|||
userRightDir[bodyData.liTrackingID] = dotRight >= 0f ? Vector3.right : Vector3.left; |
|||
//Debug.Log($"Created history for userId: {history[bodyIndex].userId}, index: {bodyIndex}, time: {DateTime.UtcNow}, dotR: {dotRight:F3}, rDir: {userRightDir[bodyData.liTrackingID]}");
|
|||
} |
|||
|
|||
// filter
|
|||
if (bodyIndex >= 0) |
|||
{ |
|||
FilterBodyJoints(ref bodyData, bodyIndex, bodyTimestamp, s2wMatrix, spaceScale); |
|||
} |
|||
} |
|||
|
|||
// free unused history - moved to sensor-int
|
|||
//CleanUpUserHistory();
|
|||
} |
|||
|
|||
// Update the filter for all body joints
|
|||
private void FilterBodyJoints(ref KinectInterop.BodyData bodyData, int bodyIndex, long bodyTimestamp, Matrix4x4 s2wMatrix, Vector3 spaceScale) |
|||
{ |
|||
//long nowTicks = DateTime.UtcNow.Ticks;
|
|||
long deltaTicks = bodyTimestamp - history[bodyIndex].lastTimestamp; |
|||
float deltaTime = deltaTicks * 0.0000001f; |
|||
|
|||
// w2s matrix
|
|||
Matrix4x4 w2sMatrix = s2wMatrix.inverse; |
|||
|
|||
if (_fixBodySpin) |
|||
{ |
|||
bool isBodyOK = CheckJointPair(ref bodyData, bodyIndex, (int)KinectInterop.JointType.ShoulderLeft, (int)KinectInterop.JointType.ShoulderRight, deltaTime, bodyTimestamp); |
|||
|
|||
if(isBodyOK) |
|||
{ |
|||
SaveAllJoints(ref bodyData, bodyIndex, bodyTimestamp); |
|||
} |
|||
else |
|||
{ |
|||
if ((!tryRecoverPose) && history[bodyIndex].frameCount > 0) |
|||
{ |
|||
RestoreAllJoints(ref bodyData, bodyIndex); |
|||
} |
|||
else |
|||
{ |
|||
SwapAllJoints(ref bodyData, bodyIndex, bodyTimestamp); |
|||
SwapAllJointsZpos(ref bodyData, s2wMatrix, spaceScale, bodyTimestamp); |
|||
} |
|||
|
|||
} |
|||
} |
|||
|
|||
Vector3 hipPosL = bodyData.joint[(int)KinectInterop.JointType.HipLeft].position; |
|||
Vector3 hipPosR = bodyData.joint[(int)KinectInterop.JointType.HipRight].position; |
|||
Vector3 hipsDir = hipPosR - hipPosL; |
|||
|
|||
// check and fix leg-crossing issues
|
|||
if (_fixLegCross) |
|||
{ |
|||
// check for and fix invalid l-r directions between legs
|
|||
CheckAndFixLegPair(ref bodyData, bodyIndex, (int)KinectInterop.JointType.HipLeft, (int)KinectInterop.JointType.HipRight, bodyTimestamp); |
|||
CheckAndFixLegPair(ref bodyData, bodyIndex, (int)KinectInterop.JointType.KneeLeft, (int)KinectInterop.JointType.KneeRight, bodyTimestamp); |
|||
CheckAndFixLegPair(ref bodyData, bodyIndex, (int)KinectInterop.JointType.AnkleLeft, (int)KinectInterop.JointType.AnkleRight, bodyTimestamp); |
|||
CheckAndFixLegPair(ref bodyData, bodyIndex, (int)KinectInterop.JointType.FootLeft, (int)KinectInterop.JointType.FootRight, bodyTimestamp); |
|||
|
|||
CheckAndFixLegInwardDir(ref bodyData, (int)KinectInterop.JointType.KneeLeft, hipsDir, w2sMatrix, spaceScale, bodyTimestamp); |
|||
CheckAndFixLegInwardDir(ref bodyData, (int)KinectInterop.JointType.KneeRight, -hipsDir, w2sMatrix, spaceScale, bodyTimestamp); |
|||
//CheckAndFixLegInwardDir(ref bodyData, (int)KinectInterop.JointType.AnkleLeft, hipsDir, w2sMatrix, spaceScale, bodyTimestamp);
|
|||
//CheckAndFixLegInwardDir(ref bodyData, (int)KinectInterop.JointType.AnkleRight, -hipsDir, w2sMatrix, spaceScale, bodyTimestamp);
|
|||
} |
|||
|
|||
////check and fix knee &ankle angles
|
|||
//if (FIX_JOINT_ANGLE)
|
|||
//{
|
|||
// CheckAndFixLegJointAngle(ref bodyData, (int)KinectInterop.JointType.KneeLeft, -hipsDir, 35f, 180f, w2sMatrix, spaceScale, bodyTimestamp);
|
|||
// CheckAndFixLegJointAngle(ref bodyData, (int)KinectInterop.JointType.KneeRight, -hipsDir, 35f, 180f, w2sMatrix, spaceScale, bodyTimestamp);
|
|||
// CheckAndFixLegJointAngle(ref bodyData, (int)KinectInterop.JointType.AnkleLeft, hipsDir, 45f, 135f, w2sMatrix, spaceScale, bodyTimestamp);
|
|||
// CheckAndFixLegJointAngle(ref bodyData, (int)KinectInterop.JointType.AnkleRight, hipsDir, 45f, 135f, w2sMatrix, spaceScale, bodyTimestamp);
|
|||
//}
|
|||
|
|||
// update body root positions
|
|||
bodyData.position = bodyData.joint[0].position; |
|||
bodyData.kinectPos = bodyData.joint[0].kinectPos; |
|||
|
|||
////if (!isBodyOK)
|
|||
//{
|
|||
// string sSwap = (!isBodyOK ? "1" : "0") + (isHipsSwap ? "1" : "0") +
|
|||
// (isKneesSwap ? "1" : "0") + (isAnklesSwap ? "1" : "0") + (isFeetSwap ? "1" : "0");
|
|||
|
|||
// Vector3 shL = bodyData.joint[(int)KinectInterop.JointType.ShoulderLeft].position;
|
|||
// Vector3 shR = bodyData.joint[(int)KinectInterop.JointType.ShoulderRight].position;
|
|||
// Vector3 hipL = bodyData.joint[(int)KinectInterop.JointType.HipLeft].position;
|
|||
// Vector3 hipR = bodyData.joint[(int)KinectInterop.JointType.HipRight].position;
|
|||
// Vector3 kneeL = bodyData.joint[(int)KinectInterop.JointType.KneeLeft].position;
|
|||
// Vector3 kneeR = bodyData.joint[(int)KinectInterop.JointType.KneeRight].position;
|
|||
// Vector3 ankleL = bodyData.joint[(int)KinectInterop.JointType.AnkleLeft].position;
|
|||
// Vector3 ankleR = bodyData.joint[(int)KinectInterop.JointType.AnkleRight].position;
|
|||
// Vector3 footL = bodyData.joint[(int)KinectInterop.JointType.FootLeft].position;
|
|||
// Vector3 footR = bodyData.joint[(int)KinectInterop.JointType.FootRight].position;
|
|||
|
|||
// Vector3 neck = bodyData.joint[(int)KinectInterop.JointType.Neck].position;
|
|||
// Vector3 head = bodyData.joint[(int)KinectInterop.JointType.Head].position;
|
|||
// Vector3 nose = bodyData.joint[(int)KinectInterop.JointType.Nose].position;
|
|||
|
|||
// Debug.Log($" ts: {bodyTimestamp}, dt: {deltaTime:F6}, swap: {sSwap}, shL: {shL}, shR: {shR}, hipL: {hipL}, hipR: {hipR}, kneeL: {kneeL}, kneeR: {kneeR}, ankleL: {ankleL}, ankleR: {ankleR}, footL: {footL}, footR: {footR}, neck: {neck}, head: {head}, nose: {nose}\n");
|
|||
//}
|
|||
} |
|||
|
|||
// check the given joint pair for spinning rotation
|
|||
private bool CheckJointPair(ref KinectInterop.BodyData bodyData, int bodyIndex, int jointL, int jointR, float deltaTime, long bodyTimestamp) |
|||
{ |
|||
bool isPairOK = true; |
|||
|
|||
Vector3 curPosL = bodyData.joint[jointL].position; |
|||
Vector3 curPosR = bodyData.joint[jointR].position; |
|||
|
|||
Vector3 curDirLR = curPosR - curPosL; |
|||
//curDirLR.z = -curDirLR.z;
|
|||
|
|||
// update the saved right-dir, if needed
|
|||
if (isForwardFacing == FACING_DIRECTION.FORWARD_AND_BACKWARD && deltaTime > maxSpinTime) |
|||
{ |
|||
var dotRight = Vector3.Dot(curDirLR, Vector3.right); |
|||
userRightDir[bodyData.liTrackingID] = dotRight >= 0f ? Vector3.right : Vector3.left; |
|||
//Debug.Log($"Updated r-dir for userId: {bodyData.liTrackingID}, dotR: {dotRight:F3}, rDir: {userRightDir[bodyData.liTrackingID]}");
|
|||
} |
|||
|
|||
// right direction
|
|||
Vector3 prevDirLR = Vector3.right; |
|||
switch (isForwardFacing) |
|||
{ |
|||
case FACING_DIRECTION.FORWARD_AND_BACKWARD: |
|||
if(userRightDir.ContainsKey(bodyData.liTrackingID)) |
|||
prevDirLR = userRightDir[bodyData.liTrackingID]; |
|||
break; |
|||
case FACING_DIRECTION.FORWARD_ONLY: |
|||
prevDirLR = Vector3.right; |
|||
break; |
|||
case FACING_DIRECTION.BACKWARD_ONLY: |
|||
prevDirLR = Vector3.left; |
|||
break; |
|||
default: |
|||
Debug.LogWarning($"Unknown value for IS_FORWARD_FACING: {isForwardFacing}"); |
|||
break; |
|||
} |
|||
|
|||
// check for different directions
|
|||
float dotPrevCur = Vector3.Dot(prevDirLR, curDirLR.normalized); |
|||
if (curDirLR != Vector3.zero && prevDirLR != Vector3.zero && dotPrevCur < MIN_ANGLE_COS && |
|||
(isForwardFacing != FACING_DIRECTION.FORWARD_AND_BACKWARD || deltaTime <= maxSpinTime)) |
|||
{ |
|||
isPairOK = false; |
|||
} |
|||
|
|||
//if(!isPairOK)
|
|||
//{
|
|||
// string curTime = DateTime.Now.ToString("HH:mm:ss.fff");
|
|||
// Debug.Log($"check LR for uID: {bodyData.liTrackingID} - {isPairOK}, dot: {dotPrevCur:F3}, dt: {deltaTime:F3}, time: {curTime}, ts: {bodyTimestamp}, cpL: {curPosL}, cpR: {curPosR}, cDir: {curDirLR:F2}, pDir: {prevDirLR:F2}\n"); // System.IO.File.AppendAllText(logFilename,
|
|||
//}
|
|||
|
|||
return isPairOK; |
|||
} |
|||
|
|||
// saves all joints to history
|
|||
private void SaveAllJoints(ref KinectInterop.BodyData bodyData, int bodyIndex, long bodyTimestamp) |
|||
{ |
|||
int jointCount = bodyData.joint.Length; |
|||
|
|||
for(int j = 0; j < jointCount; j++) |
|||
{ |
|||
history[bodyIndex].jointHistory[j].lastPosition = bodyData.joint[j].position; |
|||
history[bodyIndex].jointHistory[j].lastKinectPos = bodyData.joint[j].kinectPos; |
|||
history[bodyIndex].jointHistory[j].lastTrackingState = bodyData.joint[j].trackingState; |
|||
} |
|||
|
|||
history[bodyIndex].lastTimestamp = (long)bodyData.bodyTimestamp; |
|||
history[bodyIndex].lastUpdateTime = DateTime.UtcNow.Ticks; |
|||
history[bodyIndex].frameCount++; |
|||
|
|||
//string curTime = DateTime.Now.ToString("HH:mm:ss.fff");
|
|||
//Debug.Log($" saved joints - uID: {bodyData.liTrackingID} time: {curTime}, ts: {bodyTimestamp}\n"); // System.IO.File.AppendAllText(logFilename,
|
|||
} |
|||
|
|||
// restores all joints from history
|
|||
private void RestoreAllJoints(ref KinectInterop.BodyData bodyData, int bodyIndex) |
|||
{ |
|||
int jointCount = bodyData.joint.Length; |
|||
|
|||
for (int j = 0; j < jointCount; j++) |
|||
{ |
|||
bodyData.joint[j].position = history[bodyIndex].jointHistory[j].lastPosition; |
|||
bodyData.joint[j].kinectPos = history[bodyIndex].jointHistory[j].lastKinectPos; |
|||
bodyData.joint[j].trackingState = history[bodyIndex].jointHistory[j].lastTrackingState; |
|||
} |
|||
|
|||
// restore body timestamp
|
|||
bodyData.bodyTimestamp = (ulong)history[bodyIndex].lastTimestamp; |
|||
|
|||
// prevent history clean ups
|
|||
history[bodyIndex].lastUpdateTime = DateTime.UtcNow.Ticks; |
|||
|
|||
//string curTime = DateTime.Now.ToString("HH:mm:ss.fff");
|
|||
//Debug.Log($" restored joints - uID: {bodyData.liTrackingID}, ts: {history[bodyIndex].lastTimestamp}, time: {curTime}\n"); // System.IO.File.AppendAllText(logFilename,
|
|||
} |
|||
|
|||
// swaps all left & right joints
|
|||
private void SwapAllJoints(ref KinectInterop.BodyData bodyData, int bodyIndex, long bodyTimestamp) |
|||
{ |
|||
SwapJointsData(ref bodyData, (int)KinectInterop.JointType.ClavicleLeft, (int)KinectInterop.JointType.ClavicleRight, bodyIndex); |
|||
SwapJointsData(ref bodyData, (int)KinectInterop.JointType.ShoulderLeft, (int)KinectInterop.JointType.ShoulderRight, bodyIndex); |
|||
SwapJointsData(ref bodyData, (int)KinectInterop.JointType.ElbowLeft, (int)KinectInterop.JointType.ElbowRight, bodyIndex); |
|||
SwapJointsData(ref bodyData, (int)KinectInterop.JointType.WristLeft, (int)KinectInterop.JointType.WristRight, bodyIndex); |
|||
SwapJointsData(ref bodyData, (int)KinectInterop.JointType.HandLeft, (int)KinectInterop.JointType.HandRight, bodyIndex); |
|||
SwapJointsData(ref bodyData, (int)KinectInterop.JointType.HandtipLeft, (int)KinectInterop.JointType.HandtipRight, bodyIndex); |
|||
SwapJointsData(ref bodyData, (int)KinectInterop.JointType.ThumbLeft, (int)KinectInterop.JointType.ThumbRight, bodyIndex); |
|||
|
|||
SwapJointsData(ref bodyData, (int)KinectInterop.JointType.HipLeft, (int)KinectInterop.JointType.HipRight, bodyIndex); |
|||
SwapJointsData(ref bodyData, (int)KinectInterop.JointType.KneeLeft, (int)KinectInterop.JointType.KneeRight, bodyIndex); |
|||
SwapJointsData(ref bodyData, (int)KinectInterop.JointType.AnkleLeft, (int)KinectInterop.JointType.AnkleRight, bodyIndex); |
|||
SwapJointsData(ref bodyData, (int)KinectInterop.JointType.FootLeft, (int)KinectInterop.JointType.FootRight, bodyIndex); |
|||
|
|||
SwapJointsData(ref bodyData, (int)KinectInterop.JointType.EyeLeft, (int)KinectInterop.JointType.EyeRight, bodyIndex); |
|||
SwapJointsData(ref bodyData, (int)KinectInterop.JointType.EarLeft, (int)KinectInterop.JointType.EarRight, bodyIndex); |
|||
|
|||
//string curTime = DateTime.Now.ToString("HH:mm:ss.fff");
|
|||
//Debug.Log($" swapped joints - uID: {bodyData.liTrackingID}, ts: {bodyData.bodyTimestamp}, time: {curTime}\n"); // System.IO.File.AppendAllText(logFilename,
|
|||
} |
|||
|
|||
// restores all joints from history
|
|||
private void SwapAllJointsZpos(ref KinectInterop.BodyData bodyData, Matrix4x4 s2wMatrix, Vector3 spaceScale, long bodyTimestamp) |
|||
{ |
|||
float pelPosZ = bodyData.joint[(int)KinectInterop.JointType.Pelvis].kinectPos.z; |
|||
int jointCount = bodyData.joint.Length; |
|||
|
|||
for (int j = 1; j < jointCount; j++) |
|||
{ |
|||
int joint = j; |
|||
|
|||
Vector3 kinectPos = bodyData.joint[joint].kinectPos; |
|||
float jointDiffZ = kinectPos.z - pelPosZ; |
|||
kinectPos.z -= 2 * jointDiffZ; |
|||
|
|||
bodyData.joint[joint].kinectPos = kinectPos; |
|||
bodyData.joint[joint].position = s2wMatrix.MultiplyPoint3x4(new Vector3(kinectPos.x * spaceScale.x, kinectPos.y * spaceScale.y, kinectPos.z)); |
|||
} |
|||
|
|||
//string curTime = DateTime.Now.ToString("HH:mm:ss.fff");
|
|||
//Debug.Log($" swapZpos joints - uID: {bodyData.liTrackingID}, ts: {bodyTimestamp}, time: {curTime}\n"); // System.IO.File.AppendAllText(logFilename,
|
|||
} |
|||
|
|||
// checks the given leg pair for incorrect direction, and fixes it if needed
|
|||
private void CheckAndFixLegPair(ref KinectInterop.BodyData bodyData, int bodyIndex, int jointL, int jointR, long bodyTimestamp) |
|||
{ |
|||
bool isPairOK = true; |
|||
|
|||
Vector3 legPosL = bodyData.joint[jointL].position; |
|||
Vector3 legPosR = bodyData.joint[jointR].position; |
|||
Vector3 legDirLR = legPosR - legPosL; |
|||
legDirLR.z = -legDirLR.z; |
|||
|
|||
Vector3 shPosL = bodyData.joint[(int)KinectInterop.JointType.ShoulderLeft].position; |
|||
Vector3 shPosR = bodyData.joint[(int)KinectInterop.JointType.ShoulderRight].position; |
|||
Vector3 shDirLR = shPosR - shPosL; |
|||
shDirLR.z = -shDirLR.z; |
|||
|
|||
// check for different directions
|
|||
float dotShLeg = Vector3.Dot(shDirLR.normalized, legDirLR.normalized); |
|||
if (legDirLR != Vector3.zero && shDirLR != Vector3.zero && dotShLeg < 0f) |
|||
{ |
|||
isPairOK = false; |
|||
} |
|||
|
|||
//if (jointL == (int)KinectInterop.JointType.KneeLeft)
|
|||
//{
|
|||
// string curTime = DateTime.Now.ToString("HH:mm:ss.fff");
|
|||
// Debug.Log($"time: {curTime}, dot: {dotPrevCur:F3}, lpL: {legPosL}, lpR: {legPosR}, lDir: {legDirLR}, pDir: {hipDirLR}\n"); // System.IO.File.AppendAllText(logFilename,
|
|||
//}
|
|||
|
|||
if (!isPairOK) |
|||
{ |
|||
// fix the issue
|
|||
SwapJointsData(ref bodyData, jointL, jointR, bodyIndex); |
|||
//Debug.Log($" swapping {(KinectInterop.JointType)jointL}-{(KinectInterop.JointType)jointR} for uID: {bodyData.liTrackingID}, ts: {bodyTimestamp}, shDir: {shDirLR}, legDir: {legDirLR}, dot: {dotShLeg:F3}\n"); // System.IO.File.AppendAllText(logFilename,
|
|||
} |
|||
} |
|||
|
|||
// swaps the positional data of two joints
|
|||
private void SwapJointsData(ref KinectInterop.BodyData bodyData, int jointL, int jointR, int bodyIndex) |
|||
{ |
|||
KinectInterop.TrackingState trackingStateL = bodyData.joint[jointL].trackingState; |
|||
Vector3 kinectPosL = bodyData.joint[jointL].kinectPos; |
|||
Vector3 positionL = bodyData.joint[jointL].position; |
|||
|
|||
KinectInterop.TrackingState trackingStateR = bodyData.joint[jointR].trackingState; |
|||
Vector3 kinectPosR = bodyData.joint[jointR].kinectPos; |
|||
Vector3 positionR = bodyData.joint[jointR].position; |
|||
|
|||
bodyData.joint[jointL].trackingState = trackingStateR; |
|||
bodyData.joint[jointL].kinectPos = kinectPosR; |
|||
bodyData.joint[jointL].position = positionR; |
|||
|
|||
bodyData.joint[jointR].trackingState = trackingStateL; |
|||
bodyData.joint[jointR].kinectPos = kinectPosL; |
|||
bodyData.joint[jointR].position = positionL; |
|||
} |
|||
|
|||
//// checks the given leg joint for incorrect angle, and fixes it if needed
|
|||
//private void CheckAndFixLegJointAngle(ref KinectInterop.BodyData bodyData, int midJoint, Vector3 hipsDir, float minAngle, float maxAngle,
|
|||
// Matrix4x4 w2sMatrix, Vector3 spaceScale, long bodyTimestamp)
|
|||
//{
|
|||
// int parJoint = (int)KinectInterop.GetParentJoint((KinectInterop.JointType)midJoint);
|
|||
// int nextJoint = (int)KinectInterop.GetNextJoint((KinectInterop.JointType)midJoint);
|
|||
|
|||
// if(bodyData.joint[midJoint].trackingState == KinectInterop.TrackingState.NotTracked ||
|
|||
// bodyData.joint[parJoint].trackingState == KinectInterop.TrackingState.NotTracked ||
|
|||
// bodyData.joint[nextJoint].trackingState == KinectInterop.TrackingState.NotTracked)
|
|||
// {
|
|||
// return;
|
|||
// }
|
|||
|
|||
// Vector3 midJointPos = bodyData.joint[midJoint].position;
|
|||
// Vector3 parJointPos = bodyData.joint[parJoint].position;
|
|||
// Vector3 nextJointPos = bodyData.joint[nextJoint].position;
|
|||
|
|||
// Vector3 parJointDir = parJointPos - midJointPos;
|
|||
// Vector3 nextJointDir = nextJointPos - midJointPos;
|
|||
|
|||
// // check the angle
|
|||
// float dirAngle = Vector3.SignedAngle(parJointDir.normalized, nextJointDir.normalized, hipsDir.normalized);
|
|||
// //Debug.Log($" {(KinectInterop.JointType)midJoint} for uID {bodyData.liTrackingID} - dirs-angle: {dirAngle:F1}, parDir: {parJointDir}, nextDir: {nextJointDir}, hipsDir: {hipsDir}, min: {minAngle}, max: {maxAngle}");
|
|||
|
|||
// if (parJointDir != Vector3.zero && nextJointDir != Vector3.zero && hipsDir != Vector3.zero &&
|
|||
// (dirAngle < minAngle || dirAngle > maxAngle))
|
|||
// {
|
|||
// Vector3 crossDir = Vector3.Cross(parJointDir.normalized, nextJointDir.normalized);
|
|||
// float turnAngle = Mathf.Abs(Mathf.DeltaAngle(dirAngle, minAngle)) < Mathf.Abs(Mathf.DeltaAngle(dirAngle, maxAngle)) ? minAngle : maxAngle;
|
|||
// Quaternion turnRotation = Quaternion.AngleAxis(turnAngle, crossDir.normalized);
|
|||
|
|||
// Vector3 newJointDir = turnRotation * parJointDir;
|
|||
// newJointDir *= nextJointDir.magnitude / parJointDir.magnitude; // scale
|
|||
|
|||
// Vector3 newJointPos = midJointPos + newJointDir;
|
|||
// bodyData.joint[nextJoint].position = newJointPos;
|
|||
|
|||
// Vector3 newKinectPos = w2sMatrix.MultiplyPoint3x4(newJointPos);
|
|||
// bodyData.joint[nextJoint].kinectPos = new Vector3(newKinectPos.x * spaceScale.x, newKinectPos.y * spaceScale.y, newKinectPos.z);
|
|||
|
|||
// Debug.Log($" fix angle @ {(KinectInterop.JointType)midJoint} for uID {bodyData.liTrackingID} - old: {dirAngle:F1} new: {turnAngle:F1}, ts: {bodyTimestamp}, newDir: {newJointDir}, newPos: {newJointPos}\n"); // System.IO.File.AppendAllText(logFilename,
|
|||
// }
|
|||
//}
|
|||
|
|||
// checks the given leg joint for incorrect (inward) direction, and fixes it if needed
|
|||
private void CheckAndFixLegInwardDir(ref KinectInterop.BodyData bodyData, int midJoint, Vector3 hipsDir, Matrix4x4 w2sMatrix, Vector3 spaceScale, long bodyTimestamp) |
|||
{ |
|||
int parJoint = (int)KinectInterop.GetParentJoint((KinectInterop.JointType)midJoint); |
|||
|
|||
if (bodyData.joint[midJoint].trackingState == KinectInterop.TrackingState.NotTracked || |
|||
bodyData.joint[parJoint].trackingState == KinectInterop.TrackingState.NotTracked) |
|||
{ |
|||
return; |
|||
} |
|||
|
|||
Vector3 midJointPos = bodyData.joint[midJoint].position; |
|||
Vector3 parJointPos = bodyData.joint[parJoint].position; |
|||
Vector3 parJointDir = midJointPos - parJointPos; |
|||
|
|||
Vector3 hipsBackDir = Vector3.Cross(hipsDir.normalized, parJointDir.normalized); |
|||
float dotJointDir = Vector3.Dot(hipsDir.normalized, parJointDir.normalized); |
|||
|
|||
if(dotJointDir > 0f) |
|||
{ |
|||
// fix the joint position
|
|||
Vector3 newJointDir = Vector3.Cross(hipsBackDir.normalized, hipsDir.normalized); |
|||
newJointDir *= parJointDir.magnitude; |
|||
|
|||
Vector3 newJointPos = parJointPos + newJointDir; |
|||
bodyData.joint[midJoint].position = newJointPos; |
|||
Vector3 newKinectPos = w2sMatrix.MultiplyPoint3x4(newJointPos); |
|||
bodyData.joint[midJoint].kinectPos = new Vector3(newKinectPos.x * spaceScale.x, newKinectPos.y * spaceScale.y, newKinectPos.z); |
|||
|
|||
//Debug.Log($" fix inward @ {(KinectInterop.JointType)midJoint} for uID {bodyData.liTrackingID} - dot: {dotJointDir:F3} ts: {bodyTimestamp}, oldDir: {parJointDir}, newDir: {newJointDir}, newPos: {newJointPos}\n"); // System.IO.File.AppendAllText(logFilename,
|
|||
} |
|||
} |
|||
|
|||
// returns the history index for the given user, or -1 if not found
|
|||
private int GetUserIndex(ulong userId) |
|||
{ |
|||
for (int i = 0; i < history.Length; i++) |
|||
{ |
|||
if (history[i].userId == userId) |
|||
return i; |
|||
} |
|||
|
|||
return -1; |
|||
} |
|||
|
|||
// returns the 1st free history index, or -1 if not found
|
|||
private int GetFreeIndex() |
|||
{ |
|||
for (int i = 0; i < history.Length; i++) |
|||
{ |
|||
if (history[i].userId == 0) |
|||
return i; |
|||
} |
|||
|
|||
return -1; |
|||
} |
|||
|
|||
// frees history indices that were unused for long time
|
|||
public void CleanUpUserHistory() |
|||
{ |
|||
DateTime dtNow = DateTime.UtcNow; |
|||
long timeNow = dtNow.Ticks; |
|||
|
|||
for (int i = 0; i < history.Length; i++) |
|||
{ |
|||
if (history[i].userId != 0 && history[i].lastUpdateTime != 0 && (timeNow - history[i].lastUpdateTime) >= 10000000) |
|||
{ |
|||
//Debug.Log("Removing history for userId " + history[i].userId + ", index: " + i + ", time: " + dtNow + ", not used since: " + (timeNow - history[i].lastUpdateTime) + " ticks");
|
|||
history[i].userId = 0; |
|||
history[i].lastTimestamp = 0; |
|||
history[i].lastUpdateTime = 0; |
|||
history[i].frameCount = 0; |
|||
} |
|||
} |
|||
} |
|||
|
|||
|
|||
// body history data used by the filter
|
|||
private struct BodyHistoryData |
|||
{ |
|||
public ulong userId; |
|||
public long lastTimestamp; |
|||
public long lastUpdateTime; |
|||
public JointHistoryData[] jointHistory; |
|||
public uint frameCount; |
|||
|
|||
|
|||
public BodyHistoryData(int jointCount) |
|||
{ |
|||
userId = 0; |
|||
lastTimestamp = 0; |
|||
lastUpdateTime = 0; |
|||
jointHistory = new JointHistoryData[jointCount]; |
|||
frameCount = 0; |
|||
} |
|||
} |
|||
|
|||
// joint history data used by the filter
|
|||
private struct JointHistoryData |
|||
{ |
|||
// last joint position
|
|||
public Vector3 lastPosition; |
|||
|
|||
// last sensor position
|
|||
public Vector3 lastKinectPos; |
|||
|
|||
// last tracking state
|
|||
public KinectInterop.TrackingState lastTrackingState; |
|||
} |
|||
|
|||
} |
|||
} |
|||
|
@ -0,0 +1,11 @@ |
|||
fileFormatVersion: 2 |
|||
guid: 017113e2f305e944e9954ff2c290c874 |
|||
MonoImporter: |
|||
externalObjects: {} |
|||
serializedVersion: 2 |
|||
defaultReferences: [] |
|||
executionOrder: 0 |
|||
icon: {instanceID: 0} |
|||
userData: |
|||
assetBundleName: |
|||
assetBundleVariant: |
@ -0,0 +1,336 @@ |
|||
using UnityEngine; |
|||
using System; |
|||
using System.Collections; |
|||
using System.Collections.Generic; |
|||
|
|||
|
|||
namespace com.rfilkov.kinect |
|||
{ |
|||
/// <summary>
|
|||
/// Filter to correct the joint orientations to constraint to the range of plausible human motion.
|
|||
/// </summary>
|
|||
public class BoneOrientationConstraints |
|||
{ |
|||
// constraint types
|
|||
public enum CT { None = 0, LimA = 1, LimST = 2, LimH = 3 } |
|||
|
|||
// list of joint constraints
|
|||
private readonly List<BoneOrientationConstraint> jointConstraints = new List<BoneOrientationConstraint>(); |
|||
|
|||
private UnityEngine.UI.Text debugText; |
|||
|
|||
private long frameNum = 0; |
|||
//private float currentTime = 0f;
|
|||
|
|||
|
|||
// Initializes a new instance of the BoneOrientationConstraints class.
|
|||
public BoneOrientationConstraints() |
|||
{ |
|||
} |
|||
|
|||
public void SetDebugText(UnityEngine.UI.Text debugText) |
|||
{ |
|||
this.debugText = debugText; |
|||
} |
|||
|
|||
// AddDefaultConstraints - Adds a set of default joint constraints for normal human poses.
|
|||
// This is a reasonable set of constraints for plausible human bio-mechanics.
|
|||
public void AddDefaultConstraints() |
|||
{ |
|||
// SpineNaval
|
|||
AddBoneOrientationConstraint((int)KinectInterop.JointType.SpineNaval, CT.LimST, Vector3.up, 5f, 0f); |
|||
|
|||
// SpineChest
|
|||
AddBoneOrientationConstraint((int)KinectInterop.JointType.SpineChest, CT.LimST, Vector3.up, 5f, 0f); |
|||
|
|||
// Neck
|
|||
AddBoneOrientationConstraint((int)KinectInterop.JointType.Neck, CT.LimST, Vector3.up, 10f, 10f); |
|||
|
|||
// Head
|
|||
AddBoneOrientationConstraint((int)KinectInterop.JointType.Head, CT.LimST, Vector3.up, 50f, 80f); |
|||
|
|||
// ShoulderLeft, ShoulderRight
|
|||
AddBoneOrientationConstraint((int)KinectInterop.JointType.ShoulderLeft, CT.LimST, Vector3.left, 180f, 180f); |
|||
AddBoneOrientationConstraint((int)KinectInterop.JointType.ShoulderRight, CT.LimST, Vector3.right, 180f, 180f); |
|||
|
|||
// ElbowLeft, ElbowRight
|
|||
AddBoneOrientationConstraint((int)KinectInterop.JointType.ElbowLeft, CT.LimST, Vector3.left, 180f, 180f); |
|||
AddBoneOrientationConstraint((int)KinectInterop.JointType.ElbowRight, CT.LimST, Vector3.right, 180f, 180f); |
|||
|
|||
// WristLeft, WristRight
|
|||
AddBoneOrientationConstraint((int)KinectInterop.JointType.WristLeft, CT.LimST, Vector3.left, 60f, 40f); |
|||
AddBoneOrientationConstraint((int)KinectInterop.JointType.WristRight, CT.LimST, Vector3.right, 60f, 40f); |
|||
|
|||
// HandLeft, HandRight
|
|||
AddBoneOrientationConstraint((int)KinectInterop.JointType.HandLeft, CT.LimH, Vector3.forward, -5f, -80f); |
|||
AddBoneOrientationConstraint((int)KinectInterop.JointType.HandRight, CT.LimH, Vector3.forward, 5f, 80f); |
|||
|
|||
// HipLeft, HipRight
|
|||
AddBoneOrientationConstraint((int)KinectInterop.JointType.HipLeft, CT.LimST, Vector3.down, 120f, 0f); |
|||
AddBoneOrientationConstraint((int)KinectInterop.JointType.HipRight, CT.LimST, Vector3.down, 120f, 0f); |
|||
|
|||
// KneeLeft, KneeRight
|
|||
AddBoneOrientationConstraint((int)KinectInterop.JointType.KneeLeft, CT.LimH, Vector3.right, 0f, 150f); |
|||
AddBoneOrientationConstraint((int)KinectInterop.JointType.KneeRight, CT.LimH, Vector3.right, 0f, 150f); |
|||
|
|||
//// AnkleLeft, AnkleRight
|
|||
////AddBoneOrientationConstraint((int)KinectInterop.JointType.AnkleLeft, CT.LimST, Vector3.forward, 30f, 0f);
|
|||
////AddBoneOrientationConstraint((int)KinectInterop.JointType.AnkleRight, CT.LimST, Vector3.forward, 30f, 0f);
|
|||
//AddBoneOrientationConstraint((int)KinectInterop.JointType.AnkleLeft, CT.LimA, Vector3.forward, -5f, 5f); // lat
|
|||
//AddBoneOrientationConstraint((int)KinectInterop.JointType.AnkleLeft, CT.LimA, Vector3.right, -10f, 10f); // sag
|
|||
//AddBoneOrientationConstraint((int)KinectInterop.JointType.AnkleLeft, CT.LimA, Vector3.up, -30f, 30f); // rot
|
|||
//AddBoneOrientationConstraint((int)KinectInterop.JointType.AnkleRight, CT.LimA, Vector3.forward, -5f, 5f); // lat
|
|||
//AddBoneOrientationConstraint((int)KinectInterop.JointType.AnkleRight, CT.LimA, Vector3.right, -10f, 10f); // sag
|
|||
//AddBoneOrientationConstraint((int)KinectInterop.JointType.AnkleRight, CT.LimA, Vector3.up, -30f, 30f); // rot
|
|||
} |
|||
|
|||
// Apply the orientation constraints
|
|||
public void Constrain(ref KinectInterop.BodyData bodyData) |
|||
{ |
|||
KinectManager kinectManager = KinectManager.Instance; |
|||
frameNum++; |
|||
|
|||
for (int i = 0; i < jointConstraints.Count; i++) |
|||
{ |
|||
BoneOrientationConstraint jc = this.jointConstraints[i]; |
|||
|
|||
if (jc.thisJoint == (int)KinectInterop.JointType.Pelvis || bodyData.joint[jc.thisJoint].normalRotation == Quaternion.identity) |
|||
continue; |
|||
if (kinectManager.ignoreZCoordinates && (jc.thisJoint == (int)KinectInterop.JointType.KneeLeft || jc.thisJoint == (int)KinectInterop.JointType.KneeRight)) |
|||
continue; |
|||
if (bodyData.joint[jc.thisJoint].trackingState == KinectInterop.TrackingState.NotTracked) |
|||
continue; |
|||
|
|||
int prevJoint = (int)KinectInterop.GetParentJoint((KinectInterop.JointType)jc.thisJoint); |
|||
if (bodyData.joint[prevJoint].trackingState == KinectInterop.TrackingState.NotTracked) |
|||
continue; |
|||
|
|||
Quaternion rotParentN = bodyData.joint[prevJoint].normalRotation; |
|||
//Quaternion rotDefaultN = Quaternion.identity; // Quaternion.FromToRotation(KinectInterop.JointBaseDir[prevJoint], KinectInterop.JointBaseDir[jc.thisJoint]);
|
|||
//rotParentN = rotParentN * rotDefaultN;
|
|||
|
|||
Quaternion rotJointN = bodyData.joint[jc.thisJoint].normalRotation; |
|||
Quaternion rotLocalN = Quaternion.Inverse(rotParentN) * rotJointN; |
|||
Vector3 eulerAnglesN = rotLocalN.eulerAngles; |
|||
|
|||
bool isConstrained = false; |
|||
//string sDebug = string.Empty;
|
|||
|
|||
for (int a = 0; a < jc.axisConstrainrs.Count; a++) |
|||
{ |
|||
AxisOrientationConstraint ac = jc.axisConstrainrs[a]; |
|||
Quaternion rotLimited = rotLocalN; |
|||
|
|||
switch (ac.consType) |
|||
{ |
|||
case 0: |
|||
break; |
|||
|
|||
case CT.LimA: |
|||
eulerAnglesN = LimitAngles(eulerAnglesN, ac.axis, ac.angleMin, ac.angleMax); |
|||
rotLimited = Quaternion.Euler(eulerAnglesN); |
|||
break; |
|||
|
|||
case CT.LimST: |
|||
rotLimited = LimitSwing(rotLocalN, ac.axis, ac.angleMin); |
|||
rotLimited = LimitTwist(rotLimited, ac.axis, ac.angleMax); |
|||
break; |
|||
|
|||
case CT.LimH: |
|||
float lastAngle = bodyData.joint[jc.thisJoint].lastAngle; |
|||
rotLimited = LimitHinge(rotLocalN, ac.axis, ac.angleMin, ac.angleMax, ref lastAngle); |
|||
bodyData.joint[jc.thisJoint].lastAngle = lastAngle; |
|||
break; |
|||
|
|||
default: |
|||
throw new Exception("Undefined constraint type found: " + (int)ac.consType); |
|||
} |
|||
|
|||
if (rotLimited != rotLocalN) |
|||
{ |
|||
rotLocalN = rotLimited; |
|||
isConstrained = true; |
|||
} |
|||
} |
|||
|
|||
//if (sDebug.Length > 0)
|
|||
//{
|
|||
// if (debugText != null && jc.thisJoint == (int)KinectInterop.JointType.ElbowLeft)
|
|||
// {
|
|||
// // debugText.text = sDebug;
|
|||
// }
|
|||
|
|||
// Debug.Log(sDebug);
|
|||
//}
|
|||
|
|||
if (isConstrained) |
|||
{ |
|||
rotJointN = rotParentN * rotLocalN; |
|||
|
|||
Vector3 eulerJoint = rotJointN.eulerAngles; |
|||
Vector3 eulerJointM = new Vector3(eulerJoint.x, -eulerJoint.y, -eulerJoint.z); |
|||
Quaternion rotJointM = Quaternion.Euler(eulerJointM); |
|||
|
|||
// put it back into the bone orientations
|
|||
bodyData.joint[jc.thisJoint].normalRotation = rotJointN; |
|||
bodyData.joint[jc.thisJoint].mirroredRotation = rotJointM; |
|||
} |
|||
|
|||
} |
|||
} |
|||
|
|||
// find the bone constraint structure for given joint
|
|||
// returns the structure index in the list, or -1 if the bone structure is not found
|
|||
private int FindBoneOrientationConstraint(int thisJoint) |
|||
{ |
|||
for (int i = 0; i < jointConstraints.Count; i++) |
|||
{ |
|||
if (jointConstraints[i].thisJoint == thisJoint) |
|||
return i; |
|||
} |
|||
|
|||
return -1; |
|||
} |
|||
|
|||
// AddJointConstraint - Adds a joint constraint to the system.
|
|||
private void AddBoneOrientationConstraint(int thisJoint, CT consType, Vector3 axis, float angleMin, float angleMax) |
|||
{ |
|||
int index = FindBoneOrientationConstraint(thisJoint); |
|||
|
|||
BoneOrientationConstraint jc = index >= 0 ? jointConstraints[index] : new BoneOrientationConstraint(thisJoint); |
|||
|
|||
if (index < 0) |
|||
{ |
|||
index = jointConstraints.Count; |
|||
jointConstraints.Add(jc); |
|||
} |
|||
|
|||
AxisOrientationConstraint constraint = new AxisOrientationConstraint(consType, axis, angleMin, angleMax); |
|||
jc.axisConstrainrs.Add(constraint); |
|||
|
|||
jointConstraints[index] = jc; |
|||
} |
|||
|
|||
private Vector3 LimitAngles(Vector3 eulerAngles, Vector3 axis, float limitMin, float limitMax) |
|||
{ |
|||
int iAxis = (axis.x != 0f) ? 0 : (axis.y != 0f) ? 1 : (axis.z != 0f) ? 2 : -1; |
|||
|
|||
if (iAxis >= 0) |
|||
{ |
|||
float angle = eulerAngles[iAxis]; |
|||
if (angle > 180f) |
|||
{ |
|||
angle = angle - 360f; |
|||
} |
|||
|
|||
float newAngle = Mathf.Clamp(angle, limitMin, limitMax); |
|||
if (newAngle < 0f) |
|||
{ |
|||
newAngle += 360f; |
|||
} |
|||
|
|||
eulerAngles[iAxis] = newAngle; |
|||
} |
|||
|
|||
return eulerAngles; |
|||
} |
|||
|
|||
private Quaternion LimitSwing(Quaternion rotation, Vector3 axis, float limit) |
|||
{ |
|||
if (rotation == Quaternion.identity) |
|||
return rotation; |
|||
if (limit >= 180f) |
|||
return rotation; |
|||
|
|||
Vector3 swingAxis = rotation * axis; |
|||
|
|||
Quaternion swingRot = Quaternion.FromToRotation(axis, swingAxis); |
|||
Quaternion limSwingRot = Quaternion.RotateTowards(Quaternion.identity, swingRot, limit); |
|||
Quaternion backRot = Quaternion.FromToRotation(swingAxis, limSwingRot * axis); |
|||
|
|||
return backRot * rotation; |
|||
} |
|||
|
|||
private Quaternion LimitTwist(Quaternion rotation, Vector3 axis, float limit) |
|||
{ |
|||
limit = Mathf.Clamp(limit, 0f, 180f); |
|||
if (limit >= 180f) |
|||
return rotation; |
|||
|
|||
Vector3 orthoAxis = new Vector3(axis.y, axis.z, axis.x); |
|||
Vector3 orthoTangent = orthoAxis; |
|||
|
|||
Vector3 normal = rotation * axis; |
|||
Vector3.OrthoNormalize(ref normal, ref orthoTangent); |
|||
|
|||
Vector3 rotOrthoTangent = rotation * orthoAxis; |
|||
Vector3.OrthoNormalize(ref normal, ref rotOrthoTangent); |
|||
|
|||
Quaternion fixedRot = Quaternion.FromToRotation(rotOrthoTangent, orthoTangent) * rotation; |
|||
|
|||
if (limit <= 0f) |
|||
return fixedRot; |
|||
|
|||
return Quaternion.RotateTowards(fixedRot, rotation, limit); |
|||
} |
|||
|
|||
private Quaternion LimitHinge(Quaternion rotation, Vector3 axis, float limitMin, float limitMax, ref float lastAngle) |
|||
{ |
|||
if (limitMin == 0f && limitMax == 0f) |
|||
return Quaternion.AngleAxis(0, axis); |
|||
|
|||
Quaternion rotOnAxis = Quaternion.FromToRotation(rotation * axis, axis) * rotation; // limit-1
|
|||
Quaternion lastRotation = Quaternion.AngleAxis(lastAngle, axis); |
|||
|
|||
Quaternion rotAdded = rotOnAxis * Quaternion.Inverse(lastRotation); |
|||
float rotAngle = Quaternion.Angle(Quaternion.identity, rotAdded); |
|||
|
|||
Vector3 secAxis = new Vector3(axis.z, axis.x, axis.y); |
|||
Vector3 cross = Vector3.Cross(secAxis, axis); |
|||
|
|||
if (Vector3.Dot(rotAdded * secAxis, cross) > 0f) |
|||
{ |
|||
rotAngle = -rotAngle; |
|||
} |
|||
|
|||
rotAngle = Mathf.Clamp(lastAngle + rotAngle, limitMin, limitMax); |
|||
|
|||
return Quaternion.AngleAxis(rotAngle, axis); |
|||
} |
|||
|
|||
private struct BoneOrientationConstraint |
|||
{ |
|||
public int thisJoint; |
|||
public List<AxisOrientationConstraint> axisConstrainrs; |
|||
|
|||
|
|||
public BoneOrientationConstraint(int thisJoint) |
|||
{ |
|||
this.thisJoint = thisJoint; |
|||
axisConstrainrs = new List<AxisOrientationConstraint>(); |
|||
} |
|||
} |
|||
|
|||
|
|||
private struct AxisOrientationConstraint |
|||
{ |
|||
public CT consType; |
|||
public Vector3 axis; |
|||
|
|||
public float angleMin; |
|||
public float angleMax; |
|||
|
|||
|
|||
public AxisOrientationConstraint(CT consType, Vector3 axis, float angleMin, float angleMax) |
|||
{ |
|||
this.consType = consType; |
|||
this.axis = axis; |
|||
|
|||
// Set the min and max rotations in degrees
|
|||
this.angleMin = angleMin; |
|||
this.angleMax = angleMax; |
|||
} |
|||
} |
|||
|
|||
} |
|||
} |
@ -0,0 +1,8 @@ |
|||
fileFormatVersion: 2 |
|||
guid: 24a68b3ab03e9e24d8fb673f4f8d107a |
|||
MonoImporter: |
|||
serializedVersion: 2 |
|||
defaultReferences: [] |
|||
executionOrder: 0 |
|||
icon: {instanceID: 0} |
|||
userData: |
@ -0,0 +1,447 @@ |
|||
using UnityEngine; |
|||
|
|||
using System; |
|||
using System.Collections; |
|||
using System.Collections.Generic; |
|||
|
|||
|
|||
namespace com.rfilkov.kinect |
|||
{ |
|||
// predefined smoothing types
|
|||
public enum SmoothingType : int { None, Default, Light, Medium, Aggressive } |
|||
|
|||
|
|||
/// <summary>
|
|||
/// Parameters used for smoothing of the body-joint positions between frames.
|
|||
/// </summary>
|
|||
public class SmoothParameters |
|||
{ |
|||
public float smoothing; |
|||
public float correction; |
|||
public float prediction; |
|||
public float jitterRadius; |
|||
public float maxDeviationRadius; |
|||
} |
|||
|
|||
|
|||
/// <summary>
|
|||
/// Implementation of a Holt Double Exponential Smoothing filter. The double exponential
|
|||
/// smooths the curve and predicts. There is also noise jitter removal. And maximum
|
|||
/// prediction bounds. The parameters are commented in the Init function.
|
|||
/// </summary>
|
|||
public class JointPositionsFilter |
|||
{ |
|||
// The history data.
|
|||
//private JointHistoryData[,] history;
|
|||
private BodyHistoryData[] history; |
|||
|
|||
// The smoothing parameters for this filter.
|
|||
private SmoothParameters smoothParameters; |
|||
private SmoothingType smoothingType = SmoothingType.Default; |
|||
|
|||
// True when the filter parameters are initialized.
|
|||
private bool init; |
|||
|
|||
// userId to index
|
|||
private Dictionary<ulong, int> dictUserIdToIndex = new Dictionary<ulong, int>(); |
|||
|
|||
|
|||
/// Initializes a new instance of the class.
|
|||
public JointPositionsFilter() |
|||
{ |
|||
init = false; |
|||
} |
|||
|
|||
// Initialize the filter with a default set of TransformSmoothParameters.
|
|||
public void Init() |
|||
{ |
|||
// Specify some defaults
|
|||
Init(0.5f, 0.5f, 0.5f, 0.05f, 0.04f); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Initialize the filter with a set of manually specified TransformSmoothParameters.
|
|||
/// </summary>
|
|||
/// <param name="smoothingValue">Smoothing = [0..1], lower values is closer to the raw data and more noisy.</param>
|
|||
/// <param name="correctionValue">Correction = [0..1], higher values correct faster and feel more responsive.</param>
|
|||
/// <param name="predictionValue">Prediction = [0..n], how many frames into the future we want to predict.</param>
|
|||
/// <param name="jitterRadiusValue">JitterRadius = The deviation distance in m that defines jitter.</param>
|
|||
/// <param name="maxDeviationRadiusValue">MaxDeviation = The maximum distance in m that filtered positions are allowed to deviate from raw data.</param>
|
|||
public void Init(float smoothingValue, float correctionValue, float predictionValue, float jitterRadiusValue, float maxDeviationRadiusValue) |
|||
{ |
|||
this.smoothingType = SmoothingType.Default; |
|||
smoothParameters = new SmoothParameters(); |
|||
|
|||
smoothParameters.smoothing = smoothingValue; // How much soothing will occur. Will lag when too high
|
|||
smoothParameters.correction = correctionValue; // How much to correct back from prediction. Can make things springy
|
|||
smoothParameters.prediction = predictionValue; // Amount of prediction into the future to use. Can over shoot when too high
|
|||
smoothParameters.jitterRadius = jitterRadiusValue; // Size of the radius where jitter is removed. Can do too much smoothing when too high
|
|||
smoothParameters.maxDeviationRadius = maxDeviationRadiusValue; // Size of the max prediction radius Can snap back to noisy data when too high
|
|||
|
|||
// Check for divide by zero. Use an epsilon of a 10th of a millimeter
|
|||
smoothParameters.jitterRadius = Math.Max(0.0001f, this.smoothParameters.jitterRadius); |
|||
|
|||
Reset(); |
|||
init = true; |
|||
} |
|||
|
|||
// Initialize the filter with a set of SmoothParameters.
|
|||
public void Init(SmoothParameters smoothParameters) |
|||
{ |
|||
this.smoothingType = SmoothingType.Default; |
|||
this.smoothParameters = smoothParameters; |
|||
|
|||
Reset(); |
|||
init = true; |
|||
} |
|||
|
|||
// Initialize the filter with a set of SmoothParameters.
|
|||
public void Init(SmoothingType smoothingType) |
|||
{ |
|||
this.smoothingType = smoothingType; |
|||
smoothParameters = new SmoothParameters(); |
|||
|
|||
switch (smoothingType) |
|||
{ |
|||
case SmoothingType.Light: |
|||
smoothParameters.smoothing = 0.3f; |
|||
smoothParameters.correction = 0.35f; |
|||
smoothParameters.prediction = 0.35f; |
|||
smoothParameters.jitterRadius = 0.15f; |
|||
smoothParameters.maxDeviationRadius = 0.15f; |
|||
break; |
|||
case SmoothingType.Medium: |
|||
smoothParameters.smoothing = 0.5f; |
|||
smoothParameters.correction = 0.1f; |
|||
smoothParameters.prediction = 0.5f; |
|||
smoothParameters.jitterRadius = 0.1f; |
|||
smoothParameters.maxDeviationRadius = 0.1f; |
|||
break; |
|||
case SmoothingType.Aggressive: |
|||
smoothParameters.smoothing = 0.7f; |
|||
smoothParameters.correction = 0.3f; |
|||
smoothParameters.prediction = 1.0f; |
|||
smoothParameters.jitterRadius = 1.0f; |
|||
smoothParameters.maxDeviationRadius = 1.0f; |
|||
break; |
|||
|
|||
//case SmoothingType.Default:
|
|||
default: |
|||
smoothParameters.smoothing = 0.5f; |
|||
smoothParameters.correction = 0.5f; |
|||
smoothParameters.prediction = 0.5f; |
|||
smoothParameters.jitterRadius = 0.05f; |
|||
smoothParameters.maxDeviationRadius = 0.04f; |
|||
break; |
|||
} |
|||
|
|||
Reset(); |
|||
init = true; |
|||
} |
|||
|
|||
// Resets the filter to default values.
|
|||
public void Reset(ulong userId = 0) |
|||
{ |
|||
KinectManager kinectManager = KinectManager.Instance; |
|||
int maxBodyCount = 10; // kinectManager.GetMaxBodyCount();
|
|||
int jointCount = kinectManager.GetJointCount(); |
|||
|
|||
if (userId == 0) |
|||
{ |
|||
//history = new JointHistoryData[kinectManager.GetMaxBodyCount(), kinectManager.GetJointCount()];
|
|||
history = new BodyHistoryData[maxBodyCount]; |
|||
for (int i = 0; i < maxBodyCount; i++) |
|||
{ |
|||
history[i] = new BodyHistoryData(jointCount); |
|||
} |
|||
} |
|||
else |
|||
{ |
|||
// clean the history of the given user only
|
|||
for (int i = 0; i < maxBodyCount; i++) |
|||
{ |
|||
if (history[i].userId == userId) |
|||
{ |
|||
history[i].userId = 0; |
|||
history[i].lastUpdateTime = 0; |
|||
|
|||
for (int j = 0; j < history[i].jointHistory.Length; j++) |
|||
{ |
|||
history[i].jointHistory[j].frameCount = 0; |
|||
} |
|||
|
|||
//Debug.Log("Removed pos history for userId " + userId + ", index: " + i);
|
|||
} |
|||
} |
|||
} |
|||
|
|||
//Debug.Log("BodyCount: " + kinectManager.GetMaxBodyCount() + ", JointCount: " + kinectManager.GetJointCount());
|
|||
} |
|||
|
|||
//// Update the filter with a new frame of data and smooth.
|
|||
//public void UpdateFilter(ref KinectInterop.BodyData[] alTrackedBodies)
|
|||
//{
|
|||
// if (!init)
|
|||
// {
|
|||
// // initialize with by-default parameters
|
|||
// Init();
|
|||
// }
|
|||
|
|||
// if (smoothingType == SmoothingType.None)
|
|||
// return;
|
|||
|
|||
// SmoothParameters tempSmoothingParams = new SmoothParameters();
|
|||
// tempSmoothingParams.smoothing = this.smoothParameters.smoothing;
|
|||
// tempSmoothingParams.correction = this.smoothParameters.correction;
|
|||
// tempSmoothingParams.prediction = this.smoothParameters.prediction;
|
|||
|
|||
// int bodyCount = alTrackedBodies != null ? alTrackedBodies.Length : 0;
|
|||
// for (int bodyIndex = 0; bodyIndex < bodyCount; bodyIndex++)
|
|||
// {
|
|||
// if (alTrackedBodies[bodyIndex].bIsTracked)
|
|||
// {
|
|||
// FilterBodyJoints(ref alTrackedBodies[bodyIndex], /**bodyIndex*/ alTrackedBodies[bodyIndex].iBodyIndex, tempSmoothingParams);
|
|||
// }
|
|||
// }
|
|||
//}
|
|||
|
|||
// Update the filter with a new frame of data and smooth.
|
|||
public void UpdateFilter(ref KinectInterop.BodyData bodyData) |
|||
{ |
|||
if (!init) |
|||
{ |
|||
// initialize with by-default parameters
|
|||
Init(); |
|||
} |
|||
|
|||
if (smoothingType == SmoothingType.None) |
|||
return; |
|||
|
|||
SmoothParameters tempSmoothingParams = new SmoothParameters(); |
|||
tempSmoothingParams.smoothing = smoothParameters.smoothing; |
|||
tempSmoothingParams.correction = smoothParameters.correction; |
|||
tempSmoothingParams.prediction = smoothParameters.prediction; |
|||
|
|||
if (bodyData.bIsTracked) |
|||
{ |
|||
// get body index
|
|||
int bodyIndex = GetUserIndex(bodyData.liTrackingID); |
|||
if (bodyIndex < 0) |
|||
{ |
|||
bodyIndex = GetFreeIndex(); |
|||
if (bodyIndex >= 0) |
|||
history[bodyIndex].userId = bodyData.liTrackingID; |
|||
//Debug.Log("Created history for userId: " + history[bodyIndex].userId + ", index: " + bodyIndex + ", time: " + DateTime.UtcNow);
|
|||
} |
|||
|
|||
// filter
|
|||
if (bodyIndex >= 0) |
|||
{ |
|||
FilterBodyJoints(ref bodyData, bodyIndex, tempSmoothingParams); |
|||
} |
|||
} |
|||
|
|||
// free unused history
|
|||
//CleanUpUserHistory();
|
|||
} |
|||
|
|||
// Update the filter for all body joints
|
|||
private void FilterBodyJoints(ref KinectInterop.BodyData bodyData, int bodyIndex, SmoothParameters tempSmoothingParams) |
|||
{ |
|||
KinectManager kinectManager = KinectManager.Instance; |
|||
int jointCount = kinectManager.GetJointCount(); |
|||
long lastUpdateTime = history[bodyIndex].lastUpdateTime; |
|||
|
|||
for (int jointIndex = 0; jointIndex < jointCount; jointIndex++) |
|||
{ |
|||
//// If not tracked, we smooth a bit more by using a bigger jitter radius
|
|||
//// Always filter end joints highly as they are more noisy
|
|||
//if (bodyData.joint[jointIndex].trackingState != KinectInterop.TrackingState.Tracked ||
|
|||
// jointIndex == (int)KinectInterop.JointType.FootLeft || jointIndex == (int)KinectInterop.JointType.FootRight ||
|
|||
// jointIndex == (int)KinectInterop.JointType.HandLeft || jointIndex == (int)KinectInterop.JointType.HandRight ||
|
|||
// jointIndex == (int)KinectInterop.JointType.HandtipLeft || jointIndex == (int)KinectInterop.JointType.HandtipRight ||
|
|||
// jointIndex == (int)KinectInterop.JointType.ThumbLeft || jointIndex == (int)KinectInterop.JointType.ThumbRight)
|
|||
////|| jointIndex == (int)KinectInterop.JointType.Head)
|
|||
//{
|
|||
// tempSmoothingParams.jitterRadius = smoothParameters.jitterRadius * 2.0f;
|
|||
// tempSmoothingParams.maxDeviationRadius = smoothParameters.maxDeviationRadius * 2.0f;
|
|||
//}
|
|||
//else
|
|||
{ |
|||
tempSmoothingParams.jitterRadius = smoothParameters.jitterRadius; |
|||
tempSmoothingParams.maxDeviationRadius = smoothParameters.maxDeviationRadius; |
|||
} |
|||
|
|||
bodyData.joint[jointIndex].position = FilterJoint(bodyData.joint[jointIndex].position, bodyIndex, jointIndex, tempSmoothingParams); |
|||
} |
|||
|
|||
bodyData.position = bodyData.joint[0].position; |
|||
//Debug.Log(" updated pos history for userId: " + history[bodyIndex].userId + ", index: " + bodyIndex + ", time: " + history[bodyIndex].lastUpdateTime + " (" + lastUpdateTime + ")");
|
|||
} |
|||
|
|||
// Update the filter for one joint
|
|||
private Vector3 FilterJoint(Vector3 rawPosition, int bodyIndex, int jointIndex, SmoothParameters smoothingParameters) |
|||
{ |
|||
Vector3 filteredPosition; |
|||
Vector3 diffVec; |
|||
Vector3 trend; |
|||
|
|||
float diffVal; |
|||
float diffFactor; |
|||
|
|||
Vector3 prevFilteredPosition = history[bodyIndex].jointHistory[jointIndex].filteredPosition; |
|||
Vector3 prevTrend = history[bodyIndex].jointHistory[jointIndex].trend; |
|||
Vector3 prevRawPosition = history[bodyIndex].jointHistory[jointIndex].rawPosition; |
|||
bool jointIsValid = (rawPosition != Vector3.zero); |
|||
|
|||
// If joint is invalid, reset the filter
|
|||
if (!jointIsValid) |
|||
{ |
|||
history[bodyIndex].jointHistory[jointIndex].frameCount = 0; |
|||
} |
|||
|
|||
// Initial start values
|
|||
if (history[bodyIndex].jointHistory[jointIndex].frameCount == 0) |
|||
{ |
|||
filteredPosition = rawPosition; |
|||
trend = Vector3.zero; |
|||
} |
|||
else if (history[bodyIndex].jointHistory[jointIndex].frameCount == 1) |
|||
{ |
|||
filteredPosition = (rawPosition + prevRawPosition) * 0.5f; |
|||
diffVec = filteredPosition - prevFilteredPosition; |
|||
trend = (diffVec * smoothingParameters.correction) + (prevTrend * (1.0f - smoothingParameters.correction)); |
|||
} |
|||
else |
|||
{ |
|||
// First apply jitter filter
|
|||
diffVec = rawPosition - prevFilteredPosition; |
|||
diffVal = Math.Abs(diffVec.magnitude); |
|||
diffFactor = diffVal / smoothingParameters.jitterRadius; |
|||
|
|||
if (diffVal <= smoothingParameters.jitterRadius) |
|||
{ |
|||
filteredPosition = (rawPosition * diffFactor) + (prevFilteredPosition * (1.0f - diffFactor)); |
|||
} |
|||
else |
|||
{ |
|||
filteredPosition = rawPosition; |
|||
} |
|||
|
|||
// Now the double exponential smoothing filter
|
|||
filteredPosition = (filteredPosition * (1.0f - smoothingParameters.smoothing)) + ((prevFilteredPosition + prevTrend) * smoothingParameters.smoothing); |
|||
|
|||
diffVec = filteredPosition - prevFilteredPosition; |
|||
trend = (diffVec * smoothingParameters.correction) + (prevTrend * (1.0f - smoothingParameters.correction)); |
|||
} |
|||
|
|||
// Predict into the future to reduce latency
|
|||
Vector3 predictedPosition = filteredPosition + (trend * smoothingParameters.prediction); |
|||
|
|||
// Check that we are not too far away from raw data
|
|||
diffVec = predictedPosition - rawPosition; |
|||
diffVal = Mathf.Abs(diffVec.magnitude); |
|||
diffFactor = smoothingParameters.maxDeviationRadius / diffVal; |
|||
|
|||
if (diffVal > smoothingParameters.maxDeviationRadius) |
|||
{ |
|||
predictedPosition = (predictedPosition * diffFactor) + (rawPosition * (1.0f - diffFactor)); |
|||
} |
|||
|
|||
// Save the data from this frame
|
|||
history[bodyIndex].jointHistory[jointIndex].rawPosition = rawPosition; |
|||
history[bodyIndex].jointHistory[jointIndex].filteredPosition = filteredPosition; |
|||
history[bodyIndex].jointHistory[jointIndex].trend = trend; |
|||
history[bodyIndex].jointHistory[jointIndex].frameCount++; |
|||
|
|||
DateTime dtNow = DateTime.UtcNow; |
|||
history[bodyIndex].lastUpdateTime = dtNow.Ticks; |
|||
|
|||
return predictedPosition; |
|||
} |
|||
|
|||
|
|||
// returns the history index for the given user, or -1 if not found
|
|||
private int GetUserIndex(ulong userId) |
|||
{ |
|||
for (int i = 0; i < history.Length; i++) |
|||
{ |
|||
if (history[i].userId == userId) |
|||
return i; |
|||
} |
|||
|
|||
return -1; |
|||
} |
|||
|
|||
// returns the 1st free history index, or -1 if not found
|
|||
private int GetFreeIndex() |
|||
{ |
|||
for (int i = 0; i < history.Length; i++) |
|||
{ |
|||
if (history[i].userId == 0) |
|||
return i; |
|||
} |
|||
|
|||
return -1; |
|||
} |
|||
|
|||
// frees history indices that were unused for long time
|
|||
public void CleanUpUserHistory() |
|||
{ |
|||
DateTime dtNow = DateTime.UtcNow; |
|||
long timeNow = dtNow.Ticks; |
|||
|
|||
for (int i = 0; i < history.Length; i++) |
|||
{ |
|||
if (history[i].userId != 0 && (timeNow - history[i].lastUpdateTime) >= 10000000) |
|||
{ |
|||
//Debug.Log("Removed pos history for userId " + history[i].userId + ", index: " + i + ", time: " + dtNow + ", not used since: " + (timeNow - history[i].lastUpdateTime) + " ticks");
|
|||
|
|||
history[i].userId = 0; |
|||
history[i].lastUpdateTime = 0; |
|||
|
|||
for (int j = 0; j < history[i].jointHistory.Length; j++) |
|||
{ |
|||
history[i].jointHistory[j].frameCount = 0; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
|
|||
// body history data used by the filter
|
|||
private struct BodyHistoryData |
|||
{ |
|||
public ulong userId; |
|||
|
|||
public long lastUpdateTime; |
|||
|
|||
public JointHistoryData[] jointHistory; |
|||
|
|||
|
|||
public BodyHistoryData(int jointCount) |
|||
{ |
|||
userId = 0; |
|||
lastUpdateTime = 0; |
|||
jointHistory = new JointHistoryData[jointCount]; |
|||
} |
|||
} |
|||
|
|||
// joint history data used by the filter
|
|||
private struct JointHistoryData |
|||
{ |
|||
// Gets or sets Historical Position.
|
|||
public Vector3 rawPosition; |
|||
|
|||
// Gets or sets Historical Filtered Position.
|
|||
public Vector3 filteredPosition; |
|||
|
|||
// Gets or sets Historical Trend.
|
|||
public Vector3 trend; |
|||
|
|||
// Gets or sets Historical FrameCount.
|
|||
public uint frameCount; |
|||
} |
|||
} |
|||
} |
|||
|
@ -0,0 +1,11 @@ |
|||
fileFormatVersion: 2 |
|||
guid: 31507b0aadcf2ce48bbb4b902f62ada7 |
|||
MonoImporter: |
|||
externalObjects: {} |
|||
serializedVersion: 2 |
|||
defaultReferences: [] |
|||
executionOrder: 0 |
|||
icon: {instanceID: 0} |
|||
userData: |
|||
assetBundleName: |
|||
assetBundleVariant: |
@ -0,0 +1,398 @@ |
|||
using UnityEngine; |
|||
|
|||
using System; |
|||
using System.Collections; |
|||
using System.Collections.Generic; |
|||
|
|||
|
|||
namespace com.rfilkov.kinect |
|||
{ |
|||
/// <summary>
|
|||
/// Implementation of a Holt Double Exponential Smoothing filter. The double exponential
|
|||
/// smooths the curve and predicts. There is also noise jitter removal. And maximum
|
|||
/// prediction bounds. The parameters are commented in the Init function.
|
|||
/// </summary>
|
|||
public class JointVelocitiesFilter |
|||
{ |
|||
// The history data.
|
|||
//private VelocityHistoryData[,] history;
|
|||
private BodyHistoryData[] history; |
|||
|
|||
// The smoothing parameters for this filter.
|
|||
private SmoothParameters smoothParameters; |
|||
private SmoothingType smoothingType = SmoothingType.Default; |
|||
|
|||
// True when the filter parameters are initialized.
|
|||
private bool init; |
|||
|
|||
// userId to index
|
|||
private Dictionary<ulong, int> dictUserIdToIndex = new Dictionary<ulong, int>(); |
|||
|
|||
|
|||
/// Initializes a new instance of the class.
|
|||
public JointVelocitiesFilter() |
|||
{ |
|||
init = false; |
|||
} |
|||
|
|||
// Initialize the filter with a default set of TransformSmoothParameters.
|
|||
public void Init() |
|||
{ |
|||
// Specify some defaults
|
|||
Init(0.5f, 0.5f, 0.5f, 0.05f, 0.04f); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Initialize the filter with a set of manually specified TransformSmoothParameters.
|
|||
/// </summary>
|
|||
/// <param name="smoothingValue">Smoothing = [0..1], lower values is closer to the raw data and more noisy.</param>
|
|||
/// <param name="correctionValue">Correction = [0..1], higher values correct faster and feel more responsive.</param>
|
|||
/// <param name="predictionValue">Prediction = [0..n], how many frames into the future we want to predict.</param>
|
|||
/// <param name="jitterRadiusValue">JitterRadius = The deviation distance in m that defines jitter.</param>
|
|||
/// <param name="maxDeviationRadiusValue">MaxDeviation = The maximum distance in m that filtered positions are allowed to deviate from raw data.</param>
|
|||
public void Init(float smoothingValue, float correctionValue, float predictionValue, float jitterRadiusValue, float maxDeviationRadiusValue) |
|||
{ |
|||
smoothParameters = new SmoothParameters(); |
|||
|
|||
smoothParameters.smoothing = smoothingValue; // How much soothing will occur. Will lag when too high
|
|||
smoothParameters.correction = correctionValue; // How much to correct back from prediction. Can make things springy
|
|||
smoothParameters.prediction = predictionValue; // Amount of prediction into the future to use. Can over shoot when too high
|
|||
smoothParameters.jitterRadius = jitterRadiusValue; // Size of the radius where jitter is removed. Can do too much smoothing when too high
|
|||
smoothParameters.maxDeviationRadius = maxDeviationRadiusValue; // Size of the max prediction radius Can snap back to noisy data when too high
|
|||
|
|||
// Check for divide by zero. Use an epsilon of a 10th of a millimeter
|
|||
smoothParameters.jitterRadius = Math.Max(0.0001f, smoothParameters.jitterRadius); |
|||
|
|||
Reset(); |
|||
init = true; |
|||
} |
|||
|
|||
// Initialize the filter with a set of TransformSmoothParameters.
|
|||
public void Init(SmoothParameters smoothParameters) |
|||
{ |
|||
this.smoothingType = SmoothingType.Default; |
|||
this.smoothParameters = smoothParameters; |
|||
|
|||
Reset(); |
|||
init = true; |
|||
} |
|||
|
|||
// Initialize the filter with a set of SmoothParameters.
|
|||
public void Init(SmoothingType smoothingType) |
|||
{ |
|||
this.smoothingType = smoothingType; |
|||
smoothParameters = new SmoothParameters(); |
|||
|
|||
switch (smoothingType) |
|||
{ |
|||
case SmoothingType.Light: |
|||
smoothParameters.smoothing = 0.3f; |
|||
smoothParameters.correction = 0.35f; |
|||
smoothParameters.prediction = 0.35f; |
|||
smoothParameters.jitterRadius = 0.15f; |
|||
smoothParameters.maxDeviationRadius = 0.15f; |
|||
break; |
|||
case SmoothingType.Medium: |
|||
smoothParameters.smoothing = 0.5f; |
|||
smoothParameters.correction = 0.1f; |
|||
smoothParameters.prediction = 0.5f; |
|||
smoothParameters.jitterRadius = 0.1f; |
|||
smoothParameters.maxDeviationRadius = 0.1f; |
|||
break; |
|||
case SmoothingType.Aggressive: |
|||
smoothParameters.smoothing = 0.7f; |
|||
smoothParameters.correction = 0.3f; |
|||
smoothParameters.prediction = 1.0f; |
|||
smoothParameters.jitterRadius = 1.0f; |
|||
smoothParameters.maxDeviationRadius = 1.0f; |
|||
break; |
|||
|
|||
//case SmoothingType.Default:
|
|||
default: |
|||
smoothParameters.smoothing = 0.5f; |
|||
smoothParameters.correction = 0.5f; |
|||
smoothParameters.prediction = 0.5f; |
|||
smoothParameters.jitterRadius = 0.05f; |
|||
smoothParameters.maxDeviationRadius = 0.04f; |
|||
break; |
|||
} |
|||
|
|||
Reset(); |
|||
init = true; |
|||
} |
|||
|
|||
// Resets the filter to default values.
|
|||
public void Reset() |
|||
{ |
|||
KinectManager kinectManager = KinectManager.Instance; |
|||
int maxBodyCount = 10; // kinectManager.GetMaxBodyCount();
|
|||
int jointCount = kinectManager.GetJointCount(); |
|||
|
|||
//history = new JointHistoryData[kinectManager.GetMaxBodyCount(), kinectManager.GetJointCount()];
|
|||
history = new BodyHistoryData[maxBodyCount]; |
|||
for (int i = 0; i < maxBodyCount; i++) |
|||
{ |
|||
history[i] = new BodyHistoryData(jointCount); |
|||
} |
|||
|
|||
//Debug.Log("BodyCount: " + kinectManager.GetMaxBodyCount() + ", JointCount: " + kinectManager.GetJointCount());
|
|||
} |
|||
|
|||
//// Update the filter with a new frame of data and smooth.
|
|||
//public void UpdateFilter(ref KinectInterop.BodyData[] alTrackedBodies)
|
|||
//{
|
|||
// if (init == false)
|
|||
// {
|
|||
// Init(); // initialize with default parameters
|
|||
// }
|
|||
|
|||
// SmoothParameters tempSmoothingParams = new SmoothParameters();
|
|||
|
|||
// tempSmoothingParams.smoothing = smoothParameters.smoothing;
|
|||
// tempSmoothingParams.correction = smoothParameters.correction;
|
|||
// tempSmoothingParams.prediction = smoothParameters.prediction;
|
|||
|
|||
// int bodyCount = alTrackedBodies != null ? alTrackedBodies.Length : 0;
|
|||
// for (int bodyIndex = 0; bodyIndex < bodyCount; bodyIndex++)
|
|||
// {
|
|||
// if (alTrackedBodies[bodyIndex].bIsTracked)
|
|||
// {
|
|||
// FilterBodyJoints(ref alTrackedBodies[bodyIndex], /**bodyIndex*/ alTrackedBodies[bodyIndex].iBodyIndex, tempSmoothingParams);
|
|||
// }
|
|||
// }
|
|||
//}
|
|||
|
|||
// Update the filter with a new frame of data and smooth.
|
|||
public void UpdateFilter(ref KinectInterop.BodyData bodyData) |
|||
{ |
|||
if (!init) |
|||
{ |
|||
// initialize with by-default parameters
|
|||
Init(); |
|||
} |
|||
|
|||
if (smoothingType == SmoothingType.None) |
|||
return; |
|||
|
|||
SmoothParameters tempSmoothingParams = new SmoothParameters(); |
|||
tempSmoothingParams.smoothing = smoothParameters.smoothing; |
|||
tempSmoothingParams.correction = smoothParameters.correction; |
|||
tempSmoothingParams.prediction = smoothParameters.prediction; |
|||
|
|||
if (bodyData.bIsTracked) |
|||
{ |
|||
// get body index
|
|||
int bodyIndex = GetUserIndex(bodyData.liTrackingID); |
|||
if (bodyIndex < 0) |
|||
{ |
|||
bodyIndex = GetFreeIndex(); |
|||
if (bodyIndex >= 0) |
|||
history[bodyIndex].userId = bodyData.liTrackingID; |
|||
//Debug.Log("Created history for userId: " + history[bodyIndex].userId + ", index: " + bodyIndex + ", time: " + DateTime.UtcNow);
|
|||
} |
|||
|
|||
// filter
|
|||
if (bodyIndex >= 0) |
|||
{ |
|||
FilterBodyJoints(ref bodyData, bodyIndex, tempSmoothingParams); |
|||
} |
|||
} |
|||
|
|||
// free unused history
|
|||
//CleanUpUserHistory();
|
|||
} |
|||
|
|||
// Update the filter for all body joints
|
|||
private void FilterBodyJoints(ref KinectInterop.BodyData bodyData, int bodyIndex, SmoothParameters tempSmoothingParams) |
|||
{ |
|||
KinectManager kinectManager = KinectManager.Instance; |
|||
int jointCount = kinectManager.GetJointCount(); |
|||
long lastUpdateTime = history[bodyIndex].lastUpdateTime; |
|||
|
|||
for (int jointIndex = 0; jointIndex < jointCount; jointIndex++) |
|||
{ |
|||
//// If not tracked, we smooth a bit more by using a bigger jitter radius
|
|||
//// Always filter end joints highly as they are more noisy
|
|||
//if (bodyData.joint[jointIndex].trackingState != KinectInterop.TrackingState.Tracked ||
|
|||
// jointIndex == (int)KinectInterop.JointType.FootLeft || jointIndex == (int)KinectInterop.JointType.FootRight ||
|
|||
// jointIndex == (int)KinectInterop.JointType.HandLeft || jointIndex == (int)KinectInterop.JointType.HandRight ||
|
|||
// jointIndex == (int)KinectInterop.JointType.HandtipLeft || jointIndex == (int)KinectInterop.JointType.HandtipRight ||
|
|||
// jointIndex == (int)KinectInterop.JointType.ThumbLeft || jointIndex == (int)KinectInterop.JointType.ThumbRight)
|
|||
////|| jointIndex == (int)KinectInterop.JointType.Head)
|
|||
//{
|
|||
// tempSmoothingParams.jitterRadius = smoothParameters.jitterRadius * 2.0f;
|
|||
// tempSmoothingParams.maxDeviationRadius = smoothParameters.maxDeviationRadius * 2.0f;
|
|||
//}
|
|||
//else
|
|||
{ |
|||
tempSmoothingParams.jitterRadius = smoothParameters.jitterRadius; |
|||
tempSmoothingParams.maxDeviationRadius = smoothParameters.maxDeviationRadius; |
|||
} |
|||
|
|||
bodyData.joint[jointIndex].posVel = FilterJoint(bodyData.joint[jointIndex].posVel, bodyIndex, jointIndex, tempSmoothingParams); |
|||
} |
|||
|
|||
//Debug.Log(" updated vel history for userId: " + history[bodyIndex].userId + ", index: " + bodyIndex + ", time: " + history[bodyIndex].lastUpdateTime + " (" + lastUpdateTime + ")");
|
|||
} |
|||
|
|||
// Update the filter for one joint
|
|||
private Vector3 FilterJoint(Vector3 rawVelocity, int bodyIndex, int jointIndex, SmoothParameters smoothingParameters) |
|||
{ |
|||
Vector3 filteredVelocity; |
|||
Vector3 diffVec; |
|||
Vector3 trend; |
|||
float diffVal; |
|||
|
|||
Vector3 prevFilteredVelocity = history[bodyIndex].jointHistory[jointIndex].filteredVelocity; |
|||
Vector3 prevTrend = history[bodyIndex].jointHistory[jointIndex].trend; |
|||
Vector3 prevRawVelocity = history[bodyIndex].jointHistory[jointIndex].rawVelocity; |
|||
bool jointIsValid = (rawVelocity != Vector3.zero); |
|||
|
|||
// If joint is invalid, reset the filter
|
|||
if (!jointIsValid) |
|||
{ |
|||
history[bodyIndex].jointHistory[jointIndex].frameCount = 0; |
|||
} |
|||
|
|||
// Initial start values
|
|||
if (history[bodyIndex].jointHistory[jointIndex].frameCount == 0) |
|||
{ |
|||
filteredVelocity = rawVelocity; |
|||
trend = Vector3.zero; |
|||
} |
|||
else if (history[bodyIndex].jointHistory[jointIndex].frameCount == 1) |
|||
{ |
|||
filteredVelocity = (rawVelocity + prevRawVelocity) * 0.5f; |
|||
diffVec = filteredVelocity - prevFilteredVelocity; |
|||
trend = (diffVec * smoothingParameters.correction) + (prevTrend * (1.0f - smoothingParameters.correction)); |
|||
} |
|||
else |
|||
{ |
|||
// First apply jitter filter
|
|||
diffVec = rawVelocity - prevFilteredVelocity; |
|||
diffVal = Math.Abs(diffVec.magnitude); |
|||
|
|||
if (diffVal <= smoothingParameters.jitterRadius) |
|||
{ |
|||
filteredVelocity = (rawVelocity * (diffVal / smoothingParameters.jitterRadius)) + (prevFilteredVelocity * (1.0f - (diffVal / smoothingParameters.jitterRadius))); |
|||
} |
|||
else |
|||
{ |
|||
filteredVelocity = rawVelocity; |
|||
} |
|||
|
|||
// Now the double exponential smoothing filter
|
|||
filteredVelocity = (filteredVelocity * (1.0f - smoothingParameters.smoothing)) + ((prevFilteredVelocity + prevTrend) * smoothingParameters.smoothing); |
|||
|
|||
diffVec = filteredVelocity - prevFilteredVelocity; |
|||
trend = (diffVec * smoothingParameters.correction) + (prevTrend * (1.0f - smoothingParameters.correction)); |
|||
} |
|||
|
|||
// Predict into the future to reduce latency
|
|||
Vector3 predictedVelocity = filteredVelocity + (trend * smoothingParameters.prediction); |
|||
|
|||
// Check that we are not too far away from raw data
|
|||
diffVec = predictedVelocity - rawVelocity; |
|||
diffVal = Mathf.Abs(diffVec.magnitude); |
|||
|
|||
if (diffVal > smoothingParameters.maxDeviationRadius) |
|||
{ |
|||
predictedVelocity = (predictedVelocity * (smoothingParameters.maxDeviationRadius / diffVal)) + (rawVelocity * (1.0f - (smoothingParameters.maxDeviationRadius / diffVal))); |
|||
} |
|||
|
|||
// Save the data from this frame
|
|||
history[bodyIndex].jointHistory[jointIndex].rawVelocity = rawVelocity; |
|||
history[bodyIndex].jointHistory[jointIndex].filteredVelocity = filteredVelocity; |
|||
history[bodyIndex].jointHistory[jointIndex].trend = trend; |
|||
history[bodyIndex].jointHistory[jointIndex].frameCount++; |
|||
|
|||
DateTime dtNow = DateTime.UtcNow; |
|||
history[bodyIndex].lastUpdateTime = dtNow.Ticks; |
|||
|
|||
return predictedVelocity; |
|||
} |
|||
|
|||
|
|||
// returns the history index for the given user, or -1 if not found
|
|||
private int GetUserIndex(ulong userId) |
|||
{ |
|||
for (int i = 0; i < history.Length; i++) |
|||
{ |
|||
if (history[i].userId == userId) |
|||
return i; |
|||
} |
|||
|
|||
return -1; |
|||
} |
|||
|
|||
// returns the 1st free history index, or -1 if not found
|
|||
private int GetFreeIndex() |
|||
{ |
|||
for (int i = 0; i < history.Length; i++) |
|||
{ |
|||
if (history[i].userId == 0) |
|||
return i; |
|||
} |
|||
|
|||
return -1; |
|||
} |
|||
|
|||
// frees history indices that were unused for long time
|
|||
public void CleanUpUserHistory() |
|||
{ |
|||
DateTime dtNow = DateTime.UtcNow; |
|||
long timeNow = dtNow.Ticks; |
|||
|
|||
for (int i = 0; i < history.Length; i++) |
|||
{ |
|||
if (history[i].userId != 0 && (timeNow - history[i].lastUpdateTime) >= 10000000) |
|||
{ |
|||
//Debug.Log("Removed vel history for userId " + history[i].userId + ", index: " + i + ", time: " + dtNow + ", not used since: " + (timeNow - history[i].lastUpdateTime) + " ticks");
|
|||
|
|||
history[i].userId = 0; |
|||
history[i].lastUpdateTime = 0; |
|||
|
|||
for (int j = 0; j < history[i].jointHistory.Length; j++) |
|||
{ |
|||
history[i].jointHistory[j].frameCount = 0; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
|
|||
// body history data used by the filter
|
|||
private struct BodyHistoryData |
|||
{ |
|||
public ulong userId; |
|||
|
|||
public long lastUpdateTime; |
|||
|
|||
public VelocityHistoryData[] jointHistory; |
|||
|
|||
|
|||
public BodyHistoryData(int jointCount) |
|||
{ |
|||
userId = 0; |
|||
lastUpdateTime = 0; |
|||
jointHistory = new VelocityHistoryData[jointCount]; |
|||
} |
|||
} |
|||
|
|||
// velocity history data used by the filter
|
|||
private struct VelocityHistoryData |
|||
{ |
|||
// Gets or sets Historical Velocity.
|
|||
public Vector3 rawVelocity; |
|||
|
|||
// Gets or sets Historical Filtered Velocity.
|
|||
public Vector3 filteredVelocity; |
|||
|
|||
// Gets or sets Historical Trend.
|
|||
public Vector3 trend; |
|||
|
|||
// Gets or sets Historical FrameCount.
|
|||
public uint frameCount; |
|||
} |
|||
} |
|||
} |
@ -0,0 +1,12 @@ |
|||
fileFormatVersion: 2 |
|||
guid: 732d87a0d677e4845a26771e56b5c7d6 |
|||
timeCreated: 1491826911 |
|||
licenseType: Store |
|||
MonoImporter: |
|||
serializedVersion: 2 |
|||
defaultReferences: [] |
|||
executionOrder: 0 |
|||
icon: {instanceID: 0} |
|||
userData: |
|||
assetBundleName: |
|||
assetBundleVariant: |
@ -0,0 +1,302 @@ |
|||
using System; |
|||
|
|||
namespace AHRS |
|||
{ |
|||
/// <summary>
|
|||
/// MahonyAHRS class. Madgwick's implementation of Mayhony's AHRS algorithm.
|
|||
/// </summary>
|
|||
/// <remarks>
|
|||
/// See: http://www.x-io.co.uk/node/8#open_source_ahrs_and_imu_algorithms
|
|||
/// </remarks>
|
|||
public class MahonyAHRS |
|||
{ |
|||
/// <summary>
|
|||
/// Gets or sets the sample period.
|
|||
/// </summary>
|
|||
public float SamplePeriod { get; set; } |
|||
|
|||
/// <summary>
|
|||
/// Gets or sets the algorithm proportional gain.
|
|||
/// </summary>
|
|||
public float Kp { get; set; } |
|||
|
|||
/// <summary>
|
|||
/// Gets or sets the algorithm integral gain.
|
|||
/// </summary>
|
|||
public float Ki { get; set; } |
|||
|
|||
/// <summary>
|
|||
/// Gets or sets the Quaternion output.
|
|||
/// </summary>
|
|||
public float[] Quaternion { get; set; } |
|||
|
|||
/// <summary>
|
|||
/// Error squared.
|
|||
/// </summary>
|
|||
public float E2 { get; set; } |
|||
|
|||
/// <summary>
|
|||
/// Gets or sets the integral error.
|
|||
/// </summary>
|
|||
private float[] eInt { get; set; } |
|||
|
|||
/// <summary>
|
|||
/// Initializes a new instance of the <see cref="MadgwickAHRS"/> class.
|
|||
/// </summary>
|
|||
/// <param name="samplePeriod">
|
|||
/// Sample period.
|
|||
/// </param>
|
|||
public MahonyAHRS(float samplePeriod) |
|||
: this(samplePeriod, 1f, 0f) |
|||
{ |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Initializes a new instance of the <see cref="MadgwickAHRS"/> class.
|
|||
/// </summary>
|
|||
/// <param name="samplePeriod">
|
|||
/// Sample period.
|
|||
/// </param>
|
|||
/// <param name="kp">
|
|||
/// Algorithm proportional gain.
|
|||
/// </param>
|
|||
public MahonyAHRS(float samplePeriod, float kp) |
|||
: this(samplePeriod, kp, 0f) |
|||
{ |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Initializes a new instance of the <see cref="MadgwickAHRS"/> class.
|
|||
/// </summary>
|
|||
/// <param name="samplePeriod">
|
|||
/// Sample period.
|
|||
/// </param>
|
|||
/// <param name="kp">
|
|||
/// Algorithm proportional gain.
|
|||
/// </param>
|
|||
/// <param name="ki">
|
|||
/// Algorithm integral gain.
|
|||
/// </param>
|
|||
public MahonyAHRS(float samplePeriod, float kp, float ki) |
|||
{ |
|||
SamplePeriod = samplePeriod; |
|||
Kp = kp; |
|||
Ki = ki; |
|||
Quaternion = new float[] { 1f, 0f, 0f, 0f }; |
|||
eInt = new float[] { 0f, 0f, 0f }; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Algorithm AHRS update method. Requires only gyroscope and accelerometer data.
|
|||
/// </summary>
|
|||
/// <param name="gx">
|
|||
/// Gyroscope x axis measurement in radians/s.
|
|||
/// </param>
|
|||
/// <param name="gy">
|
|||
/// Gyroscope y axis measurement in radians/s.
|
|||
/// </param>
|
|||
/// <param name="gz">
|
|||
/// Gyroscope z axis measurement in radians/s.
|
|||
/// </param>
|
|||
/// <param name="ax">
|
|||
/// Accelerometer x axis measurement in any calibrated units.
|
|||
/// </param>
|
|||
/// <param name="ay">
|
|||
/// Accelerometer y axis measurement in any calibrated units.
|
|||
/// </param>
|
|||
/// <param name="az">
|
|||
/// Accelerometer z axis measurement in any calibrated units.
|
|||
/// </param>
|
|||
/// <param name="mx">
|
|||
/// Magnetometer x axis measurement in any calibrated units.
|
|||
/// </param>
|
|||
/// <param name="my">
|
|||
/// Magnetometer y axis measurement in any calibrated units.
|
|||
/// </param>
|
|||
/// <param name="mz">
|
|||
/// Magnetometer z axis measurement in any calibrated units.
|
|||
/// </param>
|
|||
/// <remarks>
|
|||
/// Optimised for minimal arithmetic.
|
|||
/// </remarks>
|
|||
public void Update(float gx, float gy, float gz, float ax, float ay, float az, float mx, float my, float mz) |
|||
{ |
|||
float q1 = Quaternion[0], q2 = Quaternion[1], q3 = Quaternion[2], q4 = Quaternion[3]; // short name local variable for readability
|
|||
float norm; |
|||
float hx, hy, bx, bz; |
|||
float vx, vy, vz, wx, wy, wz; |
|||
float ex, ey, ez; |
|||
float pa, pb, pc; |
|||
|
|||
// Auxiliary variables to avoid repeated arithmetic
|
|||
float q1q1 = q1 * q1; |
|||
float q1q2 = q1 * q2; |
|||
float q1q3 = q1 * q3; |
|||
float q1q4 = q1 * q4; |
|||
float q2q2 = q2 * q2; |
|||
float q2q3 = q2 * q3; |
|||
float q2q4 = q2 * q4; |
|||
float q3q3 = q3 * q3; |
|||
float q3q4 = q3 * q4; |
|||
float q4q4 = q4 * q4; |
|||
|
|||
// Normalise accelerometer measurement
|
|||
norm = (float)Math.Sqrt(ax * ax + ay * ay + az * az); |
|||
if (norm == 0f) return; // handle NaN
|
|||
norm = 1 / norm; // use reciprocal for division
|
|||
ax *= norm; |
|||
ay *= norm; |
|||
az *= norm; |
|||
|
|||
// Normalise magnetometer measurement
|
|||
norm = (float)Math.Sqrt(mx * mx + my * my + mz * mz); |
|||
if (norm == 0f) return; // handle NaN
|
|||
norm = 1 / norm; // use reciprocal for division
|
|||
mx *= norm; |
|||
my *= norm; |
|||
mz *= norm; |
|||
|
|||
// Reference direction of Earth's magnetic field
|
|||
hx = 2f * mx * (0.5f - q3q3 - q4q4) + 2f * my * (q2q3 - q1q4) + 2f * mz * (q2q4 + q1q3); |
|||
hy = 2f * mx * (q2q3 + q1q4) + 2f * my * (0.5f - q2q2 - q4q4) + 2f * mz * (q3q4 - q1q2); |
|||
bx = (float)Math.Sqrt((hx * hx) + (hy * hy)); |
|||
bz = 2f * mx * (q2q4 - q1q3) + 2f * my * (q3q4 + q1q2) + 2f * mz * (0.5f - q2q2 - q3q3); |
|||
|
|||
// Estimated direction of gravity and magnetic field
|
|||
vx = 2f * (q2q4 - q1q3); |
|||
vy = 2f * (q1q2 + q3q4); |
|||
vz = q1q1 - q2q2 - q3q3 + q4q4; |
|||
wx = 2f * bx * (0.5f - q3q3 - q4q4) + 2f * bz * (q2q4 - q1q3); |
|||
wy = 2f * bx * (q2q3 - q1q4) + 2f * bz * (q1q2 + q3q4); |
|||
wz = 2f * bx * (q1q3 + q2q4) + 2f * bz * (0.5f - q2q2 - q3q3); |
|||
|
|||
// Error is cross product between estimated direction and measured direction of gravity
|
|||
ex = (ay * vz - az * vy) + (my * wz - mz * wy); |
|||
ey = (az * vx - ax * vz) + (mz * wx - mx * wz); |
|||
ez = (ax * vy - ay * vx) + (mx * wy - my * wx); |
|||
if (Ki > 0f) |
|||
{ |
|||
eInt[0] += ex; // accumulate integral error
|
|||
eInt[1] += ey; |
|||
eInt[2] += ez; |
|||
} |
|||
else |
|||
{ |
|||
eInt[0] = 0.0f; // prevent integral wind up
|
|||
eInt[1] = 0.0f; |
|||
eInt[2] = 0.0f; |
|||
} |
|||
|
|||
// error squared
|
|||
E2 = ex * ex + ey * ey + ez * ez; |
|||
|
|||
// Apply feedback terms
|
|||
gx = gx + Kp * ex + Ki * eInt[0]; |
|||
gy = gy + Kp * ey + Ki * eInt[1]; |
|||
gz = gz + Kp * ez + Ki * eInt[2]; |
|||
|
|||
// Integrate rate of change of quaternion
|
|||
pa = q2; |
|||
pb = q3; |
|||
pc = q4; |
|||
q1 = q1 + (-q2 * gx - q3 * gy - q4 * gz) * (0.5f * SamplePeriod); |
|||
q2 = pa + (q1 * gx + pb * gz - pc * gy) * (0.5f * SamplePeriod); |
|||
q3 = pb + (q1 * gy - pa * gz + pc * gx) * (0.5f * SamplePeriod); |
|||
q4 = pc + (q1 * gz + pa * gy - pb * gx) * (0.5f * SamplePeriod); |
|||
|
|||
// Normalise quaternion
|
|||
norm = (float)Math.Sqrt(q1 * q1 + q2 * q2 + q3 * q3 + q4 * q4); |
|||
norm = 1.0f / norm; |
|||
Quaternion[0] = q1 * norm; |
|||
Quaternion[1] = q2 * norm; |
|||
Quaternion[2] = q3 * norm; |
|||
Quaternion[3] = q4 * norm; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Algorithm IMU update method. Requires only gyroscope and accelerometer data.
|
|||
/// </summary>
|
|||
/// <param name="gx">
|
|||
/// Gyroscope x axis measurement in radians/s.
|
|||
/// </param>
|
|||
/// <param name="gy">
|
|||
/// Gyroscope y axis measurement in radians/s.
|
|||
/// </param>
|
|||
/// <param name="gz">
|
|||
/// Gyroscope z axis measurement in radians/s.
|
|||
/// </param>
|
|||
/// <param name="ax">
|
|||
/// Accelerometer x axis measurement in any calibrated units.
|
|||
/// </param>
|
|||
/// <param name="ay">
|
|||
/// Accelerometer y axis measurement in any calibrated units.
|
|||
/// </param>
|
|||
/// <param name="az">
|
|||
/// Accelerometer z axis measurement in any calibrated units.
|
|||
/// </param>
|
|||
public void Update(float gx, float gy, float gz, float ax, float ay, float az) |
|||
{ |
|||
float q1 = Quaternion[0], q2 = Quaternion[1], q3 = Quaternion[2], q4 = Quaternion[3]; // short name local variable for readability
|
|||
float norm; |
|||
float vx, vy, vz; |
|||
float ex, ey, ez; |
|||
float pa, pb, pc; |
|||
|
|||
// Normalise accelerometer measurement
|
|||
norm = (float)Math.Sqrt(ax * ax + ay * ay + az * az); |
|||
if (norm == 0f) return; // handle NaN
|
|||
norm = 1 / norm; // use reciprocal for division
|
|||
ax *= norm; |
|||
ay *= norm; |
|||
az *= norm; |
|||
|
|||
// Estimated direction of gravity
|
|||
vx = 2.0f * (q2 * q4 - q1 * q3); |
|||
vy = 2.0f * (q1 * q2 + q3 * q4); |
|||
vz = q1 * q1 - q2 * q2 - q3 * q3 + q4 * q4; |
|||
|
|||
// Error is cross product between estimated direction and measured direction of gravity
|
|||
ex = (ay * vz - az * vy); |
|||
ey = (az * vx - ax * vz); |
|||
ez = (ax * vy - ay * vx); |
|||
if (Ki > 0f) |
|||
{ |
|||
eInt[0] += ex; // accumulate integral error
|
|||
eInt[1] += ey; |
|||
eInt[2] += ez; |
|||
} |
|||
else |
|||
{ |
|||
eInt[0] = 0.0f; // prevent integral wind up
|
|||
eInt[1] = 0.0f; |
|||
eInt[2] = 0.0f; |
|||
} |
|||
|
|||
// error squared
|
|||
E2 = ex * ex + ey * ey + ez * ez; |
|||
|
|||
// Apply feedback terms
|
|||
gx = gx + Kp * ex + Ki * eInt[0]; |
|||
gy = gy + Kp * ey + Ki * eInt[1]; |
|||
gz = gz + Kp * ez + Ki * eInt[2]; |
|||
|
|||
// Integrate rate of change of quaternion
|
|||
pa = q2; |
|||
pb = q3; |
|||
pc = q4; |
|||
q1 = q1 + (-q2 * gx - q3 * gy - q4 * gz) * (0.5f * SamplePeriod); |
|||
q2 = pa + (q1 * gx + pb * gz - pc * gy) * (0.5f * SamplePeriod); |
|||
q3 = pb + (q1 * gy - pa * gz + pc * gx) * (0.5f * SamplePeriod); |
|||
q4 = pc + (q1 * gz + pa * gy - pb * gx) * (0.5f * SamplePeriod); |
|||
|
|||
// Normalise quaternion
|
|||
norm = (float)Math.Sqrt(q1 * q1 + q2 * q2 + q3 * q3 + q4 * q4); |
|||
norm = 1.0f / norm; |
|||
Quaternion[0] = q1 * norm; |
|||
Quaternion[1] = q2 * norm; |
|||
Quaternion[2] = q3 * norm; |
|||
Quaternion[3] = q4 * norm; |
|||
} |
|||
} |
|||
} |
|||
|
@ -0,0 +1,11 @@ |
|||
fileFormatVersion: 2 |
|||
guid: 75a75a12bb874514596c7fc355cd0c3f |
|||
MonoImporter: |
|||
externalObjects: {} |
|||
serializedVersion: 2 |
|||
defaultReferences: [] |
|||
executionOrder: 0 |
|||
icon: {instanceID: 0} |
|||
userData: |
|||
assetBundleName: |
|||
assetBundleVariant: |
@ -0,0 +1,80 @@ |
|||
using System.Collections; |
|||
using System.Collections.Generic; |
|||
using UnityEngine; |
|||
using com.rfilkov.kinect; |
|||
|
|||
|
|||
namespace com.rfilkov.components |
|||
{ |
|||
/// <summary>
|
|||
/// This component makes the game object follow the position and rotation of the sensor.
|
|||
/// </summary>
|
|||
public class FollowSensorTransform : MonoBehaviour |
|||
{ |
|||
[Tooltip("Depth sensor index - 0 is the 1st one, 1 - the 2nd one, etc.")] |
|||
public int sensorIndex = 0; |
|||
|
|||
[Tooltip("Smooth factor used for the game object movement and rotation.")] |
|||
public float smoothFactor = 0f; |
|||
|
|||
[Tooltip("Whether to follow the sensor's depth or color camera pose.")] |
|||
public ReferencePose referencePose = ReferencePose.DepthCameraPose; |
|||
public enum ReferencePose : int { DepthCameraPose = 0, ColorCameraPose = 1 }; |
|||
|
|||
|
|||
// reference to the KinectManager
|
|||
private KinectManager kinectManager = null; |
|||
private KinectInterop.SensorData sensorData = null; |
|||
|
|||
// sensor position and rotation
|
|||
Vector3 sensorWorldPos = Vector3.zero; |
|||
Quaternion sensorWorldRot = Quaternion.identity; |
|||
|
|||
|
|||
void Start() |
|||
{ |
|||
// get reference to KinectManager
|
|||
kinectManager = KinectManager.Instance; |
|||
sensorData = kinectManager ? kinectManager.GetSensorData(sensorIndex) : null; |
|||
} |
|||
|
|||
void Update() |
|||
{ |
|||
if(kinectManager && kinectManager.IsInitialized()) |
|||
{ |
|||
Transform sensorTrans = kinectManager.GetSensorTransform(sensorIndex); |
|||
|
|||
if(sensorTrans != null) |
|||
{ |
|||
sensorWorldPos = sensorTrans.position; |
|||
sensorWorldRot = sensorTrans.rotation; |
|||
|
|||
if (referencePose != ReferencePose.DepthCameraPose && sensorData != null && sensorData.sensorInterface != null) |
|||
{ |
|||
Matrix4x4 sensorTransMat = Matrix4x4.identity; |
|||
sensorTransMat.SetTRS(sensorTrans.position, sensorTrans.rotation, Vector3.one); |
|||
|
|||
Matrix4x4 sensorToRefMat = sensorData.sensorInterface.GetDepthToColorCameraMatrix(); |
|||
|
|||
sensorTransMat = sensorTransMat * sensorToRefMat; |
|||
sensorWorldPos = sensorTransMat.GetColumn(3); |
|||
sensorWorldRot = sensorTransMat.rotation; |
|||
} |
|||
|
|||
} |
|||
|
|||
if (smoothFactor != 0f) |
|||
{ |
|||
transform.position = Vector3.Lerp(transform.position, sensorWorldPos, smoothFactor * Time.deltaTime); |
|||
transform.rotation = Quaternion.Slerp(transform.rotation, sensorWorldRot, smoothFactor * Time.deltaTime); |
|||
} |
|||
else |
|||
{ |
|||
transform.position = sensorWorldPos; |
|||
transform.rotation = sensorWorldRot; |
|||
} |
|||
} |
|||
} |
|||
|
|||
} |
|||
} |
@ -0,0 +1,11 @@ |
|||
fileFormatVersion: 2 |
|||
guid: 9a17e9eb7ba629243bfc955c820d04a1 |
|||
MonoImporter: |
|||
externalObjects: {} |
|||
serializedVersion: 2 |
|||
defaultReferences: [] |
|||
executionOrder: 0 |
|||
icon: {instanceID: 0} |
|||
userData: |
|||
assetBundleName: |
|||
assetBundleVariant: |
@ -0,0 +1,140 @@ |
|||
using UnityEngine; |
|||
using System.Collections; |
|||
using com.rfilkov.kinect; |
|||
|
|||
|
|||
namespace com.rfilkov.components |
|||
{ |
|||
/// <summary>
|
|||
/// FollowUserJointPose makes the game object's transform follow the given user's joint pose.
|
|||
/// </summary>
|
|||
public class FollowUserJointPose : MonoBehaviour |
|||
{ |
|||
[Tooltip("Depth sensor index - 0 is the 1st one, 1 - the 2nd one, etc. -1 means the sensor doesn't matter")] |
|||
private int sensorIndex = -1; |
|||
|
|||
[Tooltip("Index of the player, tracked by this component. 0 means the 1st player, 1 - the 2nd one, 2 - the 3rd one, etc.")] |
|||
public int playerIndex = 0; |
|||
|
|||
[Tooltip("The sensor's joint we want to follow.")] |
|||
public KinectInterop.JointType followJoint = KinectInterop.JointType.Head; |
|||
|
|||
[Tooltip("Whether the joint view is mirrored or not.")] |
|||
public bool mirroredView = false; |
|||
|
|||
[Tooltip("Whether to move the object's transform.")] |
|||
public bool moveTransform = true; |
|||
|
|||
[Tooltip("Whether to rotate the object's transform.")] |
|||
public bool rotateTransform = true; |
|||
|
|||
[Tooltip("Scene object that will be used to represent the sensor's position and rotation in the scene.")] |
|||
public Transform sensorTransform; |
|||
|
|||
[Tooltip("Offset of the object to the joint's position.")] |
|||
public Vector3 positionOffset = Vector3.zero; |
|||
|
|||
[Tooltip("Scale factor of the joint position.")] |
|||
public Vector3 motionScale = Vector3.one; |
|||
|
|||
[Tooltip("Scale factor of the joint rotation.")] |
|||
private Vector3 rotationFactor = Vector3.zero; |
|||
|
|||
[Tooltip("Smooth factor used for object's position and rotation smoothing.")] |
|||
public float smoothFactor = 10f; |
|||
|
|||
|
|||
private KinectManager kinectManager = null; |
|||
private Quaternion initialRotation = Quaternion.identity; |
|||
|
|||
private Vector3 vPosJoint = Vector3.zero; |
|||
private Quaternion qRotJoint = Quaternion.identity; |
|||
|
|||
|
|||
void Start() |
|||
{ |
|||
kinectManager = KinectManager.Instance; |
|||
|
|||
initialRotation = transform.rotation; |
|||
//initialRotation = mirroredView ? Quaternion.Euler(0f, 180f, 0f) : Quaternion.identity;
|
|||
} |
|||
|
|||
void Update() |
|||
{ |
|||
if (kinectManager && kinectManager.IsInitialized()) |
|||
{ |
|||
if (sensorIndex >= 0 || kinectManager.IsUserDetected(playerIndex)) |
|||
{ |
|||
ulong userId = sensorIndex < 0 ? kinectManager.GetUserIdByIndex(playerIndex) : (ulong)playerIndex; |
|||
|
|||
if (sensorIndex >= 0 || kinectManager.IsJointTracked(userId, followJoint)) |
|||
{ |
|||
if (sensorTransform != null) |
|||
{ |
|||
if (sensorIndex < 0) |
|||
vPosJoint = kinectManager.GetJointKinectPosition(userId, followJoint, true); |
|||
else |
|||
vPosJoint = kinectManager.GetSensorJointKinectPosition(sensorIndex, (int)userId, followJoint, true); |
|||
} |
|||
else |
|||
{ |
|||
if (sensorIndex < 0) |
|||
vPosJoint = kinectManager.GetJointPosition(userId, followJoint); |
|||
else |
|||
vPosJoint = kinectManager.GetSensorJointPosition(sensorIndex, (int)userId, followJoint); |
|||
} |
|||
|
|||
if (positionOffset != Vector3.zero) |
|||
{ |
|||
vPosJoint += positionOffset; |
|||
} |
|||
|
|||
if (sensorTransform) |
|||
{ |
|||
vPosJoint = sensorTransform.TransformPoint(vPosJoint); |
|||
} |
|||
|
|||
if(motionScale != Vector3.one) |
|||
{ |
|||
vPosJoint = new Vector3(vPosJoint.x * motionScale.x, vPosJoint.y * motionScale.y, vPosJoint.z * motionScale.z); |
|||
} |
|||
|
|||
if (sensorIndex < 0) |
|||
qRotJoint = kinectManager.GetJointOrientation(userId, followJoint, !mirroredView); |
|||
else |
|||
qRotJoint = kinectManager.GetSensorJointOrientation(sensorIndex, (int)userId, followJoint, !mirroredView); |
|||
|
|||
qRotJoint = initialRotation * qRotJoint; |
|||
|
|||
if(rotationFactor != Vector3.zero) |
|||
{ |
|||
qRotJoint = Quaternion.Euler(rotationFactor) * qRotJoint; |
|||
} |
|||
|
|||
if (moveTransform || rotateTransform) |
|||
{ |
|||
if (smoothFactor != 0f) |
|||
{ |
|||
if(moveTransform) |
|||
transform.position = Vector3.Lerp(transform.position, vPosJoint, smoothFactor * Time.deltaTime); |
|||
if(rotateTransform) |
|||
transform.rotation = Quaternion.Slerp(transform.rotation, qRotJoint, smoothFactor * Time.deltaTime); |
|||
} |
|||
else |
|||
{ |
|||
if(moveTransform) |
|||
transform.position = vPosJoint; |
|||
if(rotateTransform) |
|||
transform.rotation = qRotJoint; |
|||
} |
|||
|
|||
} |
|||
} |
|||
|
|||
} |
|||
} |
|||
} |
|||
|
|||
} |
|||
} |
|||
|
@ -0,0 +1,8 @@ |
|||
fileFormatVersion: 2 |
|||
guid: c98460a12114096448a40a69ef6b30e8 |
|||
MonoImporter: |
|||
serializedVersion: 2 |
|||
defaultReferences: [] |
|||
executionOrder: 0 |
|||
icon: {instanceID: 0} |
|||
userData: |
@ -0,0 +1,311 @@ |
|||
using System.Collections; |
|||
using System.Collections.Generic; |
|||
using UnityEngine; |
|||
using com.rfilkov.kinect; |
|||
|
|||
|
|||
namespace com.rfilkov.components |
|||
{ |
|||
/// <summary>
|
|||
/// ForegroundBlendRenderer provides volumetric rendering and lighting of the real environment, filtered by the background-removal manager.
|
|||
/// </summary>
|
|||
public class ForegroundBlendRenderer : MonoBehaviour |
|||
{ |
|||
[Tooltip("Reference to background removal manager. If left to None, it looks up the first available BR-manager in the scene.")] |
|||
public BackgroundRemovalManager backgroundRemovalManager = null; |
|||
|
|||
[Tooltip("Depth value in meters, used for invalid depth points.")] |
|||
public float invalidDepthValue = 0f; |
|||
|
|||
[Tooltip("Whether to maximize the rendered object on the screen, or not.")] |
|||
public bool maximizeOnScreen = true; |
|||
|
|||
[Tooltip("Whether to apply per-pixel lighting on the foreground, or not.")] |
|||
public bool applyLighting = false; |
|||
|
|||
[Tooltip("Camera used to scale the mesh, to fill the camera's background. If left empty, it will default to the main camera in the scene.")] |
|||
public Camera foregroundCamera; |
|||
|
|||
[Tooltip("Background image (if any) that needs to be overlayed by this blend renderer.")] |
|||
public UnityEngine.UI.RawImage backgroundImage; |
|||
|
|||
|
|||
// references to KM and data
|
|||
private KinectManager kinectManager = null; |
|||
private KinectInterop.SensorData sensorData = null; |
|||
private DepthSensorBase sensorInt = null; |
|||
private Material matRenderer = null; |
|||
|
|||
// depth image buffer (in depth camera resolution)
|
|||
private ComputeBuffer depthImageBuffer = null; |
|||
|
|||
// textures
|
|||
private Texture alphaTex = null; |
|||
private Texture colorTex = null; |
|||
|
|||
// lighting
|
|||
private FragmentLighting lighting = new FragmentLighting(); |
|||
|
|||
// saved screen width & height
|
|||
private int lastScreenW = 0; |
|||
private int lastScreenH = 0; |
|||
private int lastColorW = 0; |
|||
private int lastColorH = 0; |
|||
private float lastAnchorPos = 0f; |
|||
private Vector3 initialScale = Vector3.one; |
|||
|
|||
// distances
|
|||
private float distToBackImage = 0f; |
|||
private float distToTransform = 0f; |
|||
|
|||
|
|||
void Start() |
|||
{ |
|||
kinectManager = KinectManager.Instance; |
|||
initialScale = transform.localScale; |
|||
|
|||
if (backgroundRemovalManager == null) |
|||
{ |
|||
backgroundRemovalManager = FindObjectOfType<BackgroundRemovalManager>(); |
|||
} |
|||
|
|||
// get distance to back image
|
|||
if(backgroundImage) |
|||
{ |
|||
Canvas canvas = backgroundImage.canvas; |
|||
|
|||
if (canvas.renderMode == RenderMode.ScreenSpaceCamera) |
|||
distToBackImage = canvas.planeDistance; |
|||
else |
|||
distToBackImage = 0f; |
|||
} |
|||
|
|||
// get distance to transform
|
|||
distToTransform = transform.localPosition.z; |
|||
|
|||
// set renderer material
|
|||
Renderer meshRenderer = GetComponent<Renderer>(); |
|||
if (meshRenderer) |
|||
{ |
|||
Shader blendShader = Shader.Find("Kinect/ForegroundBlendShader"); |
|||
if(blendShader != null) |
|||
{ |
|||
matRenderer = new Material(blendShader); |
|||
meshRenderer.material = matRenderer; |
|||
} |
|||
} |
|||
|
|||
// get sensor data
|
|||
if (kinectManager && kinectManager.IsInitialized() && backgroundRemovalManager && backgroundRemovalManager.enabled) |
|||
{ |
|||
sensorData = kinectManager.GetSensorData(backgroundRemovalManager.sensorIndex); |
|||
sensorInt = sensorData != null ? (DepthSensorBase)sensorData.sensorInterface : null; |
|||
} |
|||
|
|||
if (foregroundCamera == null) |
|||
{ |
|||
foregroundCamera = Camera.main; |
|||
} |
|||
|
|||
// find scene lights
|
|||
Light[] sceneLights = GameObject.FindObjectsOfType<Light>(); |
|||
lighting.SetLightsAndBounds(sceneLights, transform.position, new Vector3(20f, 20f, 20f)); |
|||
|
|||
//Debug.Log("sceneLights: " + sceneLights.Length);
|
|||
//for(int i = 0; i < sceneLights.Length; i++)
|
|||
//{
|
|||
// Debug.Log(i.ToString() + " - " + sceneLights[i].name + " - " + sceneLights[i].type);
|
|||
//}
|
|||
} |
|||
|
|||
|
|||
void OnDestroy() |
|||
{ |
|||
if (sensorData != null && sensorData.colorDepthBuffer != null) |
|||
{ |
|||
sensorData.colorDepthBuffer.Release(); |
|||
sensorData.colorDepthBuffer = null; |
|||
} |
|||
|
|||
if (depthImageBuffer != null) |
|||
{ |
|||
//depthImageCopy = null;
|
|||
|
|||
depthImageBuffer.Release(); |
|||
depthImageBuffer = null; |
|||
} |
|||
|
|||
// release lighting resources
|
|||
lighting.ReleaseResources(); |
|||
} |
|||
|
|||
|
|||
void Update() |
|||
{ |
|||
if (matRenderer == null || sensorData == null || sensorInt == null) |
|||
return; |
|||
|
|||
if(alphaTex == null || alphaTex.width != sensorData.colorImageWidth || alphaTex.height != sensorData.colorImageHeight) |
|||
{ |
|||
// alpha texture
|
|||
alphaTex = backgroundRemovalManager.GetAlphaTex(); |
|||
|
|||
if(alphaTex != null) |
|||
{ |
|||
matRenderer.SetTexture("_AlphaTex", alphaTex); |
|||
} |
|||
} |
|||
|
|||
if(colorTex == null || colorTex.width != sensorData.colorImageWidth || colorTex.height != sensorData.colorImageHeight) |
|||
{ |
|||
// color texture
|
|||
colorTex = !backgroundRemovalManager.computeAlphaMaskOnly ? backgroundRemovalManager.GetForegroundTex() : alphaTex; // sensorInt.pointCloudColorTexture
|
|||
|
|||
if (colorTex != null) |
|||
{ |
|||
matRenderer.SetInt("_TexResX", colorTex.width); |
|||
matRenderer.SetInt("_TexResY", colorTex.height); |
|||
|
|||
matRenderer.SetTexture("_ColorTex", colorTex); |
|||
} |
|||
} |
|||
|
|||
if (colorTex == null || alphaTex == null /**|| foregroundCamera == null*/) |
|||
return; |
|||
|
|||
if (sensorInt.pointCloudResolution == DepthSensorBase.PointCloudResolution.DepthCameraResolution) |
|||
{ |
|||
int depthBufferLength = sensorData.depthImageWidth * sensorData.depthImageHeight / 2; |
|||
if (depthImageBuffer == null || depthImageBuffer.count != depthBufferLength) |
|||
{ |
|||
//int depthImageLength = sensorData.depthImageWidth * sensorData.depthImageHeight;
|
|||
//depthImageCopy = new ushort[depthImageLength];
|
|||
|
|||
depthImageBuffer = KinectInterop.CreateComputeBuffer(depthImageBuffer, depthBufferLength, sizeof(uint)); |
|||
matRenderer.SetBuffer("_DepthMap", depthImageBuffer); |
|||
//Debug.Log("Created depthImageBuffer with len: " + depthBufferLength);
|
|||
} |
|||
|
|||
if (depthImageBuffer != null && sensorData.depthImage != null) |
|||
{ |
|||
//KinectInterop.CopyBytes(sensorData.depthImage, sizeof(ushort), depthImageCopy, sizeof(ushort));
|
|||
KinectInterop.SetComputeBufferData(depthImageBuffer, sensorData.depthImage, depthBufferLength, sizeof(uint)); |
|||
} |
|||
|
|||
//Debug.Log("ForegroundBlendRenderer DepthFrameTime: " + lastDepthFrameTime);
|
|||
} |
|||
else |
|||
{ |
|||
int bufferLength = sensorData.colorImageWidth * sensorData.colorImageHeight / 2; |
|||
if (sensorData.colorDepthBuffer == null || sensorData.colorDepthBuffer.count != bufferLength) |
|||
{ |
|||
sensorData.colorDepthBuffer = new ComputeBuffer(bufferLength, sizeof(uint)); |
|||
matRenderer.SetBuffer("_DepthMap", sensorData.colorDepthBuffer); |
|||
//Debug.Log("Created colorDepthBuffer with len: " + bufferLength);
|
|||
} |
|||
|
|||
//Debug.Log("ForegroundBlendRenderer ColorDepthBufferTime: " + sensorData.lastColorDepthBufferTime);
|
|||
} |
|||
|
|||
matRenderer.SetFloat("_DepthDistance", 0f); |
|||
matRenderer.SetFloat("_InvDepthVal", invalidDepthValue); |
|||
|
|||
int curScreenW = foregroundCamera ? foregroundCamera.pixelWidth : Screen.width; |
|||
int curScreenH = foregroundCamera ? foregroundCamera.pixelHeight : Screen.height; |
|||
if (lastScreenW != curScreenW || lastScreenH != curScreenH || lastColorW != sensorData.colorImageWidth || lastColorH != sensorData.colorImageHeight) |
|||
{ |
|||
ScaleRendererTransform(curScreenW, curScreenH); |
|||
} |
|||
|
|||
Vector2 anchorPos = backgroundImage ? backgroundImage.rectTransform.anchoredPosition : Vector2.zero; |
|||
float curAnchorPos = anchorPos.x + anchorPos.y; // Mathf.Abs(anchorPos.x) + Mathf.Abs(anchorPos.y);
|
|||
if (Mathf.Abs(curAnchorPos - lastAnchorPos) >= 20f) |
|||
{ |
|||
//Debug.Log("anchorPos: " + anchorPos + ", curAnchorPos: " + curAnchorPos + ", lastAnchorPos: " + lastAnchorPos + ", diff: " + Mathf.Abs(curAnchorPos - lastAnchorPos));
|
|||
CenterRendererTransform(anchorPos, curAnchorPos); |
|||
} |
|||
|
|||
// update lighting parameters
|
|||
lighting.UpdateLighting(matRenderer, applyLighting); |
|||
} |
|||
|
|||
// scales the renderer's transform properly
|
|||
private void ScaleRendererTransform(int curScreenW, int curScreenH) |
|||
{ |
|||
lastScreenW = curScreenW; |
|||
lastScreenH = curScreenH; |
|||
lastColorW = sensorData.colorImageWidth; |
|||
lastColorH = sensorData.colorImageHeight; |
|||
|
|||
Vector3 localScale = Vector3.one; // transform.localScale;
|
|||
|
|||
if (maximizeOnScreen && foregroundCamera) |
|||
{ |
|||
float objectZ = distToTransform; // transform.localPosition.z; // the transform should be a child of the camera
|
|||
float screenW = foregroundCamera.pixelWidth; |
|||
float screenH = foregroundCamera.pixelHeight; |
|||
|
|||
if (backgroundImage) |
|||
{ |
|||
PortraitBackground portraitBack = backgroundImage.gameObject.GetComponent<PortraitBackground>(); |
|||
|
|||
if (portraitBack != null) |
|||
{ |
|||
Rect backRect = portraitBack.GetBackgroundRect(); |
|||
screenW = backRect.width; |
|||
screenH = backRect.height; |
|||
} |
|||
} |
|||
|
|||
Vector3 vLeft = foregroundCamera.ScreenToWorldPoint(new Vector3(0f, screenH / 2f, objectZ)); |
|||
Vector3 vRight = foregroundCamera.ScreenToWorldPoint(new Vector3(screenW, screenH / 2f, objectZ)); |
|||
float distLeftRight = (vRight - vLeft).magnitude; |
|||
|
|||
Vector3 vBottom = foregroundCamera.ScreenToWorldPoint(new Vector3(screenW / 2f, 0f, objectZ)); |
|||
Vector3 vTop = foregroundCamera.ScreenToWorldPoint(new Vector3(screenW / 2f, screenH, objectZ)); |
|||
float distBottomTop = (vTop - vBottom).magnitude; |
|||
|
|||
localScale.x = distLeftRight / initialScale.x; |
|||
localScale.y = distBottomTop / initialScale.y; |
|||
//Debug.Log("ForegroundRenderer scale: " + localScale + ", screenW: " + screenW + ", screenH: " + screenH + ", objZ: " + objectZ +
|
|||
// "\nleft: " + vLeft + ", right: " + vRight + ", bottom: " + vBottom + ", vTop: " + vTop +
|
|||
// "\ndH: " + distLeftRight + ", dV: " + distBottomTop + ", initialScale: " + initialScale);
|
|||
} |
|||
|
|||
// scale according to color-tex resolution
|
|||
//localScale.y = localScale.x * colorTex.height / colorTex.width;
|
|||
|
|||
// apply color image scale
|
|||
Vector3 colorImageScale = kinectManager.GetColorImageScale(backgroundRemovalManager.sensorIndex); |
|||
if (colorImageScale.x < 0f) |
|||
localScale.x = -localScale.x; |
|||
if (colorImageScale.y < 0f) |
|||
localScale.y = -localScale.y; |
|||
|
|||
transform.localScale = localScale; |
|||
} |
|||
|
|||
// centers the renderer's transform, according to the background image
|
|||
private void CenterRendererTransform(Vector2 anchorPos, float curAnchorPos) |
|||
{ |
|||
lastAnchorPos = curAnchorPos; |
|||
|
|||
if (foregroundCamera && distToBackImage > 0f) |
|||
{ |
|||
float objectZ = distToTransform; // transform.localPosition.z; // the transform should be a child of the camera
|
|||
float screenW = sensorData.colorImageWidth; // foregroundCamera.pixelWidth;
|
|||
float screenH = sensorData.colorImageHeight; // foregroundCamera.pixelHeight;
|
|||
|
|||
Vector2 screenCenter = new Vector2(screenW / 2f, screenH / 2f); |
|||
Vector2 anchorScaled = new Vector2(anchorPos.x * distToTransform / distToBackImage, anchorPos.y * distToTransform / distToBackImage); |
|||
Vector3 vCenter = foregroundCamera.ScreenToWorldPoint(new Vector3(screenCenter.x + anchorScaled.x, screenCenter.y + anchorScaled.y, objectZ)); |
|||
transform.position = vCenter; |
|||
|
|||
//Vector3 vLocalPos = transform.localPosition;
|
|||
//string sLocalPos = string.Format("({0:F3}, {1:F3}, {2:F3})", vLocalPos.x, vLocalPos.y, vLocalPos.z);
|
|||
//Debug.Log("ForegroundRenderer anchor: " + anchorPos + ", screenW: " + screenW + ", screenH: " + screenH + ", objZ: " + objectZ + ", localPos: " + sLocalPos);
|
|||
} |
|||
} |
|||
|
|||
} |
|||
} |
@ -0,0 +1,11 @@ |
|||
fileFormatVersion: 2 |
|||
guid: fd5b5453b69e67b43b1c57c4d95ccada |
|||
MonoImporter: |
|||
externalObjects: {} |
|||
serializedVersion: 2 |
|||
defaultReferences: [] |
|||
executionOrder: 0 |
|||
icon: {instanceID: 0} |
|||
userData: |
|||
assetBundleName: |
|||
assetBundleVariant: |
@ -0,0 +1,62 @@ |
|||
using UnityEngine; |
|||
using System.Collections; |
|||
using UnityEngine.UI; |
|||
using com.rfilkov.kinect; |
|||
|
|||
|
|||
namespace com.rfilkov.components |
|||
{ |
|||
/// <summary>
|
|||
/// ForegroundToRawImage sets the texture of the RawImage-component to be the BRM's foreground texture.
|
|||
/// </summary>
|
|||
public class ForegroundToRawImage : MonoBehaviour |
|||
{ |
|||
private RawImage rawImage; |
|||
private KinectManager kinectManager = null; |
|||
private BackgroundRemovalManager backManager = null; |
|||
|
|||
|
|||
void Start() |
|||
{ |
|||
rawImage = GetComponent<RawImage>(); |
|||
|
|||
kinectManager = KinectManager.Instance; |
|||
backManager = FindObjectOfType<BackgroundRemovalManager>(); |
|||
} |
|||
|
|||
|
|||
void Update() |
|||
{ |
|||
if (rawImage && rawImage.texture == null) |
|||
{ |
|||
if (kinectManager && backManager && backManager.enabled /**&& backManager.IsBackgroundRemovalInitialized()*/) |
|||
{ |
|||
rawImage.texture = backManager.GetForegroundTex(); // user's foreground texture
|
|||
rawImage.rectTransform.localScale = kinectManager.GetColorImageScale(backManager.sensorIndex); |
|||
rawImage.color = Color.white; |
|||
|
|||
} |
|||
} |
|||
//else if (rawImage && rawImage.texture != null)
|
|||
//{
|
|||
// if (KinectManager.Instance == null)
|
|||
// {
|
|||
// rawImage.texture = null;
|
|||
// rawImage.color = Color.clear;
|
|||
// }
|
|||
//}
|
|||
} |
|||
|
|||
|
|||
void OnApplicationPause(bool isPaused) |
|||
{ |
|||
// fix for app pause & restore (UWP)
|
|||
if (isPaused && rawImage && rawImage.texture != null) |
|||
{ |
|||
rawImage.texture = null; |
|||
rawImage.color = Color.clear; |
|||
} |
|||
} |
|||
|
|||
} |
|||
} |
@ -0,0 +1,12 @@ |
|||
fileFormatVersion: 2 |
|||
guid: 0ca948b232bd1ce48b254bb84048cbab |
|||
timeCreated: 1505645515 |
|||
licenseType: Store |
|||
MonoImporter: |
|||
serializedVersion: 2 |
|||
defaultReferences: [] |
|||
executionOrder: 0 |
|||
icon: {instanceID: 0} |
|||
userData: |
|||
assetBundleName: |
|||
assetBundleVariant: |
@ -0,0 +1,80 @@ |
|||
using UnityEngine; |
|||
using System.Collections; |
|||
using com.rfilkov.kinect; |
|||
|
|||
|
|||
namespace com.rfilkov.components |
|||
{ |
|||
/// <summary>
|
|||
/// ForegroundToRenderer sets the texture of the Renderer-component to be the BRM's foreground texture.
|
|||
/// </summary>
|
|||
public class ForegroundToRenderer : MonoBehaviour |
|||
{ |
|||
[Tooltip("Reference to background removal manager. If left to None, it looks up the first available BR-manager in the scene.")] |
|||
public BackgroundRemovalManager backgroundRemovalManager = null; |
|||
|
|||
|
|||
// component references
|
|||
private Renderer thisRenderer = null; |
|||
private KinectManager kinectManager = null; |
|||
|
|||
|
|||
void Start() |
|||
{ |
|||
thisRenderer = GetComponent<Renderer>(); |
|||
kinectManager = KinectManager.Instance; |
|||
|
|||
if(backgroundRemovalManager == null) |
|||
{ |
|||
backgroundRemovalManager = FindObjectOfType<BackgroundRemovalManager>(); |
|||
} |
|||
|
|||
if (kinectManager && kinectManager.IsInitialized() && backgroundRemovalManager && backgroundRemovalManager.enabled) |
|||
{ |
|||
Vector3 localScale = transform.localScale; |
|||
localScale.z = localScale.x * kinectManager.GetColorImageHeight(backgroundRemovalManager.sensorIndex) / kinectManager.GetColorImageWidth(backgroundRemovalManager.sensorIndex); |
|||
//localScale.x = -localScale.x;
|
|||
|
|||
// apply color image scale
|
|||
Vector3 colorImageScale = kinectManager.GetColorImageScale(backgroundRemovalManager.sensorIndex); |
|||
if (colorImageScale.x > 0f) |
|||
localScale.x = -localScale.x; |
|||
if (colorImageScale.y > 0f) |
|||
localScale.z = -localScale.z; |
|||
|
|||
transform.localScale = localScale; |
|||
} |
|||
} |
|||
|
|||
|
|||
void Update() |
|||
{ |
|||
if (thisRenderer && thisRenderer.material.mainTexture == null) |
|||
{ |
|||
if (kinectManager && backgroundRemovalManager && backgroundRemovalManager.enabled /**&& backManager.IsBackgroundRemovalInitialized()*/) |
|||
{ |
|||
thisRenderer.material.mainTexture = backgroundRemovalManager.GetForegroundTex(); |
|||
//Debug.Log("BR-manager: " + backroundRemovalManager + ", user index: " + backroundRemovalManager.playerIndex);
|
|||
} |
|||
} |
|||
//else if (thisRenderer && thisRenderer.material.mainTexture != null)
|
|||
//{
|
|||
// if (KinectManager.Instance == null)
|
|||
// {
|
|||
// thisRenderer.sharedMaterial.mainTexture = null;
|
|||
// }
|
|||
//}
|
|||
} |
|||
|
|||
|
|||
void OnApplicationPause(bool isPaused) |
|||
{ |
|||
// fix for app pause & restore (UWP)
|
|||
if (isPaused && thisRenderer && thisRenderer.material.mainTexture != null) |
|||
{ |
|||
thisRenderer.sharedMaterial.mainTexture = null; |
|||
} |
|||
} |
|||
|
|||
} |
|||
} |
@ -0,0 +1,12 @@ |
|||
fileFormatVersion: 2 |
|||
guid: 1516a5d945302e546a218c0c7a3f048c |
|||
timeCreated: 1478884175 |
|||
licenseType: Store |
|||
MonoImporter: |
|||
serializedVersion: 2 |
|||
defaultReferences: [] |
|||
executionOrder: 0 |
|||
icon: {instanceID: 0} |
|||
userData: |
|||
assetBundleName: |
|||
assetBundleVariant: |
@ -0,0 +1,184 @@ |
|||
using System.Collections; |
|||
using System.Collections.Generic; |
|||
using UnityEngine; |
|||
|
|||
namespace com.rfilkov.components |
|||
{ |
|||
public class FragmentLighting |
|||
{ |
|||
// lighting structures
|
|||
private Light[] sceneLights = null; |
|||
private Bounds lightBounds; |
|||
|
|||
private Vector4[] dirLightData = new Vector4[2]; |
|||
|
|||
[SerializeField] |
|||
public struct PointLight |
|||
{ |
|||
public Vector4 color; |
|||
public float range; |
|||
public Vector3 pos; |
|||
} |
|||
|
|||
private const int SIZE_POINT_LIGHT = 32; |
|||
private const int MAX_POINT_LIGHTS = 8; |
|||
|
|||
[SerializeField] |
|||
private PointLight[] pointLights = new PointLight[MAX_POINT_LIGHTS]; |
|||
private ComputeBuffer pointLightsBuffer = null; |
|||
private int pointLightsNumber = 0; |
|||
|
|||
[SerializeField] |
|||
public struct SpotLight |
|||
{ |
|||
public Vector4 color; |
|||
public Vector3 pos; |
|||
public Vector4 dir; |
|||
public Vector4 pars; |
|||
} |
|||
|
|||
private const int SIZE_SPOT_LIGHT = 60; |
|||
private const int MAX_SPOT_LIGHTS = 8; |
|||
|
|||
[SerializeField] |
|||
public SpotLight[] spotLights = new SpotLight[MAX_SPOT_LIGHTS]; |
|||
private ComputeBuffer spotLightsBuffer = null; |
|||
private int spotLightsNumber = 0; |
|||
|
|||
const int NUMBER_LIGHTS_MAX = MAX_POINT_LIGHTS / 2 + MAX_SPOT_LIGHTS / 2; |
|||
|
|||
|
|||
/// <summary>
|
|||
/// Sets the scene lights and lighted volume bounds.
|
|||
/// </summary>
|
|||
public void SetLightsAndBounds(Light[] sceneLights, Vector3 centerPos, Vector3 sizeBounds) |
|||
{ |
|||
this.sceneLights = sceneLights; |
|||
this.lightBounds = new Bounds(centerPos, sizeBounds); |
|||
} |
|||
|
|||
|
|||
/// <summary>
|
|||
/// Releases the used native resources.
|
|||
/// </summary>
|
|||
public void ReleaseResources() |
|||
{ |
|||
if (pointLightsBuffer != null) |
|||
{ |
|||
pointLightsBuffer.Release(); |
|||
pointLightsBuffer = null; |
|||
} |
|||
|
|||
if (spotLightsBuffer != null) |
|||
{ |
|||
spotLightsBuffer.Release(); |
|||
spotLightsBuffer = null; |
|||
} |
|||
} |
|||
|
|||
|
|||
/// <summary>
|
|||
/// Updates the lighting parameters of the material.
|
|||
/// </summary>
|
|||
public void UpdateLighting(Material matRenderer, bool bApplyLighting) |
|||
{ |
|||
matRenderer.SetInt("_ApplyLights", bApplyLighting ? 1 : 0); |
|||
matRenderer.SetInt("_ApplyShadows", 0); |
|||
matRenderer.SetFloat("_Metallic", 0); |
|||
|
|||
ApplyLighting(matRenderer, bApplyLighting); |
|||
} |
|||
|
|||
// applies the current lights
|
|||
private void ApplyLighting(Material matRenderer, bool bApplyLighting) |
|||
{ |
|||
const float interiorCone = 0.1f; // interior cone of the spotlight
|
|||
dirLightData[1] = Vector4.zero; |
|||
|
|||
int pi = 0; |
|||
int si = 0; |
|||
|
|||
if(bApplyLighting) |
|||
{ |
|||
foreach (Light light in sceneLights) |
|||
{ |
|||
if (!light.gameObject.activeInHierarchy || !light.enabled) |
|||
continue; |
|||
|
|||
if (light.type == LightType.Directional || Vector3.Distance(lightBounds.center, light.transform.position) < (light.range + lightBounds.extents.x)) |
|||
{ |
|||
if (light.type != LightType.Directional && light.shadows != LightShadows.None) |
|||
{ |
|||
light.shadows = LightShadows.None; |
|||
} |
|||
|
|||
if (light.type == LightType.Point) |
|||
{ |
|||
if (pi < MAX_POINT_LIGHTS) |
|||
{ |
|||
pointLights[pi].color = light.color * light.intensity; |
|||
pointLights[pi].pos = light.gameObject.transform.position; |
|||
pointLights[pi].range = light.range; |
|||
|
|||
pi++; |
|||
} |
|||
} |
|||
else if (light.type == LightType.Spot) |
|||
{ |
|||
if (si < MAX_SPOT_LIGHTS) |
|||
{ |
|||
Vector3 vLightFwd = light.gameObject.transform.forward.normalized; |
|||
|
|||
spotLights[si].color = light.color * light.intensity; |
|||
spotLights[si].pos = light.gameObject.transform.position; |
|||
spotLights[si].dir = new Vector4(vLightFwd.x, vLightFwd.y, vLightFwd.z, Mathf.Cos((light.spotAngle / 2.0f) * Mathf.Deg2Rad)); |
|||
spotLights[si].pars = new Vector4(light.spotAngle, light.intensity, 1.0f / light.range, interiorCone); |
|||
|
|||
si++; |
|||
} |
|||
} |
|||
else if (light.type == LightType.Directional) |
|||
{ |
|||
Vector3 vLightFwd = light.gameObject.transform.forward.normalized; |
|||
dirLightData[0] = new Vector4(vLightFwd.x, vLightFwd.y, vLightFwd.z, 0); |
|||
dirLightData[1] = light.color * light.intensity; |
|||
} |
|||
|
|||
} |
|||
} |
|||
} |
|||
|
|||
if (pointLightsBuffer == null) |
|||
{ |
|||
pointLightsBuffer = new ComputeBuffer(MAX_POINT_LIGHTS, SIZE_POINT_LIGHT); |
|||
pointLightsBuffer.SetData(pointLights); |
|||
|
|||
matRenderer.SetBuffer("_PointLights", pointLightsBuffer); |
|||
} |
|||
else |
|||
{ |
|||
pointLightsBuffer.SetData(pointLights); |
|||
} |
|||
|
|||
if (spotLightsBuffer == null) |
|||
{ |
|||
spotLightsBuffer = new ComputeBuffer(MAX_SPOT_LIGHTS, SIZE_SPOT_LIGHT); |
|||
spotLightsBuffer.SetData(spotLights); |
|||
matRenderer.SetBuffer("_SpotLights", spotLightsBuffer); |
|||
} |
|||
else |
|||
{ |
|||
spotLightsBuffer.SetData(spotLights); |
|||
} |
|||
|
|||
pointLightsNumber = pi; |
|||
spotLightsNumber = si; |
|||
|
|||
matRenderer.SetInt("_PointLightsNumber", pointLightsNumber); |
|||
matRenderer.SetInt("_SpotLightsNumber", spotLightsNumber); |
|||
|
|||
matRenderer.SetVectorArray("_DirectionalLights", dirLightData); |
|||
} |
|||
|
|||
} |
|||
} |
@ -0,0 +1,11 @@ |
|||
fileFormatVersion: 2 |
|||
guid: fe7f7748efd77d2498b7b6f26646756d |
|||
MonoImporter: |
|||
externalObjects: {} |
|||
serializedVersion: 2 |
|||
defaultReferences: [] |
|||
executionOrder: 0 |
|||
icon: {instanceID: 0} |
|||
userData: |
|||
assetBundleName: |
|||
assetBundleVariant: |
@ -0,0 +1,184 @@ |
|||
using UnityEngine; |
|||
using System.Collections; |
|||
|
|||
|
|||
namespace com.rfilkov.components |
|||
{ |
|||
/// <summary>
|
|||
/// HmdHeadMover moves the avatar model, according to the camera position reported by the HMD tracker.
|
|||
/// Don't forget to enable the 'External root motion'-setting of the AvatarController-component in this case.
|
|||
/// </summary>
|
|||
public class HmdHeadMover : MonoBehaviour |
|||
{ |
|||
[Tooltip("The transform that needs to be followed by the avatar's head, usually the eye-camera position reported by the HMD tracker. When left empty, it defaults to the main camera's position.")] |
|||
public Transform targetTransform; |
|||
|
|||
[Tooltip("The transform of the avatar's head. When left empty, it defaults to the head position, as reported by Animator-component.")] |
|||
private Transform headTransform; |
|||
|
|||
[Tooltip("Whether the avatar's feet must stick to the ground.")] |
|||
public bool groundedFeet = false; |
|||
|
|||
[Tooltip("The transform of the avatar's left toes, if grounding is enabled.")] |
|||
private Transform leftToes; |
|||
|
|||
[Tooltip("The transform of the avatar's right toes, if grounding is enabled.")] |
|||
private Transform rightToes; |
|||
|
|||
|
|||
// grounder constants and variables
|
|||
//private const int raycastLayers = ~2; // Ignore Raycast
|
|||
private const float maxFootDistanceGround = 0.02f; // maximum distance from lower foot to the ground
|
|||
private const float maxFootDistanceTime = 0.02f; // 0.2f; // maximum allowed time, the lower foot to be distant from the ground
|
|||
//private Transform leftFoot, rightFoot;
|
|||
|
|||
private float fFootDistanceInitial = 0f; |
|||
private float fFootDistance = 0f; |
|||
private float fFootDistanceTime = 0f; |
|||
|
|||
|
|||
void Start() |
|||
{ |
|||
// if the target transform is not set, use the camera transform
|
|||
if (targetTransform == null && Camera.main != null) |
|||
{ |
|||
targetTransform = Camera.main.transform; |
|||
} |
|||
} |
|||
|
|||
|
|||
void LateUpdate() |
|||
{ |
|||
// move the head and body to the target
|
|||
MoveHeadToTarget(); |
|||
} |
|||
|
|||
|
|||
// moves the avatar's head to the target, and the rest of its body too
|
|||
private void MoveHeadToTarget() |
|||
{ |
|||
if (headTransform == null) |
|||
{ |
|||
Animator animatorComponent = GetComponent<Animator>(); |
|||
headTransform = animatorComponent ? animatorComponent.GetBoneTransform(HumanBodyBones.Head) : null; |
|||
} |
|||
|
|||
if (!targetTransform || !headTransform) |
|||
return; |
|||
|
|||
Transform trans = headTransform.transform; |
|||
Vector3 posTrans = targetTransform.position; |
|||
|
|||
while (trans.parent != null) |
|||
{ |
|||
Transform transParent = trans.parent; |
|||
|
|||
Vector3 dirParent = transParent.position - trans.position; |
|||
posTrans += dirParent; |
|||
|
|||
trans = transParent; |
|||
} |
|||
|
|||
if (groundedFeet) |
|||
{ |
|||
// keep the current correction
|
|||
float fLastTgtY = posTrans.y; |
|||
posTrans.y += fFootDistance; |
|||
|
|||
float fNewDistance = GetDistanceToGround(); |
|||
float fNewDistanceTime = Time.time; |
|||
|
|||
// Debug.Log(string.Format("PosY: {0:F2}, LastY: {1:F2}, TgrY: {2:F2}, NewDist: {3:F2}, Corr: {4:F2}, Time: {5:F2}", bodyRoot != null ? bodyRoot.position.y : transform.position.y,
|
|||
// fLastTgtY, targetPos.y, fNewDistance, fFootDistance, fNewDistanceTime));
|
|||
|
|||
if (Mathf.Abs(fNewDistance) >= 0.01f && Mathf.Abs(fNewDistance - fFootDistanceInitial) >= maxFootDistanceGround) |
|||
{ |
|||
if ((fNewDistanceTime - fFootDistanceTime) >= maxFootDistanceTime) |
|||
{ |
|||
fFootDistance += (fNewDistance - fFootDistanceInitial); |
|||
fFootDistanceTime = fNewDistanceTime; |
|||
|
|||
posTrans.y = fLastTgtY + fFootDistance; |
|||
|
|||
// Debug.Log(string.Format(" >> change({0:F2})! - Corr: {1:F2}, LastY: {2:F2}, TgrY: {3:F2} at time {4:F2}",
|
|||
// (fNewDistance - fFootDistanceInitial), fFootDistance, fLastTgtY, targetPos.y, fFootDistanceTime));
|
|||
} |
|||
} |
|||
else |
|||
{ |
|||
fFootDistanceTime = fNewDistanceTime; |
|||
} |
|||
} |
|||
|
|||
// set root transform position
|
|||
if (trans) |
|||
{ |
|||
trans.position = posTrans; |
|||
} |
|||
|
|||
// Vector3 posDiff = targetTransform.position - headTransform.position;
|
|||
// transform.position += posDiff;
|
|||
|
|||
//Debug.Log("PosTrans: " + posTrans + ", Transofrm: " + transform.position);
|
|||
} |
|||
|
|||
|
|||
// returns the lower distance distance from left or right foot to the ground, or 1000f if no LF/RF transforms are found
|
|||
private float GetDistanceToGround() |
|||
{ |
|||
if (leftToes == null && rightToes == null) |
|||
{ |
|||
Animator animatorComponent = GetComponent<Animator>(); |
|||
|
|||
if (animatorComponent) |
|||
{ |
|||
leftToes = animatorComponent.GetBoneTransform(HumanBodyBones.LeftToes); |
|||
rightToes = animatorComponent.GetBoneTransform(HumanBodyBones.RightToes); |
|||
} |
|||
} |
|||
|
|||
float fDistMin = 1000f; |
|||
float fDistLeft = leftToes ? GetTransformDistanceToGround(leftToes) : fDistMin; |
|||
float fDistRight = rightToes ? GetTransformDistanceToGround(rightToes) : fDistMin; |
|||
fDistMin = Mathf.Abs(fDistLeft) < Mathf.Abs(fDistRight) ? fDistLeft : fDistRight; |
|||
|
|||
if (fDistMin == 1000f) |
|||
{ |
|||
fDistMin = 0f; // fFootDistanceInitial;
|
|||
} |
|||
|
|||
// Debug.Log (string.Format ("LFootY: {0:F2}, Dist: {1:F2}, RFootY: {2:F2}, Dist: {3:F2}, Min: {4:F2}", leftToes ? leftToes.position.y : 0f, fDistLeft,
|
|||
// rightToes ? rightToes.position.y : 0f, fDistRight, fDistMin));
|
|||
|
|||
return fDistMin; |
|||
} |
|||
|
|||
|
|||
// returns distance from the given transform to the underlying object.
|
|||
private float GetTransformDistanceToGround(Transform trans) |
|||
{ |
|||
if (!trans) |
|||
return 0f; |
|||
|
|||
// RaycastHit hit;
|
|||
// if(Physics.Raycast(trans.position, Vector3.down, out hit, 2f, raycastLayers))
|
|||
// {
|
|||
// return -hit.distance;
|
|||
// }
|
|||
// else if(Physics.Raycast(trans.position, Vector3.up, out hit, 2f, raycastLayers))
|
|||
// {
|
|||
// return hit.distance;
|
|||
// }
|
|||
// else
|
|||
// {
|
|||
// if (trans.position.y < 0)
|
|||
// return -trans.position.y;
|
|||
// else
|
|||
// return 1000f;
|
|||
// }
|
|||
|
|||
return -trans.position.y; |
|||
} |
|||
|
|||
} |
|||
} |
@ -0,0 +1,12 @@ |
|||
fileFormatVersion: 2 |
|||
guid: 7dcb1ccbe48396140817168afcabd300 |
|||
timeCreated: 1505201861 |
|||
licenseType: Store |
|||
MonoImporter: |
|||
serializedVersion: 2 |
|||
defaultReferences: [] |
|||
executionOrder: 0 |
|||
icon: {instanceID: 0} |
|||
userData: |
|||
assetBundleName: |
|||
assetBundleVariant: |
@ -0,0 +1,390 @@ |
|||
using System; |
|||
using System.Collections; |
|||
using UnityEngine; |
|||
using UnityEngine.Serialization; |
|||
using UnityEngine.EventSystems; |
|||
|
|||
|
|||
namespace com.rfilkov.components |
|||
{ |
|||
/// <summary>
|
|||
/// InteractionInputModule is the input module that can be used as component of the Unity-UI EventSystem.
|
|||
/// </summary>
|
|||
public class InteractionInputModule : PointerInputModule, InteractionListenerInterface |
|||
{ |
|||
[Tooltip("Index of the player, tracked by the respective InteractionManager. 0 means the 1st player, 1 - the 2nd one, 2 - the 3rd one, etc.")] |
|||
public int playerIndex = 0; |
|||
|
|||
[Tooltip("Whether the left hand interaction is allowed by the respective InteractionManager.")] |
|||
public bool leftHandInteraction = true; |
|||
|
|||
[Tooltip("Whether the right hand interaction is allowed by the respective InteractionManager.")] |
|||
public bool rightHandInteraction = true; |
|||
|
|||
[Tooltip("Whether to process the hand cursor movements (i.e for hovering ui-elements), or not.")] |
|||
public bool processCursorMovement = false; |
|||
|
|||
|
|||
//private bool m_isLeftHand = false;
|
|||
private bool m_leftHandGrip = false; |
|||
private bool m_rightHandGrip = false; |
|||
private Vector3 m_handCursorPos = Vector3.zero; |
|||
private Vector2 m_lastCursorPos = Vector2.zero; |
|||
|
|||
private PointerEventData.FramePressState m_framePressState = PointerEventData.FramePressState.NotChanged; |
|||
private readonly MouseState m_MouseState = new MouseState(); |
|||
|
|||
// interaction manager for the same player
|
|||
private InteractionManager intManager; |
|||
|
|||
// The single instance of InteractionInputModule
|
|||
//private static InteractionInputModule instance;
|
|||
|
|||
|
|||
///// <summary>
|
|||
///// Gets the single InteractionInputModule instance.
|
|||
///// </summary>
|
|||
///// <value>The InteractionInputModule instance.</value>
|
|||
//public static InteractionInputModule Instance
|
|||
//{
|
|||
// get
|
|||
// {
|
|||
// return instance;
|
|||
// }
|
|||
//}
|
|||
|
|||
//protected InteractionInputModule()
|
|||
//{
|
|||
// instance = this;
|
|||
//}
|
|||
|
|||
|
|||
protected override void Awake() |
|||
{ |
|||
base.Awake(); |
|||
|
|||
intManager = InteractionManager.GetInstance(playerIndex, leftHandInteraction, rightHandInteraction); |
|||
} |
|||
|
|||
|
|||
[SerializeField] |
|||
[FormerlySerializedAs("m_AllowActivationOnMobileDevice")] |
|||
private bool m_ForceModuleActive; |
|||
|
|||
|
|||
public bool forceModuleActive |
|||
{ |
|||
get { return m_ForceModuleActive; } |
|||
set { m_ForceModuleActive = value; } |
|||
} |
|||
|
|||
public override bool IsModuleSupported() |
|||
{ |
|||
return m_ForceModuleActive || intManager != null; |
|||
} |
|||
|
|||
public override bool ShouldActivateModule() |
|||
{ |
|||
if (!base.ShouldActivateModule()) |
|||
return false; |
|||
|
|||
if (intManager == null) |
|||
{ |
|||
intManager = InteractionManager.GetInstance(playerIndex, leftHandInteraction, rightHandInteraction); |
|||
} |
|||
|
|||
//bool shouldActivate |= (InteractionManager.Instance != null && InteractionManager.Instance.IsInteractionInited());
|
|||
bool shouldActivate = m_ForceModuleActive || (m_framePressState != PointerEventData.FramePressState.NotChanged); |
|||
|
|||
if (!shouldActivate && processCursorMovement && intManager && |
|||
(intManager.IsLeftHandPrimary() || intManager.IsRightHandPrimary())) |
|||
{ |
|||
bool bIsLeftHand = intManager.IsLeftHandPrimary(); |
|||
|
|||
// check for cursor pos change
|
|||
Vector2 handCursorPos = bIsLeftHand ? intManager.GetLeftHandScreenPos() : intManager.GetRightHandScreenPos(); |
|||
|
|||
if (handCursorPos != m_lastCursorPos) |
|||
{ |
|||
m_lastCursorPos = handCursorPos; |
|||
shouldActivate = true; |
|||
} |
|||
} |
|||
|
|||
return shouldActivate; |
|||
} |
|||
|
|||
// public override void ActivateModule()
|
|||
// {
|
|||
// base.ActivateModule();
|
|||
//
|
|||
// var toSelect = eventSystem.currentSelectedGameObject;
|
|||
// if (toSelect == null)
|
|||
// toSelect = eventSystem.firstSelectedGameObject;
|
|||
//
|
|||
// eventSystem.SetSelectedGameObject(toSelect, GetBaseEventData());
|
|||
// }
|
|||
|
|||
// public override void DeactivateModule()
|
|||
// {
|
|||
// base.DeactivateModule();
|
|||
// ClearSelection();
|
|||
// }
|
|||
|
|||
public override void Process() |
|||
{ |
|||
if (intManager == null) |
|||
{ |
|||
intManager = InteractionManager.GetInstance(playerIndex, leftHandInteraction, rightHandInteraction); |
|||
} |
|||
|
|||
CheckGrippedCursorPosition(); |
|||
ProcessInteractionEvent(); |
|||
} |
|||
|
|||
private void CheckGrippedCursorPosition() |
|||
{ |
|||
if (intManager) |
|||
{ |
|||
bool bIsLeftHand = intManager.IsLeftHandPrimary(); |
|||
|
|||
// check for gripped hand
|
|||
bool bHandGrip = bIsLeftHand ? m_leftHandGrip : m_rightHandGrip; |
|||
|
|||
// check for cursor pos change
|
|||
Vector2 handCursorPos = bIsLeftHand ? intManager.GetLeftHandScreenPos() : intManager.GetRightHandScreenPos(); |
|||
|
|||
if (bHandGrip && handCursorPos != (Vector2)m_handCursorPos) |
|||
{ |
|||
// emulate new press
|
|||
m_framePressState = PointerEventData.FramePressState.Pressed; |
|||
m_handCursorPos = handCursorPos; |
|||
} |
|||
else if (processCursorMovement) |
|||
{ |
|||
m_handCursorPos = handCursorPos; |
|||
} |
|||
} |
|||
} |
|||
|
|||
protected void ProcessInteractionEvent() |
|||
{ |
|||
// Emulate mouse data
|
|||
var mouseData = GetMousePointerEventData(0); |
|||
var leftButtonData = mouseData.GetButtonState(PointerEventData.InputButton.Left).eventData; |
|||
|
|||
// Process the interaction data
|
|||
ProcessHandPressRelease(leftButtonData); |
|||
ProcessMove(leftButtonData.buttonData); |
|||
ProcessDrag(leftButtonData.buttonData); |
|||
} |
|||
|
|||
protected override MouseState GetMousePointerEventData(int id) |
|||
{ |
|||
// Populate the left button...
|
|||
PointerEventData leftData; |
|||
var created = GetPointerData(kMouseLeftId, out leftData, true); |
|||
|
|||
leftData.Reset(); |
|||
|
|||
Vector2 handPos = new Vector2(m_handCursorPos.x * Screen.width, m_handCursorPos.y * Screen.height); |
|||
|
|||
if (created) |
|||
{ |
|||
leftData.position = handPos; |
|||
} |
|||
|
|||
leftData.delta = handPos - leftData.position; |
|||
leftData.position = handPos; |
|||
//leftData.scrollDelta = 0f;
|
|||
leftData.button = PointerEventData.InputButton.Left; |
|||
|
|||
eventSystem.RaycastAll(leftData, m_RaycastResultCache); |
|||
var raycast = FindFirstRaycast(m_RaycastResultCache); |
|||
leftData.pointerCurrentRaycast = raycast; |
|||
m_RaycastResultCache.Clear(); |
|||
|
|||
m_MouseState.SetButtonState(PointerEventData.InputButton.Left, m_framePressState, leftData); |
|||
m_framePressState = PointerEventData.FramePressState.NotChanged; |
|||
|
|||
return m_MouseState; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Process the current hand press or release event.
|
|||
/// </summary>
|
|||
protected void ProcessHandPressRelease(MouseButtonEventData data) |
|||
{ |
|||
var pointerEvent = data.buttonData; |
|||
var currentOverGo = pointerEvent.pointerCurrentRaycast.gameObject; |
|||
|
|||
// PointerDown notification
|
|||
if (data.PressedThisFrame()) |
|||
{ |
|||
pointerEvent.eligibleForClick = true; |
|||
pointerEvent.delta = Vector2.zero; |
|||
pointerEvent.dragging = false; |
|||
pointerEvent.useDragThreshold = true; |
|||
pointerEvent.pressPosition = pointerEvent.position; |
|||
pointerEvent.pointerPressRaycast = pointerEvent.pointerCurrentRaycast; |
|||
|
|||
DeselectIfSelectionChanged(currentOverGo, pointerEvent); |
|||
|
|||
// search for the control that will receive the press
|
|||
// if we can't find a press handler set the press
|
|||
// handler to be what would receive a click.
|
|||
var newPressed = ExecuteEvents.ExecuteHierarchy(currentOverGo, pointerEvent, ExecuteEvents.pointerDownHandler); |
|||
|
|||
// didnt find a press handler... search for a click handler
|
|||
if (newPressed == null) |
|||
newPressed = ExecuteEvents.GetEventHandler<IPointerClickHandler>(currentOverGo); |
|||
|
|||
//Debug.Log("Pressed: " + newPressed);
|
|||
|
|||
float time = Time.unscaledTime; |
|||
|
|||
if (newPressed == pointerEvent.lastPress) |
|||
{ |
|||
var diffTime = time - pointerEvent.clickTime; |
|||
if (diffTime < 0.3f) |
|||
++pointerEvent.clickCount; |
|||
else |
|||
pointerEvent.clickCount = 1; |
|||
|
|||
pointerEvent.clickTime = time; |
|||
} |
|||
else |
|||
{ |
|||
pointerEvent.clickCount = 1; |
|||
} |
|||
|
|||
pointerEvent.pointerPress = newPressed; |
|||
pointerEvent.rawPointerPress = currentOverGo; |
|||
|
|||
pointerEvent.clickTime = time; |
|||
|
|||
// Save the drag handler as well
|
|||
pointerEvent.pointerDrag = ExecuteEvents.GetEventHandler<IDragHandler>(currentOverGo); |
|||
|
|||
if (pointerEvent.pointerDrag != null) |
|||
ExecuteEvents.Execute(pointerEvent.pointerDrag, pointerEvent, ExecuteEvents.initializePotentialDrag); |
|||
} |
|||
|
|||
// PointerUp notification
|
|||
if (data.ReleasedThisFrame()) |
|||
{ |
|||
// Debug.Log("Executing pressup on: " + pointer.pointerPress);
|
|||
ExecuteEvents.Execute(pointerEvent.pointerPress, pointerEvent, ExecuteEvents.pointerUpHandler); |
|||
|
|||
// see if we mouse up on the same element that we clicked on...
|
|||
var pointerUpHandler = ExecuteEvents.GetEventHandler<IPointerClickHandler>(currentOverGo); |
|||
|
|||
// PointerClick and Drop events
|
|||
if (pointerEvent.pointerPress != null && pointerEvent.pointerPress == pointerUpHandler && pointerEvent.eligibleForClick) |
|||
{ |
|||
ExecuteEvents.Execute(pointerEvent.pointerPress, pointerEvent, ExecuteEvents.pointerClickHandler); |
|||
} |
|||
else if (pointerEvent.pointerDrag != null && pointerEvent.dragging) |
|||
{ |
|||
ExecuteEvents.ExecuteHierarchy(currentOverGo, pointerEvent, ExecuteEvents.dropHandler); |
|||
} |
|||
|
|||
pointerEvent.eligibleForClick = false; |
|||
pointerEvent.pointerPress = null; |
|||
pointerEvent.rawPointerPress = null; |
|||
|
|||
if (pointerEvent.pointerDrag != null && pointerEvent.dragging) |
|||
ExecuteEvents.Execute(pointerEvent.pointerDrag, pointerEvent, ExecuteEvents.endDragHandler); |
|||
|
|||
pointerEvent.dragging = false; |
|||
pointerEvent.pointerDrag = null; |
|||
|
|||
// redo pointer enter / exit to refresh state
|
|||
// so that if we moused over somethign that ignored it before
|
|||
// due to having pressed on something else
|
|||
// it now gets it.
|
|||
if (currentOverGo != pointerEvent.pointerEnter) |
|||
{ |
|||
HandlePointerExitAndEnter(pointerEvent, null); |
|||
HandlePointerExitAndEnter(pointerEvent, currentOverGo); |
|||
} |
|||
} |
|||
} |
|||
|
|||
|
|||
public void HandGripDetected(ulong userId, int userIndex, bool isRightHand, bool isHandInteracting, Vector3 handScreenPos) |
|||
{ |
|||
if (userIndex != playerIndex || !isHandInteracting) |
|||
return; |
|||
|
|||
bool bHandValid = (leftHandInteraction && !isRightHand) || (rightHandInteraction && isRightHand); |
|||
if (!bHandValid) |
|||
return; |
|||
|
|||
//Debug.Log("HandGripDetected");
|
|||
|
|||
m_framePressState = PointerEventData.FramePressState.Pressed; |
|||
//m_isLeftHand = !isRightHand;
|
|||
m_handCursorPos = handScreenPos; |
|||
|
|||
if (!isRightHand) |
|||
m_leftHandGrip = true; |
|||
else |
|||
m_rightHandGrip = true; |
|||
} |
|||
|
|||
public void HandReleaseDetected(ulong userId, int userIndex, bool isRightHand, bool isHandInteracting, Vector3 handScreenPos) |
|||
{ |
|||
if (userIndex != playerIndex || !isHandInteracting) |
|||
return; |
|||
|
|||
bool bHandValid = (leftHandInteraction && !isRightHand) || (rightHandInteraction && isRightHand); |
|||
if (!bHandValid) |
|||
return; |
|||
|
|||
//Debug.Log("HandReleaseDetected");
|
|||
|
|||
m_framePressState = PointerEventData.FramePressState.Released; |
|||
//m_isLeftHand = !isRightHand;
|
|||
m_handCursorPos = handScreenPos; |
|||
|
|||
if (!isRightHand) |
|||
m_leftHandGrip = false; |
|||
else |
|||
m_rightHandGrip = false; |
|||
} |
|||
|
|||
public bool HandClickDetected(ulong userId, int userIndex, bool isRightHand, Vector3 handScreenPos) |
|||
{ |
|||
if (userIndex != playerIndex) |
|||
return false; |
|||
|
|||
bool bHandValid = (leftHandInteraction && !isRightHand) || (rightHandInteraction && isRightHand); |
|||
if (!bHandValid) |
|||
return false; |
|||
|
|||
//Debug.Log("HandClickDetected");
|
|||
|
|||
StartCoroutine(EmulateMouseClick(isRightHand, handScreenPos)); |
|||
return true; |
|||
} |
|||
|
|||
|
|||
private IEnumerator EmulateMouseClick(bool isRightHand, Vector3 handScreenPos) |
|||
{ |
|||
m_framePressState = PointerEventData.FramePressState.Pressed; |
|||
//m_isLeftHand = !isRightHand;
|
|||
m_handCursorPos = handScreenPos; |
|||
|
|||
yield return new WaitForSeconds(0.2f); |
|||
|
|||
m_framePressState = PointerEventData.FramePressState.Released; |
|||
//m_isLeftHand = !isRightHand;
|
|||
m_handCursorPos = handScreenPos; |
|||
|
|||
yield return null; |
|||
} |
|||
|
|||
|
|||
} |
|||
} |
@ -0,0 +1,12 @@ |
|||
fileFormatVersion: 2 |
|||
guid: 243c3bcf7560250429841bbe09055f45 |
|||
timeCreated: 1475044533 |
|||
licenseType: Store |
|||
MonoImporter: |
|||
serializedVersion: 2 |
|||
defaultReferences: [] |
|||
executionOrder: 0 |
|||
icon: {instanceID: 0} |
|||
userData: |
|||
assetBundleName: |
|||
assetBundleVariant: |
File diff suppressed because it is too large
@ -0,0 +1,8 @@ |
|||
fileFormatVersion: 2 |
|||
guid: a669d15f0035bbf4889c8092b5bcd201 |
|||
MonoImporter: |
|||
serializedVersion: 2 |
|||
defaultReferences: [] |
|||
executionOrder: 0 |
|||
icon: {instanceID: 0} |
|||
userData: |
@ -0,0 +1,8 @@ |
|||
fileFormatVersion: 2 |
|||
guid: 9170a0cec4179764c9b1bf2a9a9c38f6 |
|||
folderAsset: yes |
|||
DefaultImporter: |
|||
externalObjects: {} |
|||
userData: |
|||
assetBundleName: |
|||
assetBundleVariant: |
File diff suppressed because it is too large
@ -0,0 +1,11 @@ |
|||
fileFormatVersion: 2 |
|||
guid: 73b91e3d1fd43b147958460a9f767c75 |
|||
MonoImporter: |
|||
externalObjects: {} |
|||
serializedVersion: 2 |
|||
defaultReferences: [] |
|||
executionOrder: 0 |
|||
icon: {instanceID: 0} |
|||
userData: |
|||
assetBundleName: |
|||
assetBundleVariant: |
@ -0,0 +1,59 @@ |
|||
using System.Collections; |
|||
using System.Collections.Generic; |
|||
using UnityEngine; |
|||
|
|||
namespace com.rfilkov.kinect |
|||
{ |
|||
/// <summary>
|
|||
/// Descriptor of the sensor interface.
|
|||
/// </summary>
|
|||
[System.Serializable] |
|||
public class DepthSensorDescriptor |
|||
{ |
|||
/// <summary>
|
|||
/// Sensor type.
|
|||
/// </summary>
|
|||
public string sensorType; |
|||
|
|||
/// <summary>
|
|||
/// Full path to the sensor interface.
|
|||
/// </summary>
|
|||
public string sensorInterface; |
|||
|
|||
/// <summary>
|
|||
/// Settings of the sensor interface.
|
|||
/// </summary>
|
|||
public string sensorIntSettings; |
|||
|
|||
/// <summary>
|
|||
/// Sensor interface version.
|
|||
/// </summary>
|
|||
public string sensorIntVersion; |
|||
|
|||
/// <summary>
|
|||
/// Transform position.
|
|||
/// </summary>
|
|||
public Vector3 transformPos; |
|||
|
|||
/// <summary>
|
|||
/// Transform rotation.
|
|||
/// </summary>
|
|||
public Vector3 transformRot; |
|||
|
|||
/// <summary>
|
|||
/// Full class path to the depth predictor.
|
|||
/// </summary>
|
|||
public string depthPredictor; |
|||
|
|||
/// <summary>
|
|||
/// Full class path to the body tracking predictor.
|
|||
/// </summary>
|
|||
public string bodyTrackingPredictor; |
|||
|
|||
/// <summary>
|
|||
/// Full class path to the body segmentation predictor.
|
|||
/// </summary>
|
|||
public string bodySegmentationPredictor; |
|||
|
|||
} |
|||
} |
@ -0,0 +1,11 @@ |
|||
fileFormatVersion: 2 |
|||
guid: f862c1359c67d47e4a77a4b5c762744b |
|||
MonoImporter: |
|||
externalObjects: {} |
|||
serializedVersion: 2 |
|||
defaultReferences: [] |
|||
executionOrder: 0 |
|||
icon: {instanceID: 0} |
|||
userData: |
|||
assetBundleName: |
|||
assetBundleVariant: |
@ -0,0 +1,168 @@ |
|||
using UnityEngine; |
|||
using System.Collections; |
|||
using System.Collections.Generic; |
|||
|
|||
namespace com.rfilkov.kinect |
|||
{ |
|||
/// <summary>
|
|||
/// DepthSensorInterface is the template for all sensor-interface implementations.
|
|||
/// </summary>
|
|||
public interface DepthSensorInterface |
|||
{ |
|||
// returns the depth sensor platform
|
|||
KinectInterop.DepthSensorPlatform GetSensorPlatform(); |
|||
|
|||
// returns the device-id of the currently opened sensor
|
|||
string GetSensorDeviceId(); |
|||
|
|||
// returns the type of sensor interface settings
|
|||
System.Type GetSensorSettingsType(); |
|||
|
|||
// returns sensor interface settings
|
|||
DepthSensorBase.BaseSensorSettings GetSensorSettings(DepthSensorBase.BaseSensorSettings settings); |
|||
|
|||
// sets sensor interface settings
|
|||
void SetSensorSettings(DepthSensorBase.BaseSensorSettings settings); |
|||
|
|||
// returns the list of available sensors, controlled by this sensor interface
|
|||
List<KinectInterop.SensorDeviceInfo> GetAvailableSensors(); |
|||
|
|||
// opens the given sensor and inits needed resources. returns new sensor-data object
|
|||
KinectInterop.SensorData OpenSensor(KinectManager kinectManager, KinectInterop.FrameSource dwFlags, bool bSyncDepthAndColor, bool bSyncBodyAndDepth); |
|||
|
|||
// closes the sensor and frees used resources
|
|||
void CloseSensor(KinectInterop.SensorData sensorData); |
|||
|
|||
// enables or disables the pose stream
|
|||
void EnablePoseStream(KinectInterop.SensorData sensorData, bool bEnable); |
|||
|
|||
// enables or disables synchronization of frames between the master & sub sensors
|
|||
bool EnableSensorSync(KinectInterop.SensorData sensorData, bool bEnable); |
|||
|
|||
// returns true if the sensor is master, false if it's standalone or subordinate
|
|||
bool IsSensorMaster(); |
|||
|
|||
// checks if the given sensor frame timestamp is synched with the master or not
|
|||
bool IsSensorFrameSynched(ulong frameTime, ulong masterTime); |
|||
|
|||
// set minimum & maximum infrared values, used in IR texture generation
|
|||
void SetMinMaxInfraredValues(float minValue, float maxValue); |
|||
|
|||
// initializes the secondary sensor data, after sensor initialization
|
|||
void InitSensorData(KinectInterop.SensorData sensorData, KinectManager kinectManager); |
|||
|
|||
// checks whether the sensor data is valid. can wait for valid data, as in case of the net-interface
|
|||
bool IsSensorDataValid(); |
|||
|
|||
// returns the body tracker orientation angle (Z-angle), in degrees
|
|||
float GetBodyTrackerOrientationAngle(); |
|||
|
|||
// polls data frames in the sensor-specific thread
|
|||
void PollSensorFrames(KinectInterop.SensorData sensorData); |
|||
|
|||
// polls coordinate transformation frames and data in the sensor-specific thread
|
|||
void PollCoordTransformFrames(KinectInterop.SensorData sensorData); |
|||
|
|||
// post-processes the sensor data after polling
|
|||
void PollSensorFrameTimes(KinectInterop.SensorData sensorData); |
|||
|
|||
// updates sensor data, if needed
|
|||
// returns true if update is successful, false otherwise
|
|||
bool UpdateSensorData(KinectInterop.SensorData sensorData, KinectManager kinectManager, bool isPlayMode); |
|||
|
|||
// updates transformed frame textures, if needed
|
|||
// returns true if update is successful, false otherwise
|
|||
bool UpdateTransformedFrameTextures(KinectInterop.SensorData sensorData, KinectManager kinectManager); |
|||
|
|||
// updates the selected sensor textures, if needed
|
|||
// returns true if update is successful, false otherwise
|
|||
bool UpdateSensorTextures(KinectInterop.SensorData sensorData, KinectManager kinectManager, ulong prevDepthFrameTime, ulong prevIrFrameTime); |
|||
|
|||
// returns sensor transform. Please note transform updates depend on the getPoseFrames-KM setting.
|
|||
Transform GetSensorTransform(); |
|||
|
|||
// returns depth-to-color-camera matrix
|
|||
Matrix4x4 GetDepthToColorCameraMatrix(); |
|||
|
|||
// returns sensor-to-world matrix
|
|||
Matrix4x4 GetSensorToWorldMatrix(); |
|||
|
|||
// sets sensor-to-world matrix
|
|||
void SetSensorToWorldMatrix(Vector3 sensorWorldPosition, Quaternion sensorWorldRotation, bool isUpdateTransform); |
|||
|
|||
// sets sensor-to-world matrix
|
|||
void SetSensorToWorldMatrix(Matrix4x4 mSensor2World, bool isUpdateTransform); |
|||
|
|||
// returns the depth camera space table
|
|||
Vector3[] GetDepthCameraSpaceTable(KinectInterop.SensorData sensorData); |
|||
|
|||
// returns the color camera space table
|
|||
Vector3[] GetColorCameraSpaceTable(KinectInterop.SensorData sensorData); |
|||
|
|||
// returns depth camera space coordinates for the given depth image point
|
|||
Vector3 MapDepthPointToSpaceCoords(KinectInterop.SensorData sensorData, Vector2 depthPos, ushort depthVal); |
|||
|
|||
// returns depth image coordinates for the given depth camera space point
|
|||
Vector2 MapSpacePointToDepthCoords(KinectInterop.SensorData sensorData, Vector3 spacePos); |
|||
|
|||
// returns color camera space coordinates for the given color image point
|
|||
Vector3 MapColorPointToSpaceCoords(KinectInterop.SensorData sensorData, Vector2 colorPos, ushort depthVal); |
|||
|
|||
// returns color image coordinates for the given color camera space point
|
|||
Vector2 MapSpacePointToColorCoords(KinectInterop.SensorData sensorData, Vector3 spacePos); |
|||
|
|||
// returns color image coordinates for the given depth image point
|
|||
Vector2 MapDepthPointToColorCoords(KinectInterop.SensorData sensorData, Vector2 depthPos, ushort depthVal); |
|||
|
|||
// returns depth image coordinates for the given color image point
|
|||
Vector2 MapColorPointToDepthCoords(KinectInterop.SensorData sensorData, Vector2 colorPos, int minDist, int maxDist); |
|||
|
|||
// returns the anchor position of the background raw image
|
|||
Vector2 GetBackgroundImageAnchorPos(KinectInterop.SensorData sensorData); |
|||
|
|||
// returns the resolution in pixels of the point-cloud textures
|
|||
Vector2Int GetPointCloudTexResolution(KinectInterop.SensorData sensorData); |
|||
|
|||
// returns the net-sensor-data for network exchange
|
|||
KinectInterop.NetSensorData GetNetSensorData(KinectInterop.SensorData sensorData); |
|||
|
|||
// sets the local sensor data from the network exchange data
|
|||
void SetNetSensorData(KinectInterop.NetSensorData netSensorData, KinectInterop.SensorData sensorData, KinectManager kinectManager); |
|||
|
|||
// returns the sensor pose data for network exchange
|
|||
KinectInterop.NetPoseData GetSensorNetPoseData(KinectInterop.SensorData sensorData); |
|||
|
|||
// sets the local sensor pose data from the network exchange data
|
|||
void SetSensorNetPoseData(KinectInterop.NetPoseData netPoseData, KinectInterop.SensorData sensorData, KinectManager kinectManager); |
|||
|
|||
// enables or disables depth camera color frame processing
|
|||
void EnableDepthCameraColorFrame(KinectInterop.SensorData sensorData, bool isEnable); |
|||
|
|||
// returns the latest depth camera color frame texture along with the last frame time
|
|||
Texture GetDepthCameraColorFrameTexture(KinectInterop.SensorData sensorData, ref Texture2D copyToTex2D, ref ulong frameTime); |
|||
|
|||
// enables or disables color camera depth frame processing
|
|||
void EnableColorCameraDepthFrame(KinectInterop.SensorData sensorData, bool isEnable); |
|||
|
|||
// returns the latest color camera depth frame along with the last frame time. the returned data is ushort array.
|
|||
ushort[] GetColorCameraDepthFrame(KinectInterop.SensorData sensorData, ref ushort[] copyToFrame, ref ulong frameTime); |
|||
|
|||
// returns the latest color camera depth frame along with the last frame time. the returned data frame is byte array.
|
|||
byte[] GetColorCameraDepthFrameBytes(KinectInterop.SensorData sensorData, ref byte[] copyToFrame, ref ulong frameTime); |
|||
|
|||
// enables or disables color camera infrared frame processing
|
|||
void EnableColorCameraInfraredFrame(KinectInterop.SensorData sensorData, bool isEnableRawData, bool isEnableTexture); |
|||
|
|||
// returns the latest color camera infrared frame along with the last frame time. the returned data is ushort array.
|
|||
ushort[] GetColorCameraInfraredFrame(KinectInterop.SensorData sensorData, ref ushort[] copyToFrame, ref ulong frameTime); |
|||
|
|||
// returns the latest color camera infrared frame texture along with the last frame time
|
|||
Texture GetColorCameraInfraredFrameTexture(KinectInterop.SensorData sensorData, ref Texture2D copyToTex2D, ref ulong frameTime); |
|||
|
|||
// enables or disables color camera body-index frame processing
|
|||
void EnableColorCameraBodyIndexFrame(KinectInterop.SensorData sensorData, bool isEnable); |
|||
|
|||
// returns the latest color camera body-index frame along with the last frame time
|
|||
byte[] GetColorCameraBodyIndexFrame(KinectInterop.SensorData sensorData, ref byte[] copyToFrame, ref ulong frameTime); |
|||
} |
|||
} |
@ -0,0 +1,11 @@ |
|||
fileFormatVersion: 2 |
|||
guid: 87d17d44f68821041bda50d9aa091b2a |
|||
MonoImporter: |
|||
externalObjects: {} |
|||
serializedVersion: 2 |
|||
defaultReferences: [] |
|||
executionOrder: 0 |
|||
icon: {instanceID: 0} |
|||
userData: |
|||
assetBundleName: |
|||
assetBundleVariant: |
@ -0,0 +1,112 @@ |
|||
using UnityEngine; |
|||
using System.Collections; |
|||
using System.Collections.Generic; |
|||
|
|||
namespace com.rfilkov.kinect |
|||
{ |
|||
/// <summary>
|
|||
/// DummyK4AInterface is dummy sensor-interface to the Azure Kinect sensors.
|
|||
/// </summary>
|
|||
public class DummyK4AInterface : DepthSensorBase |
|||
{ |
|||
|
|||
public override KinectInterop.DepthSensorPlatform GetSensorPlatform() |
|||
{ |
|||
return KinectInterop.DepthSensorPlatform.DummyK4A; |
|||
} |
|||
|
|||
public override List<KinectInterop.SensorDeviceInfo> GetAvailableSensors() |
|||
{ |
|||
List<KinectInterop.SensorDeviceInfo> alSensorInfo = new List<KinectInterop.SensorDeviceInfo>(); |
|||
|
|||
KinectInterop.SensorDeviceInfo sensorInfo = new KinectInterop.SensorDeviceInfo(); |
|||
sensorInfo.sensorId = "DummyK4A"; |
|||
sensorInfo.sensorName = "Dummy Kinect-for-Azure"; |
|||
sensorInfo.sensorCaps = KinectInterop.FrameSource.TypeAll; |
|||
|
|||
alSensorInfo.Add(sensorInfo); |
|||
|
|||
return alSensorInfo; |
|||
} |
|||
|
|||
public override KinectInterop.SensorData OpenSensor(KinectManager kinectManager, KinectInterop.FrameSource dwFlags, bool bSyncDepthAndColor, bool bSyncBodyAndDepth) |
|||
{ |
|||
// save initial parameters
|
|||
base.OpenSensor(kinectManager, dwFlags, bSyncDepthAndColor, bSyncBodyAndDepth); |
|||
|
|||
List<KinectInterop.SensorDeviceInfo> alSensors = GetAvailableSensors(); |
|||
if (deviceIndex < 0 || deviceIndex >= alSensors.Count) |
|||
return null; |
|||
|
|||
sensorDeviceId = alSensors[deviceIndex].sensorId; |
|||
sensorPlatform = KinectInterop.DepthSensorPlatform.DummyK4A; |
|||
|
|||
KinectInterop.SensorData sensorData = new KinectInterop.SensorData(); |
|||
sensorData.sensorIntPlatform = sensorPlatform; |
|||
|
|||
sensorData.sensorId = alSensors[deviceIndex].sensorId; |
|||
sensorData.sensorName = alSensors[deviceIndex].sensorName; |
|||
sensorData.sensorCaps = alSensors[deviceIndex].sensorCaps; |
|||
|
|||
sensorData.colorImageWidth = 1920; // 1080p
|
|||
sensorData.colorImageHeight = 1080; |
|||
|
|||
sensorData.depthImageWidth = 640; // NFOV Unbinned
|
|||
sensorData.depthImageHeight = 576; |
|||
|
|||
sensorData.depthCamIntr = JsonUtility.FromJson<KinectInterop.CameraIntrinsics>(jsonDepthCamIntr); |
|||
sensorData.colorCamIntr = JsonUtility.FromJson<KinectInterop.CameraIntrinsics>(jsonColorCamIntr); |
|||
sensorData.depth2ColorExtr = JsonUtility.FromJson<KinectInterop.CameraExtrinsics>(jsonDepth2ColorExtr); |
|||
sensorData.color2DepthExtr = JsonUtility.FromJson<KinectInterop.CameraExtrinsics>(jsonColor2DepthExtr); |
|||
|
|||
float[] r = sensorData.depth2ColorExtr.rotation; |
|||
float[] t = sensorData.depth2ColorExtr.translation; |
|||
|
|||
depth2colorCamMat = new Matrix4x4(new Vector4(r[0], r[3], r[6], 0), new Vector4(r[1], r[4], r[7], 0), new Vector4(r[2], r[5], r[8], 0), new Vector4(t[0] * 0.001f, t[1] * 0.001f, t[2] * 0.001f, 1)); |
|||
//Debug.Log("Depth2colorCamMat Pos: " + (Vector3)depth2colorCamMat.GetColumn(3) + ", Rot: " + depth2colorCamMat.rotation.eulerAngles);
|
|||
|
|||
// flip color & depth image vertically
|
|||
sensorData.colorImageScale = new Vector3(-1f, -1f, 1f); |
|||
sensorData.depthImageScale = new Vector3(-1f, -1f, 1f); |
|||
sensorData.infraredImageScale = new Vector3(-1f, -1f, 1f); |
|||
sensorData.sensorSpaceScale = new Vector3(-1f, -1f, 1f); |
|||
sensorData.unitToMeterFactor = 0.001f; |
|||
|
|||
// depth camera offset & matrix z-flip
|
|||
sensorRotOffset = Vector3.zero; // new Vector3(6f, 0f, 0f); // the depth camera is tilted 6 degrees downwards
|
|||
sensorRotFlipZ = true; |
|||
sensorRotIgnoreY = true; |
|||
|
|||
// color camera data & intrinsics
|
|||
sensorData.colorImageFormat = TextureFormat.BGRA32; |
|||
sensorData.colorImageStride = 4; // 4 bytes per pixel
|
|||
|
|||
if(consoleLogMessages) |
|||
Debug.Log("D" + deviceIndex + " DummyK4A-sensor opened"); |
|||
|
|||
return sensorData; |
|||
} |
|||
|
|||
public override void CloseSensor(KinectInterop.SensorData sensorData) |
|||
{ |
|||
if (consoleLogMessages) |
|||
Debug.Log("D" + deviceIndex + " DummyK4A-sensor closed"); |
|||
} |
|||
|
|||
|
|||
private const string jsonDepthCamIntr = "{ \"cameraType\": 0, \"width\": 640, \"height\": 576, \"ppx\": 319.3891296386719, \"ppy\": 339.0096435546875," + |
|||
"\"fx\": 505.0830078125, \"fy\": 505.2060546875, \"distType\": 4, \"distCoeffs\": [0.45811858773231509,-0.09587264806032181,-0.008291528560221196,0.7999407649040222,-0.01724848523736,-0.03864333778619766]," + |
|||
"\"codx\": 0.0, \"cody\": 0.0, \"p2\": -0.00007324512989725918, \"p1\": -0.00015797713422216475, \"maxRadius\": 0.0, \"hFOV\": 64.7133560180664, \"vFOV\": 59.371849060058597 }"; |
|||
|
|||
private const string jsonColorCamIntr = "{ \"cameraType\": 1, \"width\": 1920, \"height\": 1080, \"ppx\": 953.6868286132813, \"ppy\": 553.8844604492188," + |
|||
"\"fx\": 903.1810913085938, \"fy\": 903.4053955078125, \"distType\": 4, \"distCoeffs\": [0.8302328586578369,-2.98026442527771,1.6583690643310547,0.7071738839149475,-2.815004825592041,1.5919547080993653]," + |
|||
"\"codx\": 0.0, \"cody\": 0.0, \"p2\": -0.0001697207917459309, \"p1\": 0.0007688929326832295, \"maxRadius\": 0.0, \"hFOV\": 93.49346160888672, \"vFOV\": 61.73675537109375 }"; |
|||
|
|||
private const string jsonDepth2ColorExtr = "{ \"rotation\": [0.9999944567680359,0.003319731680676341,-0.00013891232083551586,-0.0032980330288410188," + |
|||
"0.9968001842498779,0.07986554503440857,0.00040359998820349574,-0.07986464351415634,0.9968056082725525]," + |
|||
"\"translation\": [-31.988178253173829,-2.296376943588257,4.040627956390381] }"; |
|||
|
|||
private const string jsonColor2DepthExtr = "{ \"rotation\": [1.0,0.0,0.0,0.0,1.0,0.0,0.0,0.0,1.0]," + |
|||
"\"translation\": [0.0,0.0,0.0] }"; |
|||
} |
|||
} |
@ -0,0 +1,11 @@ |
|||
fileFormatVersion: 2 |
|||
guid: 71599eb36be4254469c6650b3f455914 |
|||
MonoImporter: |
|||
externalObjects: {} |
|||
serializedVersion: 2 |
|||
defaultReferences: [] |
|||
executionOrder: 0 |
|||
icon: {instanceID: 0} |
|||
userData: |
|||
assetBundleName: |
|||
assetBundleVariant: |
File diff suppressed because it is too large
@ -0,0 +1,11 @@ |
|||
fileFormatVersion: 2 |
|||
guid: 3187c833c6106e54facb6071b57475a2 |
|||
MonoImporter: |
|||
externalObjects: {} |
|||
serializedVersion: 2 |
|||
defaultReferences: [] |
|||
executionOrder: 0 |
|||
icon: {instanceID: 0} |
|||
userData: |
|||
assetBundleName: |
|||
assetBundleVariant: |
File diff suppressed because it is too large
@ -0,0 +1,11 @@ |
|||
fileFormatVersion: 2 |
|||
guid: e8f864d895d4d9c4cb2a9b96bb86c08b |
|||
MonoImporter: |
|||
externalObjects: {} |
|||
serializedVersion: 2 |
|||
defaultReferences: [] |
|||
executionOrder: 0 |
|||
icon: {instanceID: 0} |
|||
userData: |
|||
assetBundleName: |
|||
assetBundleVariant: |
@ -0,0 +1,426 @@ |
|||
using Microsoft.Azure.Kinect.Sensor; |
|||
using System; |
|||
using System.Collections; |
|||
using System.Collections.Generic; |
|||
using UnityEngine; |
|||
|
|||
|
|||
namespace com.rfilkov.kinect |
|||
{ |
|||
/// <summary>
|
|||
/// Kinect4AzureSyncher synhronizes the captures received from master and sub k4a-devices.
|
|||
/// </summary>
|
|||
public class Kinect4AzureSyncher |
|||
{ |
|||
|
|||
// data for each sensor
|
|||
private class SyncherSensorData |
|||
{ |
|||
public long expDelay; |
|||
|
|||
public long capTimestamp; |
|||
public Capture capture; |
|||
|
|||
public long pushCapTimestamp; |
|||
public Capture pushCapture; |
|||
|
|||
public long btTimestamp; |
|||
} |
|||
|
|||
// max allowed timestamp error
|
|||
private const long MAX_TIME_ERROR = 100000; // 10000; // 1000;
|
|||
|
|||
// available sensor interfaces
|
|||
private List<Kinect4AzureInterface> sensorInts = new List<Kinect4AzureInterface>(); |
|||
private List<KinectInterop.SensorData> sensorDatas = new List<KinectInterop.SensorData>(); |
|||
private List<long> expectedDelays = new List<long>(); |
|||
|
|||
// number of sensors and index of the master
|
|||
private int numSensors = 0; |
|||
private int iMaster = -1; // index of the master interface
|
|||
|
|||
// master play time
|
|||
private long masterPlayTime = 0; |
|||
private object masterPlayLock = new object(); |
|||
|
|||
// syncher sensor data
|
|||
private SyncherSensorData[] syncherData = null; |
|||
// syncher lock object
|
|||
private object syncherLock = new object(); |
|||
|
|||
|
|||
public Kinect4AzureSyncher() |
|||
{ |
|||
} |
|||
|
|||
|
|||
// initializes the syncher for the given sensor interface
|
|||
public int StartSyncherForSensor(Kinect4AzureInterface sensorInt, KinectInterop.SensorData sensorData, bool isMaster, long expectedDelay) |
|||
{ |
|||
if (sensorInt == null) |
|||
return -1; |
|||
|
|||
int sensorIndex = numSensors; |
|||
sensorInts.Add(sensorInt); |
|||
sensorDatas.Add(sensorData); |
|||
expectedDelays.Add(expectedDelay); |
|||
numSensors++; |
|||
|
|||
if(isMaster) |
|||
{ |
|||
if (iMaster >= 0) |
|||
throw new Exception("Master index already set at " + iMaster + ". Current interface index is " + sensorIndex + ". Multiple masters are not supported."); |
|||
|
|||
iMaster = sensorIndex; |
|||
} |
|||
|
|||
//Debug.Log("Started syncher for sensor D" + sensorInt.deviceIndex + ", delay: " + expectedDelay + ", index: " + sensorIndex + ", master: " + iMaster);
|
|||
|
|||
return sensorIndex; |
|||
} |
|||
|
|||
|
|||
// releases the resources taken by the syncher data
|
|||
public void StopSyncher() |
|||
{ |
|||
lock(syncherLock) |
|||
{ |
|||
for (int i = numSensors - 1; i >= 0; i--) |
|||
{ |
|||
if (syncherData != null && syncherData[i] != null) |
|||
{ |
|||
if (syncherData[i].capture != null) |
|||
{ |
|||
syncherData[i].capture.Dispose(); |
|||
syncherData[i].capture = null; |
|||
} |
|||
|
|||
if (syncherData[i].pushCapture != null) |
|||
{ |
|||
syncherData[i].pushCapture.Dispose(); |
|||
syncherData[i].pushCapture = null; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
//Debug.Log("Stopped syncher for " + numSensors + " sensors.");
|
|||
} |
|||
|
|||
|
|||
// sets master device play time
|
|||
public void SetMasterPlayTime(long playTime) |
|||
{ |
|||
lock(masterPlayLock) |
|||
{ |
|||
masterPlayTime = playTime; |
|||
} |
|||
} |
|||
|
|||
|
|||
// returns the latest master play time
|
|||
public long GetMasterPlayTime() |
|||
{ |
|||
long playTime = 0; |
|||
|
|||
lock(masterPlayLock) |
|||
{ |
|||
playTime = masterPlayTime; |
|||
} |
|||
|
|||
return playTime; |
|||
} |
|||
|
|||
|
|||
// returns the master index, or -1 if no master is set
|
|||
public int GetMasterIndex() |
|||
{ |
|||
return iMaster; |
|||
} |
|||
|
|||
|
|||
// checks if the given frame time is synched or not
|
|||
public bool IsSensorFrameSynched(int sensorIndex, ulong frameTime, ulong masterTime) |
|||
{ |
|||
if(syncherData != null && sensorIndex >= 0 && sensorIndex < syncherData.Length) |
|||
{ |
|||
long expTime = (long)masterTime + syncherData[sensorIndex].expDelay; |
|||
long subError = (long)frameTime - expTime; |
|||
|
|||
if (frameTime != 0 && subError >= -MAX_TIME_ERROR && subError <= MAX_TIME_ERROR) |
|||
{ |
|||
return true; |
|||
} |
|||
} |
|||
|
|||
return false; |
|||
} |
|||
|
|||
|
|||
// updates the sensor capture
|
|||
public void UpdateCapture(int sensorIndex, long capTimestamp, Capture capture) |
|||
{ |
|||
if (capture == null) |
|||
return; |
|||
|
|||
if (capTimestamp == 0) |
|||
{ |
|||
//Debug.Log("Ignoring capture for syncher index " + sensorIndex + ". Timestamp: " + capTimestamp);
|
|||
capture.Dispose(); |
|||
|
|||
return; |
|||
} |
|||
|
|||
lock (syncherLock) |
|||
{ |
|||
if (syncherData == null || numSensors != syncherData.Length || syncherData[sensorIndex] == null) |
|||
{ |
|||
CreateSyncherData(sensorIndex); |
|||
} |
|||
|
|||
// dispose current capture
|
|||
if (syncherData[sensorIndex].capture != null) |
|||
{ |
|||
//Debug.Log("Disposing capture for syncher index " + sensorIndex + ". Timestamp: " + syncherData[sensorIndex].capTimestamp);
|
|||
|
|||
syncherData[sensorIndex].capture.Dispose(); |
|||
syncherData[sensorIndex].capture = null; |
|||
} |
|||
|
|||
// set new capture
|
|||
//Debug.Log("Setting capture for syncher index " + sensorIndex + ". Timestamp: " + capTimestamp);
|
|||
|
|||
syncherData[sensorIndex].capTimestamp = capTimestamp; |
|||
syncherData[sensorIndex].capture = capture; |
|||
|
|||
// check for synched captures
|
|||
bool bAllSynched = numSensors > 1 && iMaster >= 0 && syncherData[iMaster] != null && syncherData[iMaster].capTimestamp != 0; |
|||
|
|||
if (bAllSynched) |
|||
{ |
|||
long masterTime = syncherData[iMaster].capTimestamp; |
|||
|
|||
for (int i = 0; i < numSensors; i++) |
|||
{ |
|||
if (syncherData[i] == null || syncherData[i].capTimestamp == 0) |
|||
{ |
|||
bAllSynched = false; |
|||
break; |
|||
} |
|||
|
|||
long subTime = syncherData[i].capTimestamp; |
|||
long expTime = masterTime + syncherData[i].expDelay; |
|||
long subError = subTime - expTime; |
|||
|
|||
if (i != iMaster && (subTime == 0 || subError < -MAX_TIME_ERROR || subError > MAX_TIME_ERROR)) |
|||
{ |
|||
bAllSynched = false; |
|||
break; |
|||
} |
|||
} |
|||
} |
|||
|
|||
if (bAllSynched) |
|||
{ |
|||
//Debug.Log("Synched captures. Index: " + sensorIndex + " MasterTime: " + syncherData[iMaster].capTimestamp);
|
|||
|
|||
// process synched sensor captures
|
|||
for (int i = 0; i < numSensors; i++) |
|||
{ |
|||
Kinect4AzureInterface sensorInt = sensorInts[i]; |
|||
KinectInterop.SensorData sensorData = sensorDatas[i]; |
|||
|
|||
//Debug.Log(" Processing capture " + i + ". Timestamp: " + syncherData[i].capTimestamp);
|
|||
sensorInt.ProcessSensorCapture(sensorData, syncherData[i].capture); |
|||
syncherData[i].capture = null; |
|||
} |
|||
} |
|||
else |
|||
{ |
|||
//Debug.Log("Captures not synched. Index: " + sensorIndex + " ThisTime: " + syncherData[sensorIndex].capTimestamp +
|
|||
// ", MasterTime: " + syncherData[iMaster].capTimestamp + ", diff: " + (syncherData[iMaster].capTimestamp - syncherData[sensorIndex].capTimestamp));
|
|||
} |
|||
} |
|||
} |
|||
|
|||
|
|||
// updates the push bt-capture
|
|||
public void UpdatePushBtCapture(int sensorIndex, long capTimestamp, Capture capture) |
|||
{ |
|||
if (capture == null || capture.Depth == null) |
|||
return; |
|||
|
|||
if (capTimestamp == 0) |
|||
{ |
|||
//Debug.Log("Ignoring push-capture for syncher index " + sensorIndex + ". Timestamp: " + capTimestamp);
|
|||
capture.Dispose(); |
|||
|
|||
return; |
|||
} |
|||
|
|||
lock (syncherLock) |
|||
{ |
|||
if (syncherData == null || numSensors != syncherData.Length || syncherData[sensorIndex] == null) |
|||
{ |
|||
CreateSyncherData(sensorIndex); |
|||
} |
|||
|
|||
// dispose current capture
|
|||
if (syncherData[sensorIndex].pushCapture != null) |
|||
{ |
|||
//Debug.Log("Disposing push-capture for syncher index " + sensorIndex + ". Timestamp: " + syncherData[sensorIndex].pushCapTimestamp);
|
|||
|
|||
syncherData[sensorIndex].pushCapture.Dispose(); |
|||
syncherData[sensorIndex].pushCapture = null; |
|||
} |
|||
|
|||
// set new capture
|
|||
//Debug.Log("Setting push-capture for syncher index " + sensorIndex + ". Timestamp: " + capTimestamp);
|
|||
|
|||
syncherData[sensorIndex].pushCapTimestamp = capTimestamp; |
|||
syncherData[sensorIndex].pushCapture = capture; |
|||
|
|||
// check for synched captures
|
|||
bool bAllSynched = numSensors > 1 && iMaster >= 0 && syncherData[iMaster] != null && syncherData[iMaster].pushCapTimestamp != 0; |
|||
|
|||
if (bAllSynched) |
|||
{ |
|||
long masterTime = syncherData[iMaster].pushCapTimestamp; |
|||
|
|||
for (int i = 0; i < numSensors; i++) |
|||
{ |
|||
if (syncherData[i] == null || syncherData[i].pushCapTimestamp == 0) |
|||
{ |
|||
bAllSynched = false; |
|||
break; |
|||
} |
|||
|
|||
long subTime = syncherData[i].pushCapTimestamp; |
|||
long expTime = masterTime + syncherData[i].expDelay; |
|||
long subError = subTime - expTime; |
|||
|
|||
if (i != iMaster && (subTime == 0 || subError < -MAX_TIME_ERROR || subError > MAX_TIME_ERROR)) |
|||
{ |
|||
bAllSynched = false; |
|||
break; |
|||
} |
|||
} |
|||
} |
|||
|
|||
if (bAllSynched) |
|||
{ |
|||
//Debug.Log("Synched push-captures. Index: " + sensorIndex + " MasterTime: " + syncherData[iMaster].pushCapTimestamp);
|
|||
|
|||
// process synched sensor captures
|
|||
for (int i = 0; i < numSensors; i++) |
|||
{ |
|||
Kinect4AzureInterface sensorInt = sensorInts[i]; |
|||
KinectInterop.SensorData sensorData = sensorDatas[i]; |
|||
|
|||
//Debug.Log(" Processing push capture " + i + ". Timestamp: " + syncherData[i].pushCapTimestamp);
|
|||
sensorInt.PushBodyFrame(sensorData, syncherData[i].pushCapture, true); |
|||
syncherData[i].pushCapture = null; |
|||
} |
|||
} |
|||
else |
|||
{ |
|||
//Debug.Log("Push-captures not synched. Index: " + sensorIndex + " ThisTime: " + syncherData[sensorIndex].pushCapTimestamp +
|
|||
// ", MasterTime: " + syncherData[iMaster].pushCapTimestamp + ", diff: " + (syncherData[iMaster].pushCapTimestamp - syncherData[sensorIndex].pushCapTimestamp));
|
|||
} |
|||
} |
|||
} |
|||
|
|||
|
|||
// updates the body tracking frame
|
|||
public void UpdateBtFrame(int sensorIndex, long frameTimestamp) |
|||
{ |
|||
if(frameTimestamp == 0) |
|||
{ |
|||
//Debug.Log("Ignoring bt-frame for syncher index " + sensorIndex + ". Timestamp: " + frameTimestamp);
|
|||
return; |
|||
} |
|||
|
|||
lock (syncherLock) |
|||
{ |
|||
if(syncherData == null || numSensors != syncherData.Length || syncherData[sensorIndex] == null) |
|||
{ |
|||
CreateSyncherData(sensorIndex); |
|||
} |
|||
|
|||
// set new frame
|
|||
//Debug.Log("Setting bt-frame for syncher index " + sensorIndex + ". Timestamp: " + frameTimestamp);
|
|||
syncherData[sensorIndex].btTimestamp = frameTimestamp; |
|||
|
|||
// check for synched body frames
|
|||
bool bAllSynched = numSensors > 1 && iMaster >= 0 && syncherData[iMaster] != null && syncherData[iMaster].btTimestamp != 0; |
|||
|
|||
if (bAllSynched) |
|||
{ |
|||
long masterTime = syncherData[iMaster].btTimestamp; |
|||
|
|||
for (int i = 0; i < numSensors; i++) |
|||
{ |
|||
if(syncherData[i] == null || syncherData[i].btTimestamp == 0) |
|||
{ |
|||
bAllSynched = false; |
|||
break; |
|||
} |
|||
|
|||
long subTime = syncherData[i].btTimestamp; |
|||
long expTime = masterTime + syncherData[i].expDelay; |
|||
long subError = subTime - expTime; |
|||
|
|||
if (i != iMaster && (subTime == 0 || subError < -MAX_TIME_ERROR || subError > MAX_TIME_ERROR)) |
|||
{ |
|||
bAllSynched = false; |
|||
break; |
|||
} |
|||
} |
|||
} |
|||
|
|||
if (bAllSynched) |
|||
{ |
|||
//Debug.Log("Synched bt-frames. Index: " + sensorIndex + " MasterTime: " + syncherData[iMaster].btTimestamp);
|
|||
|
|||
// process synched body frames
|
|||
for (int i = 0; i < numSensors; i++) |
|||
{ |
|||
Kinect4AzureInterface sensorInt = sensorInts[i]; |
|||
KinectInterop.SensorData sensorData = sensorDatas[i]; |
|||
|
|||
//Debug.Log(" Processing bt-frame " + i + ". Timestamp: " + syncherData[i].btTimestamp);
|
|||
sensorInt.ProcessBodyFrame(sensorData, IntPtr.Zero, true); |
|||
sensorInt.ProcessBtSensorCapture(sensorData); |
|||
} |
|||
} |
|||
else |
|||
{ |
|||
//Debug.Log("Bt-frames not synched. Index: " + sensorIndex + " ThisTime: " + syncherData[sensorIndex].btTimestamp +
|
|||
// ", MasterTime: " + syncherData[iMaster].btTimestamp + ", diff: " + (syncherData[iMaster].btTimestamp - syncherData[sensorIndex].btTimestamp));
|
|||
} |
|||
} |
|||
} |
|||
|
|||
|
|||
// creates and returns syncher data, as needed
|
|||
private SyncherSensorData CreateSyncherData(int sensorIndex) |
|||
{ |
|||
if(syncherData == null || numSensors != syncherData.Length) |
|||
{ |
|||
syncherData = new SyncherSensorData[numSensors]; |
|||
} |
|||
|
|||
if(syncherData[sensorIndex] == null) |
|||
{ |
|||
syncherData[sensorIndex] = new SyncherSensorData(); |
|||
syncherData[sensorIndex].expDelay = expectedDelays[sensorIndex]; |
|||
|
|||
syncherData[sensorIndex].capture = null; |
|||
syncherData[sensorIndex].pushCapture = null; |
|||
} |
|||
|
|||
return syncherData[sensorIndex]; |
|||
} |
|||
|
|||
} |
|||
} |
@ -0,0 +1,11 @@ |
|||
fileFormatVersion: 2 |
|||
guid: a4978d3a33592e54f9bcb609ec842a27 |
|||
MonoImporter: |
|||
externalObjects: {} |
|||
serializedVersion: 2 |
|||
defaultReferences: [] |
|||
executionOrder: 0 |
|||
icon: {instanceID: 0} |
|||
userData: |
|||
assetBundleName: |
|||
assetBundleVariant: |
@ -0,0 +1,628 @@ |
|||
using System.Collections; |
|||
using System.Collections.Generic; |
|||
using UnityEngine; |
|||
using com.rfilkov.kinect; |
|||
|
|||
|
|||
namespace com.rfilkov.kinect |
|||
{ |
|||
/// <summary>
|
|||
/// KinectFloorDetector is based on the 'Azure Kinect Floor Plane Detection Sample' that demonstrates one way to estimate the floor plane.
|
|||
/// </summary>
|
|||
public class KinectFloorDetector |
|||
{ |
|||
/// <summary>
|
|||
/// Smoothing factor used for sensor position and rotation update.
|
|||
/// </summary>
|
|||
public float smoothFactor = 5f; |
|||
|
|||
// reference to the sensor data
|
|||
private KinectInterop.SensorData sensorData = null; |
|||
private Vector3 spaceScale = Vector3.one; |
|||
private ulong lastDepthFrameTime = 0; |
|||
|
|||
// IMU data
|
|||
private Vector3 imuUpVector = Vector3.zero; |
|||
//private Transform imuVectorTrans = null;
|
|||
|
|||
// data buffers
|
|||
private Vector3[] depth2SpaceTable = null; |
|||
private int depth2SpaceWidth = 0, depth2SpaceHeight = 0; |
|||
private float[] histMinMax = null; |
|||
private float[] planePosNorm = null; |
|||
|
|||
private int binAggregation = 6; |
|||
public int minFloorPointCount = 1024; |
|||
public float planeMaxTiltInDeg = 5f; |
|||
private float histBinSize = 0f; |
|||
private int histBufferLength = 0; |
|||
|
|||
// compute buffers
|
|||
private ComputeBuffer pointCloudSpaceBuffer = null; |
|||
private ComputeBuffer pointCloudDepthBuffer = null; |
|||
|
|||
private ComputeBuffer pointCloudPosBuffer = null; |
|||
private ComputeBuffer pointCloudOfsBuffer = null; |
|||
private ComputeBuffer pointCloudMaskBuffer = null; |
|||
|
|||
private ComputeBuffer ofsHistMinMaxBuffer = null; |
|||
private ComputeBuffer ofsHistBinLeftBuffer = null; |
|||
private ComputeBuffer ofsHistBinCountBuffer = null; |
|||
private ComputeBuffer histCumulativeCountBuffer = null; |
|||
private ComputeBuffer planeIndicesBuffer = null; |
|||
private ComputeBuffer planePosNormBuffer = null; |
|||
|
|||
// compute shaders
|
|||
private ComputeShader floorDetOffsetEstShader = null; |
|||
private int floorDetOffsetEstKernel = -1; |
|||
|
|||
private ComputeShader floorDetOffsetMinMaxShader = null; |
|||
private int floorDetOffsetMinMaxKernel = -1; |
|||
|
|||
private ComputeShader floorDetOffsetHistShader = null; |
|||
private int floorDetOffsetHistKernel = -1; |
|||
|
|||
private ComputeShader floorDetPlaneEstShader = null; |
|||
private int floorDetPlaneEstKernel = -1; |
|||
|
|||
// results
|
|||
private bool bPlaneValid = false; |
|||
private Vector3 vPlanePos = Vector3.zero; |
|||
private Vector3 vPlaneNorm = Vector3.up; |
|||
private Quaternion qSensorRot = Quaternion.identity; |
|||
private Plane floorPlane = new Plane(); |
|||
private float fSensorHeight = 1f; |
|||
|
|||
// time
|
|||
private const float SMOOTH_TIME_THRESHOLD = 1f; |
|||
private float fLastTimeSecs = 0f; |
|||
|
|||
// routine params
|
|||
private const int WAIT_FRAMES_BEFORE_GPUGET = 2; |
|||
private int minDepthDistance = 0; |
|||
private int maxDepthDistance = 10000; |
|||
|
|||
//private bool isImuVectorSet = false;
|
|||
private bool isDepthFrameSet = false; |
|||
|
|||
private MonoBehaviour callerInstance = null; |
|||
private IEnumerator floorRoutine = null; |
|||
private bool isRoutineRunning = false; |
|||
|
|||
|
|||
/// <summary>
|
|||
/// Checks if a floor plane has been detected or not.
|
|||
/// </summary>
|
|||
/// <returns>true if the floor plane is valid, false otherwise</returns>
|
|||
public bool IsFloorValid() |
|||
{ |
|||
return bPlaneValid; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Gets the timestamp of the depth frame used for floor plane detection.
|
|||
/// </summary>
|
|||
/// <returns>Depth frame timestamp</returns>
|
|||
public ulong GetDepthTimestamp() |
|||
{ |
|||
return lastDepthFrameTime; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Gets the floor plane position.
|
|||
/// </summary>
|
|||
/// <returns>Floor plane position</returns>
|
|||
public Vector3 GetFloorPosition() |
|||
{ |
|||
return vPlanePos; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Gets the floor plane normal.
|
|||
/// </summary>
|
|||
/// <returns>Floor plane normal</returns>
|
|||
public Vector3 GetFloorNormal() |
|||
{ |
|||
return vPlaneNorm; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Gets the detected floor plane.
|
|||
/// </summary>
|
|||
/// <returns>Detected floor plane</returns>
|
|||
public Plane GetFloorPlane() |
|||
{ |
|||
return floorPlane; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Gets the estimated sensor position, in meters.
|
|||
/// </summary>
|
|||
/// <returns>Sensor position, in meters</returns>
|
|||
public Vector3 GetSensorPosition() |
|||
{ |
|||
return new Vector3(0f, fSensorHeight, 0f); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Gets the estimated sensor rotation.
|
|||
/// </summary>
|
|||
/// <returns>Sensor rotation</returns>
|
|||
public Quaternion GetSensorRotation() |
|||
{ |
|||
return qSensorRot; |
|||
} |
|||
|
|||
|
|||
/// <summary>
|
|||
/// Initializes the buffers and shaders used by the floor detector.
|
|||
/// </summary>
|
|||
/// <param name="sensorData">Sensor data</param>
|
|||
/// <param name="maxDepthMm">Max depth distance in mm</param>
|
|||
public void InitFloorDetector(MonoBehaviour caller, KinectInterop.SensorData sensorData, int maxDepthMm) |
|||
{ |
|||
this.callerInstance = caller; |
|||
this.sensorData = sensorData; |
|||
|
|||
if (sensorData == null || sensorData.depthImageWidth == 0 || sensorData.depthImageHeight == 0) |
|||
return; |
|||
|
|||
if (floorDetOffsetEstShader == null) |
|||
{ |
|||
floorDetOffsetEstShader = Resources.Load("FloorDetectionOffsetEstShader") as ComputeShader; |
|||
floorDetOffsetEstKernel = floorDetOffsetEstShader != null ? floorDetOffsetEstShader.FindKernel("EstimatePointCloudPosOfs") : -1; |
|||
} |
|||
|
|||
if (floorDetOffsetMinMaxShader == null) |
|||
{ |
|||
floorDetOffsetMinMaxShader = Resources.Load("FloorDetectionOffsetMinMaxShader") as ComputeShader; |
|||
floorDetOffsetMinMaxKernel = floorDetOffsetMinMaxShader != null ? floorDetOffsetMinMaxShader.FindKernel("EstimateOffsetMinMax") : -1; |
|||
} |
|||
|
|||
if (floorDetOffsetHistShader == null) |
|||
{ |
|||
floorDetOffsetHistShader = Resources.Load("FloorDetectionOffsetHistShader") as ComputeShader; |
|||
floorDetOffsetHistKernel = floorDetOffsetHistShader != null ? floorDetOffsetHistShader.FindKernel("EstimateOffsetHist") : -1; |
|||
} |
|||
|
|||
if (floorDetPlaneEstShader == null) |
|||
{ |
|||
floorDetPlaneEstShader = Resources.Load("FloorDetectionPlanePointsShader") as ComputeShader; |
|||
floorDetPlaneEstKernel = floorDetPlaneEstShader != null ? floorDetPlaneEstShader.FindKernel("EstimatePlanePoints") : -1; |
|||
} |
|||
|
|||
if (pointCloudSpaceBuffer == null) |
|||
{ |
|||
int spaceBufferLength = sensorData.depthImageWidth * sensorData.depthImageHeight * 3; |
|||
pointCloudSpaceBuffer = new ComputeBuffer(spaceBufferLength, sizeof(float)); |
|||
} |
|||
|
|||
if(pointCloudDepthBuffer == null) |
|||
{ |
|||
int depthBufferLength = (sensorData.depthImageWidth * sensorData.depthImageHeight) >> 1; |
|||
pointCloudDepthBuffer = new ComputeBuffer(depthBufferLength, sizeof(uint)); |
|||
} |
|||
|
|||
if (pointCloudPosBuffer == null) |
|||
{ |
|||
int posBufferLength = sensorData.depthImageWidth * sensorData.depthImageHeight * 3; |
|||
pointCloudPosBuffer = new ComputeBuffer(posBufferLength, sizeof(float)); |
|||
} |
|||
|
|||
if (pointCloudOfsBuffer == null) |
|||
{ |
|||
int ofsBufferLength = sensorData.depthImageWidth * sensorData.depthImageHeight; |
|||
pointCloudOfsBuffer = new ComputeBuffer(ofsBufferLength, sizeof(float)); |
|||
} |
|||
|
|||
if (pointCloudMaskBuffer == null) |
|||
{ |
|||
int maskBufferLength = sensorData.depthImageWidth * sensorData.depthImageHeight; |
|||
pointCloudMaskBuffer = new ComputeBuffer(maskBufferLength, sizeof(int)); |
|||
} |
|||
|
|||
if (ofsHistMinMaxBuffer == null) |
|||
{ |
|||
histMinMax = new float[2]; |
|||
ofsHistMinMaxBuffer = new ComputeBuffer(histMinMax.Length, sizeof(float)); |
|||
} |
|||
|
|||
// hist bin size
|
|||
float planeDisplacementRangeInMeters = 0.050f; // 5 cm in meters
|
|||
//binAggregation = 6;
|
|||
histBinSize = planeDisplacementRangeInMeters / binAggregation; |
|||
|
|||
float fMaxDepth = (float)maxDepthMm / 1000f; |
|||
histBufferLength = Mathf.FloorToInt(2 * fMaxDepth / histBinSize) + 1; |
|||
//Debug.Log("histBinSize: " + histBinSize + ", histBufferLength: " + histBufferLength);
|
|||
|
|||
if (ofsHistBinLeftBuffer == null) |
|||
{ |
|||
ofsHistBinLeftBuffer = new ComputeBuffer(histBufferLength, sizeof(float)); |
|||
} |
|||
|
|||
if (ofsHistBinCountBuffer == null) |
|||
{ |
|||
ofsHistBinCountBuffer = new ComputeBuffer(histBufferLength, sizeof(uint)); |
|||
} |
|||
|
|||
if (histCumulativeCountBuffer == null) |
|||
{ |
|||
histCumulativeCountBuffer = new ComputeBuffer(histBufferLength, sizeof(uint)); |
|||
} |
|||
|
|||
if(planeIndicesBuffer == null) |
|||
{ |
|||
int planeIndicesLength = sensorData.depthImageWidth * sensorData.depthImageHeight; |
|||
planeIndicesBuffer = new ComputeBuffer(planeIndicesLength, sizeof(uint)); |
|||
} |
|||
|
|||
if(planePosNormBuffer == null) |
|||
{ |
|||
planePosNorm = new float[4 * 3]; // pos & norm are v3
|
|||
planePosNormBuffer = new ComputeBuffer(planePosNorm.Length, sizeof(float)); |
|||
} |
|||
|
|||
spaceScale = sensorData.sensorSpaceScale; |
|||
//minFloorPointCount = 1024;
|
|||
//planeMaxTiltInDeg = 5f;
|
|||
imuUpVector = Vector3.up; |
|||
|
|||
bPlaneValid = false; |
|||
|
|||
if(callerInstance != null) |
|||
{ |
|||
isRoutineRunning = true; |
|||
floorRoutine = UpdateFloorAsync(); |
|||
callerInstance.StartCoroutine(floorRoutine); |
|||
} |
|||
} |
|||
|
|||
|
|||
/// <summary>
|
|||
/// Releases the buffers and shaders used by the floor detector.
|
|||
/// </summary>
|
|||
public void FinishFloorDetector() |
|||
{ |
|||
if(isRoutineRunning) |
|||
{ |
|||
isRoutineRunning = false; |
|||
callerInstance.StopCoroutine(floorRoutine); |
|||
floorRoutine = null; |
|||
} |
|||
|
|||
if (pointCloudSpaceBuffer != null) |
|||
{ |
|||
pointCloudSpaceBuffer.Dispose(); |
|||
pointCloudSpaceBuffer = null; |
|||
} |
|||
|
|||
if (pointCloudDepthBuffer != null) |
|||
{ |
|||
pointCloudDepthBuffer.Dispose(); |
|||
pointCloudDepthBuffer = null; |
|||
} |
|||
|
|||
if (pointCloudPosBuffer != null) |
|||
{ |
|||
pointCloudPosBuffer.Dispose(); |
|||
pointCloudPosBuffer = null; |
|||
} |
|||
|
|||
if (pointCloudOfsBuffer != null) |
|||
{ |
|||
pointCloudOfsBuffer.Dispose(); |
|||
pointCloudOfsBuffer = null; |
|||
} |
|||
|
|||
if (pointCloudMaskBuffer != null) |
|||
{ |
|||
pointCloudMaskBuffer.Dispose(); |
|||
pointCloudMaskBuffer = null; |
|||
} |
|||
|
|||
if (ofsHistMinMaxBuffer != null) |
|||
{ |
|||
ofsHistMinMaxBuffer.Dispose(); |
|||
ofsHistMinMaxBuffer = null; |
|||
} |
|||
|
|||
if (ofsHistBinLeftBuffer != null) |
|||
{ |
|||
ofsHistBinLeftBuffer.Dispose(); |
|||
ofsHistBinLeftBuffer = null; |
|||
} |
|||
|
|||
if (ofsHistBinCountBuffer != null) |
|||
{ |
|||
ofsHistBinCountBuffer.Dispose(); |
|||
ofsHistBinCountBuffer = null; |
|||
} |
|||
|
|||
if (histCumulativeCountBuffer != null) |
|||
{ |
|||
histCumulativeCountBuffer.Dispose(); |
|||
histCumulativeCountBuffer = null; |
|||
} |
|||
|
|||
if(planeIndicesBuffer != null) |
|||
{ |
|||
planeIndicesBuffer.Dispose(); |
|||
planeIndicesBuffer = null; |
|||
} |
|||
|
|||
if(planePosNormBuffer != null) |
|||
{ |
|||
planePosNormBuffer.Dispose(); |
|||
planePosNormBuffer = null; |
|||
} |
|||
|
|||
if (floorDetOffsetEstShader != null) |
|||
{ |
|||
floorDetOffsetEstShader = null; |
|||
} |
|||
|
|||
if (floorDetOffsetMinMaxShader != null) |
|||
{ |
|||
floorDetOffsetMinMaxShader = null; |
|||
} |
|||
|
|||
if (floorDetOffsetHistShader != null) |
|||
{ |
|||
floorDetOffsetHistShader = null; |
|||
} |
|||
|
|||
if(floorDetPlaneEstShader != null) |
|||
{ |
|||
floorDetPlaneEstShader = null; |
|||
} |
|||
} |
|||
|
|||
|
|||
///// <summary>
|
|||
///// Updates the IMU up vector from the sample.
|
|||
///// </summary>
|
|||
///// <param name="imuAcc">IMU accelerometer sample</param>
|
|||
///// <param name="accDepthRot">Extrinsics rotation between the accelerometer and depth sensor</param>
|
|||
//public void UpdateImuUpVector(Vector3 imuAcc, float[] accDepthRot)
|
|||
//{
|
|||
// Vector3 Rx = new Vector3(accDepthRot[0], accDepthRot[1], accDepthRot[2]);
|
|||
// Vector3 Ry = new Vector3(accDepthRot[3], accDepthRot[4], accDepthRot[5]);
|
|||
// Vector3 Rz = new Vector3(accDepthRot[6], accDepthRot[7], accDepthRot[8]);
|
|||
// Vector3 depthAcc = new Vector3( Vector3.Dot(Rx, imuAcc), Vector3.Dot(Ry, imuAcc), Vector3.Dot(Rz, imuAcc));
|
|||
|
|||
// //Vector3 depthGravity = depthAcc * -1f;
|
|||
// //imuUpVector = (depthGravity * -1f).normalized;
|
|||
// imuUpVector = depthAcc.normalized;
|
|||
// //isImuVectorSet = true;
|
|||
|
|||
// //Debug.Log("imuUpVector: " + imuUpVector);
|
|||
//}
|
|||
|
|||
|
|||
/// <summary>
|
|||
/// Updates the IMU up vector.
|
|||
/// </summary>
|
|||
/// <param name="imuUpVector">IMU up vector</param>
|
|||
public void UpdateImuUpVector(Vector3 imuUpVector) |
|||
{ |
|||
this.imuUpVector = imuUpVector.normalized; |
|||
} |
|||
|
|||
|
|||
/// <summary>
|
|||
/// Executes the floor detector shaders with the current depth frame data.
|
|||
/// </summary>
|
|||
/// <param name="depthFrame">Depth frame data</param>
|
|||
/// <param name="depthFrameTime">Depth frame time</param>
|
|||
/// <param name="depthFrameLock">Depth frame lock object</param>
|
|||
/// <param name="minDistance">Min depth distance, in meters</param>
|
|||
/// <param name="maxDistance">Max depth distance, in meters</param>
|
|||
/// <returns>true if the floor plane is detected, false otherwise</returns>
|
|||
public bool UpdateFloorDetector(ushort[] depthFrame, ulong depthFrameTime, ref object depthFrameLock, float minDistance, float maxDistance) |
|||
{ |
|||
if (sensorData == null || depthFrame == null || sensorData.depthImageWidth == 0 || sensorData.depthImageHeight == 0) |
|||
return false; |
|||
if (lastDepthFrameTime == depthFrameTime) |
|||
return false; |
|||
|
|||
lastDepthFrameTime = depthFrameTime; |
|||
|
|||
minDepthDistance = (int)(minDistance * 1000f); |
|||
maxDepthDistance = (int)(maxDistance * 1000f); |
|||
|
|||
if (depth2SpaceWidth != sensorData.depthImageWidth || depth2SpaceHeight != sensorData.depthImageHeight) |
|||
{ |
|||
depth2SpaceTable = sensorData.sensorInterface.GetDepthCameraSpaceTable(sensorData); |
|||
depth2SpaceWidth = sensorData.depthImageWidth; |
|||
depth2SpaceHeight = sensorData.depthImageHeight; |
|||
|
|||
pointCloudSpaceBuffer.SetData(depth2SpaceTable); |
|||
depth2SpaceTable = null; |
|||
//Debug.Log("Set space table for width: " + depth2SpaceWidth + ", height: " + depth2SpaceHeight);
|
|||
} |
|||
|
|||
// FloorDetectionOffsetEstShader
|
|||
//lock(depthFrameLock)
|
|||
{ |
|||
KinectInterop.SetComputeBufferData(pointCloudDepthBuffer, depthFrame, depthFrame.Length >> 1, sizeof(uint)); |
|||
} |
|||
|
|||
isDepthFrameSet = true; |
|||
|
|||
////Debug.Log("imuUpVector: " + imuUpVector);
|
|||
//if(imuVectorTrans == null)
|
|||
//{
|
|||
// GameObject imuVectorObj = GameObject.CreatePrimitive(PrimitiveType.Cube);
|
|||
// imuVectorObj.name = "ImuVectorObj";
|
|||
|
|||
// imuVectorTrans = imuVectorObj.transform;
|
|||
// imuVectorTrans.localScale = new Vector3(0.1f, 0.2f, 0.5f);
|
|||
// imuVectorTrans.position = new Vector3(0, 1f, 1f);
|
|||
//}
|
|||
|
|||
//imuVectorTrans.rotation = Quaternion.LookRotation(imuUpVector.normalized);
|
|||
|
|||
if(bPlaneValid) |
|||
{ |
|||
bPlaneValid = false; |
|||
return true; |
|||
} |
|||
|
|||
return false; |
|||
} |
|||
|
|||
|
|||
// updates the floor parameters async
|
|||
private IEnumerator UpdateFloorAsync() |
|||
{ |
|||
while(isRoutineRunning) |
|||
{ |
|||
// wait for imu vector & depth frame
|
|||
while (/**!isImuVectorSet ||*/ !isDepthFrameSet) |
|||
{ |
|||
yield return null; |
|||
} |
|||
|
|||
//isImuVectorSet = false;
|
|||
isDepthFrameSet = false; |
|||
|
|||
KinectInterop.SetComputeShaderInt2(floorDetOffsetEstShader, "PointCloudRes", sensorData.depthImageWidth, sensorData.depthImageHeight); |
|||
//KinectInterop.SetComputeShaderFloat2(floorDetOffsetEstShader, "SpaceScale", sensorData.sensorSpaceScale.x, sensorData.sensorSpaceScale.y);
|
|||
KinectInterop.SetComputeShaderFloat3(floorDetOffsetEstShader, "ImuUpVector", imuUpVector); |
|||
|
|||
floorDetOffsetEstShader.SetInt("MinDepth", minDepthDistance); |
|||
floorDetOffsetEstShader.SetInt("MaxDepth", maxDepthDistance); |
|||
|
|||
floorDetOffsetEstShader.SetBuffer(floorDetOffsetEstKernel, "SpaceTable", pointCloudSpaceBuffer); |
|||
floorDetOffsetEstShader.SetBuffer(floorDetOffsetEstKernel, "DepthMap", pointCloudDepthBuffer); |
|||
|
|||
floorDetOffsetEstShader.SetBuffer(floorDetOffsetEstKernel, "PointCloudPos", pointCloudPosBuffer); |
|||
floorDetOffsetEstShader.SetBuffer(floorDetOffsetEstKernel, "PointCloudOfs", pointCloudOfsBuffer); |
|||
floorDetOffsetEstShader.SetBuffer(floorDetOffsetEstKernel, "PointCloudMask", pointCloudMaskBuffer); |
|||
|
|||
floorDetOffsetEstShader.Dispatch(floorDetOffsetEstKernel, sensorData.depthImageWidth / 8, sensorData.depthImageHeight / 8, 1); |
|||
|
|||
// FloorDetectionOffsetMinMaxShader
|
|||
KinectInterop.SetComputeShaderInt2(floorDetOffsetMinMaxShader, "PointCloudRes", sensorData.depthImageWidth, sensorData.depthImageHeight); |
|||
floorDetOffsetMinMaxShader.SetInt("OfsHistBinLength", histBufferLength); |
|||
floorDetOffsetMinMaxShader.SetBuffer(floorDetOffsetMinMaxKernel, "PointCloudOfs", pointCloudOfsBuffer); |
|||
floorDetOffsetMinMaxShader.SetBuffer(floorDetOffsetMinMaxKernel, "PointCloudMask", pointCloudMaskBuffer); |
|||
|
|||
floorDetOffsetMinMaxShader.SetBuffer(floorDetOffsetMinMaxKernel, "OfsMinMax", ofsHistMinMaxBuffer); |
|||
floorDetOffsetMinMaxShader.SetBuffer(floorDetOffsetMinMaxKernel, "OfsHistBinCount", ofsHistBinCountBuffer); |
|||
|
|||
floorDetOffsetMinMaxShader.Dispatch(floorDetOffsetMinMaxKernel, 1, 1, 1); |
|||
|
|||
//ofsHistMinMaxBuffer.GetData(histMinMax);
|
|||
//Debug.Log("Hist min: " + histMinMax[0] + ", max: " + histMinMax[1]);
|
|||
|
|||
// FloorDetectionOffsetHistShader
|
|||
KinectInterop.SetComputeShaderInt2(floorDetOffsetHistShader, "PointCloudRes", sensorData.depthImageWidth, sensorData.depthImageHeight); |
|||
//floorDetOffsetHistShader.SetInt("PointCloudOfsLength", sensorData.depthImageWidth * sensorData.depthImageHeight);
|
|||
floorDetOffsetHistShader.SetInt("OfsHistBinLength", histBufferLength); |
|||
floorDetOffsetHistShader.SetFloat("BinSize", histBinSize); |
|||
|
|||
floorDetOffsetHistShader.SetBuffer(floorDetOffsetHistKernel, "PointCloudOfs", pointCloudOfsBuffer); |
|||
floorDetOffsetHistShader.SetBuffer(floorDetOffsetHistKernel, "PointCloudMask", pointCloudMaskBuffer); |
|||
floorDetOffsetHistShader.SetBuffer(floorDetOffsetHistKernel, "OfsMinMax", ofsHistMinMaxBuffer); |
|||
|
|||
floorDetOffsetHistShader.SetBuffer(floorDetOffsetHistKernel, "OfsHistBinCount", ofsHistBinCountBuffer); |
|||
//floorDetOffsetHistShader.SetBuffer(floorDetOffsetHistKernel, "OfsHistBinLeft", ofsHistBinLeftBuffer);
|
|||
|
|||
floorDetOffsetHistShader.Dispatch(floorDetOffsetHistKernel, sensorData.depthImageWidth / 1, sensorData.depthImageHeight / 1, 1); |
|||
//floorDetOffsetHistShader.Dispatch(floorDetOffsetHistKernel, 1, 1, 1);
|
|||
|
|||
// FloorDetectionPlanePointsShader
|
|||
floorDetPlaneEstShader.SetInt("OfsHistBinLength", histBufferLength); |
|||
floorDetPlaneEstShader.SetInt("PointCloudOfsLength", sensorData.depthImageWidth * sensorData.depthImageHeight); |
|||
floorDetPlaneEstShader.SetFloat("BinSize", histBinSize); |
|||
floorDetPlaneEstShader.SetInt("BinAggregation", binAggregation); |
|||
floorDetPlaneEstShader.SetInt("MinimumFloorPointCount", minFloorPointCount / 4); |
|||
|
|||
floorDetPlaneEstShader.SetBuffer(floorDetPlaneEstKernel, "OfsHistBinCount", ofsHistBinCountBuffer); |
|||
floorDetPlaneEstShader.SetBuffer(floorDetPlaneEstKernel, "PointCloudPos", pointCloudPosBuffer); |
|||
floorDetPlaneEstShader.SetBuffer(floorDetPlaneEstKernel, "PointCloudOfs", pointCloudOfsBuffer); |
|||
floorDetPlaneEstShader.SetBuffer(floorDetPlaneEstKernel, "PointCloudMask", pointCloudMaskBuffer); |
|||
floorDetPlaneEstShader.SetBuffer(floorDetPlaneEstKernel, "OfsMinMax", ofsHistMinMaxBuffer); |
|||
|
|||
floorDetPlaneEstShader.SetBuffer(floorDetPlaneEstKernel, "OfsHistBinLeft", ofsHistBinLeftBuffer); |
|||
floorDetPlaneEstShader.SetBuffer(floorDetPlaneEstKernel, "HistCumulativeCount", histCumulativeCountBuffer); |
|||
floorDetPlaneEstShader.SetBuffer(floorDetPlaneEstKernel, "InlierIndices", planeIndicesBuffer); |
|||
floorDetPlaneEstShader.SetBuffer(floorDetPlaneEstKernel, "PlanePosNorm", planePosNormBuffer); |
|||
|
|||
floorDetPlaneEstShader.Dispatch(floorDetPlaneEstKernel, 1, 1, 1); |
|||
|
|||
// wait some frames before GetData()
|
|||
for (int i = 0; i < WAIT_FRAMES_BEFORE_GPUGET; i++) |
|||
{ |
|||
yield return null; |
|||
} |
|||
|
|||
//uint[] histCumCount = new uint[histBufferLength];
|
|||
//histCumulativeCountBuffer.GetData(histCumCount);
|
|||
//uint maxDiffCount = 0;
|
|||
|
|||
//System.Text.StringBuilder sbCumCount = new System.Text.StringBuilder();
|
|||
//for(int i = 1; (i + binAggregation) < histCumCount.Length; i++) // i += binAggregation
|
|||
//{
|
|||
// uint diffCount = histCumCount[i + binAggregation - 1] - histCumCount[i - 1];
|
|||
// if (maxDiffCount < diffCount)
|
|||
// maxDiffCount = diffCount;
|
|||
|
|||
// if (diffCount > 0)
|
|||
// sbCumCount.Append(i).Append('-').Append(diffCount).Append(" ");
|
|||
//}
|
|||
//Debug.Log("histCumCount(" + maxDiffCount + "): " + sbCumCount);
|
|||
|
|||
planePosNormBuffer.GetData(planePosNorm); |
|||
|
|||
vPlanePos = new Vector3(planePosNorm[0], planePosNorm[1], planePosNorm[2]); |
|||
vPlaneNorm = new Vector3(planePosNorm[3], planePosNorm[4], planePosNorm[5]); |
|||
//Vector3 vPlaneOfs = new Vector3(planePosNorm[6], planePosNorm[7], planePosNorm[8]);
|
|||
//Vector3 vPlaneOfs2 = new Vector3(planePosNorm[9], planePosNorm[10], planePosNorm[11]);
|
|||
|
|||
bPlaneValid = (vPlaneNorm != Vector3.zero); |
|||
|
|||
if (bPlaneValid) |
|||
{ |
|||
//Debug.Log("Plane pos: " + vPlanePos + ", norm: " + vPlaneNorm.normalized + ", rot: " + qSensorRot.eulerAngles + ", ofs: " + vPlaneOfs + ", ofs2: " + vPlaneOfs2);
|
|||
|
|||
vPlaneNorm = vPlaneNorm.normalized; |
|||
if (Vector3.Dot(vPlaneNorm, imuUpVector) < 0f) |
|||
{ |
|||
vPlaneNorm = -vPlaneNorm; |
|||
//Debug.Log("Inverted plane normal: " + vPlaneNorm);
|
|||
} |
|||
|
|||
float floorTiltInDeg = Mathf.Acos(Vector3.Dot(vPlaneNorm, imuUpVector)) * Mathf.Rad2Deg; |
|||
if (floorTiltInDeg < planeMaxTiltInDeg) |
|||
{ |
|||
// For reduced jitter, use gravity for floor normal.
|
|||
vPlaneNorm = imuUpVector; |
|||
//Debug.Log("Used gravity for normal: " + vPlaneNorm + ", tiltAngle: " + floorTiltInDeg);
|
|||
} |
|||
|
|||
// get results
|
|||
float fCurTimeSecs = Time.time; |
|||
bool bSmoothResult = (fCurTimeSecs - fLastTimeSecs) < SMOOTH_TIME_THRESHOLD; |
|||
//Debug.Log("SmoothResult: " + bSmoothResult);
|
|||
fLastTimeSecs = fCurTimeSecs; |
|||
|
|||
vPlanePos = new Vector3(vPlanePos.x * spaceScale.x, vPlanePos.y * spaceScale.y, vPlanePos.z * spaceScale.z); |
|||
vPlaneNorm = new Vector3(vPlaneNorm.x * spaceScale.x, vPlaneNorm.y * spaceScale.y, vPlaneNorm.z * spaceScale.z); |
|||
|
|||
Quaternion curSensorRot = Quaternion.FromToRotation(vPlaneNorm, Vector3.up); |
|||
qSensorRot = bSmoothResult ? Quaternion.Slerp(qSensorRot, curSensorRot, smoothFactor * Time.deltaTime) : curSensorRot; |
|||
|
|||
floorPlane = new Plane(vPlaneNorm, vPlanePos); |
|||
float curSensorHeight = floorPlane.GetDistanceToPoint(Vector3.zero); |
|||
fSensorHeight = bSmoothResult ? Mathf.Lerp(fSensorHeight, curSensorHeight, smoothFactor * Time.deltaTime) : curSensorHeight; |
|||
|
|||
//Debug.Log("Floor pos: " + vPlanePos + ", norm: " + vPlaneNorm + ", rot: " + qSensorRot.eulerAngles + ", height: " + curSensorHeight + ", smoothed: " + fSensorHeight);
|
|||
} |
|||
} |
|||
} |
|||
|
|||
} |
|||
} |
@ -0,0 +1,11 @@ |
|||
fileFormatVersion: 2 |
|||
guid: 11fddd48107804c42827afb3bbf5533a |
|||
MonoImporter: |
|||
externalObjects: {} |
|||
serializedVersion: 2 |
|||
defaultReferences: [] |
|||
executionOrder: 0 |
|||
icon: {instanceID: 0} |
|||
userData: |
|||
assetBundleName: |
|||
assetBundleVariant: |
File diff suppressed because it is too large
@ -0,0 +1,11 @@ |
|||
fileFormatVersion: 2 |
|||
guid: eb612f05db283eb42a3801236d897315 |
|||
MonoImporter: |
|||
externalObjects: {} |
|||
serializedVersion: 2 |
|||
defaultReferences: [] |
|||
executionOrder: 0 |
|||
icon: {instanceID: 0} |
|||
userData: |
|||
assetBundleName: |
|||
assetBundleVariant: |
File diff suppressed because it is too large
Some files were not shown because too many files changed in this diff
Loading…
Reference in new issue