Эх сурвалжийг харах

[unity] Detecting and reporting more incorrect shader/component configurations via Inspector warning boxes and log output. Closes #1765.

Harald Csaszar 5 жил өмнө
parent
commit
75efe44fab

+ 1 - 0
CHANGELOG.md

@@ -242,6 +242,7 @@
   * Now providing a `Canvas Group Tint Black` parameter at the `SkeletonGraphic` Inspector in the `Advanced` section. When using the `Spine/SkeletonGraphic Tint Black` shader you can enable this parameter to receive proper blending results when using `Additive` blend mode under a `CanvasGroup`. Be sure to also have the parameter `CanvasGroup Compatible` enabled at the shader. Note that the normal `Spine/SkeletonGraphic` does not support `Additive` blend mode at a `CanvasGroup`, as it requires additional shader channels to work.
   * Added `Mix and Match Skins` example scene to demonstrate how the 3.8 Skin API and combining skins can be used for a wardrobe and equipment use case.
   * Spine Timeline Extensions: Added `Hold Previous` parameter at `SpineAnimationStateClip`.
+  * Added more warning messages at incompatible SkeletonRenderer/SkeletonGraphic Component vs Material settings. They appear both as an info box in the Inspector as well as upon initialization in the Console log window. The Inspector box warnings can be disabled via `Edit - Preferences - Spine`.
 
 * **Changes of default values**
   * `SkeletonMecanim`'s `Layer Mix Mode` now defaults to `MixMode.MixNext` instead of `MixMode.MixAlways`.

+ 6 - 0
spine-unity/Assets/Spine/Editor/spine-unity/Editor/Components/SkeletonGraphicInspector.cs

@@ -162,6 +162,12 @@ namespace Spine.Unity.Editor {
 				return;
 			}
 
+			string errorMessage = null;
+			if (SpineEditorUtilities.Preferences.componentMaterialWarning &&
+				MaterialChecks.IsMaterialSetupProblematic(thisSkeletonGraphic, ref errorMessage)) {
+				EditorGUILayout.HelpBox(errorMessage, MessageType.Error, true);
+			}
+
 			bool isSingleRendererOnly = (!allowMultipleCanvasRenderers.hasMultipleDifferentValues && allowMultipleCanvasRenderers.boolValue == false);
 			bool isSeparationEnabledButNotMultipleRenderers =
 				 isSingleRendererOnly && (!enableSeparatorSlots.hasMultipleDifferentValues && enableSeparatorSlots.boolValue == true);

+ 2 - 1
spine-unity/Assets/Spine/Editor/spine-unity/Editor/Components/SkeletonRendererInspector.cs

