Selaa lähdekoodia

[unity] SkeletonRagdoll components now support bone scale at any bone in the skeleton hierarchy, including negative scale and root bone scale. Closes #1879.

Harald Csaszar 4 vuotta sitten
vanhempi
commit
d146a81d21

+ 3 - 0
CHANGELOG.md

@@ -95,6 +95,9 @@
   * All Spine shaders (also including URP and LWRP shaders) now support `PMA Vertex Colors` in combination with `Linear` color space. Thus when using Spine shaders, you should always enable `PMA Vertex Colors` at the `SkeletonRenderer` component. This allows using single pass `Additive` Slots rendering. Note that textures shall still be exported as `Straight alpha` when using `Linear` color space, so combine `PMA Vertex Colors` with `Straight Texture`. All `Sprite` shaders now provide an additional blend mode for this, named `PMA Vertex, Straight Texture` which shall be the preferred Sprite shader blend mode in `Linear` color space.
   * Additive Slots have always been lit before they were written to the target buffer. Now all lit shaders provide an additional parameter `Light Affects Additive` which defaults to `false`, as it is the more intuitive default value. You can enable the old behaviour by setting this parameter to `true`.
   * `SkeletonRootMotion` and `SkeletonMecanimRootMotion` components now support arbitrary bones in the hierarchy as `Root Motion Bone`. Previously there were problems when selecting a non-root bone as `Root Motion Bone`. `Skeleton.ScaleX` and `.ScaleY` and parent bone scale is now respected as well.
+  * URP and LWRP `Sprite` and `SkeletonLit` shaders no longer require `Advanced - Add Normals` enabled to properly cast and receive shadows. It is recommended to disable `Add Normals` if normals are otherwise not needed.
+  * Added an example component `RootMotionDeltaCompensation` located in `Spine Examples/Scripts/Sample Components` which can be used for applying simple delta compensation. You can enable and disable the component to toggle delta compensation of the currently playing animation on and off.
+  * `SkeletonRagdoll` and `SkeletonRagdoll2D` now support bone scale at any bone in the skeleton hierarchy. This includes negative scale and root bone scale.
 
 * **Changes of default values**
 

+ 64 - 36
spine-unity/Assets/Spine Examples/Scripts/Sample Components/SkeletonUtility Modules/SkeletonRagdoll.cs

