Pascal Serrarens 17741d862a First commit
2022-01-12 10:50:57 +01:00

359 lines
16 KiB
C#

using System.Collections;
using UnityEngine;
namespace Passer.Humanoid {
using Humanoid.Tracking;
public class HeadMovements : Movements {
protected HeadTarget headTarget;
#region Start
public override void Start(HumanoidControl _humanoid, HumanoidTarget _target) {
base.Start(_humanoid, _target);
headTarget = (HeadTarget)_target;
if (headTarget.neck.bone.transform != null)
headTarget.neck.bone.transform.rotation = headTarget.neck.target.transform.rotation * headTarget.neck.target.toBoneRotation;
if (headTarget.head.bone.transform != null)
headTarget.head.bone.transform.rotation = headTarget.head.target.transform.rotation * headTarget.head.target.toBoneRotation;
}
#endregion
#region Update
public static void Update(HeadTarget headTarget) {
if (headTarget.head.bone.transform == null || !headTarget.humanoid.calculateBodyPose)
return;
#if hFACE
if (headTarget.neck.target.confidence.rotation < 0.2F && headTarget.head.target.confidence.rotation < 0.2F &&
headTarget.face.leftEye.target.confidence.rotation > 0.2F) {
UpdateHeadBonesFromGazeDirection(headTarget);
}
else {
#endif
if (Application.isPlaying && headTarget.humanoid.primaryTarget == HumanoidControl.PrimaryTarget.Hips) {
UpdateNeckRotation(headTarget);
UpdateNeckPositionFromHips(headTarget);
UpdateHeadPositionFromNeck(headTarget);
}
else {
UpdateHead(headTarget);
UpdateNeck(headTarget);
}
#if hFACE
}
headTarget.face.UpdateMovements();
#endif
}
private static void UpdateHead(HeadTarget headTarget) {
if (headTarget.head.target.transform == null)
return;
headTarget.head.SetBoneRotation(headTarget.head.target.transform.rotation);
// Commented out the requirement for position confidence > 0 because of Oculus Go/GearVR positional issue
if (!Application.isPlaying ||
(headTarget.humanoid.targetsRig.runtimeAnimatorController == null
|| headTarget.head.target.confidence.position > 0)
)
headTarget.head.SetBonePosition(headTarget.head.target.transform.position);
}
private static void UpdateHeadPositionFromNeck(HeadTarget headTarget) {
headTarget.head.SetBoneRotation(headTarget.head.target.transform.rotation);
Vector3 neckPosition = headTarget.neck.bone.transform.position;
Vector3 headPosition = neckPosition + headTarget.head.target.transform.rotation * Vector3.up * headTarget.neck.bone.length;
headTarget.head.SetBonePosition(headPosition);
}
private static void UpdateNeck(HeadTarget headTarget) {
if (headTarget.neck.bone.transform == null)
return;
Vector3 headPosition = headTarget.head.bone.transform.position;
Quaternion headRotation = headTarget.head.bone.transform.rotation;
if (headTarget.neck.target.confidence.rotation > headTarget.head.target.confidence.rotation) {
Quaternion neckRotation = headTarget.neck.target.transform.rotation;
headTarget.neck.SetBoneRotation(neckRotation);
if (headTarget.humanoid.targetsRig.runtimeAnimatorController == null ||
headTarget.head.target.confidence.position > 0) {
Vector3 neckPosition = headPosition - neckRotation * Vector3.up * headTarget.neck.bone.length;
headTarget.neck.SetBonePosition(neckPosition);
}
}
else {
Quaternion hipsTargetRotation = headTarget.humanoid.hipsTarget.hips.bone.targetRotation * Quaternion.Inverse(headTarget.humanoid.hipsTarget.hips.bone.baseRotation);
Quaternion neckRotation =
Quaternion.Slerp(headTarget.head.bone.targetRotation, hipsTargetRotation, 0.3F) * headTarget.neck.bone.baseRotation;
headTarget.neck.SetBoneRotation(neckRotation);
// Commented out the requirement for position confidence > 0 because of Oculus Go/GearVR positional issue
if (!Application.isPlaying || (
headTarget.humanoid.targetsRig.runtimeAnimatorController == null
|| headTarget.head.target.confidence.position > 0
)) {
Vector3 neckPosition = headPosition - neckRotation * Vector3.up * headTarget.neck.bone.length;
headTarget.neck.SetBonePosition(neckPosition);
}
}
headTarget.head.bone.transform.position = headPosition;
headTarget.head.bone.transform.rotation = headRotation;
}
private static void UpdateNeckRotation(HeadTarget headTarget) {
Quaternion headRotation = headTarget.head.bone.transform.rotation;
if (headTarget.neck.target.confidence.rotation > headTarget.head.target.confidence.rotation) {
Quaternion neckRotation = headTarget.neck.target.transform.rotation;
headTarget.neck.SetBoneRotation(neckRotation);
}
else {
Quaternion hipsTargetRotation = headTarget.humanoid.hipsTarget.hips.bone.targetRotation * Quaternion.Inverse(headTarget.humanoid.hipsTarget.hips.bone.baseRotation);
Quaternion neckRotation =
Quaternion.Slerp(headTarget.head.bone.targetRotation, hipsTargetRotation, 0.3F) * headTarget.neck.bone.baseRotation;
headTarget.neck.SetBoneRotation(neckRotation);
}
headTarget.head.bone.transform.rotation = headRotation;
}
private static void UpdateNeckPositionFromHips(HeadTarget headTarget) {
HipsTarget hipsTarget = headTarget.humanoid.hipsTarget;
Vector3 hipsPosition = hipsTarget.hips.bone.transform.position;
if (hipsTarget.chest.bone.transform != null) {
Vector3 chestTopPosition = hipsTarget.chest.bone.transform.position + hipsTarget.chest.bone.targetRotation * Vector3.up * hipsTarget.chest.bone.length;
//Vector3 spineVector = chestTopPosition - hipsPosition;
headTarget.neck.SetBonePosition(chestTopPosition);
}
else if (hipsTarget.spine.bone.transform != null) {
Vector3 spineTopPosition = hipsTarget.spine.bone.transform.position + hipsTarget.spine.bone.targetRotation * Vector3.up * hipsTarget.spine.bone.length;
//Vector3 spineVector = spineTopPosition - hipsPosition;
headTarget.neck.SetBonePosition(spineTopPosition);
}
}
public static Quaternion CalculateNeckRotation(Quaternion hipRotation, Quaternion headRotation) {
Vector3 headAnglesCharacterSpace = (Quaternion.Inverse(hipRotation) * headRotation).eulerAngles;
float neckYRotation = UnityAngles.Normalize(headAnglesCharacterSpace.y) * 0.6F;
Quaternion neckRotation = hipRotation * Quaternion.Euler(headAnglesCharacterSpace.x, neckYRotation, headAnglesCharacterSpace.z);
return neckRotation;
}
public static Vector3 CalculateNeckPositionFromEyes(Vector3 eyePosition, Quaternion eyeRotation, Vector3 eye2neck) {
Vector3 neckPosition = eyePosition + eyeRotation * eye2neck;
return neckPosition;
}
public static Vector3 CalculateNeckPositionFromHead(Vector3 headPosition, Quaternion headRotation, float neckBoneLength) {
Vector3 neckPosition = headPosition - headRotation * Vector3.up * neckBoneLength;
return neckPosition;
}
static float lastTime;
private static void UpdateHeadBonesFromGazeDirection(HeadTarget headTarget) {
if (headTarget.humanoid.hipsTarget.hips.bone.transform == null)
return;
#if hFACE
Quaternion neckParentRotation = headTarget.humanoid.hipsTarget.hips.bone.transform.rotation * headTarget.humanoid.hipsTarget.hips.bone.toTargetRotation;
Quaternion oldNeckRotation = headTarget.neck.bone.transform.rotation * headTarget.neck.bone.toTargetRotation;
Vector3 localNeckAngles = (Quaternion.Inverse(neckParentRotation) * oldNeckRotation).eulerAngles;
Quaternion predictedRotation = Quaternion.Slerp(Quaternion.identity, headTarget.neck.bone.rotationVelocity, Time.deltaTime);
Quaternion eyeRotation = headTarget.face.leftEye.target.transform.rotation;
Vector3 localEyeAngles = (Quaternion.Inverse(oldNeckRotation) * eyeRotation).eulerAngles;
if (Quaternion.Angle(oldNeckRotation, eyeRotation) < 2) {
lastTime = Time.time;
return;
}
float headTensionY = CalculateTension(localNeckAngles, headTarget.neck.bone.maxAngles);
float eyeTensionY = CalculateTension(localEyeAngles, headTarget.face.leftEye.bone.maxAngles);
float f = Mathf.Clamp01(1 - (headTensionY / eyeTensionY));
Quaternion targetRotation = Quaternion.LookRotation(headTarget.face.gazeDirection, Vector3.up); // this will overshoot, because the lookdirection itself is affected...
Quaternion neckRotation = Quaternion.RotateTowards(oldNeckRotation, targetRotation, f * 2);
Quaternion desiredRotation = Quaternion.Inverse(oldNeckRotation) * neckRotation;
// This limits the speed changes
float deltaTime = Time.time - lastTime;
Quaternion resultRotation = Quaternion.RotateTowards(predictedRotation, desiredRotation, deltaTime * 100);
neckRotation = oldNeckRotation * resultRotation;
headTarget.neck.bone.transform.rotation = neckRotation * headTarget.neck.target.toBoneRotation;
lastTime = Time.time;
#endif
}
private void UpdateHeadBonesFromLookDirection() {
Quaternion neckParentRotation = headTarget.humanoid.hipsTarget.hips.bone.transform.rotation * headTarget.humanoid.hipsTarget.hips.bone.toTargetRotation;
Quaternion oldNeckRotation = headTarget.neck.bone.transform.rotation * headTarget.neck.bone.toTargetRotation;
Vector3 localNeckAngles = (Quaternion.Inverse(neckParentRotation) * oldNeckRotation).eulerAngles;
Quaternion predictedRotation = Quaternion.Slerp(Quaternion.identity, headTarget.neck.bone.rotationVelocity, Time.deltaTime);
Quaternion targetRotation = Quaternion.LookRotation(headTarget.lookDirection, Vector3.up); // this will overshoot, because the lookdirection itself is affected...
Quaternion neckRotation = targetRotation;
Quaternion desiredRotation = Quaternion.Inverse(oldNeckRotation) * neckRotation;
// This limits the speed changes
Quaternion resultRotation = Quaternion.RotateTowards(predictedRotation, desiredRotation, Time.deltaTime * 6);
neckRotation = oldNeckRotation * resultRotation;
headTarget.neck.bone.transform.rotation = neckRotation * headTarget.neck.target.toBoneRotation;
}
#endregion
private static float CalculateTension(Vector3 angle, Vector3 maxAngle) {
float dX = CalculateTension(angle.x, maxAngle.x);
float dY = CalculateTension(angle.y, maxAngle.y);
float dZ = CalculateTension(angle.z, maxAngle.z);
return (dX + dY + dZ);
}
private static float CalculateTension(float angle, float maxAngle) {
return (maxAngle != 0) ? Mathf.Abs(Angle.Normalize(angle) / maxAngle) : 0;
}
}
public class HeadCollisionHandler : MonoBehaviour {
static public Collider AddHeadCollider(GameObject headObject) {
HeadTarget headTarget = headObject.GetComponent<HeadTarget>();
if (headTarget.headRigidbody == null) {
headTarget.headRigidbody = headObject.AddComponent<Rigidbody>();
if (headTarget.headRigidbody != null) {
headTarget.headRigidbody.mass = 1;
headTarget.headRigidbody.useGravity = false;
headTarget.headRigidbody.isKinematic = true;
}
}
Collider collider = headObject.GetComponent<Collider>();
if (collider != null)
return collider;
SphereCollider sphereCollider = headObject.AddComponent<SphereCollider>();
if (sphereCollider != null) {
sphereCollider.isTrigger = true;
sphereCollider.radius = 0.1F;
sphereCollider.center = new Vector3(0, 0, 0.05F);
}
return sphereCollider;
}
private HumanoidControl humanoid;
private Material fadeMaterial;
public void Initialize(HumanoidControl _humanoid) {
humanoid = _humanoid;
//if (humanoid.headTarget.collisionFader)
// FindFadeMaterial();
}
private void FindFadeMaterial() {
#if pUNITYXR
if (humanoid.unityXR == null || humanoid.unityXR.trackerComponent == null)
return;
Passer.Tracking.UnityXR unityXRtracker = humanoid.unityXR.trackerComponent as Passer.Tracking.UnityXR;
if (unityXRtracker.hmd == null || unityXRtracker.hmd.unityCamera == null)
#endif
return;
#if pUNITYXR
Transform plane = unityXRtracker.hmd.unityCamera.transform.Find("Fader");
#endif
#if pUNITYXR || hLEGACYXR
if (plane != null) {
Renderer renderer = plane.GetComponent<Renderer>();
if (renderer != null) {
renderer.enabled = true;
fadeMaterial = renderer.sharedMaterial;
Color color = fadeMaterial.color;
color.a = 0.0F;
fadeMaterial.color = color;
}
}
#endif
}
void OnTriggerEnter(Collider otherCollider) {
if (fadeMaterial == null || otherCollider.isTrigger)
return;
if (otherCollider.attachedRigidbody == null) {
DoFadeOut();
humanoid.headTarget.isInsideCollider = true;
}
}
void OnTriggerExit(Collider otherCollider) {
if (fadeMaterial == null || otherCollider.isTrigger)
return;
if (otherCollider.attachedRigidbody == null) {
DoFadeIn();
humanoid.headTarget.isInsideCollider = false;
}
}
private void DoFadeOut() {
StartCoroutine(FadeOut(fadeMaterial));
}
private void DoFadeIn() {
StartCoroutine(FadeIn(fadeMaterial));
}
private bool faded = false;
public float fadeTime = 0.3F;
public IEnumerator FadeOut(Material fadeMaterial) {
if (!faded) {
float elapsedTime = 0.0f;
Color color = fadeMaterial.color;
color.a = 0.0f;
fadeMaterial.color = color;
while (elapsedTime < fadeTime) {
yield return new WaitForEndOfFrame();
elapsedTime += Time.deltaTime;
color.a = Mathf.Clamp01(elapsedTime / fadeTime);
fadeMaterial.color = color;
}
}
faded = true;
}
public IEnumerator FadeIn(Material fadeMaterial) {
if (faded) {
float elapsedTime = 0.0f;
Color color = fadeMaterial.color;
while (elapsedTime < fadeTime) {
yield return new WaitForEndOfFrame();
elapsedTime += Time.deltaTime;
color.a = 1.0f - Mathf.Clamp01(elapsedTime / fadeTime);
fadeMaterial.color = color;
}
}
faded = false;
}
}
}