diff --git a/Runtime/HumanoidFree/Scripts/Targets/HeadTarget.cs b/Runtime/HumanoidFree/Scripts/Targets/HeadTarget.cs
index f80ed97..d77156e 100644
--- a/Runtime/HumanoidFree/Scripts/Targets/HeadTarget.cs
+++ b/Runtime/HumanoidFree/Scripts/Targets/HeadTarget.cs
@@ -1017,9 +1017,12 @@ namespace Passer.Humanoid {
/// Sets the rotation of the head around the X axis
public void RotationX(float angle) {
- Vector3 angles = (Quaternion.Inverse(humanoid.transform.rotation) * transform.rotation).eulerAngles;
+ Quaternion localTargetRotation = Quaternion.Inverse(humanoid.transform.rotation) * transform.rotation;
+ GetSwingTwist(Vector3.right, localTargetRotation, out Quaternion swing, out Quaternion twist);
+
float xAngle = angle * maxXangle;
- transform.rotation = humanoid.transform.rotation * Quaternion.Euler(xAngle, angles.y, angles.z);
+ Quaternion newLocalTargetRotation = Quaternion.AngleAxis(xAngle, Vector3.right) * swing;
+ transform.rotation = humanoid.transform.rotation * newLocalTargetRotation;
}
/// Sets the rotation of the head around the Y axis
@@ -1029,6 +1032,29 @@ namespace Passer.Humanoid {
transform.rotation = humanoid.transform.rotation * Quaternion.Euler(angles.x, yAngle, angles.z);
}
+ public static Quaternion GetRotationAround(Vector3 axis, Quaternion rotation) {
+ Vector3 ra = new Vector3(rotation.x, rotation.y, rotation.z); // rotation axis
+ Vector3 p = Vector3.Project(ra, axis); // return projection v1 on to v2 (parallel component)
+ Quaternion twist = new Quaternion(p.x, p.y, p.z, rotation.w);
+ twist = Normalize(twist);
+ return twist;
+ }
+
+ public static Quaternion Normalize(Quaternion q) {
+ float length = Mathf.Sqrt(q.x * q.x + q.y * q.y + q.z * q.z + q.w * q.w);
+ if (length == 0)
+ return Quaternion.identity;
+
+ float scale = 1.0f / length;
+ Quaternion q1 = new Quaternion(q.x * scale, q.y * scale, q.z * scale, q.w * scale);
+ return q1;
+ }
+
+ public static void GetSwingTwist(Vector3 axis, Quaternion rotation, out Quaternion swing, out Quaternion twist) {
+ twist = GetRotationAround(axis, rotation);
+ swing = rotation * Quaternion.Inverse(twist);
+ }
+
#endregion
#region Tools
diff --git a/Samples/Sites.meta b/Samples/Sites.meta
deleted file mode 100644
index 1879ab5..0000000
--- a/Samples/Sites.meta
+++ /dev/null
@@ -1,8 +0,0 @@
-fileFormatVersion: 2
-guid: a1d84f5cb24b89d4db7c1d3b83949a69
-folderAsset: yes
-DefaultImporter:
- externalObjects: {}
- userData:
- assetBundleName:
- assetBundleVariant:
diff --git a/Samples/Visitors.meta b/Samples/Visitors.meta
deleted file mode 100644
index 27edab5..0000000
--- a/Samples/Visitors.meta
+++ /dev/null
@@ -1,8 +0,0 @@
-fileFormatVersion: 2
-guid: 03517ba7d9815ac47985f7ce3a768240
-folderAsset: yes
-DefaultImporter:
- externalObjects: {}
- userData:
- assetBundleName:
- assetBundleVariant:
diff --git a/package.json b/package.json
index c05d011..f01cbf6 100644
--- a/package.json
+++ b/package.json
@@ -24,12 +24,12 @@
{
"displayName": "Sites",
"description": "Example sites which can be visited with a Visitor",
- "path": "Samples/Sites"
+ "path": "Samples~/Sites"
},
{
"displayName": "Visitors",
"description": "The default Visitors which can browse sites",
- "path": "Samples/Visitors"
+ "path": "Samples~/Visitors"
}
]
}
\ No newline at end of file