@@ -68,12 +68,22 @@ namespace Spine.Unity.Examples {
 		public int colliderLayer = 0;
 		[Range(0, 1)]
 		public float mix = 1;
-		public bool oldRagdollBehaviour = true;
+		public bool oldRagdollBehaviour = false;
 		#endregion
 
 		ISkeletonAnimation targetSkeletonComponent;
 		Skeleton skeleton;
+		struct BoneFlipEntry {
+			public BoneFlipEntry (bool flipX, bool flipY) {
+				this.flipX = flipX;
+				this.flipY = flipY;
+			}
+
+			public bool flipX;
+			public bool flipY;
+		}
 		Dictionary<Bone, Transform> boneTable = new Dictionary<Bone, Transform>();
+		Dictionary<Bone, BoneFlipEntry> boneFlipTable = new Dictionary<Bone, BoneFlipEntry>();
 		Transform ragdollRoot;
 		public Rigidbody RootRigidbody { get; private set; }
 		public Bone StartingBone { get; private set; }
@@ -139,12 +149,12 @@ namespace Spine.Unity.Examples {
 				if (b == StartingBone) {
 					ragdollRoot = new GameObject("RagdollRoot").transform;
 					ragdollRoot.SetParent(transform, false);
-					if (b == skeleton.RootBone) { // RagdollRoot is skeleton root.
-						ragdollRoot.localPosition = new Vector3(b.WorldX, b.WorldY, 0);
-						ragdollRoot.localRotation = Quaternion.Euler(0, 0, GetPropagatedRotation(b));
+					if (b == skeleton.RootBone) { // RagdollRoot is skeleton root's parent, thus the skeleton's scale and position.
+						ragdollRoot.localPosition = new Vector3(skeleton.X, skeleton.Y, 0);
+						ragdollRoot.localRotation = (skeleton.ScaleX < 0) ? Quaternion.Euler(0, 0, 180.0f) : Quaternion.identity;
 					} else {
 						ragdollRoot.localPosition = new Vector3(b.Parent.WorldX, b.Parent.WorldY, 0);
-						ragdollRoot.localRotation = Quaternion.Euler(0, 0, GetPropagatedRotation(b.Parent));
+						ragdollRoot.localRotation = Quaternion.Euler(0, 0, b.Parent.WorldRotationX - b.Parent.ShearX);
 					}
 					parentTransform = ragdollRoot;
 					rootOffset = t.position - transform.position;
@@ -295,7 +305,6 @@ namespace Spine.Unity.Examples {
 			t.localRotation = Quaternion.Euler(0, 0, b.WorldRotationX - b.ShearX);
 			t.localScale = new Vector3(b.WorldScaleX, b.WorldScaleY, 1);
 
-			// MITCH: You left "todo: proper ragdoll branching"
 			var colliders = AttachBoundingBoxRagdollColliders(b);
 			if (colliders.Count == 0) {
 				float length = b.Data.Length;
@@ -316,20 +325,38 @@ namespace Spine.Unity.Examples {
 		}
 
 		void UpdateSpineSkeleton (ISkeletonAnimation skeletonRenderer) {
-			bool flipX = skeleton.ScaleX < 0;
-			bool flipY = skeleton.ScaleY < 0;
-			bool flipXOR = flipX ^ flipY;
-			bool flipOR = flipX || flipY;
+			bool parentFlipX;
+			bool parentFlipY;
+			GetStartBoneParentFlipState(out parentFlipX, out parentFlipY);
 
 			foreach (var pair in boneTable) {
 				var b = pair.Key;
 				var t = pair.Value;
 				bool isStartingBone = b == StartingBone;
-				Transform parentTransform = isStartingBone ? ragdollRoot : boneTable[b.Parent];
+				var parentBone = b.Parent;
+				Transform parentTransform = isStartingBone ? ragdollRoot : boneTable[parentBone];
+				if (!isStartingBone) {
+					var parentBoneFlip = boneFlipTable[parentBone];
+					parentFlipX = parentBoneFlip.flipX;
+					parentFlipY = parentBoneFlip.flipY;
+				}
+				bool flipX = parentFlipX ^ (b.ScaleX < 0);
+				bool flipY = parentFlipY ^ (b.ScaleY < 0);
+
+				BoneFlipEntry boneFlip;
+				boneFlipTable.TryGetValue(b, out boneFlip);
+				boneFlip.flipX = flipX;
+				boneFlip.flipY = flipY;
+				boneFlipTable[b] = boneFlip;
+
+				bool flipXOR = flipX ^ flipY;
+				bool parentFlipXOR = parentFlipX ^ parentFlipY;
+
 				if (!oldRagdollBehaviour && isStartingBone) {
 					if (b != skeleton.RootBone) { // RagdollRoot is not skeleton root.
-						ragdollRoot.localPosition = new Vector3(b.Parent.WorldX, b.Parent.WorldY, 0);
-						ragdollRoot.localRotation = Quaternion.Euler(0, 0, GetPropagatedRotation(b.Parent));
+						ragdollRoot.localPosition = new Vector3(parentBone.WorldX, parentBone.WorldY, 0);
+						ragdollRoot.localRotation = Quaternion.Euler(0, 0, parentBone.WorldRotationX - parentBone.ShearX);
+						ragdollRoot.localScale = new Vector3(parentBone.WorldScaleX, parentBone.WorldScaleY, 1);
 					}
 				}
 				Vector3 parentTransformWorldPosition = parentTransform.position;
@@ -339,26 +366,26 @@ namespace Spine.Unity.Examples {
 				parentSpaceHelper.rotation = parentTransformWorldRotation;
 				parentSpaceHelper.localScale = parentTransform.lossyScale;
 
+				if (oldRagdollBehaviour) {
+					if (isStartingBone && b != skeleton.RootBone) {
+						Vector3 localPosition = new Vector3(b.Parent.WorldX, b.Parent.WorldY, 0);
+						parentSpaceHelper.position = ragdollRoot.TransformPoint(localPosition);
+						parentSpaceHelper.localRotation = Quaternion.Euler(0, 0, parentBone.WorldRotationX - parentBone.ShearX);
+						parentSpaceHelper.localScale = new Vector3(parentBone.WorldScaleX, parentBone.WorldScaleY, 1);
+					}
+				}
+
 				Vector3 boneWorldPosition = t.position;
 				Vector3 right = parentSpaceHelper.InverseTransformDirection(t.right);
 
 				Vector3 boneLocalPosition = parentSpaceHelper.InverseTransformPoint(boneWorldPosition);
 				float boneLocalRotation = Mathf.Atan2(right.y, right.x) * Mathf.Rad2Deg;
 
-				if (flipOR) {
-					if (isStartingBone) {
-						if (flipX) boneLocalPosition.x *= -1f;
-						if (flipY) boneLocalPosition.y *= -1f;
+				if (flipXOR) boneLocalPosition.y *= -1f;
+				if (parentFlipXOR != flipXOR) boneLocalPosition.y *= -1f;
 
-						boneLocalRotation = boneLocalRotation * (flipXOR ? -1f : 1f);
-						if (flipX) boneLocalRotation += 180;
-					} else {
-						if (flipXOR) {
-							boneLocalRotation *= -1f;
-							boneLocalPosition.y *= -1f; // wtf??
-						}
-					}
-				}
+				if (parentFlipXOR) boneLocalRotation *= -1f;
+				if (parentFlipX != flipX) boneLocalRotation += 180;
 
 				b.X = Mathf.Lerp(b.X, boneLocalPosition.x, mix);
 				b.Y = Mathf.Lerp(b.Y, boneLocalPosition.y, mix);
@@ -367,6 +394,17 @@ namespace Spine.Unity.Examples {
 			}
 		}
 
+		void GetStartBoneParentFlipState (out bool parentFlipX, out bool parentFlipY) {
+			parentFlipX = skeleton.ScaleX < 0;
+			parentFlipY = skeleton.ScaleY < 0;
+			var parent = this.StartingBone == null ? null : this.StartingBone.Parent;
+			while (parent != null) {
+				parentFlipX ^= parent.ScaleX < 0;
+				parentFlipY ^= parent.ScaleY < 0;
+				parent = parent.Parent;
+			}
+		}
+
 		List<Collider> AttachBoundingBoxRagdollColliders (Bone b) {
 			const string AttachmentNameMarker = "ragdoll";
 			var colliders = new List<Collider>();
@@ -399,16 +437,6 @@ namespace Spine.Unity.Examples {
 			return colliders;
 		}
 
-		static float GetPropagatedRotation (Bone b) {
-			Bone parent = b.Parent;
-			float a = b.AppliedRotation;
-			while (parent != null) {
-				a += parent.AppliedRotation;
-				parent = parent.Parent;
-			}
-			return a;
-		}
-
 		public class LayerFieldAttribute : PropertyAttribute {}
 	}
 

+ 65 - 36
spine-unity/Assets/Spine Examples/Scripts/Sample Components/SkeletonUtility Modules/SkeletonRagdoll2D.cs

@@ -71,12 +71,22 @@ namespace Spine.Unity.Examples {
 		public int colliderLayer = 0;
 		[Range(0, 1)]
 		public float mix = 1;
-		public bool oldRagdollBehaviour = true;
+		public bool oldRagdollBehaviour = false;
 		#endregion
 
 		ISkeletonAnimation targetSkeletonComponent;
 		Skeleton skeleton;
+		struct BoneFlipEntry {
+			public BoneFlipEntry (bool flipX, bool flipY) {
+				this.flipX = flipX;
+				this.flipY = flipY;
+			}
+
+			public bool flipX;
+			public bool flipY;
+		}
 		Dictionary<Bone, Transform> boneTable = new Dictionary<Bone, Transform>();
+		Dictionary<Bone, BoneFlipEntry> boneFlipTable = new Dictionary<Bone, BoneFlipEntry>();
 		Transform ragdollRoot;
 		public Rigidbody2D RootRigidbody { get; private set; }
 		public Bone StartingBone { get; private set; }
@@ -141,12 +151,12 @@ namespace Spine.Unity.Examples {
 				if (b == startingBone) {
 					ragdollRoot = new GameObject("RagdollRoot").transform;
 					ragdollRoot.SetParent(transform, false);
-					if (b == skeleton.RootBone) { // RagdollRoot is skeleton root.
-						ragdollRoot.localPosition = new Vector3(b.WorldX, b.WorldY, 0);
-						ragdollRoot.localRotation = Quaternion.Euler(0, 0, GetPropagatedRotation(b));
+					if (b == skeleton.RootBone) { // RagdollRoot is skeleton root's parent, thus the skeleton's scale and position.
+						ragdollRoot.localPosition = new Vector3(skeleton.X, skeleton.Y, 0);
+						ragdollRoot.localRotation = (skeleton.ScaleX < 0) ? Quaternion.Euler(0, 0, 180.0f) : Quaternion.identity;
 					} else {
 						ragdollRoot.localPosition = new Vector3(b.Parent.WorldX, b.Parent.WorldY, 0);
-						ragdollRoot.localRotation = Quaternion.Euler(0, 0, GetPropagatedRotation(b.Parent));
+						ragdollRoot.localRotation = Quaternion.Euler(0, 0, b.Parent.WorldRotationX - b.Parent.ShearX);
 					}
 					parentTransform = ragdollRoot;
 					rootOffset = t.position - transform.position;
@@ -307,7 +317,6 @@ namespace Spine.Unity.Examples {
 			t.localRotation = Quaternion.Euler(0, 0, b.WorldRotationX - b.ShearX);
 			t.localScale = new Vector3(b.WorldScaleX, b.WorldScaleY, 1);
 
-			// MITCH: You left "todo: proper ragdoll branching"
 			var colliders = AttachBoundingBoxRagdollColliders(b, boneGameObject, skeleton, this.gravityScale);
 			if (colliders.Count == 0) {
 				float length = b.Data.Length;
@@ -331,21 +340,39 @@ namespace Spine.Unity.Examples {
 
 		/// <summary>Performed every skeleton animation update to translate Unity Transforms positions into Spine bone transforms.</summary>
 		void UpdateSpineSkeleton (ISkeletonAnimation animatedSkeleton) {
-			bool flipX = skeleton.ScaleX < 0;
-			bool flipY = skeleton.ScaleY < 0;
-			bool flipXOR = flipX ^ flipY;
-			bool flipOR = flipX || flipY;
+			bool parentFlipX;
+			bool parentFlipY;
 			var startingBone = this.StartingBone;
+			GetStartBoneParentFlipState(out parentFlipX, out parentFlipY);
 
 			foreach (var pair in boneTable) {
 				var b = pair.Key;
 				var t = pair.Value;
 				bool isStartingBone = (b == startingBone);
-				Transform parentTransform = isStartingBone ? ragdollRoot : boneTable[b.Parent];
+				var parentBone = b.Parent;
+				Transform parentTransform = isStartingBone ? ragdollRoot : boneTable[parentBone];
+				if (!isStartingBone) {
+					var parentBoneFlip = boneFlipTable[parentBone];
+					parentFlipX = parentBoneFlip.flipX;
+					parentFlipY = parentBoneFlip.flipY;
+				}
+				bool flipX = parentFlipX ^ (b.ScaleX < 0);
+				bool flipY = parentFlipY ^ (b.ScaleY < 0);
+
+				BoneFlipEntry boneFlip;
+				boneFlipTable.TryGetValue(b, out boneFlip);
+				boneFlip.flipX = flipX;
+				boneFlip.flipY = flipY;
+				boneFlipTable[b] = boneFlip;
+
+				bool flipXOR = flipX ^ flipY;
+				bool parentFlipXOR = parentFlipX ^ parentFlipY;
+
 				if (!oldRagdollBehaviour && isStartingBone) {
 					if (b != skeleton.RootBone) { // RagdollRoot is not skeleton root.
-						ragdollRoot.localPosition = new Vector3(b.Parent.WorldX, b.Parent.WorldY, 0);
-						ragdollRoot.localRotation = Quaternion.Euler(0, 0, GetPropagatedRotation(b.Parent));
+						ragdollRoot.localPosition = new Vector3(parentBone.WorldX, parentBone.WorldY, 0);
+						ragdollRoot.localRotation = Quaternion.Euler(0, 0, parentBone.WorldRotationX - parentBone.ShearX);
+						ragdollRoot.localScale = new Vector3(parentBone.WorldScaleX, parentBone.WorldScaleY, 1);
 					}
 				}
 
@@ -356,25 +383,26 @@ namespace Spine.Unity.Examples {
 				parentSpaceHelper.rotation = parentTransformWorldRotation;
 				parentSpaceHelper.localScale = parentTransform.lossyScale;
 
+				if (oldRagdollBehaviour) {
+					if (isStartingBone && b != skeleton.RootBone) {
+						Vector3 localPosition = new Vector3(b.Parent.WorldX, b.Parent.WorldY, 0);
+						parentSpaceHelper.position = ragdollRoot.TransformPoint(localPosition);
+						parentSpaceHelper.localRotation = Quaternion.Euler(0, 0, parentBone.WorldRotationX - parentBone.ShearX);
+						parentSpaceHelper.localScale = new Vector3(parentBone.WorldScaleX, parentBone.WorldScaleY, 1);
+					}
+				}
+
 				Vector3 boneWorldPosition = t.position;
 				Vector3 right = parentSpaceHelper.InverseTransformDirection(t.right);
 
 				Vector3 boneLocalPosition = parentSpaceHelper.InverseTransformPoint(boneWorldPosition);
 				float boneLocalRotation = Mathf.Atan2(right.y, right.x) * Mathf.Rad2Deg;
-				if (flipOR) {
-					if (isStartingBone) {
-						if (flipX) boneLocalPosition.x *= -1f;
-						if (flipY) boneLocalPosition.y *= -1f;
 
-						boneLocalRotation = boneLocalRotation * (flipXOR ? -1f : 1f);
-						if (flipX) boneLocalRotation += 180;
-					} else {
-						if (flipXOR) {
-							boneLocalRotation *= -1f;
-							boneLocalPosition.y *= -1f; // wtf??
-						}
-					}
-				}
+				if (flipXOR) boneLocalPosition.y *= -1f;
+				if (parentFlipXOR != flipXOR) boneLocalPosition.y *= -1f;
+
+				if (parentFlipXOR) boneLocalRotation *= -1f;
+				if (parentFlipX != flipX) boneLocalRotation += 180;
 
 				b.X = Mathf.Lerp(b.X, boneLocalPosition.x, mix);
 				b.Y = Mathf.Lerp(b.Y, boneLocalPosition.y, mix);
@@ -383,6 +411,17 @@ namespace Spine.Unity.Examples {
 			}
 		}
 
+		void GetStartBoneParentFlipState (out bool parentFlipX, out bool parentFlipY) {
+			parentFlipX = skeleton.ScaleX < 0;
+			parentFlipY = skeleton.ScaleY < 0;
+			var parent = this.StartingBone == null ? null : this.StartingBone.Parent;
+			while (parent != null) {
+				parentFlipX ^= parent.ScaleX < 0;
+				parentFlipY ^= parent.ScaleY < 0;
+				parent = parent.Parent;
+			}
+		}
+
 		static List<Collider2D> AttachBoundingBoxRagdollColliders (Bone b, GameObject go, Skeleton skeleton, float gravityScale) {
 			const string AttachmentNameMarker = "ragdoll";
 			var colliders = new List<Collider2D>();
@@ -414,16 +453,6 @@ namespace Spine.Unity.Examples {
 			return colliders;
 		}
 
-		static float GetPropagatedRotation (Bone b) {
-			Bone parent = b.Parent;
-			float a = b.AppliedRotation;
-			while (parent != null) {
-				a += parent.AppliedRotation;
-				parent = parent.Parent;
-			}
-			return a;
-		}
-
 		static Vector3 FlipScale (bool flipX, bool flipY) {
 			return new Vector3(flipX ? -1f : 1f, flipY ? -1f : 1f, 1f);
 		}