From a04f04d8b052d8d0ee9c77dceff4ecd50a07c603 Mon Sep 17 00:00:00 2001 From: Harald Csaszar Date: Tue, 24 Sep 2019 15:48:59 +0200 Subject: [PATCH 1/5] [unity] Fixed AtlasUtilities texture clone not copying wrap mode or filter mode. Closes #1478. --- .../spine-unity/Utility/AtlasUtilities.cs | 21 +++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/spine-unity/Assets/Spine/Runtime/spine-unity/Utility/AtlasUtilities.cs b/spine-unity/Assets/Spine/Runtime/spine-unity/Utility/AtlasUtilities.cs index 6b57c1ddb..b5371e031 100644 --- a/spine-unity/Assets/Spine/Runtime/spine-unity/Utility/AtlasUtilities.cs +++ b/spine-unity/Assets/Spine/Runtime/spine-unity/Utility/AtlasUtilities.cs @@ -269,7 +269,8 @@ namespace Spine.Unity.AttachmentTools { newTexture.name = newAssetName; // Copy settings if (texturesToPack.Count > 0) { - newTexture.anisoLevel = texturesToPack[0].anisoLevel; + var sourceTexture = texturesToPack[0]; + newTexture.CopyTextureAttributesFrom(sourceTexture); } var rects = newTexture.PackTextures(texturesToPack.ToArray(), padding, maxAtlasSize); @@ -362,7 +363,11 @@ namespace Spine.Unity.AttachmentTools { // Fill a new texture with the collected attachment textures. var newTexture = new Texture2D(maxAtlasSize, maxAtlasSize, textureFormat, mipmaps); newTexture.mipMapBias = AtlasUtilities.DefaultMipmapBias; - newTexture.anisoLevel = texturesToPack[0].anisoLevel; + + if (texturesToPack.Count > 0) { + var sourceTexture = texturesToPack[0]; + newTexture.CopyTextureAttributesFrom(sourceTexture); + } newTexture.name = newName; var rects = newTexture.PackTextures(texturesToPack.ToArray(), padding, maxAtlasSize); @@ -428,6 +433,7 @@ namespace Spine.Unity.AttachmentTools { int width = (int)r.width; int height = (int)r.height; output = new Texture2D(width, height, textureFormat, mipmaps) { name = ar.name }; + output.CopyTextureAttributesFrom(sourceTexture); AtlasUtilities.CopyTexture(sourceTexture, r, output); CachedRegionTextures.Add(ar, output); CachedRegionTexturesList.Add(output); @@ -440,12 +446,14 @@ namespace Spine.Unity.AttachmentTools { var spriteTexture = s.texture; var r = s.textureRect; var newTexture = new Texture2D((int)r.width, (int)r.height, textureFormat, mipmaps); + newTexture.CopyTextureAttributesFrom(spriteTexture); AtlasUtilities.CopyTexture(spriteTexture, r, newTexture); return newTexture; } static Texture2D GetClone (this Texture2D t, TextureFormat textureFormat = SpineTextureFormat, bool mipmaps = UseMipMaps) { var newTexture = new Texture2D((int)t.width, (int)t.height, textureFormat, mipmaps); + newTexture.CopyTextureAttributesFrom(t); AtlasUtilities.CopyTexture(t, new Rect(0, 0, t.width, t.height), newTexture); return newTexture; } @@ -566,6 +574,15 @@ namespace Spine.Unity.AttachmentTools { return material.mainTexture as Texture2D; } + static void CopyTextureAttributesFrom(this Texture2D destination, Texture2D source) { + destination.filterMode = source.filterMode; + destination.anisoLevel = source.anisoLevel; + destination.alphaIsTransparency = source.alphaIsTransparency; + destination.wrapModeU = source.wrapModeU; + destination.wrapModeV = source.wrapModeV; + destination.wrapModeW = source.wrapModeW; + } + static void ApplyPMA (this Texture2D texture, bool applyImmediately = true) { var pixels = texture.GetPixels(); for (int i = 0, n = pixels.Length; i < n; i++) { From 5d34140af5d5d1203831e057c6f88b7eea6934a3 Mon Sep 17 00:00:00 2001 From: Harald Csaszar Date: Tue, 24 Sep 2019 17:07:12 +0200 Subject: [PATCH 2/5] [unity] Fixed Skin created by GetRepackedSkin not copying bones list. Closes #1474. --- spine-csharp/src/ExposedList.cs | 17 +++++++++++++++++ .../spine-unity/Utility/AtlasUtilities.cs | 2 ++ 2 files changed, 19 insertions(+) diff --git a/spine-csharp/src/ExposedList.cs b/spine-csharp/src/ExposedList.cs index 9bec2297a..7d0b69711 100644 --- a/spine-csharp/src/ExposedList.cs +++ b/spine-csharp/src/ExposedList.cs @@ -141,6 +141,23 @@ namespace Spine { } } + // Additional overload provided because ExposedList only implements IEnumerable, + // leading to sub-optimal behavior: It grows multiple times as it assumes not + // to know the final size ahead of insertion. + public void AddRange (ExposedList list) { + CheckCollection(list); + + int collectionCount = list.Count; + if (collectionCount == 0) + return; + + GrowIfNeeded(collectionCount); + list.CopyTo(Items, Count); + Count += collectionCount; + + version++; + } + public void AddRange (IEnumerable collection) { CheckCollection(collection); diff --git a/spine-unity/Assets/Spine/Runtime/spine-unity/Utility/AtlasUtilities.cs b/spine-unity/Assets/Spine/Runtime/spine-unity/Utility/AtlasUtilities.cs index b5371e031..64cb5ead9 100644 --- a/spine-unity/Assets/Spine/Runtime/spine-unity/Utility/AtlasUtilities.cs +++ b/spine-unity/Assets/Spine/Runtime/spine-unity/Utility/AtlasUtilities.cs @@ -324,6 +324,8 @@ namespace Spine.Unity.AttachmentTools { var skinAttachments = o.Attachments; var newSkin = new Skin(newName); + newSkin.bones.AddRange(o.bones); + // Use these to detect and use shared regions. var existingRegions = new Dictionary(); var regionIndexes = new List(); From 1a85c69aa84dffbc4ab55b309f964ceed6af2c7f Mon Sep 17 00:00:00 2001 From: Harald Csaszar Date: Wed, 25 Sep 2019 11:03:54 +0200 Subject: [PATCH 3/5] [unity] Fixed Skin created by GetRepackedSkin not copying constraints list. See #1474. --- .../Assets/Spine/Runtime/spine-unity/Utility/AtlasUtilities.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/spine-unity/Assets/Spine/Runtime/spine-unity/Utility/AtlasUtilities.cs b/spine-unity/Assets/Spine/Runtime/spine-unity/Utility/AtlasUtilities.cs index 64cb5ead9..02c882a3d 100644 --- a/spine-unity/Assets/Spine/Runtime/spine-unity/Utility/AtlasUtilities.cs +++ b/spine-unity/Assets/Spine/Runtime/spine-unity/Utility/AtlasUtilities.cs @@ -325,7 +325,8 @@ namespace Spine.Unity.AttachmentTools { var newSkin = new Skin(newName); newSkin.bones.AddRange(o.bones); - + newSkin.constraints.AddRange(o.constraints); + // Use these to detect and use shared regions. var existingRegions = new Dictionary(); var regionIndexes = new List(); From b3eb4534e7c0a05db5e3a7852639176055758d4c Mon Sep 17 00:00:00 2001 From: Harald Csaszar Date: Wed, 25 Sep 2019 15:26:15 +0200 Subject: [PATCH 4/5] [unity] Fixed incorrect draw order issue when using LWRP and 3+ submeshes with alternating materials. Introduces a new "Fix draw order" parameter at SkeletonRenderer inspector. Closes #1486. --- .../Components/SkeletonRendererInspector.cs | 13 ++++- .../Components/SkeletonRenderer.cs | 47 +++++++++++++++++++ 2 files changed, 58 insertions(+), 2 deletions(-) diff --git a/spine-unity/Assets/Spine/Editor/spine-unity/Editor/Components/SkeletonRendererInspector.cs b/spine-unity/Assets/Spine/Editor/spine-unity/Editor/Components/SkeletonRendererInspector.cs index d049c01a3..3789624b4 100644 --- a/spine-unity/Assets/Spine/Editor/spine-unity/Editor/Components/SkeletonRendererInspector.cs +++ b/spine-unity/Assets/Spine/Editor/spine-unity/Editor/Components/SkeletonRendererInspector.cs @@ -33,6 +33,10 @@ #define NO_PREFAB_MESH #endif +#if UNITY_2018_1_OR_NEWER +#define PER_MATERIAL_PROPERTY_BLOCKS +#endif + #if UNITY_2017_1_OR_NEWER #define BUILT_IN_SPRITE_MASK_COMPONENT #endif @@ -55,7 +59,7 @@ namespace Spine.Unity.Editor { protected SerializedProperty skeletonDataAsset, initialSkinName; protected SerializedProperty initialFlipX, initialFlipY; - protected SerializedProperty singleSubmesh, separatorSlotNames, clearStateOnDisable, immutableTriangles; + protected SerializedProperty singleSubmesh, separatorSlotNames, clearStateOnDisable, immutableTriangles, fixDrawOrder; protected SerializedProperty normals, tangents, zSpacing, pmaVertexColors, tintBlack; // MeshGenerator settings protected SerializedProperty maskInteraction; protected SerializedProperty maskMaterialsNone, maskMaterialsInside, maskMaterialsOutside; @@ -70,7 +74,7 @@ namespace Spine.Unity.Editor { protected bool deleteOutsideMaskMaterialsQueued = false; protected GUIContent SkeletonDataAssetLabel, SkeletonUtilityButtonContent; - protected GUIContent PMAVertexColorsLabel, ClearStateOnDisableLabel, ZSpacingLabel, ImmubleTrianglesLabel, TintBlackLabel, SingleSubmeshLabel; + protected GUIContent PMAVertexColorsLabel, ClearStateOnDisableLabel, ZSpacingLabel, ImmubleTrianglesLabel, TintBlackLabel, SingleSubmeshLabel, FixDrawOrderLabel; protected GUIContent NormalsLabel, TangentsLabel, MaskInteractionLabel; protected GUIContent MaskMaterialsHeadingLabel, MaskMaterialsNoneLabel, MaskMaterialsInsideLabel, MaskMaterialsOutsideLabel; protected GUIContent SetMaterialButtonLabel, ClearMaterialButtonLabel, DeleteMaterialButtonLabel; @@ -116,6 +120,7 @@ namespace Spine.Unity.Editor { TangentsLabel = 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."); 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 also use the default [Spine/Skeleton Tint Black] shader.\n\nIf you only need to tint the whole skeleton and not individual parts, the [Spine/Skeleton Tint] shader is recommended for better efficiency and changing/animating the _Black material property via MaterialPropertyBlock."); SingleSubmeshLabel = new GUIContent("Use Single Submesh", "Simplifies submesh generation by assuming you are only using one Material and need only one submesh. This is will disable multiple materials, render separation, and custom slot materials."); + FixDrawOrderLabel = new GUIContent("Fix Draw Order", "Applies only when 3+ submeshes are used (2+ materials with alternating order, e.g. \"A B A\"). If true, MaterialPropertyBlocks are assigned at each material to prevent aggressive batching of submeshes by e.g. the LWRP renderer, leading to incorrect draw order (e.g. \"A1 B A2\" changed to \"A1A2 B\"). You can disable this parameter when everything is drawn correctly to save the additional performance cost."); MaskInteractionLabel = new GUIContent("Mask Interaction", "SkeletonRenderer's interaction with a Sprite Mask."); MaskMaterialsHeadingLabel = new GUIContent("Mask Interaction Materials", "Materials used for different interaction with sprite masks."); MaskMaterialsNoneLabel = new GUIContent("Normal Materials", "Normal materials used when Mask Interaction is set to None."); @@ -137,6 +142,7 @@ namespace Spine.Unity.Editor { clearStateOnDisable = so.FindProperty("clearStateOnDisable"); tintBlack = so.FindProperty("tintBlack"); singleSubmesh = so.FindProperty("singleSubmesh"); + fixDrawOrder = so.FindProperty("fixDrawOrder"); maskInteraction = so.FindProperty("maskInteraction"); maskMaterialsNone = so.FindProperty("maskMaterials.materialsMaskDisabled"); maskMaterialsInside = so.FindProperty("maskMaterials.materialsInsideMask"); @@ -334,6 +340,9 @@ namespace Spine.Unity.Editor { using (new SpineInspectorUtility.LabelWidthScope()) { // Optimization options if (singleSubmesh != null) EditorGUILayout.PropertyField(singleSubmesh, SingleSubmeshLabel); + #if PER_MATERIAL_PROPERTY_BLOCKS + if (fixDrawOrder != null) EditorGUILayout.PropertyField(fixDrawOrder, FixDrawOrderLabel); + #endif if (immutableTriangles != null) EditorGUILayout.PropertyField(immutableTriangles, ImmubleTrianglesLabel); EditorGUILayout.PropertyField(clearStateOnDisable, ClearStateOnDisableLabel); EditorGUILayout.Space(); diff --git a/spine-unity/Assets/Spine/Runtime/spine-unity/Components/SkeletonRenderer.cs b/spine-unity/Assets/Spine/Runtime/spine-unity/Components/SkeletonRenderer.cs index 06046dcfa..e61906580 100644 --- a/spine-unity/Assets/Spine/Runtime/spine-unity/Components/SkeletonRenderer.cs +++ b/spine-unity/Assets/Spine/Runtime/spine-unity/Components/SkeletonRenderer.cs @@ -31,6 +31,10 @@ #define NEW_PREFAB_SYSTEM #endif +#if UNITY_2018_1_OR_NEWER +#define PER_MATERIAL_PROPERTY_BLOCKS +#endif + #if UNITY_2017_1_OR_NEWER #define BUILT_IN_SPRITE_MASK_COMPONENT #endif @@ -90,6 +94,15 @@ namespace Spine.Unity { /// This disables SkeletonRenderSeparator functionality. public bool singleSubmesh = false; + #if PER_MATERIAL_PROPERTY_BLOCKS + /// Applies only when 3+ submeshes are used (2+ materials with alternating order, e.g. "A B A"). + /// If true, MaterialPropertyBlocks are assigned at each material to prevent aggressive batching of submeshes + /// by e.g. the LWRP renderer, leading to incorrect draw order (e.g. "A1 B A2" changed to "A1A2 B"). + /// You can disable this parameter when everything is drawn correctly to save the additional performance cost. + /// + public bool fixDrawOrder = true; + #endif + /// If true, the mesh generator adds normals to the output mesh. For better performance and reduced memory requirements, use a shader that assumes the desired normal. [UnityEngine.Serialization.FormerlySerializedAs("calculateNormals")] public bool addNormals = false; @@ -422,6 +435,12 @@ namespace Spine.Unity { AssignSpriteMaskMaterials(); } #endif + + #if PER_MATERIAL_PROPERTY_BLOCKS + if (fixDrawOrder && meshRenderer.sharedMaterials.Length > 2) { + SetDrawOrderMaterialPropertyBlocks(); + } + #endif } public void FindAndApplySeparatorSlots (string startsWith, bool clearExistingSeparators = true, bool updateStringArray = false) { @@ -580,5 +599,33 @@ namespace Spine.Unity { #endif // UNITY_EDITOR #endif //#if BUILT_IN_SPRITE_MASK_COMPONENT + + #if PER_MATERIAL_PROPERTY_BLOCKS + private MaterialPropertyBlock reusedPropertyBlock; + public static readonly int SUBMESH_DUMMY_PARAM_ID = Shader.PropertyToID("_Submesh"); + + /// + /// This method was introduced as a workaround for too aggressive submesh draw call batching, + /// leading to incorrect draw order when 3+ materials are used at submeshes in alternating order. + /// Otherwise, e.g. when using Lightweight Render Pipeline, deliberately separated draw calls + /// "A1 B A2" are reordered to "A1A2 B", regardless of batching-related project settings. + /// + private void SetDrawOrderMaterialPropertyBlocks() { + if (reusedPropertyBlock == null) reusedPropertyBlock = new MaterialPropertyBlock(); + + bool hasPerRendererBlock = meshRenderer.HasPropertyBlock(); + if (hasPerRendererBlock) { + meshRenderer.GetPropertyBlock(reusedPropertyBlock); + } + + for (int i = 0; i < meshRenderer.sharedMaterials.Length; ++i) { + if (!hasPerRendererBlock) meshRenderer.GetPropertyBlock(reusedPropertyBlock, i); + // Note: this parameter shall not exist at any shader, then Unity will create separate + // material instances (not in terms of memory cost or leakage). + reusedPropertyBlock.SetFloat(SUBMESH_DUMMY_PARAM_ID, i); + meshRenderer.SetPropertyBlock(reusedPropertyBlock, i); + } + } + #endif } } From f8073dc13d9b9fca9cc85e8028fa5bfe302753ed Mon Sep 17 00:00:00 2001 From: Harald Csaszar Date: Wed, 25 Sep 2019 15:45:37 +0200 Subject: [PATCH 5/5] [unity] Updated changelog with entry for "Fix draw order" parameter. Changed default value to `disabled` to maintain previous behaviour. See #1486. --- CHANGELOG.md | 5 +++++ .../Spine/Runtime/spine-unity/Components/SkeletonRenderer.cs | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b9b9675bf..22f5f773c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -256,6 +256,11 @@ * `WaitForSpineAnimationComplete` now proves an additional `bool includeEndEvent` parameter, defaults to `false` (previous behaviour). * Added a new `WaitForSpineAnimationEnd` yield instruction. * Added a new generic `WaitForSpineAnimation` yield instruction which can be configured to wait for any combination of animation track events. It is now used as base class for `WaitForSpineAnimationComplete` and `WaitForSpineAnimationEnd`. + * Additional **Fix Draw Order** parameter at SkeletonRenderer, defaults to `disabled` (previous behaviour). + Applies only when 3+ submeshes are used (2+ materials with alternating order, e.g. "A B A"). + If true, MaterialPropertyBlocks are assigned at each material to prevent aggressive batching of submeshes + by e.g. the LWRP renderer, leading to incorrect draw order (e.g. "A1 B A2" changed to "A1A2 B"). + You can leave this parameter disabled when everything is drawn correctly to save the additional performance cost. * **Changes of default values** * `SkeletonMecanim`'s `Layer Mix Mode` now defaults to `MixMode.SpineStyle` instead of `MixMode.MixAlways`. diff --git a/spine-unity/Assets/Spine/Runtime/spine-unity/Components/SkeletonRenderer.cs b/spine-unity/Assets/Spine/Runtime/spine-unity/Components/SkeletonRenderer.cs index e61906580..876709338 100644 --- a/spine-unity/Assets/Spine/Runtime/spine-unity/Components/SkeletonRenderer.cs +++ b/spine-unity/Assets/Spine/Runtime/spine-unity/Components/SkeletonRenderer.cs @@ -100,7 +100,7 @@ namespace Spine.Unity { /// by e.g. the LWRP renderer, leading to incorrect draw order (e.g. "A1 B A2" changed to "A1A2 B"). /// You can disable this parameter when everything is drawn correctly to save the additional performance cost. /// - public bool fixDrawOrder = true; + public bool fixDrawOrder = false; #endif /// If true, the mesh generator adds normals to the output mesh. For better performance and reduced memory requirements, use a shader that assumes the desired normal.