@@ -289,7 +289,8 @@ namespace Spine.Unity.Editor {
 				return;
 
 			string errorMessage = null;
-			if (MaterialChecks.IsMaterialSetupProblematic((SkeletonRenderer)this.target, ref errorMessage)) {
+			if (SpineEditorUtilities.Preferences.componentMaterialWarning &&
+				MaterialChecks.IsMaterialSetupProblematic((SkeletonRenderer)this.target, ref errorMessage)) {
 				EditorGUILayout.HelpBox(errorMessage, MessageType.Error, true);
 			}
 

+ 16 - 9
spine-unity/Assets/Spine/Editor/spine-unity/Editor/Utility/Preferences.cs

@@ -130,6 +130,9 @@ namespace Spine.Unity.Editor {
 			const string TEXTUREIMPORTER_WARNING_KEY = "SPINE_TEXTUREIMPORTER_WARNING";
 			public static bool textureImporterWarning = SpinePreferences.DEFAULT_TEXTUREIMPORTER_WARNING;
 
+			const string COMPONENTMATERIAL_WARNING_KEY = "SPINE_COMPONENTMATERIAL_WARNING";
+			public static bool componentMaterialWarning = SpinePreferences.DEFAULT_COMPONENTMATERIAL_WARNING;
+
 			public const float DEFAULT_MIPMAPBIAS = SpinePreferences.DEFAULT_MIPMAPBIAS;
 
 			public const string SCENE_ICONS_SCALE_KEY = "SPINE_SCENE_ICONS_SCALE";
@@ -162,6 +165,7 @@ namespace Spine.Unity.Editor {
 				mecanimEventIncludeFolderName = EditorPrefs.GetBool(MECANIM_EVENT_INCLUDE_FOLDERNAME_KEY, SpinePreferences.DEFAULT_MECANIM_EVENT_INCLUDE_FOLDERNAME);
 				atlasTxtImportWarning = EditorPrefs.GetBool(ATLASTXT_WARNING_KEY, SpinePreferences.DEFAULT_ATLASTXT_WARNING);
 				textureImporterWarning = EditorPrefs.GetBool(TEXTUREIMPORTER_WARNING_KEY, SpinePreferences.DEFAULT_TEXTUREIMPORTER_WARNING);
+				componentMaterialWarning = EditorPrefs.GetBool(COMPONENTMATERIAL_WARNING_KEY, SpinePreferences.DEFAULT_COMPONENTMATERIAL_WARNING);
 				timelineUseBlendDuration = EditorPrefs.GetBool(TIMELINE_USE_BLEND_DURATION_KEY, SpinePreferences.DEFAULT_TIMELINE_USE_BLEND_DURATION);
 				handleScale = EditorPrefs.GetFloat(SCENE_ICONS_SCALE_KEY, SpinePreferences.DEFAULT_SCENE_ICONS_SCALE);
 				preferencesLoaded = true;
@@ -180,6 +184,7 @@ namespace Spine.Unity.Editor {
 				newPreferences.mecanimEventIncludeFolderName = EditorPrefs.GetBool(MECANIM_EVENT_INCLUDE_FOLDERNAME_KEY, SpinePreferences.DEFAULT_MECANIM_EVENT_INCLUDE_FOLDERNAME);
 				newPreferences.atlasTxtImportWarning = EditorPrefs.GetBool(ATLASTXT_WARNING_KEY, SpinePreferences.DEFAULT_ATLASTXT_WARNING);
 				newPreferences.textureImporterWarning = EditorPrefs.GetBool(TEXTUREIMPORTER_WARNING_KEY, SpinePreferences.DEFAULT_TEXTUREIMPORTER_WARNING);
+				newPreferences.componentMaterialWarning = EditorPrefs.GetBool(COMPONENTMATERIAL_WARNING_KEY, SpinePreferences.DEFAULT_COMPONENTMATERIAL_WARNING);
 				newPreferences.timelineUseBlendDuration = EditorPrefs.GetBool(TIMELINE_USE_BLEND_DURATION_KEY, SpinePreferences.DEFAULT_TIMELINE_USE_BLEND_DURATION);
 				newPreferences.handleScale = EditorPrefs.GetFloat(SCENE_ICONS_SCALE_KEY, SpinePreferences.DEFAULT_SCENE_ICONS_SCALE);
 			}
@@ -196,6 +201,7 @@ namespace Spine.Unity.Editor {
 				EditorPrefs.SetBool(MECANIM_EVENT_INCLUDE_FOLDERNAME_KEY, preferences.mecanimEventIncludeFolderName);
 				EditorPrefs.SetBool(ATLASTXT_WARNING_KEY, preferences.atlasTxtImportWarning);
 				EditorPrefs.SetBool(TEXTUREIMPORTER_WARNING_KEY, preferences.textureImporterWarning);
+				EditorPrefs.SetBool(COMPONENTMATERIAL_WARNING_KEY, preferences.componentMaterialWarning);
 				EditorPrefs.SetBool(TIMELINE_USE_BLEND_DURATION_KEY, preferences.timelineUseBlendDuration);
 				EditorPrefs.SetFloat(SCENE_ICONS_SCALE_KEY, preferences.handleScale);
 			}
@@ -210,11 +216,11 @@ namespace Spine.Unity.Editor {
 				showHierarchyIcons = EditorGUILayout.Toggle(new GUIContent("Show Hierarchy Icons", "Show relevant icons on GameObjects with Spine Components on them. Disable this if you have large, complex scenes."), showHierarchyIcons);
 				if (EditorGUI.EndChangeCheck()) {
 					EditorPrefs.SetBool(SHOW_HIERARCHY_ICONS_KEY, showHierarchyIcons);
-				#if NEWPLAYMODECALLBACKS
+#if NEWPLAYMODECALLBACKS
 					HierarchyHandler.IconsOnPlaymodeStateChanged(PlayModeStateChange.EnteredEditMode);
-				#else
+#else
 					HierarchyHandler.IconsOnPlaymodeStateChanged();
-				#endif
+#endif
 				}
 
 				BoolPrefsField(ref autoReloadSceneSkeletons, AUTO_RELOAD_SCENESKELETONS_KEY, new GUIContent("Auto-reload scene components", "Reloads Skeleton components in the scene whenever their SkeletonDataAsset is modified. This makes it so changes in the SkeletonDataAsset inspector are immediately reflected. This may be slow when your scenes have large numbers of SkeletonRenderers or SkeletonGraphic."));
@@ -247,6 +253,7 @@ namespace Spine.Unity.Editor {
 				{
 					SpineEditorUtilities.BoolPrefsField(ref atlasTxtImportWarning, ATLASTXT_WARNING_KEY, new GUIContent("Atlas Extension Warning", "Log a warning and recommendation whenever a `.atlas` file is found."));
 					SpineEditorUtilities.BoolPrefsField(ref textureImporterWarning, TEXTUREIMPORTER_WARNING_KEY, new GUIContent("Texture Settings Warning", "Log a warning and recommendation whenever Texture Import Settings are detected that could lead to undesired effects, e.g. white border artifacts."));
+					SpineEditorUtilities.BoolPrefsField(ref componentMaterialWarning, COMPONENTMATERIAL_WARNING_KEY, new GUIContent("Component & Material Warning", "Log a warning and recommendation whenever Component and Material settings are not compatible."));
 				}
 
 				EditorGUILayout.Space();
@@ -278,11 +285,11 @@ namespace Spine.Unity.Editor {
 					}
 				}
 
-				#if SPINE_TK2D_DEFINE
+#if SPINE_TK2D_DEFINE
 				bool isTK2DDefineSet = true;
-				#else
+#else
 				bool isTK2DDefineSet = false;
-				#endif
+#endif
 				bool isTK2DAllowed = SpineTK2DEditorUtility.IsTK2DAllowed;
 				if (SpineTK2DEditorUtility.IsTK2DInstalled() || isTK2DDefineSet) {
 					GUILayout.Space(20);
@@ -294,12 +301,12 @@ namespace Spine.Unity.Editor {
 						if (GUILayout.Button("Disable", GUILayout.Width(64)))
 							SpineTK2DEditorUtility.DisableTK2D();
 					}
-					#if !SPINE_TK2D_DEFINE
+#if !SPINE_TK2D_DEFINE
 					if (!isTK2DAllowed) {
 						EditorGUILayout.LabelField("To allow TK2D support, please modify line 67 in", EditorStyles.boldLabel);
 						EditorGUILayout.LabelField("Spine/Editor/spine-unity/Editor/Util./BuildSettings.cs", EditorStyles.boldLabel);
 					}
-					#endif
+#endif
 				}
 
 				GUILayout.Space(20);
@@ -308,7 +315,7 @@ namespace Spine.Unity.Editor {
 					SpineEditorUtilities.BoolPrefsField(ref timelineUseBlendDuration, TIMELINE_USE_BLEND_DURATION_KEY, new GUIContent("Use Blend Duration", "When enabled, MixDuration will be synced with timeline clip transition duration 'Ease In Duration'."));
 				}
 			}
-		#endif // !NEW_PREFERENCES_SETTINGS_PROVIDER
+#endif // !NEW_PREFERENCES_SETTINGS_PROVIDER
 		}
 
 		static void BoolPrefsField (ref bool currentValue, string editorPrefsKey, GUIContent label) {

+ 4 - 0
spine-unity/Assets/Spine/Editor/spine-unity/Editor/Windows/SpinePreferences.cs

@@ -84,6 +84,9 @@ namespace Spine.Unity.Editor {
 		internal const bool DEFAULT_TEXTUREIMPORTER_WARNING = true;
 		public bool textureImporterWarning = DEFAULT_TEXTUREIMPORTER_WARNING;
 
+		internal const bool DEFAULT_COMPONENTMATERIAL_WARNING = true;
+		public bool componentMaterialWarning = DEFAULT_COMPONENTMATERIAL_WARNING;
+
 		public const float DEFAULT_MIPMAPBIAS = -0.5f;
 
 		public const bool DEFAULT_AUTO_RELOAD_SCENESKELETONS = true;
@@ -165,6 +168,7 @@ namespace Spine.Unity.Editor {
 				{
 					EditorGUILayout.PropertyField(settings.FindProperty("atlasTxtImportWarning"), new GUIContent("Atlas Extension Warning", "Log a warning and recommendation whenever a `.atlas` file is found."));
 					EditorGUILayout.PropertyField(settings.FindProperty("textureImporterWarning"), new GUIContent("Texture Settings Warning", "Log a warning and recommendation whenever Texture Import Settings are detected that could lead to undesired effects, e.g. white border artifacts."));
+					EditorGUILayout.PropertyField(settings.FindProperty("componentMaterialWarning"), new GUIContent("Component & Material Warning", "Log a warning and recommendation whenever Component and Material settings are not compatible."));
 				}
 
 				EditorGUILayout.Space();

+ 127 - 7
spine-unity/Assets/Spine/Runtime/spine-unity/Utility/MaterialChecks.cs

@@ -39,24 +39,58 @@ namespace Spine.Unity {
 		static readonly int STRAIGHT_ALPHA_PARAM_ID = Shader.PropertyToID("_StraightAlphaInput");
 		static readonly string ALPHAPREMULTIPLY_ON_KEYWORD = "_ALPHAPREMULTIPLY_ON";
 		static readonly string STRAIGHT_ALPHA_KEYWORD = "_STRAIGHT_ALPHA_INPUT";
+		static readonly string[] FIXED_NORMALS_KEYWORDS = {
+			"_FIXED_NORMALS_VIEWSPACE",
+			"_FIXED_NORMALS_VIEWSPACE_BACKFACE",
+			"_FIXED_NORMALS_MODELSPACE",
+			"_FIXED_NORMALS_MODELSPACE_BACKFACE",
+			"_FIXED_NORMALS_WORLDSPACE"
+			};
+		static readonly string NORMALMAP_KEYWORD = "_NORMALMAP";
+		static readonly string CANVAS_GROUP_COMPATIBLE_KEYWORD = "_CANVAS_GROUP_COMPATIBLE";
 
 		public static readonly string kPMANotSupportedLinearMessage =
-			"Warning: Premultiply-alpha atlas textures not supported in Linear color space!\n\nPlease\n"
+			"\nWarning: Premultiply-alpha atlas textures not supported in Linear color space!\n\nPlease\n"
 			+ "a) re-export atlas as straight alpha texture with 'premultiply alpha' unchecked\n"
 			+ "   (if you have already done this, please set the 'Straight Alpha Texture' Material parameter to 'true') or\n"
 			+ "b) switch to Gamma color space via\nProject Settings - Player - Other Settings - Color Space.\n";
 		public static readonly string kZSpacingRequiredMessage =
-			"Warning: Z Spacing required on selected shader! Otherwise you will receive incorrect results.\n\nPlease\n"
+			"\nWarning: Z Spacing required on selected shader! Otherwise you will receive incorrect results.\n\nPlease\n"
 			+ "1) make sure at least minimal 'Z Spacing' is set at the SkeletonRenderer/SkeletonAnimation component under 'Advanced' and\n"
 			+ "2) ensure that the skeleton has overlapping parts on different Z depth. You can adjust this in Spine via draw order.\n";
 		public static readonly string kZSpacingRecommendedMessage =
-			"Warning: Z Spacing recommended on selected shader configuration!\n\nPlease\n"
+			"\nWarning: Z Spacing recommended on selected shader configuration!\n\nPlease\n"
 			+ "1) make sure at least minimal 'Z Spacing' is set at the SkeletonRenderer/SkeletonAnimation component under 'Advanced' and\n"
 			+ "2) ensure that the skeleton has overlapping parts on different Z depth. You can adjust this in Spine via draw order.\n";
-		public static readonly string kAddNormalsRequiredMessage =
-			"Warning: 'Add Normals' required on URP shader to receive shadows!\n\nPlease\n"
+		public static readonly string kAddNormalsMessage =
+			"\nWarning: 'Add Normals' required when not using 'Fixed Normals'!\n\nPlease\n"
 			+ "a) enable 'Add Normals' at the SkeletonRenderer/SkeletonAnimation component under 'Advanced' or\n"
-			+ "b) disable 'Receive Shadows' at the Material.";
+			+ "b) enable 'Fixed Normals' at the Material.\n";
+		public static readonly string kAddNormalsRequiredForURPShadowsMessage =
+			"\nWarning: 'Add Normals' required on URP shader to receive shadows!\n\nPlease\n"
+			+ "a) enable 'Add Normals' at the SkeletonRenderer/SkeletonAnimation component under 'Advanced' or\n"
+			+ "b) disable 'Receive Shadows' at the Material.\n";
+		public static readonly string kSolveTangentsMessage =
+			"\nWarning: 'Solve Tangents' required when using a Normal Map!\n\nPlease\n"
+			+ "a) enable 'Solve Tangents' at the SkeletonRenderer/SkeletonAnimation component under 'Advanced' or\n"
+			+ "b) clear the 'Normal Map' parameter at the Material.\n";
+		public static readonly string kNoSkeletonGraphicMaterialMessage =
+			"\nWarning: Normal non-UI shaders other than 'Spine/SkeletonGraphic *' are not compatible with 'SkeletonGraphic' components! "
+			+ "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 kTintBlackMessage =
+			"\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 'SkeletonGraphicDefault' material and change settings at the duplicate to not affect all instances.";
 
 		public static bool IsMaterialSetupProblematic (SkeletonRenderer renderer, ref string errorMessage) {
 			var materials = renderer.GetComponent<Renderer>().sharedMaterials;
@@ -67,9 +101,51 @@ namespace Spine.Unity {
 				if (renderer.zSpacing == 0) {
 					isProblematic |= IsZSpacingRequired(material, ref errorMessage);
 				}
+				if (renderer.addNormals == false && RequiresMeshNormals(material)) {
+					isProblematic = true;
+					errorMessage += kAddNormalsMessage;
+				}
+				if (renderer.calculateTangents == false && RequiresTangents(material)) {
+					isProblematic = true;
+					errorMessage += kSolveTangentsMessage;
+				}
+				if (renderer.tintBlack == false && RequiresTintBlack(material)) {
+					isProblematic = true;
+					errorMessage += kTintBlackMessage;
+				}
 				if (IsURP3DMaterial(material) && !AreShadowsDisabled(material) && renderer.addNormals == false) {
 					isProblematic = true;
-					errorMessage += kAddNormalsRequiredMessage;
+					errorMessage += kAddNormalsRequiredForURPShadowsMessage;
+				}
+			}
+			return isProblematic;
+		}
+
+		public static bool IsMaterialSetupProblematic(SkeletonGraphic skeletonGraphic, ref string errorMessage)
+		{
+			var material = skeletonGraphic.material;
+			bool isProblematic = false;
+			if (material) {
+				isProblematic |= IsMaterialSetupProblematic(material, ref errorMessage);
+				var settings = skeletonGraphic.MeshGenerator.settings;
+				if (settings.zSpacing == 0) {
+					isProblematic |= IsZSpacingRequired(material, ref errorMessage);
+				}
+				if (IsSpineNonSkeletonGraphicMaterial(material)) {
+					isProblematic = true;
+					errorMessage += kNoSkeletonGraphicMaterialMessage;
+				}
+				if (settings.tintBlack == false && RequiresTintBlack(material)) {
+					isProblematic = true;
+					errorMessage += kTintBlackMessage;
+				}
+				if (settings.tintBlack == true && CanvasNotSetupForTintBlack(skeletonGraphic)) {
+					isProblematic = true;
+					errorMessage += kCanvasTintBlackMessage;
+				}
+				if (settings.canvasGroupTintBlack == true && !IsCanvasGroupCompatible(material)) {
+					isProblematic = true;
+					errorMessage += kCanvasGroupCompatibleMessage;
 				}
 			}
 			return isProblematic;
@@ -181,9 +257,53 @@ namespace Spine.Unity {
 			return material.shader.name.Contains("Universal Render Pipeline/Spine");
 		}
 
+		static bool IsSpineNonSkeletonGraphicMaterial (Material material) {
+			return material.shader.name.Contains("Spine") && !material.shader.name.Contains("SkeletonGraphic");
+		}
+
 		static bool AreShadowsDisabled (Material material) {
 			return material.IsKeywordEnabled("_RECEIVE_SHADOWS_OFF");
 		}
+
+		static bool RequiresMeshNormals (Material material) {
+			bool anyFixedNormalSet = false;
+			foreach (string fixedNormalKeyword in FIXED_NORMALS_KEYWORDS) {
+				if (material.IsKeywordEnabled(fixedNormalKeyword)) {
+					anyFixedNormalSet = true;
+					break;
+				}
+			}
+			bool isShaderWithMeshNormals =
+				material.shader.name.Contains("Spine/Sprite/Pixel Lit") ||
+				material.shader.name.Contains("Spine/Sprite/Vertex Lit") ||
+				material.shader.name.Contains("2D/Spine/Sprite") || // covers both URP and LWRP
+				material.shader.name.Contains("Pipeline/Spine/Sprite"); // covers both URP and LWRP
+			return isShaderWithMeshNormals && !anyFixedNormalSet;
+		}
+
+		static bool RequiresTintBlack (Material material) {
+			bool isTintBlackShader =
+				material.shader.name.Contains("Spine") &&
+				material.shader.name.Contains("Tint Black");
+			return isTintBlackShader;
+		}
+
+		static bool RequiresTangents (Material material) {
+			return material.IsKeywordEnabled(NORMALMAP_KEYWORD);
+		}
+		static bool IsCanvasGroupCompatible (Material material) {
+			return material.IsKeywordEnabled(CANVAS_GROUP_COMPATIBLE_KEYWORD);
+		}
+
+		static bool CanvasNotSetupForTintBlack (SkeletonGraphic skeletonGraphic) {
+			Canvas canvas = skeletonGraphic.canvas;
+			if (!canvas)
+				return false;
+			var requiredChannels =
+				AdditionalCanvasShaderChannels.TexCoord1 |
+				AdditionalCanvasShaderChannels.TexCoord2;
+			return !canvas.additionalShaderChannels.HasFlag(requiredChannels); // HasFlag returns true if both are set.
+		}
 	}
 }