فهرست منبع

[unity] SkeletonGraphic: Added auto-detect functionality for settings and materials. Renamed `CanvasGroupTintBlack` param to `CanvasGroup Compatible` (breaking change).

Harald Csaszar 1 سال پیش
والد
کامیت
6376daf61d

+ 2 - 0
CHANGELOG.md

@@ -60,6 +60,7 @@
   * SkeletonGraphic: Added Tint Black blend mode shaders `Spine/SkeletonGraphic Tint Black Additive`, `Spine/SkeletonGraphic Tint Black Multiply` and `Spine/SkeletonGraphic Tint Black Screen`.
   * SkeletonGraphic: Added pre-defined SkeletonGraphic material sets for main workflow parameters in folders `spine-unity/Materials` instead of requiring manual copies:
     `SkeletonGraphic-PMATexture` containing materials for premultiplied-alpha texture workflow (`Straight Alpha Texture` disabled) and `SkeletonGraphic-StaightAlphaTexture` containing materials for straight alpha texture workflow (`Straight Alpha Texture` enabled). These directories contain a set of materials with `CanvasGroup Compatible` disabled for usage with `Advanced - PMA Vertex Color` enabled at the component. Each directory also provides a subdirectory `CanvasGroupCompatible` with materials with `CanvasGroup Compatible` enabled for usage with `CanvasGroup` alpha (requiring `Advanced - PMA Vertex Color` disabled at the component).
+  * SkeletonGraphic: Added auto-detect functionality for parameters `Advanced` - `Tint Black`, `CanvasGroup Compatible` and `PMA Vertex Color`. If unsure which settings are correct, hit the `Detect` button next to each parameter, in top to bottom order, or the `Detect Settings` to detect all three. Also added automatic material assignment via a `Detect Material` button in the `Advanced` section and a `Detect` button next to the `Material` property at the top of the component Inspector, as well as next to the `Blend Mode Materials` section when using multiple canvas renderers with blend modes. The suitable material is selected based on these three settings, combined with texture settings (PMA or straight alpha texture settings). If you receive incorrect results, likely your texture settings are incorrectly setup for your PMA or Straight alpha texture export settings.
   
 * **Breaking changes**
   * Changed `SpineShaderWithOutlineGUI` outline related methods from `private` to `protected virtual` to allow for custom shader GUI subclasses to switch to different outline shaders.
@@ -67,6 +68,7 @@
   * `MeshGenerator` received a new optimization option to avoid rendering fully transparent attachments at slot alpha 0 by default. Comment out `#define SLOT_ALPHA_DISABLES_ATTACHMENT` in `MeshGenerator.cs` to revert to previous behaviour. You may only need this option disabled when utilizing a custom shader which uses vertex color alpha for purposes other than transparency.
   * PhysicsConstraints: bool properties `ApplyTranslationToPhysics` and `ApplyRotationToPhysics` were changed to `Vector2 PhysicsPositionInheritanceFactor` and `float PhysicsRotationInheritanceFactor` to allow the Transform movement the be scaled by a factor before being applied to the skeleton. You can set the properties to `Vector2.zero` and `0` respectively to disable applying any Transform movement at all. The `Advanced` Inspector section `Physics Constraints` was renamed to `Physics Inheritance`, the properties in the section are now called `Position` and `Rotation`.
   * SkeletonGraphic Materials: Since the addition of new material sets for the `CanvasGroupCompatible` parameters, the default SkeletonGraphic materials all have `CanvasGroup Compatible` disabled. Please assign the respective material from the `CanvasGroupCompatible` material subdirectory if you want `CanvasGroup Compatible` enabled at your SkeletonGraphic.
+  * SkeletonGraphic: The parameter `SkeletonGraphic.MeshGenerator.settings.canvasGroupTintBlack` was changed to `canvasGroupCompatible` to help with auto-detecting correct Vertex Data and Material settings. Set the parameter to true if the SkeletonGraphic component is located below a `CanvasGroup` component. The parameter value is automatically migrated from `canvasGroupTintBlack`.
 
 * **Changes of default values**
 

+ 313 - 53
spine-unity/Assets/Spine/Editor/spine-unity/Editor/Components/SkeletonGraphicInspector.cs

@@ -61,14 +61,41 @@ namespace Spine.Unity.Editor {
 		SerializedProperty additiveMaterial, multiplyMaterial, screenMaterial;
 		SerializedProperty skeletonDataAsset, initialSkinName;
 		SerializedProperty startingAnimation, startingLoop, timeScale, freeze,
-			updateTiming, updateWhenInvisible, unscaledTime, tintBlack, layoutScaleMode, editReferenceRect;
+			updateTiming, updateWhenInvisible, unscaledTime, layoutScaleMode, editReferenceRect;
 		SerializedProperty physicsPositionInheritanceFactor, physicsRotationInheritanceFactor, physicsMovementRelativeTo;
 		SerializedProperty initialFlipX, initialFlipY;
 		SerializedProperty meshGeneratorSettings;
+		SerializedProperty useClipping, zSpacing, tintBlack, canvasGroupCompatible, pmaVertexColors, addNormals, calculateTangents, immutableTriangles;
+
 		SerializedProperty allowMultipleCanvasRenderers, separatorSlotNames, enableSeparatorSlots,
 			updateSeparatorPartLocation, updateSeparatorPartScale;
 		SerializedProperty raycastTarget, maskable;
 
+		readonly GUIContent UseClippingLabel = new GUIContent("Use Clipping",
+			"When disabled, clipping attachments are ignored. This may be used to save performance.");
+		readonly GUIContent ZSpacingLabel = new GUIContent("Z Spacing",
+			"A value other than 0 adds a space between each rendered attachment to prevent Z Fighting when using shaders" +
+			" that read or write to the depth buffer. Large values may cause unwanted parallax and spaces depending on " +
+			"camera setup.");
+		readonly GUIContent TintBlackLabel = new GUIContent("Tint Black (!)",
+			"Adds black tint vertex data to the mesh as UV2 and UV3. Black tinting requires that the shader interpret " +
+			"UV2 and UV3 as black tint colors for this effect to work. You may then want to use the " +
+			"[Spine/SkeletonGraphic Tint Black] shader.");
+		readonly GUIContent CanvasGroupCompatibleLabel = new GUIContent("CanvasGroup Compatible",
+			"Enable when using SkeletonGraphic under a CanvasGroup. " +
+			"When enabled, PMA Vertex Color alpha value is stored at uv2.g instead of color.a to capture " +
+			"CanvasGroup modifying color.a. Also helps to detect correct parameter setting combinations.");
+		readonly GUIContent PMAVertexColorsLabel = new GUIContent("PMA Vertex Colors",
+			"Use this if you are using the default Spine/Skeleton shader or any premultiply-alpha shader.");
+		readonly GUIContent AddNormalsLabel = new GUIContent("Add Normals",
+			"Use this if your shader requires vertex normals. A more efficient solution for 2D setups is to modify the " +
+			"shader to assume a single normal value for the whole mesh.");
+		readonly GUIContent CalculateTangentsLabel = new GUIContent("Solve Tangents",
+			"Calculates the tangents per frame. Use this if you are using lit shaders (usually with normal maps) that " +
+			"require vertex tangents.");
+		readonly GUIContent ImmutableTrianglesLabel = new GUIContent("Immutable Triangles",
+			"Enable to optimize rendering for skeletons that never change attachment visbility");
+
 		readonly GUIContent UnscaledTimeLabel = new GUIContent("Unscaled Time",
 			"If enabled, AnimationState uses unscaled game time (Time.unscaledDeltaTime), " +
 				"running animations independent of e.g. game pause (Time.timeScale). " +
@@ -160,6 +187,15 @@ namespace Spine.Unity.Editor {
 			meshGeneratorSettings = so.FindProperty("meshGenerator").FindPropertyRelative("settings");
 			meshGeneratorSettings.isExpanded = SkeletonRendererInspector.advancedFoldout;
 
+			useClipping = meshGeneratorSettings.FindPropertyRelative("useClipping");
+			zSpacing = meshGeneratorSettings.FindPropertyRelative("zSpacing");
+			tintBlack = meshGeneratorSettings.FindPropertyRelative("tintBlack");
+			canvasGroupCompatible = meshGeneratorSettings.FindPropertyRelative("canvasGroupCompatible");
+			pmaVertexColors = meshGeneratorSettings.FindPropertyRelative("pmaVertexColors");
+			calculateTangents = meshGeneratorSettings.FindPropertyRelative("calculateTangents");
+			addNormals = meshGeneratorSettings.FindPropertyRelative("addNormals");
+			immutableTriangles = meshGeneratorSettings.FindPropertyRelative("immutableTriangles");
+
 			allowMultipleCanvasRenderers = so.FindProperty("allowMultipleCanvasRenderers");
 			updateSeparatorPartLocation = so.FindProperty("updateSeparatorPartLocation");
 			updateSeparatorPartScale = so.FindProperty("updateSeparatorPartScale");
@@ -238,8 +274,18 @@ namespace Spine.Unity.Editor {
 				return;
 			}
 
-			EditorGUILayout.PropertyField(material);
-			EditorGUILayout.PropertyField(color);
+			using (new SpineInspectorUtility.LabelWidthScope(100)) {
+				using (new EditorGUILayout.HorizontalScope()) {
+					EditorGUILayout.PropertyField(material);
+					if (GUILayout.Button("Detect", EditorStyles.miniButton, GUILayout.Width(67f))) {
+						Undo.RecordObjects(targets, "Detect Material");
+						foreach (UnityEngine.Object skeletonGraphic in targets) {
+							DetectMaterial((SkeletonGraphic)skeletonGraphic);
+						}
+					}
+				}
+				EditorGUILayout.PropertyField(color);
+			}
 
 			string errorMessage = null;
 			if (SpineEditorUtilities.Preferences.componentMaterialWarning &&
@@ -258,55 +304,65 @@ namespace Spine.Unity.Editor {
 
 			using (new SpineInspectorUtility.BoxScope()) {
 
-				EditorGUILayout.PropertyField(meshGeneratorSettings, SpineInspectorUtility.TempContent("Advanced..."), includeChildren: true);
+				EditorGUILayout.PropertyField(meshGeneratorSettings, SpineInspectorUtility.TempContent("Advanced..."), includeChildren: false);
 				SkeletonRendererInspector.advancedFoldout = meshGeneratorSettings.isExpanded;
-
 				if (meshGeneratorSettings.isExpanded) {
 					EditorGUILayout.Space();
 					using (new SpineInspectorUtility.IndentScope()) {
-						EditorGUILayout.BeginHorizontal();
-						EditorGUILayout.PropertyField(allowMultipleCanvasRenderers, SpineInspectorUtility.TempContent("Multiple CanvasRenderers"));
+						DrawMeshSettings();
+						EditorGUILayout.Space();
 
-						if (GUILayout.Button(new GUIContent("Trim Renderers", "Remove currently unused CanvasRenderer GameObjects. These will be regenerated whenever needed."),
-							EditorStyles.miniButton, GUILayout.Width(100f))) {
+						using (new SpineInspectorUtility.LabelWidthScope()) {
 
-							foreach (UnityEngine.Object skeletonGraphic in targets) {
-								((SkeletonGraphic)skeletonGraphic).TrimRenderers();
-							}
-						}
-						EditorGUILayout.EndHorizontal();
+							EditorGUILayout.BeginHorizontal();
+							EditorGUILayout.PropertyField(allowMultipleCanvasRenderers, SpineInspectorUtility.TempContent("Multiple CanvasRenderers"));
 
-						BlendModeMaterials blendModeMaterials = thisSkeletonGraphic.skeletonDataAsset.blendModeMaterials;
-						if (allowMultipleCanvasRenderers.boolValue == true && blendModeMaterials.RequiresBlendModeMaterials) {
-							using (new SpineInspectorUtility.IndentScope()) {
-								EditorGUILayout.BeginHorizontal();
-								EditorGUILayout.LabelField("Blend Mode Materials", EditorStyles.boldLabel);
+							if (GUILayout.Button(new GUIContent("Trim Renderers", "Remove currently unused CanvasRenderer GameObjects. These will be regenerated whenever needed."),
+								EditorStyles.miniButton, GUILayout.Width(100f))) {
 
-								if (GUILayout.Button(new GUIContent("Assign Default", "Assign default Blend Mode Materials."),
-									EditorStyles.miniButton, GUILayout.Width(100f))) {
-									AssignDefaultBlendModeMaterials();
+								Undo.RecordObjects(targets, "Trim Renderers");
+								foreach (UnityEngine.Object skeletonGraphic in targets) {
+									((SkeletonGraphic)skeletonGraphic).TrimRenderers();
 								}
-								EditorGUILayout.EndHorizontal();
+							}
+							EditorGUILayout.EndHorizontal();
 
-								bool usesAdditiveMaterial = blendModeMaterials.applyAdditiveMaterial;
-								bool pmaVertexColors = thisSkeletonGraphic.MeshGenerator.settings.pmaVertexColors;
-								if (pmaVertexColors)
-									using (new EditorGUI.DisabledGroupScope(true)) {
-										EditorGUILayout.LabelField("Additive Material - Unused with PMA Vertex Colors", EditorStyles.label);
-									}
-								else if (usesAdditiveMaterial)
-									EditorGUILayout.PropertyField(additiveMaterial, SpineInspectorUtility.TempContent("Additive Material", null, "SkeletonGraphic Material for 'Additive' blend mode slots. Unused when 'PMA Vertex Colors' is enabled."));
-								else
-									using (new EditorGUI.DisabledGroupScope(true)) {
-										EditorGUILayout.LabelField("No Additive Mat - 'Apply Additive Material' disabled at SkeletonDataAsset", EditorStyles.label);
+							BlendModeMaterials blendModeMaterials = thisSkeletonGraphic.skeletonDataAsset.blendModeMaterials;
+							if (allowMultipleCanvasRenderers.boolValue == true && blendModeMaterials.RequiresBlendModeMaterials) {
+								using (new SpineInspectorUtility.IndentScope()) {
+									EditorGUILayout.BeginHorizontal();
+									EditorGUILayout.LabelField("Blend Mode Materials", EditorStyles.boldLabel);
+
+									if (GUILayout.Button(new GUIContent("Detect", "Auto-Assign Blend Mode Materials according to Vertex Data and Texture settings."),
+										EditorStyles.miniButton, GUILayout.Width(100f))) {
+
+										Undo.RecordObjects(targets, "Detect Blend Mode Materials");
+										foreach (UnityEngine.Object skeletonGraphic in targets) {
+											DetectBlendModeMaterials((SkeletonGraphic)skeletonGraphic);
+										}
 									}
-								EditorGUILayout.PropertyField(multiplyMaterial, SpineInspectorUtility.TempContent("Multiply Material", null, "SkeletonGraphic Material for 'Multiply' blend mode slots."));
-								EditorGUILayout.PropertyField(screenMaterial, SpineInspectorUtility.TempContent("Screen Material", null, "SkeletonGraphic Material for 'Screen' blend mode slots."));
+									EditorGUILayout.EndHorizontal();
+
+									bool usesAdditiveMaterial = blendModeMaterials.applyAdditiveMaterial;
+									bool pmaVertexColors = thisSkeletonGraphic.MeshGenerator.settings.pmaVertexColors;
+									if (pmaVertexColors)
+										using (new EditorGUI.DisabledGroupScope(true)) {
+											EditorGUILayout.LabelField("Additive Material - Unused with PMA Vertex Colors", EditorStyles.label);
+										}
+									else if (usesAdditiveMaterial)
+										EditorGUILayout.PropertyField(additiveMaterial, SpineInspectorUtility.TempContent("Additive Material", null, "SkeletonGraphic Material for 'Additive' blend mode slots. Unused when 'PMA Vertex Colors' is enabled."));
+									else
+										using (new EditorGUI.DisabledGroupScope(true)) {
+											EditorGUILayout.LabelField("No Additive Mat - 'Apply Additive Material' disabled at SkeletonDataAsset", EditorStyles.label);
+										}
+									EditorGUILayout.PropertyField(multiplyMaterial, SpineInspectorUtility.TempContent("Multiply Material", null, "SkeletonGraphic Material for 'Multiply' blend mode slots."));
+									EditorGUILayout.PropertyField(screenMaterial, SpineInspectorUtility.TempContent("Screen Material", null, "SkeletonGraphic Material for 'Screen' blend mode slots."));
+								}
 							}
-						}
 
-						EditorGUILayout.PropertyField(updateTiming, UpdateTimingLabel);
-						EditorGUILayout.PropertyField(updateWhenInvisible);
+							EditorGUILayout.PropertyField(updateTiming, UpdateTimingLabel);
+							EditorGUILayout.PropertyField(updateWhenInvisible);
+						}
 
 						// warning box
 						if (isSeparationEnabledButNotMultipleRenderers) {
@@ -424,6 +480,67 @@ namespace Spine.Unity.Editor {
 			}
 		}
 
+		protected void DrawMeshSettings () {
+			EditorGUILayout.PropertyField(useClipping, UseClippingLabel);
+			const float MinZSpacing = -0.1f;
+			const float MaxZSpacing = 0f;
+			EditorGUILayout.Slider(zSpacing, MinZSpacing, MaxZSpacing, ZSpacingLabel);
+			EditorGUILayout.Space();
+
+			using (new SpineInspectorUtility.LabelWidthScope()) {
+				EditorGUILayout.LabelField(SpineInspectorUtility.TempContent("Vertex Data", SpineInspectorUtility.UnityIcon<MeshFilter>()), EditorStyles.boldLabel);
+
+				using (new EditorGUILayout.HorizontalScope()) {
+					EditorGUILayout.PropertyField(tintBlack, TintBlackLabel);
+					if (GUILayout.Button("Detect", EditorStyles.miniButton, GUILayout.Width(65f))) {
+						Undo.RecordObjects(targets, "Detect Tint Black");
+						foreach (UnityEngine.Object skeletonGraphic in targets) {
+							DetectTintBlack((SkeletonGraphic)skeletonGraphic);
+						}
+					}
+				}
+				using (new EditorGUILayout.HorizontalScope()) {
+					EditorGUILayout.PropertyField(canvasGroupCompatible, CanvasGroupCompatibleLabel);
+					if (GUILayout.Button("Detect", EditorStyles.miniButton, GUILayout.Width(65f))) {
+						Undo.RecordObjects(targets, "Detect CanvasGroup Compatible");
+						foreach (UnityEngine.Object skeletonGraphic in targets) {
+							DetectCanvasGroupCompatible((SkeletonGraphic)skeletonGraphic);
+						}
+					}
+				}
+				using (new EditorGUILayout.HorizontalScope()) {
+					EditorGUILayout.PropertyField(pmaVertexColors, PMAVertexColorsLabel);
+					if (GUILayout.Button("Detect", EditorStyles.miniButton, GUILayout.Width(65f))) {
+						Undo.RecordObjects(targets, "Detect PMA Vertex Colors");
+						foreach (UnityEngine.Object skeletonGraphic in targets) {
+							DetectPMAVertexColors((SkeletonGraphic)skeletonGraphic);
+						}
+					}
+				}
+				using (new EditorGUILayout.HorizontalScope()) {
+					GUILayout.FlexibleSpace();
+					if (GUILayout.Button("Detect Settings", EditorStyles.miniButton, GUILayout.Width(100f))) {
+						Undo.RecordObjects(targets, "Detect Settings");
+						foreach (UnityEngine.Object skeletonGraphic in targets) {
+							DetectTintBlack((SkeletonGraphic)skeletonGraphic);
+							DetectCanvasGroupCompatible((SkeletonGraphic)skeletonGraphic);
+							DetectPMAVertexColors((SkeletonGraphic)skeletonGraphic);
+						}
+					}
+					if (GUILayout.Button("Detect Material", EditorStyles.miniButton, GUILayout.Width(100f))) {
+						Undo.RecordObjects(targets, "Detect Material");
+						foreach (UnityEngine.Object skeletonGraphic in targets) {
+							DetectMaterial((SkeletonGraphic)skeletonGraphic);
+						}
+					}
+				}
+
+				EditorGUILayout.PropertyField(addNormals, AddNormalsLabel);
+				EditorGUILayout.PropertyField(calculateTangents, CalculateTangentsLabel);
+				EditorGUILayout.PropertyField(immutableTriangles, ImmutableTrianglesLabel);
+			}
+		}
+
 		protected bool SkeletonHasMultipleSubmeshes () {
 			foreach (UnityEngine.Object target in targets) {
 				SkeletonGraphic skeletonGraphic = (SkeletonGraphic)target;
@@ -441,17 +558,6 @@ namespace Spine.Unity.Editor {
 			} else {
 				SpineHandles.DrawReferenceRect(skeletonGraphic, Color.blue);
 			}
-
-
-		}
-
-		protected void AssignDefaultBlendModeMaterials () {
-			foreach (UnityEngine.Object target in targets) {
-				SkeletonGraphic skeletonGraphic = (SkeletonGraphic)target;
-				skeletonGraphic.additiveMaterial = DefaultSkeletonGraphicAdditiveMaterial;
-				skeletonGraphic.multiplyMaterial = DefaultSkeletonGraphicMultiplyMaterial;
-				skeletonGraphic.screenMaterial = DefaultSkeletonGraphicScreenMaterial;
-			}
 		}
 
 		public static void SetSeparatorSlotNames (SkeletonRenderer skeletonRenderer, string[] newSlotNames) {
@@ -501,12 +607,166 @@ namespace Spine.Unity.Editor {
 				} else
 					EditorGUILayout.PropertyField(separatorSlotNames, new GUIContent(separatorSlotNames.displayName + string.Format("{0} [{1}]", terminalSlotWarning, separatorSlotNames.arraySize), SeparatorsDescription), true);
 
-				EditorGUILayout.PropertyField(enableSeparatorSlots, SpineInspectorUtility.TempContent("Enable Separation", tooltip: "Whether to enable separation at the above separator slots."));
-				EditorGUILayout.PropertyField(updateSeparatorPartLocation, SpineInspectorUtility.TempContent("Update Part Location", tooltip: "Update separator part GameObject location to match the position of the SkeletonGraphic. This can be helpful when re-parenting parts to a different GameObject."));
-				EditorGUILayout.PropertyField(updateSeparatorPartScale, SpineInspectorUtility.TempContent("Update Part Scale", tooltip: "Update separator part GameObject scale to match the scale (lossyScale) of the SkeletonGraphic. This can be helpful when re-parenting parts to a different GameObject."));
+				using (new SpineInspectorUtility.LabelWidthScope()) {
+					EditorGUILayout.PropertyField(enableSeparatorSlots, SpineInspectorUtility.TempContent("Enable Separation", tooltip: "Whether to enable separation at the above separator slots."));
+					EditorGUILayout.PropertyField(updateSeparatorPartLocation, SpineInspectorUtility.TempContent("Update Part Location", tooltip: "Update separator part GameObject location to match the position of the SkeletonGraphic. This can be helpful when re-parenting parts to a different GameObject."));
+					EditorGUILayout.PropertyField(updateSeparatorPartScale, SpineInspectorUtility.TempContent("Update Part Scale", tooltip: "Update separator part GameObject scale to match the scale (lossyScale) of the SkeletonGraphic. This can be helpful when re-parenting parts to a different GameObject."));
+				}
+			}
+		}
+
+		#region Auto Detect Setting
+		static void DetectTintBlack (SkeletonGraphic skeletonGraphic) {
+			bool requiresTintBlack = HasTintBlackSlot(skeletonGraphic);
+			if (requiresTintBlack)
+				Debug.Log(string.Format("Found Tint-Black slot at '{0}'", skeletonGraphic));
+			else
+				Debug.Log(string.Format("No Tint-Black slot found at '{0}'", skeletonGraphic));
+			skeletonGraphic.MeshGenerator.settings.tintBlack = requiresTintBlack;
+		}
+
+		static bool HasTintBlackSlot (SkeletonGraphic skeletonGraphic) {
+			SlotData[] slotsItems = skeletonGraphic.SkeletonData.Slots.Items;
+			for (int i = 0, count = skeletonGraphic.SkeletonData.Slots.Count; i < count; ++i) {
+				SlotData slotData = slotsItems[i];
+				if (slotData.HasSecondColor)
+					return true;
+			}
+			return false;
+		}
+
+		static void DetectCanvasGroupCompatible (SkeletonGraphic skeletonGraphic) {
+			bool requiresCanvasGroupCompatible = IsBelowCanvasGroup(skeletonGraphic);
+			if (requiresCanvasGroupCompatible)
+				Debug.Log(string.Format("Skeleton is a child of CanvasGroup: '{0}'", skeletonGraphic));
+			else
+				Debug.Log(string.Format("Skeleton is not a child of CanvasGroup: '{0}'", skeletonGraphic));
+			skeletonGraphic.MeshGenerator.settings.canvasGroupCompatible = requiresCanvasGroupCompatible;
+		}
+
+		static bool IsBelowCanvasGroup (SkeletonGraphic skeletonGraphic) {
+			return skeletonGraphic.gameObject.GetComponentInParent<CanvasGroup>() != null;
+		}
+
+		static void DetectPMAVertexColors (SkeletonGraphic skeletonGraphic) {
+			MeshGenerator.Settings settings = skeletonGraphic.MeshGenerator.settings;
+			bool usesSpineShader = MaterialChecks.UsesSpineShader(skeletonGraphic.material);
+			if (!usesSpineShader) {
+				Debug.Log(string.Format("Skeleton is not using a Spine shader, thus the shader is likely " +
+					"not using PMA vertex color: '{0}'", skeletonGraphic));
+				skeletonGraphic.MeshGenerator.settings.pmaVertexColors = false;
+				return;
+			}
+
+			bool requiresPMAVertexColorsDisabled = settings.canvasGroupCompatible && !settings.tintBlack;
+			if (requiresPMAVertexColorsDisabled) {
+				Debug.Log(string.Format("Skeleton requires PMA Vertex Colors disabled: '{0}'", skeletonGraphic));
+				skeletonGraphic.MeshGenerator.settings.pmaVertexColors = false;
+			} else {
+				Debug.Log(string.Format("Skeleton requires or permits PMA Vertex Colors enabled: '{0}'", skeletonGraphic));
+				skeletonGraphic.MeshGenerator.settings.pmaVertexColors = true;
 			}
 		}
 
+		static bool IsSkeletonTexturePMA (SkeletonGraphic skeletonGraphic, out bool detectionSucceeded) {
+			Texture texture = skeletonGraphic.mainTexture;
+			string texturePath = AssetDatabase.GetAssetPath(texture.GetInstanceID());
+			TextureImporter importer = (TextureImporter)TextureImporter.GetAtPath(texturePath);
+			if (importer.alphaIsTransparency != importer.sRGBTexture) {
+				Debug.LogWarning(string.Format("Texture '{0}' at skeleton '{1}' is neither configured correctly for " +
+					"PMA nor Straight Alpha.", texture, skeletonGraphic), texture);
+				detectionSucceeded = false;
+				return false;
+			}
+			detectionSucceeded = true;
+			bool isPMATexture = !importer.alphaIsTransparency && !importer.sRGBTexture;
+			return isPMATexture;
+		}
+
+		static void DetectMaterial (SkeletonGraphic skeletonGraphic) {
+			MeshGenerator.Settings settings = skeletonGraphic.MeshGenerator.settings;
+
+			bool detectionSucceeded;
+			bool usesPMATexture = IsSkeletonTexturePMA(skeletonGraphic, out detectionSucceeded);
+			if (!detectionSucceeded) {
+				Debug.LogWarning(string.Format("Unable to assign Material for skeleton '{0}'.", skeletonGraphic), skeletonGraphic);
+				return;
+			}
+
+			Material newMaterial = null;
+			if (usesPMATexture) {
+				if (settings.tintBlack) {
+					if (settings.canvasGroupCompatible)
+						newMaterial = MaterialWithName("SkeletonGraphicTintBlack-CanvasGroup");
+					else
+						newMaterial = MaterialWithName("SkeletonGraphicTintBlack");
+				} else { // not tintBlack
+					if (settings.canvasGroupCompatible)
+						newMaterial = MaterialWithName("SkeletonGraphicDefault-CanvasGroup");
+					else
+						newMaterial = MaterialWithName("SkeletonGraphicDefault");
+				}
+			} else { // straight alpha texture
+				if (settings.tintBlack) {
+					if (settings.canvasGroupCompatible)
+						newMaterial = MaterialWithName("SkeletonGraphicTintBlack-CanvasGroupStraight");
+					else
+						newMaterial = MaterialWithName("SkeletonGraphicTintBlack-Straight");
+				} else { // not tintBlack
+					if (settings.canvasGroupCompatible)
+						newMaterial = MaterialWithName("SkeletonGraphicDefault-CanvasGroupStraight");
+					else
+						newMaterial = MaterialWithName("SkeletonGraphicDefault-Straight");
+				}
+			}
+			if (newMaterial != null) {
+				Debug.Log(string.Format("Assigning material '{0}' at skeleton '{1}'",
+					newMaterial, skeletonGraphic), newMaterial);
+				skeletonGraphic.material = newMaterial;
+			}
+		}
+
+		static void DetectBlendModeMaterials (SkeletonGraphic skeletonGraphic) {
+			bool detectionSucceeded;
+			bool usesPMATexture = IsSkeletonTexturePMA(skeletonGraphic, out detectionSucceeded);
+			if (!detectionSucceeded) {
+				Debug.LogWarning(string.Format("Unable to assign Blend Mode materials for skeleton '{0}'.", skeletonGraphic), skeletonGraphic);
+				return;
+			}
+			DetectBlendModeMaterial(skeletonGraphic, BlendMode.Additive, usesPMATexture);
+			DetectBlendModeMaterial(skeletonGraphic, BlendMode.Multiply, usesPMATexture);
+			DetectBlendModeMaterial(skeletonGraphic, BlendMode.Screen, usesPMATexture);
+		}
+
+		static void DetectBlendModeMaterial (SkeletonGraphic skeletonGraphic, BlendMode blendMode, bool usesPMATexture) {
+			MeshGenerator.Settings settings = skeletonGraphic.MeshGenerator.settings;
+
+			string optionalTintBlack = settings.tintBlack ? "TintBlack" : "";
+			string blendModeString = blendMode.ToString();
+			string optionalDash = settings.canvasGroupCompatible || !usesPMATexture ? "-" : "";
+			string optionalCanvasGroup = settings.canvasGroupCompatible ? "CanvasGroup" : "";
+			string optionalStraight = !usesPMATexture ? "Straight" : "";
+
+			string materialName = string.Format("SkeletonGraphic{0}{1}{2}{3}{4}",
+				optionalTintBlack, blendModeString, optionalDash, optionalCanvasGroup, optionalStraight);
+			Material newMaterial = MaterialWithName(materialName);
+
+			if (newMaterial != null) {
+				switch(blendMode) {
+				case BlendMode.Additive:
+					skeletonGraphic.additiveMaterial = newMaterial;
+					break;
+				case BlendMode.Multiply:
+					skeletonGraphic.multiplyMaterial = newMaterial;
+					break;
+				case BlendMode.Screen:
+					skeletonGraphic.screenMaterial = newMaterial;
+					break;
+				}
+			}
+		}
+		#endregion
+
 		#region Menus
 		[MenuItem("CONTEXT/SkeletonGraphic/Match RectTransform with Mesh Bounds")]
 		static void MatchRectTransformWithBounds (MenuCommand command) {

+ 9 - 10
spine-unity/Assets/Spine/Runtime/spine-unity/Mesh Generation/MeshGenerator.cs

@@ -80,17 +80,16 @@ namespace Spine.Unity {
 		[System.Serializable]
 		public struct Settings {
 			public bool useClipping;
-			[Space]
 			[Range(-0.1f, 0f)] public float zSpacing;
-			[Space]
-			[Header("Vertex Data")]
-			public bool pmaVertexColors;
 			public bool tintBlack;
-			[Tooltip("Enable when using Additive blend mode at SkeletonGraphic under a CanvasGroup. " +
-				"When enabled, Additive alpha value is stored at uv2.g instead of color.a to capture CanvasGroup modifying color.a.")]
-			public bool canvasGroupTintBlack;
-			public bool calculateTangents;
+			[UnityEngine.Serialization.FormerlySerializedAs("canvasGroupTintBlack")]
+			[Tooltip("Enable when using SkeletonGraphic under a CanvasGroup. " +
+				"When enabled, PMA Vertex Color alpha value is stored at uv2.g instead of color.a to capture " +
+				"CanvasGroup modifying color.a. Also helps to detect correct parameter setting combinations.")]
+			public bool canvasGroupCompatible;
+			public bool pmaVertexColors;
 			public bool addNormals;
+			public bool calculateTangents;
 			public bool immutableTriangles;
 
 			static public Settings Default {
@@ -548,7 +547,7 @@ namespace Spine.Unity {
 #else
 			bool useClipping = settings.useClipping;
 #endif
-			bool canvasGroupTintBlack = settings.tintBlack && settings.canvasGroupTintBlack;
+			bool canvasGroupTintBlack = settings.tintBlack && settings.canvasGroupCompatible;
 
 			if (useClipping) {
 				if (instruction.preActiveClippingSlotSource >= 0) {
@@ -758,7 +757,7 @@ namespace Spine.Unity {
 		// Use this faster method when no clipping is involved.
 		public void BuildMeshWithArrays (SkeletonRendererInstruction instruction, bool updateTriangles) {
 			Settings settings = this.settings;
-			bool canvasGroupTintBlack = settings.tintBlack && settings.canvasGroupTintBlack;
+			bool canvasGroupTintBlack = settings.tintBlack && settings.canvasGroupCompatible;
 			int totalVertexCount = instruction.rawVertexCount;
 
 			// Add data to vertex buffers

+ 28 - 30
spine-unity/Assets/Spine/Runtime/spine-unity/Utility/MaterialChecks.cs

@@ -79,29 +79,31 @@ namespace Spine.Unity {
 			+ "This will lead to incorrect rendering on some devices.\n\n"
 			+ "Please change the assigned Material to e.g. 'SkeletonGraphicDefault' or change the used shader to one of the 'Spine/SkeletonGraphic *' shaders.\n\n"
 			+ "Note that 'Spine/SkeletonGraphic *' shall still be used when using URP.\n";
-		public static readonly string kNoSkeletonGraphicTintBlackMaterialMessage =
-			"\nWarning: Only enable 'Canvas Group Tint Black' when using a 'SkeletonGraphic Tint Black' shader!\n"
-			+ "This will lead to incorrect rendering.\n\nPlease\n"
-			+ "a) disable 'Canvas Group Tint Black' under 'Advanced' or\n"
+		public static readonly string kSkeletonGraphicTintBlackMaterialRequiredMessage =
+			"\nWarning: Only enable 'Tint Black' when using a 'SkeletonGraphic Tint Black' shader!\n"
+			+ "Otherwise this will lead to incorrect rendering.\n\nPlease\n"
+			+ "a) disable 'Tint Black' under 'Advanced' or\n"
 			+ "b) use a 'SkeletonGraphic Tint Black' Material if you need Tint Black on a CanvasGroup.\n";
 
-		public static readonly string kTintBlackMessage =
+		public static readonly string kTintBlackRequiredMessage =
 			"\nWarning: 'Advanced - Tint Black' required when using any 'Tint Black' shader!\n\nPlease\n"
 			+ "a) enable 'Tint Black' at the SkeletonRenderer/SkeletonGraphic component under 'Advanced' or\n"
 			+ "b) use a different shader at the Material.\n";
 		public static readonly string kCanvasTintBlackMessage =
 			"\nWarning: Canvas 'Additional Shader Channels' 'uv1' and 'uv2' are required when 'Advanced - Tint Black' is enabled!\n\n"
 			+ "Please enable both 'uv1' and 'uv2' channels at the parent Canvas component parameter 'Additional Shader Channels'.\n";
-		public static readonly string kCanvasGroupCompatibleMessage =
-			"\nWarning: 'Canvas Group Tint Black' is enabled at SkeletonGraphic but not 'CanvasGroup Compatible' at the Material!\n\nPlease\n"
-			+ "a) enable 'CanvasGroup Compatible' at the Material or\n"
-			+ "b) disable 'Canvas Group Tint Black' at the SkeletonGraphic component under 'Advanced'.\n"
-			+ "You may want to duplicate the 'SkeletonGraphicTintBlack' material and change settings at the duplicate to not affect all instances.";
-		public static readonly string kCanvasGroupTintBlackDisabledMessage =
-			"\nWarning: 'CanvasGroup Compatible' is enabled at the Material but 'Canvas Group Tint Black' is disabled at SkeletonGraphic!\n\nPlease\n"
+		public static readonly string kCanvasGroupCompatibleMaterialRequiredMessage =
+			"\nWarning: 'CanvasGroup Compatible' is enabled at SkeletonGraphic but not at the Material!\n\nPlease\n"
+			+ "a) use a Material with 'CanvasGroup Compatible' enabled or\n"
+			+ "b) disable 'CanvasGroup Compatible' at the SkeletonGraphic component under 'Advanced'.\n"
+			+ "You can find CanvasGroup Compatible 'SkeletonGraphicTintBlack' materials in the\n"
+			+ "'CanvasGroupCompatible' subfolder of 'SkeletonGraphic-PMATexture' or 'SkeletonGraphic-StraightAlphaTexture'.";
+		public static readonly string kCanvasGroupRequiredMessage =
+			"\nWarning: 'CanvasGroup Compatible' is enabled at the Material but disabled at SkeletonGraphic!\n\nPlease\n"
 			+ "a) disable 'CanvasGroup Compatible' at the Material or\n"
-			+ "b) enable 'Canvas Group Tint Black' at the SkeletonGraphic component under 'Advanced'.\n"
-			+ "You may want to duplicate the 'SkeletonGraphicTintBlack' material and change settings at the duplicate to not affect all instances.";
+			+ "b) enable 'CanvasGroup Compatible' at the SkeletonGraphic component under 'Advanced'.\n"
+			+ "You can find CanvasGroup Compatible 'SkeletonGraphicTintBlack' materials in the\n"
+			+ "'CanvasGroupCompatible' subfolder of 'SkeletonGraphic-PMATexture' or 'SkeletonGraphic-StraightAlphaTexture'.";
 		public static readonly string kCanvasGroupCompatiblePMAVertexMessage =
 			"\nWarning: 'CanvasGroup Compatible' is enabled at the Material and 'PMA Vertex Colors' is enabled at SkeletonGraphic!\n\nPlease\n"
 			+ "a) disable 'CanvasGroup Compatible' at the Material or\n"
@@ -126,7 +128,7 @@ namespace Spine.Unity {
 				}
 				if (renderer.tintBlack == false && RequiresTintBlack(material)) {
 					isProblematic = true;
-					errorMessage += kTintBlackMessage;
+					errorMessage += kTintBlackRequiredMessage;
 				}
 			}
 			return isProblematic;
@@ -145,29 +147,26 @@ namespace Spine.Unity {
 					isProblematic = true;
 					errorMessage += kNoSkeletonGraphicMaterialMessage;
 				}
-				if (settings.tintBlack == false && RequiresTintBlack(material)) {
+				bool isTintBlackMaterial = IsSkeletonGraphicTintBlackMaterial(material);
+				if (settings.tintBlack != isTintBlackMaterial) {
 					isProblematic = true;
-					errorMessage += kTintBlackMessage;
+					errorMessage += (settings.tintBlack == false) ?
+						kTintBlackRequiredMessage : kSkeletonGraphicTintBlackMaterialRequiredMessage;
 				}
+
 				if (settings.tintBlack == true && CanvasNotSetupForTintBlack(skeletonGraphic)) {
 					isProblematic = true;
 					errorMessage += kCanvasTintBlackMessage;
 				}
-				if (settings.canvasGroupTintBlack == true && !IsSkeletonGraphicTintBlackMaterial(material)) {
-					isProblematic = true;
-					errorMessage += kNoSkeletonGraphicTintBlackMaterialMessage;
-				}
+
 				bool isCanvasGroupCompatible = IsCanvasGroupCompatible(material);
-				if (settings.canvasGroupTintBlack == true && !isCanvasGroupCompatible) {
+				if (settings.canvasGroupCompatible != isCanvasGroupCompatible) {
 					isProblematic = true;
-					errorMessage += kCanvasGroupCompatibleMessage;
+					errorMessage += (settings.canvasGroupCompatible == false) ?
+						kCanvasGroupRequiredMessage : kCanvasGroupCompatibleMaterialRequiredMessage;
 				}
-				if (settings.tintBlack == true && settings.canvasGroupTintBlack == false && isCanvasGroupCompatible) {
-					isProblematic = true;
-					errorMessage += kCanvasGroupTintBlackDisabledMessage;
-				}
-				if (settings.pmaVertexColors == true && settings.canvasGroupTintBlack == false
-					&& isCanvasGroupCompatible) {
+
+				if (settings.pmaVertexColors == true && settings.canvasGroupCompatible == true && settings.tintBlack == false) {
 					isProblematic = true;
 					errorMessage += kCanvasGroupCompatiblePMAVertexMessage;
 				}
@@ -203,7 +202,6 @@ namespace Spine.Unity {
 			return true;
 		}
 
-
 		public static bool UsesSpineShader (Material material) {
 			return material.shader.name.Contains("Spine/");
 		}